| 1 | # API Documentation
|
|---|
| 2 |
|
|---|
| 3 | *Please use only this documented API when working with the parser. Methods
|
|---|
| 4 | not documented here are subject to change at any point.*
|
|---|
| 5 |
|
|---|
| 6 | ## `parser` function
|
|---|
| 7 |
|
|---|
| 8 | This is the module's main entry point.
|
|---|
| 9 |
|
|---|
| 10 | ```js
|
|---|
| 11 | const parser = require('postcss-selector-parser');
|
|---|
| 12 | ```
|
|---|
| 13 |
|
|---|
| 14 | ### `parser([transform], [options])`
|
|---|
| 15 |
|
|---|
| 16 | Creates a new `processor` instance
|
|---|
| 17 |
|
|---|
| 18 | ```js
|
|---|
| 19 | const processor = parser();
|
|---|
| 20 | ```
|
|---|
| 21 |
|
|---|
| 22 | Or, with optional transform function
|
|---|
| 23 |
|
|---|
| 24 | ```js
|
|---|
| 25 | const transform = selectors => {
|
|---|
| 26 | selectors.walkUniversals(selector => {
|
|---|
| 27 | selector.remove();
|
|---|
| 28 | });
|
|---|
| 29 | };
|
|---|
| 30 |
|
|---|
| 31 | const processor = parser(transform)
|
|---|
| 32 |
|
|---|
| 33 | // Example
|
|---|
| 34 | const result = processor.processSync('*.class');
|
|---|
| 35 | // => .class
|
|---|
| 36 | ```
|
|---|
| 37 |
|
|---|
| 38 | [See processor documentation](#processor)
|
|---|
| 39 |
|
|---|
| 40 | Arguments:
|
|---|
| 41 |
|
|---|
| 42 | * `transform (function)`: Provide a function to work with the parsed AST.
|
|---|
| 43 | * `options (object)`: Provide default options for all calls on the returned `Processor`.
|
|---|
| 44 |
|
|---|
| 45 | ### `parser.attribute([props])`
|
|---|
| 46 |
|
|---|
| 47 | Creates a new attribute selector.
|
|---|
| 48 |
|
|---|
| 49 | ```js
|
|---|
| 50 | parser.attribute({attribute: 'href'});
|
|---|
| 51 | // => [href]
|
|---|
| 52 | ```
|
|---|
| 53 |
|
|---|
| 54 | Arguments:
|
|---|
| 55 |
|
|---|
| 56 | * `props (object)`: The new node's properties.
|
|---|
| 57 |
|
|---|
| 58 | ### `parser.className([props])`
|
|---|
| 59 |
|
|---|
| 60 | Creates a new class selector.
|
|---|
| 61 |
|
|---|
| 62 | ```js
|
|---|
| 63 | parser.className({value: 'button'});
|
|---|
| 64 | // => .button
|
|---|
| 65 | ```
|
|---|
| 66 |
|
|---|
| 67 | Arguments:
|
|---|
| 68 |
|
|---|
| 69 | * `props (object)`: The new node's properties.
|
|---|
| 70 |
|
|---|
| 71 | ### `parser.combinator([props])`
|
|---|
| 72 |
|
|---|
| 73 | Creates a new selector combinator.
|
|---|
| 74 |
|
|---|
| 75 | ```js
|
|---|
| 76 | parser.combinator({value: '+'});
|
|---|
| 77 | // => +
|
|---|
| 78 | ```
|
|---|
| 79 |
|
|---|
| 80 | Arguments:
|
|---|
| 81 |
|
|---|
| 82 | * `props (object)`: The new node's properties.
|
|---|
| 83 |
|
|---|
| 84 | Notes:
|
|---|
| 85 | * **Descendant Combinators** The value of descendant combinators created by the
|
|---|
| 86 | parser always just a single space (`" "`). For descendant selectors with no
|
|---|
| 87 | comments, additional space is now stored in `node.spaces.before`. Depending
|
|---|
| 88 | on the location of comments, additional spaces may be stored in
|
|---|
| 89 | `node.raws.spaces.before`, `node.raws.spaces.after`, or `node.raws.value`.
|
|---|
| 90 | * **Named Combinators** Although, nonstandard and unlikely to ever become a standard,
|
|---|
| 91 | named combinators like `/deep/` and `/for/` are parsed as combinators. The
|
|---|
| 92 | `node.value` is name after being unescaped and normalized as lowercase. The
|
|---|
| 93 | original value for the combinator name is stored in `node.raws.value`.
|
|---|
| 94 |
|
|---|
| 95 |
|
|---|
| 96 | ### `parser.comment([props])`
|
|---|
| 97 |
|
|---|
| 98 | Creates a new comment.
|
|---|
| 99 |
|
|---|
| 100 | ```js
|
|---|
| 101 | parser.comment({value: '/* Affirmative, Dave. I read you. */'});
|
|---|
| 102 | // => /* Affirmative, Dave. I read you. */
|
|---|
| 103 | ```
|
|---|
| 104 |
|
|---|
| 105 | Arguments:
|
|---|
| 106 |
|
|---|
| 107 | * `props (object)`: The new node's properties.
|
|---|
| 108 |
|
|---|
| 109 | ### `parser.id([props])`
|
|---|
| 110 |
|
|---|
| 111 | Creates a new id selector.
|
|---|
| 112 |
|
|---|
| 113 | ```js
|
|---|
| 114 | parser.id({value: 'search'});
|
|---|
| 115 | // => #search
|
|---|
| 116 | ```
|
|---|
| 117 |
|
|---|
| 118 | Arguments:
|
|---|
| 119 |
|
|---|
| 120 | * `props (object)`: The new node's properties.
|
|---|
| 121 |
|
|---|
| 122 | ### `parser.nesting([props])`
|
|---|
| 123 |
|
|---|
| 124 | Creates a new nesting selector.
|
|---|
| 125 |
|
|---|
| 126 | ```js
|
|---|
| 127 | parser.nesting();
|
|---|
| 128 | // => &
|
|---|
| 129 | ```
|
|---|
| 130 |
|
|---|
| 131 | Arguments:
|
|---|
| 132 |
|
|---|
| 133 | * `props (object)`: The new node's properties.
|
|---|
| 134 |
|
|---|
| 135 | ### `parser.pseudo([props])`
|
|---|
| 136 |
|
|---|
| 137 | Creates a new pseudo selector.
|
|---|
| 138 |
|
|---|
| 139 | ```js
|
|---|
| 140 | parser.pseudo({value: '::before'});
|
|---|
| 141 | // => ::before
|
|---|
| 142 | ```
|
|---|
| 143 |
|
|---|
| 144 | Arguments:
|
|---|
| 145 |
|
|---|
| 146 | * `props (object)`: The new node's properties.
|
|---|
| 147 |
|
|---|
| 148 | ### `parser.root([props])`
|
|---|
| 149 |
|
|---|
| 150 | Creates a new root node.
|
|---|
| 151 |
|
|---|
| 152 | ```js
|
|---|
| 153 | parser.root();
|
|---|
| 154 | // => (empty)
|
|---|
| 155 | ```
|
|---|
| 156 |
|
|---|
| 157 | Arguments:
|
|---|
| 158 |
|
|---|
| 159 | * `props (object)`: The new node's properties.
|
|---|
| 160 |
|
|---|
| 161 | ### `parser.selector([props])`
|
|---|
| 162 |
|
|---|
| 163 | Creates a new selector node.
|
|---|
| 164 |
|
|---|
| 165 | ```js
|
|---|
| 166 | parser.selector();
|
|---|
| 167 | // => (empty)
|
|---|
| 168 | ```
|
|---|
| 169 |
|
|---|
| 170 | Arguments:
|
|---|
| 171 |
|
|---|
| 172 | * `props (object)`: The new node's properties.
|
|---|
| 173 |
|
|---|
| 174 | ### `parser.string([props])`
|
|---|
| 175 |
|
|---|
| 176 | Creates a new string node.
|
|---|
| 177 |
|
|---|
| 178 | ```js
|
|---|
| 179 | parser.string();
|
|---|
| 180 | // => (empty)
|
|---|
| 181 | ```
|
|---|
| 182 |
|
|---|
| 183 | Arguments:
|
|---|
| 184 |
|
|---|
| 185 | * `props (object)`: The new node's properties.
|
|---|
| 186 |
|
|---|
| 187 | ### `parser.tag([props])`
|
|---|
| 188 |
|
|---|
| 189 | Creates a new tag selector.
|
|---|
| 190 |
|
|---|
| 191 | ```js
|
|---|
| 192 | parser.tag({value: 'button'});
|
|---|
| 193 | // => button
|
|---|
| 194 | ```
|
|---|
| 195 |
|
|---|
| 196 | Arguments:
|
|---|
| 197 |
|
|---|
| 198 | * `props (object)`: The new node's properties.
|
|---|
| 199 |
|
|---|
| 200 | ### `parser.universal([props])`
|
|---|
| 201 |
|
|---|
| 202 | Creates a new universal selector.
|
|---|
| 203 |
|
|---|
| 204 | ```js
|
|---|
| 205 | parser.universal();
|
|---|
| 206 | // => *
|
|---|
| 207 | ```
|
|---|
| 208 |
|
|---|
| 209 | Arguments:
|
|---|
| 210 |
|
|---|
| 211 | * `props (object)`: The new node's properties.
|
|---|
| 212 |
|
|---|
| 213 | ## Node types
|
|---|
| 214 |
|
|---|
| 215 | ### `node.type`
|
|---|
| 216 |
|
|---|
| 217 | A string representation of the selector type. It can be one of the following;
|
|---|
| 218 | `attribute`, `class`, `combinator`, `comment`, `id`, `nesting`, `pseudo`,
|
|---|
| 219 | `root`, `selector`, `string`, `tag`, or `universal`. Note that for convenience,
|
|---|
| 220 | these constants are exposed on the main `parser` as uppercased keys. So for
|
|---|
| 221 | example you can get `id` by querying `parser.ID`.
|
|---|
| 222 |
|
|---|
| 223 | ```js
|
|---|
| 224 | parser.attribute({attribute: 'href'}).type;
|
|---|
| 225 | // => 'attribute'
|
|---|
| 226 | ```
|
|---|
| 227 |
|
|---|
| 228 | ### `node.parent`
|
|---|
| 229 |
|
|---|
| 230 | Returns the parent node.
|
|---|
| 231 |
|
|---|
| 232 | ```js
|
|---|
| 233 | root.nodes[0].parent === root;
|
|---|
| 234 | ```
|
|---|
| 235 |
|
|---|
| 236 | ### `node.toString()`, `String(node)`, or `'' + node`
|
|---|
| 237 |
|
|---|
| 238 | Returns a string representation of the node.
|
|---|
| 239 |
|
|---|
| 240 | ```js
|
|---|
| 241 | const id = parser.id({value: 'search'});
|
|---|
| 242 | console.log(String(id));
|
|---|
| 243 | // => #search
|
|---|
| 244 | ```
|
|---|
| 245 |
|
|---|
| 246 | ### `node.next()` & `node.prev()`
|
|---|
| 247 |
|
|---|
| 248 | Returns the next/previous child of the parent node.
|
|---|
| 249 |
|
|---|
| 250 | ```js
|
|---|
| 251 | const next = id.next();
|
|---|
| 252 | if (next && next.type !== 'combinator') {
|
|---|
| 253 | throw new Error('Qualified IDs are not allowed!');
|
|---|
| 254 | }
|
|---|
| 255 | ```
|
|---|
| 256 |
|
|---|
| 257 | ### `node.replaceWith(node)`
|
|---|
| 258 |
|
|---|
| 259 | Replace a node with another.
|
|---|
| 260 |
|
|---|
| 261 | ```js
|
|---|
| 262 | const attr = selectors.first.first;
|
|---|
| 263 | const className = parser.className({value: 'test'});
|
|---|
| 264 | attr.replaceWith(className);
|
|---|
| 265 | ```
|
|---|
| 266 |
|
|---|
| 267 | Arguments:
|
|---|
| 268 |
|
|---|
| 269 | * `node`: The node to substitute the original with.
|
|---|
| 270 |
|
|---|
| 271 | ### `node.remove()`
|
|---|
| 272 |
|
|---|
| 273 | Removes the node from its parent node.
|
|---|
| 274 |
|
|---|
| 275 | ```js
|
|---|
| 276 | if (node.type === 'id') {
|
|---|
| 277 | node.remove();
|
|---|
| 278 | }
|
|---|
| 279 | ```
|
|---|
| 280 |
|
|---|
| 281 | ### `node.clone([opts])`
|
|---|
| 282 |
|
|---|
| 283 | Returns a copy of a node, detached from any parent containers that the
|
|---|
| 284 | original might have had.
|
|---|
| 285 |
|
|---|
| 286 | ```js
|
|---|
| 287 | const cloned = node.clone();
|
|---|
| 288 | ```
|
|---|
| 289 |
|
|---|
| 290 | ### `node.isAtPosition(line, column)`
|
|---|
| 291 |
|
|---|
| 292 | Return a `boolean` indicating whether this node includes the character at the
|
|---|
| 293 | position of the given line and column. Returns `undefined` if the nodes lack
|
|---|
| 294 | sufficient source metadata to determine the position.
|
|---|
| 295 |
|
|---|
| 296 | Arguments:
|
|---|
| 297 |
|
|---|
| 298 | * `line`: 1-index based line number relative to the start of the selector.
|
|---|
| 299 | * `column`: 1-index based column number relative to the start of the selector.
|
|---|
| 300 |
|
|---|
| 301 | ### `node.spaces`
|
|---|
| 302 |
|
|---|
| 303 | Extra whitespaces around the node will be moved into `node.spaces.before` and
|
|---|
| 304 | `node.spaces.after`. So for example, these spaces will be moved as they have
|
|---|
| 305 | no semantic meaning:
|
|---|
| 306 |
|
|---|
| 307 | ```css
|
|---|
| 308 | h1 , h2 {}
|
|---|
| 309 | ```
|
|---|
| 310 |
|
|---|
| 311 | For descendent selectors, the value is always a single space.
|
|---|
| 312 |
|
|---|
| 313 | ```css
|
|---|
| 314 | h1 h2 {}
|
|---|
| 315 | ```
|
|---|
| 316 |
|
|---|
| 317 | Additional whitespace is found in either the `node.spaces.before` and `node.spaces.after` depending on the presence of comments or other whitespace characters. If the actual whitespace does not start or end with a single space, the node's raw value is set to the actual space(s) found in the source.
|
|---|
| 318 |
|
|---|
| 319 | ### `node.source`
|
|---|
| 320 |
|
|---|
| 321 | An object describing the node's start/end, line/column source position.
|
|---|
| 322 |
|
|---|
| 323 | Within the following CSS, the `.bar` class node ...
|
|---|
| 324 |
|
|---|
| 325 | ```css
|
|---|
| 326 | .foo,
|
|---|
| 327 | .bar {}
|
|---|
| 328 | ```
|
|---|
| 329 |
|
|---|
| 330 | ... will contain the following `source` object.
|
|---|
| 331 |
|
|---|
| 332 | ```js
|
|---|
| 333 | source: {
|
|---|
| 334 | start: {
|
|---|
| 335 | line: 2,
|
|---|
| 336 | column: 3
|
|---|
| 337 | },
|
|---|
| 338 | end: {
|
|---|
| 339 | line: 2,
|
|---|
| 340 | column: 6
|
|---|
| 341 | }
|
|---|
| 342 | }
|
|---|
| 343 | ```
|
|---|
| 344 |
|
|---|
| 345 | ### `node.sourceIndex`
|
|---|
| 346 |
|
|---|
| 347 | The zero-based index of the node within the original source string.
|
|---|
| 348 |
|
|---|
| 349 | Within the following CSS, the `.baz` class node will have a `sourceIndex` of `12`.
|
|---|
| 350 |
|
|---|
| 351 | ```css
|
|---|
| 352 | .foo, .bar, .baz {}
|
|---|
| 353 | ```
|
|---|
| 354 |
|
|---|
| 355 | ## Container types
|
|---|
| 356 |
|
|---|
| 357 | The `root`, `selector`, and `pseudo` nodes have some helper methods for working
|
|---|
| 358 | with their children.
|
|---|
| 359 |
|
|---|
| 360 | ### `container.nodes`
|
|---|
| 361 |
|
|---|
| 362 | An array of the container's children.
|
|---|
| 363 |
|
|---|
| 364 | ```js
|
|---|
| 365 | // Input: h1 h2
|
|---|
| 366 | selectors.at(0).nodes.length // => 3
|
|---|
| 367 | selectors.at(0).nodes[0].value // => 'h1'
|
|---|
| 368 | selectors.at(0).nodes[1].value // => ' '
|
|---|
| 369 | ```
|
|---|
| 370 |
|
|---|
| 371 | ### `container.first` & `container.last`
|
|---|
| 372 |
|
|---|
| 373 | The first/last child of the container.
|
|---|
| 374 |
|
|---|
| 375 | ```js
|
|---|
| 376 | selector.first === selector.nodes[0];
|
|---|
| 377 | selector.last === selector.nodes[selector.nodes.length - 1];
|
|---|
| 378 | ```
|
|---|
| 379 |
|
|---|
| 380 | ### `container.at(index)`
|
|---|
| 381 |
|
|---|
| 382 | Returns the node at position `index`.
|
|---|
| 383 |
|
|---|
| 384 | ```js
|
|---|
| 385 | selector.at(0) === selector.first;
|
|---|
| 386 | selector.at(0) === selector.nodes[0];
|
|---|
| 387 | ```
|
|---|
| 388 |
|
|---|
| 389 | Arguments:
|
|---|
| 390 |
|
|---|
| 391 | * `index`: The index of the node to return.
|
|---|
| 392 |
|
|---|
| 393 | ### `container.atPosition(line, column)`
|
|---|
| 394 |
|
|---|
| 395 | Returns the node at the source position `line` and `column`.
|
|---|
| 396 |
|
|---|
| 397 | ```js
|
|---|
| 398 | // Input: :not(.foo),\n#foo > :matches(ol, ul)
|
|---|
| 399 | selector.atPosition(1, 1); // => :not(.foo)
|
|---|
| 400 | selector.atPosition(2, 1); // => \n#foo
|
|---|
| 401 | ```
|
|---|
| 402 |
|
|---|
| 403 | Arguments:
|
|---|
| 404 |
|
|---|
| 405 | * `line`: The line number of the node to return.
|
|---|
| 406 | * `column`: The column number of the node to return.
|
|---|
| 407 |
|
|---|
| 408 | ### `container.index(node)`
|
|---|
| 409 |
|
|---|
| 410 | Return the index of the node within its container.
|
|---|
| 411 |
|
|---|
| 412 | ```js
|
|---|
| 413 | selector.index(selector.nodes[2]) // => 2
|
|---|
| 414 | ```
|
|---|
| 415 |
|
|---|
| 416 | Arguments:
|
|---|
| 417 |
|
|---|
| 418 | * `node`: A node within the current container.
|
|---|
| 419 |
|
|---|
| 420 | ### `container.length`
|
|---|
| 421 |
|
|---|
| 422 | Proxy to the length of the container's nodes.
|
|---|
| 423 |
|
|---|
| 424 | ```js
|
|---|
| 425 | container.length === container.nodes.length
|
|---|
| 426 | ```
|
|---|
| 427 |
|
|---|
| 428 | ### `container` Array iterators
|
|---|
| 429 |
|
|---|
| 430 | The container class provides proxies to certain Array methods; these are:
|
|---|
| 431 |
|
|---|
| 432 | * `container.map === container.nodes.map`
|
|---|
| 433 | * `container.reduce === container.nodes.reduce`
|
|---|
| 434 | * `container.every === container.nodes.every`
|
|---|
| 435 | * `container.some === container.nodes.some`
|
|---|
| 436 | * `container.filter === container.nodes.filter`
|
|---|
| 437 | * `container.sort === container.nodes.sort`
|
|---|
| 438 |
|
|---|
| 439 | Note that these methods only work on a container's immediate children; recursive
|
|---|
| 440 | iteration is provided by `container.walk`.
|
|---|
| 441 |
|
|---|
| 442 | ### `container.each(callback)`
|
|---|
| 443 |
|
|---|
| 444 | Iterate the container's immediate children, calling `callback` for each child.
|
|---|
| 445 | You may return `false` within the callback to break the iteration.
|
|---|
| 446 |
|
|---|
| 447 | ```js
|
|---|
| 448 | let className;
|
|---|
| 449 | selectors.each((selector, index) => {
|
|---|
| 450 | if (selector.type === 'class') {
|
|---|
| 451 | className = selector.value;
|
|---|
| 452 | return false;
|
|---|
| 453 | }
|
|---|
| 454 | });
|
|---|
| 455 | ```
|
|---|
| 456 |
|
|---|
| 457 | Note that unlike `Array#forEach()`, this iterator is safe to use whilst adding
|
|---|
| 458 | or removing nodes from the container.
|
|---|
| 459 |
|
|---|
| 460 | Arguments:
|
|---|
| 461 |
|
|---|
| 462 | * `callback (function)`: A function to call for each node, which receives `node`
|
|---|
| 463 | and `index` arguments.
|
|---|
| 464 |
|
|---|
| 465 | ### `container.walk(callback)`
|
|---|
| 466 |
|
|---|
| 467 | Like `container#each`, but will also iterate child nodes as long as they are
|
|---|
| 468 | `container` types.
|
|---|
| 469 |
|
|---|
| 470 | ```js
|
|---|
| 471 | selectors.walk((selector, index) => {
|
|---|
| 472 | // all nodes
|
|---|
| 473 | });
|
|---|
| 474 | ```
|
|---|
| 475 |
|
|---|
| 476 | Arguments:
|
|---|
| 477 |
|
|---|
| 478 | * `callback (function)`: A function to call for each node, which receives `node`
|
|---|
| 479 | and `index` arguments.
|
|---|
| 480 |
|
|---|
| 481 | This iterator is safe to use whilst mutating `container.nodes`,
|
|---|
| 482 | like `container#each`.
|
|---|
| 483 |
|
|---|
| 484 | ### `container.walk` proxies
|
|---|
| 485 |
|
|---|
| 486 | The container class provides proxy methods for iterating over types of nodes,
|
|---|
| 487 | so that it is easier to write modules that target specific selectors. Those
|
|---|
| 488 | methods are:
|
|---|
| 489 |
|
|---|
| 490 | * `container.walkAttributes`
|
|---|
| 491 | * `container.walkClasses`
|
|---|
| 492 | * `container.walkCombinators`
|
|---|
| 493 | * `container.walkComments`
|
|---|
| 494 | * `container.walkIds`
|
|---|
| 495 | * `container.walkNesting`
|
|---|
| 496 | * `container.walkPseudos`
|
|---|
| 497 | * `container.walkTags`
|
|---|
| 498 | * `container.walkUniversals`
|
|---|
| 499 |
|
|---|
| 500 | ### `container.split(callback)`
|
|---|
| 501 |
|
|---|
| 502 | This method allows you to split a group of nodes by returning `true` from
|
|---|
| 503 | a callback. It returns an array of arrays, where each inner array corresponds
|
|---|
| 504 | to the groups that you created via the callback.
|
|---|
| 505 |
|
|---|
| 506 | ```js
|
|---|
| 507 | // (input) => h1 h2>>h3
|
|---|
| 508 | const list = selectors.first.split(selector => {
|
|---|
| 509 | return selector.type === 'combinator';
|
|---|
| 510 | });
|
|---|
| 511 |
|
|---|
| 512 | // (node values) => [['h1', ' '], ['h2', '>>'], ['h3']]
|
|---|
| 513 | ```
|
|---|
| 514 |
|
|---|
| 515 | Arguments:
|
|---|
| 516 |
|
|---|
| 517 | * `callback (function)`: A function to call for each node, which receives `node`
|
|---|
| 518 | as an argument.
|
|---|
| 519 |
|
|---|
| 520 | ### `container.prepend(node)` & `container.append(node)`
|
|---|
| 521 |
|
|---|
| 522 | Add a node to the start/end of the container. Note that doing so will set
|
|---|
| 523 | the parent property of the node to this container.
|
|---|
| 524 |
|
|---|
| 525 | ```js
|
|---|
| 526 | const id = parser.id({value: 'search'});
|
|---|
| 527 | selector.append(id);
|
|---|
| 528 | ```
|
|---|
| 529 |
|
|---|
| 530 | Arguments:
|
|---|
| 531 |
|
|---|
| 532 | * `node`: The node to add.
|
|---|
| 533 |
|
|---|
| 534 | ### `container.insertBefore(old, new)` & `container.insertAfter(old, new)`
|
|---|
| 535 |
|
|---|
| 536 | Add a node before or after an existing node in a container:
|
|---|
| 537 |
|
|---|
| 538 | ```js
|
|---|
| 539 | selectors.walk(selector => {
|
|---|
| 540 | if (selector.type !== 'class') {
|
|---|
| 541 | const className = parser.className({value: 'theme-name'});
|
|---|
| 542 | selector.parent.insertAfter(selector, className);
|
|---|
| 543 | }
|
|---|
| 544 | });
|
|---|
| 545 | ```
|
|---|
| 546 |
|
|---|
| 547 | Arguments:
|
|---|
| 548 |
|
|---|
| 549 | * `old`: The existing node in the container.
|
|---|
| 550 | * `new`: The new node to add before/after the existing node.
|
|---|
| 551 |
|
|---|
| 552 | ### `container.removeChild(node)`
|
|---|
| 553 |
|
|---|
| 554 | Remove the node from the container. Note that you can also use
|
|---|
| 555 | `node.remove()` if you would like to remove just a single node.
|
|---|
| 556 |
|
|---|
| 557 | ```js
|
|---|
| 558 | selector.length // => 2
|
|---|
| 559 | selector.remove(id)
|
|---|
| 560 | selector.length // => 1;
|
|---|
| 561 | id.parent // undefined
|
|---|
| 562 | ```
|
|---|
| 563 |
|
|---|
| 564 | Arguments:
|
|---|
| 565 |
|
|---|
| 566 | * `node`: The node to remove.
|
|---|
| 567 |
|
|---|
| 568 | ### `container.removeAll()` or `container.empty()`
|
|---|
| 569 |
|
|---|
| 570 | Remove all children from the container.
|
|---|
| 571 |
|
|---|
| 572 | ```js
|
|---|
| 573 | selector.removeAll();
|
|---|
| 574 | selector.length // => 0
|
|---|
| 575 | ```
|
|---|
| 576 |
|
|---|
| 577 | ## Root nodes
|
|---|
| 578 |
|
|---|
| 579 | A root node represents a comma separated list of selectors. Indeed, all
|
|---|
| 580 | a root's `toString()` method does is join its selector children with a ','.
|
|---|
| 581 | Other than this, it has no special functionality and acts like a container.
|
|---|
| 582 |
|
|---|
| 583 | ### `root.trailingComma`
|
|---|
| 584 |
|
|---|
| 585 | This will be set to `true` if the input has a trailing comma, in order to
|
|---|
| 586 | support parsing of legacy CSS hacks.
|
|---|
| 587 |
|
|---|
| 588 | ## Selector nodes
|
|---|
| 589 |
|
|---|
| 590 | A selector node represents a single complex selector. For example, this
|
|---|
| 591 | selector string `h1 h2 h3, [href] > p`, is represented as two selector nodes.
|
|---|
| 592 | It has no special functionality of its own.
|
|---|
| 593 |
|
|---|
| 594 | ## Pseudo nodes
|
|---|
| 595 |
|
|---|
| 596 | A pseudo selector extends a container node; if it has any parameters of its
|
|---|
| 597 | own (such as `h1:not(h2, h3)`), they will be its children. Note that the pseudo
|
|---|
| 598 | `value` will always contain the colons preceding the pseudo identifier. This
|
|---|
| 599 | is so that both `:before` and `::before` are properly represented in the AST.
|
|---|
| 600 |
|
|---|
| 601 | ## Attribute nodes
|
|---|
| 602 |
|
|---|
| 603 | ### `attribute.quoted`
|
|---|
| 604 |
|
|---|
| 605 | Returns `true` if the attribute's value is wrapped in quotation marks, false if it is not.
|
|---|
| 606 | Remains `undefined` if there is no attribute value.
|
|---|
| 607 |
|
|---|
| 608 | ```css
|
|---|
| 609 | [href=foo] /* false */
|
|---|
| 610 | [href='foo'] /* true */
|
|---|
| 611 | [href="foo"] /* true */
|
|---|
| 612 | [href] /* undefined */
|
|---|
| 613 | ```
|
|---|
| 614 |
|
|---|
| 615 | ### `attribute.qualifiedAttribute`
|
|---|
| 616 |
|
|---|
| 617 | Returns the attribute name qualified with the namespace if one is given.
|
|---|
| 618 |
|
|---|
| 619 | ### `attribute.offsetOf(part)`
|
|---|
| 620 |
|
|---|
| 621 | Returns the offset of the attribute part specified relative to the
|
|---|
| 622 | start of the node of the output string. This is useful in raising
|
|---|
| 623 | error messages about a specific part of the attribute, especially
|
|---|
| 624 | in combination with `attribute.sourceIndex`.
|
|---|
| 625 |
|
|---|
| 626 | Returns `-1` if the name is invalid or the value doesn't exist in this
|
|---|
| 627 | attribute.
|
|---|
| 628 |
|
|---|
| 629 | The legal values for `part` are:
|
|---|
| 630 |
|
|---|
| 631 | * `"ns"` - alias for "namespace"
|
|---|
| 632 | * `"namespace"` - the namespace if it exists.
|
|---|
| 633 | * `"attribute"` - the attribute name
|
|---|
| 634 | * `"attributeNS"` - the start of the attribute or its namespace
|
|---|
| 635 | * `"operator"` - the match operator of the attribute
|
|---|
| 636 | * `"value"` - The value (string or identifier)
|
|---|
| 637 | * `"insensitive"` - the case insensitivity flag
|
|---|
| 638 |
|
|---|
| 639 | ### `attribute.raws.unquoted`
|
|---|
| 640 |
|
|---|
| 641 | Returns the unquoted content of the attribute's value.
|
|---|
| 642 | Remains `undefined` if there is no attribute value.
|
|---|
| 643 |
|
|---|
| 644 | ```css
|
|---|
| 645 | [href=foo] /* foo */
|
|---|
| 646 | [href='foo'] /* foo */
|
|---|
| 647 | [href="foo"] /* foo */
|
|---|
| 648 | [href] /* undefined */
|
|---|
| 649 | ```
|
|---|
| 650 |
|
|---|
| 651 | ### `attribute.spaces`
|
|---|
| 652 |
|
|---|
| 653 | Like `node.spaces` with the `before` and `after` values containing the spaces
|
|---|
| 654 | around the element, the parts of the attribute can also have spaces before
|
|---|
| 655 | and after them. The for each of `attribute`, `operator`, `value` and
|
|---|
| 656 | `insensitive` there is corresponding property of the same nam in
|
|---|
| 657 | `node.spaces` that has an optional `before` or `after` string containing only
|
|---|
| 658 | whitespace.
|
|---|
| 659 |
|
|---|
| 660 | Note that corresponding values in `attributes.raws.spaces` contain values
|
|---|
| 661 | including any comments. If set, these values will override the
|
|---|
| 662 | `attribute.spaces` value. Take care to remove them if changing
|
|---|
| 663 | `attribute.spaces`.
|
|---|
| 664 |
|
|---|
| 665 | ### `attribute.raws`
|
|---|
| 666 |
|
|---|
| 667 | The raws object stores comments and other information necessary to re-render
|
|---|
| 668 | the node exactly as it was in the source.
|
|---|
| 669 |
|
|---|
| 670 | If a comment is embedded within the identifiers for the `namespace`, `attribute`
|
|---|
| 671 | or `value` then a property is placed in the raws for that value containing the full source of the propery including comments.
|
|---|
| 672 |
|
|---|
| 673 | If a comment is embedded within the space between parts of the attribute
|
|---|
| 674 | then the raw for that space is set accordingly.
|
|---|
| 675 |
|
|---|
| 676 | Setting an attribute's property `raws` value to be deleted.
|
|---|
| 677 |
|
|---|
| 678 | For now, changing the spaces required also updating or removing any of the
|
|---|
| 679 | raws values that override them.
|
|---|
| 680 |
|
|---|
| 681 | Example: `[ /*before*/ href /* after-attr */ = /* after-operator */ te/*inside-value*/st/* wow */ /*omg*/i/*bbq*/ /*whodoesthis*/]` would parse as:
|
|---|
| 682 |
|
|---|
| 683 | ```js
|
|---|
| 684 | {
|
|---|
| 685 | attribute: "href",
|
|---|
| 686 | operator: "=",
|
|---|
| 687 | value: "test",
|
|---|
| 688 | spaces: {
|
|---|
| 689 | before: '',
|
|---|
| 690 | after: '',
|
|---|
| 691 | attribute: { before: ' ', after: ' ' },
|
|---|
| 692 | operator: { after: ' ' },
|
|---|
| 693 | value: { after: ' ' },
|
|---|
| 694 | insensitive: { after: ' ' }
|
|---|
| 695 | },
|
|---|
| 696 | raws: {
|
|---|
| 697 | spaces: {
|
|---|
| 698 | attribute: { before: ' /*before*/ ', after: ' /* after-attr */ ' },
|
|---|
| 699 | operator: { after: ' /* after-operator */ ' },
|
|---|
| 700 | value: { after: '/* wow */ /*omg*/' },
|
|---|
| 701 | insensitive: { after: '/*bbq*/ /*whodoesthis*/' }
|
|---|
| 702 | },
|
|---|
| 703 | unquoted: 'test',
|
|---|
| 704 | value: 'te/*inside-value*/st'
|
|---|
| 705 | }
|
|---|
| 706 | }
|
|---|
| 707 | ```
|
|---|
| 708 |
|
|---|
| 709 | ## `Processor`
|
|---|
| 710 |
|
|---|
| 711 | ### `ProcessorOptions`
|
|---|
| 712 |
|
|---|
| 713 | * `lossless` - When `true`, whitespace is preserved. Defaults to `true`.
|
|---|
| 714 | * `updateSelector` - When `true`, if any processor methods are passed a postcss
|
|---|
| 715 | `Rule` node instead of a string, then that Rule's selector is updated
|
|---|
| 716 | with the results of the processing. Defaults to `true`.
|
|---|
| 717 |
|
|---|
| 718 | ### `process|processSync(selectors, [options])`
|
|---|
| 719 |
|
|---|
| 720 | Processes the `selectors`, returning a string from the result of processing.
|
|---|
| 721 |
|
|---|
| 722 | Note: when the `updateSelector` option is set, the rule's selector
|
|---|
| 723 | will be updated with the resulting string.
|
|---|
| 724 |
|
|---|
| 725 | **Example:**
|
|---|
| 726 |
|
|---|
| 727 | ```js
|
|---|
| 728 | const parser = require("postcss-selector-parser");
|
|---|
| 729 | const processor = parser();
|
|---|
| 730 |
|
|---|
| 731 | let result = processor.processSync(' .class');
|
|---|
| 732 | console.log(result);
|
|---|
| 733 | // => .class
|
|---|
| 734 |
|
|---|
| 735 | // Asynchronous operation
|
|---|
| 736 | let promise = processor.process(' .class').then(result => {
|
|---|
| 737 | console.log(result)
|
|---|
| 738 | // => .class
|
|---|
| 739 | });
|
|---|
| 740 |
|
|---|
| 741 | // To have the parser normalize whitespace values, utilize the options
|
|---|
| 742 | result = processor.processSync(' .class ', {lossless: false});
|
|---|
| 743 | console.log(result);
|
|---|
| 744 | // => .class
|
|---|
| 745 |
|
|---|
| 746 | // For better syntax errors, pass a PostCSS Rule node.
|
|---|
| 747 | const postcss = require('postcss');
|
|---|
| 748 | rule = postcss.rule({selector: ' #foo > a, .class '});
|
|---|
| 749 | processor.process(rule, {lossless: false, updateSelector: true}).then(result => {
|
|---|
| 750 | console.log(result);
|
|---|
| 751 | // => #foo>a,.class
|
|---|
| 752 | console.log("rule:", rule.selector);
|
|---|
| 753 | // => rule: #foo>a,.class
|
|---|
| 754 | })
|
|---|
| 755 | ```
|
|---|
| 756 |
|
|---|
| 757 | Arguments:
|
|---|
| 758 |
|
|---|
| 759 | * `selectors (string|postcss.Rule)`: Either a selector string or a PostCSS Rule
|
|---|
| 760 | node.
|
|---|
| 761 | * `[options] (object)`: Process options
|
|---|
| 762 |
|
|---|
| 763 |
|
|---|
| 764 | ### `ast|astSync(selectors, [options])`
|
|---|
| 765 |
|
|---|
| 766 | Like `process()` and `processSync()` but after
|
|---|
| 767 | processing the `selectors` these methods return the `Root` node of the result
|
|---|
| 768 | instead of a string.
|
|---|
| 769 |
|
|---|
| 770 | Note: when the `updateSelector` option is set, the rule's selector
|
|---|
| 771 | will be updated with the resulting string.
|
|---|
| 772 |
|
|---|
| 773 | ### `transform|transformSync(selectors, [options])`
|
|---|
| 774 |
|
|---|
| 775 | Like `process()` and `processSync()` but after
|
|---|
| 776 | processing the `selectors` these methods return the value returned by the
|
|---|
| 777 | processor callback.
|
|---|
| 778 |
|
|---|
| 779 | Note: when the `updateSelector` option is set, the rule's selector
|
|---|
| 780 | will be updated with the resulting string.
|
|---|
| 781 |
|
|---|
| 782 | ### Error Handling Within Selector Processors
|
|---|
| 783 |
|
|---|
| 784 | The root node passed to the selector processor callback
|
|---|
| 785 | has a method `error(message, options)` that returns an
|
|---|
| 786 | error object. This method should always be used to raise
|
|---|
| 787 | errors relating to the syntax of selectors. The options
|
|---|
| 788 | to this method are passed to postcss's error constructor
|
|---|
| 789 | ([documentation](http://postcss.org/api/#container-error)).
|
|---|
| 790 |
|
|---|
| 791 | #### Async Error Example
|
|---|
| 792 |
|
|---|
| 793 | ```js
|
|---|
| 794 | let processor = (root) => {
|
|---|
| 795 | return new Promise((resolve, reject) => {
|
|---|
| 796 | root.walkClasses((classNode) => {
|
|---|
| 797 | if (/^(.*)[-_]/.test(classNode.value)) {
|
|---|
| 798 | let msg = "classes may not have underscores or dashes in them";
|
|---|
| 799 | reject(root.error(msg, {
|
|---|
| 800 | index: classNode.sourceIndex + RegExp.$1.length + 1,
|
|---|
| 801 | word: classNode.value
|
|---|
| 802 | }));
|
|---|
| 803 | }
|
|---|
| 804 | });
|
|---|
| 805 | resolve();
|
|---|
| 806 | });
|
|---|
| 807 | };
|
|---|
| 808 |
|
|---|
| 809 | const postcss = require("postcss");
|
|---|
| 810 | const parser = require("postcss-selector-parser");
|
|---|
| 811 | const selectorProcessor = parser(processor);
|
|---|
| 812 | const plugin = postcss.plugin('classValidator', (options) => {
|
|---|
| 813 | return (root) => {
|
|---|
| 814 | let promises = [];
|
|---|
| 815 | root.walkRules(rule => {
|
|---|
| 816 | promises.push(selectorProcessor.process(rule));
|
|---|
| 817 | });
|
|---|
| 818 | return Promise.all(promises);
|
|---|
| 819 | };
|
|---|
| 820 | });
|
|---|
| 821 | postcss(plugin()).process(`
|
|---|
| 822 | .foo-bar {
|
|---|
| 823 | color: red;
|
|---|
| 824 | }
|
|---|
| 825 | `.trim(), {from: 'test.css'}).catch((e) => console.error(e.toString()));
|
|---|
| 826 |
|
|---|
| 827 | // CssSyntaxError: classValidator: ./test.css:1:5: classes may not have underscores or dashes in them
|
|---|
| 828 | //
|
|---|
| 829 | // > 1 | .foo-bar {
|
|---|
| 830 | // | ^
|
|---|
| 831 | // 2 | color: red;
|
|---|
| 832 | // 3 | }
|
|---|
| 833 | ```
|
|---|
| 834 |
|
|---|
| 835 | #### Synchronous Error Example
|
|---|
| 836 |
|
|---|
| 837 | ```js
|
|---|
| 838 | let processor = (root) => {
|
|---|
| 839 | root.walkClasses((classNode) => {
|
|---|
| 840 | if (/.*[-_]/.test(classNode.value)) {
|
|---|
| 841 | let msg = "classes may not have underscores or dashes in them";
|
|---|
| 842 | throw root.error(msg, {
|
|---|
| 843 | index: classNode.sourceIndex,
|
|---|
| 844 | word: classNode.value
|
|---|
| 845 | });
|
|---|
| 846 | }
|
|---|
| 847 | });
|
|---|
| 848 | };
|
|---|
| 849 |
|
|---|
| 850 | const postcss = require("postcss");
|
|---|
| 851 | const parser = require("postcss-selector-parser");
|
|---|
| 852 | const selectorProcessor = parser(processor);
|
|---|
| 853 | const plugin = postcss.plugin('classValidator', (options) => {
|
|---|
| 854 | return (root) => {
|
|---|
| 855 | root.walkRules(rule => {
|
|---|
| 856 | selectorProcessor.processSync(rule);
|
|---|
| 857 | });
|
|---|
| 858 | };
|
|---|
| 859 | });
|
|---|
| 860 | postcss(plugin()).process(`
|
|---|
| 861 | .foo-bar {
|
|---|
| 862 | color: red;
|
|---|
| 863 | }
|
|---|
| 864 | `.trim(), {from: 'test.css'}).catch((e) => console.error(e.toString()));
|
|---|
| 865 |
|
|---|
| 866 | // CssSyntaxError: classValidator: ./test.css:1:5: classes may not have underscores or dashes in them
|
|---|
| 867 | //
|
|---|
| 868 | // > 1 | .foo-bar {
|
|---|
| 869 | // | ^
|
|---|
| 870 | // 2 | color: red;
|
|---|
| 871 | // 3 | }
|
|---|
| 872 | ```
|
|---|