[6a3a178] | 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()`
|
---|
| 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 = parser.id({value: 'search'});
|
---|
| 288 | String(cloned);
|
---|
| 289 |
|
---|
| 290 | // => #search
|
---|
| 291 | ```
|
---|
| 292 |
|
---|
| 293 | ### `node.isAtPosition(line, column)`
|
---|
| 294 |
|
---|
| 295 | Return a `boolean` indicating whether this node includes the character at the
|
---|
| 296 | position of the given line and column. Returns `undefined` if the nodes lack
|
---|
| 297 | sufficient source metadata to determine the position.
|
---|
| 298 |
|
---|
| 299 | Arguments:
|
---|
| 300 |
|
---|
| 301 | * `line`: 1-index based line number relative to the start of the selector.
|
---|
| 302 | * `column`: 1-index based column number relative to the start of the selector.
|
---|
| 303 |
|
---|
| 304 | ### `node.spaces`
|
---|
| 305 |
|
---|
| 306 | Extra whitespaces around the node will be moved into `node.spaces.before` and
|
---|
| 307 | `node.spaces.after`. So for example, these spaces will be moved as they have
|
---|
| 308 | no semantic meaning:
|
---|
| 309 |
|
---|
| 310 | ```css
|
---|
| 311 | h1 , h2 {}
|
---|
| 312 | ```
|
---|
| 313 |
|
---|
| 314 | For descendent selectors, the value is always a single space.
|
---|
| 315 |
|
---|
| 316 | ```css
|
---|
| 317 | h1 h2 {}
|
---|
| 318 | ```
|
---|
| 319 |
|
---|
| 320 | 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.
|
---|
| 321 |
|
---|
| 322 | ### `node.source`
|
---|
| 323 |
|
---|
| 324 | An object describing the node's start/end, line/column source position.
|
---|
| 325 |
|
---|
| 326 | Within the following CSS, the `.bar` class node ...
|
---|
| 327 |
|
---|
| 328 | ```css
|
---|
| 329 | .foo,
|
---|
| 330 | .bar {}
|
---|
| 331 | ```
|
---|
| 332 |
|
---|
| 333 | ... will contain the following `source` object.
|
---|
| 334 |
|
---|
| 335 | ```js
|
---|
| 336 | source: {
|
---|
| 337 | start: {
|
---|
| 338 | line: 2,
|
---|
| 339 | column: 3
|
---|
| 340 | },
|
---|
| 341 | end: {
|
---|
| 342 | line: 2,
|
---|
| 343 | column: 6
|
---|
| 344 | }
|
---|
| 345 | }
|
---|
| 346 | ```
|
---|
| 347 |
|
---|
| 348 | ### `node.sourceIndex`
|
---|
| 349 |
|
---|
| 350 | The zero-based index of the node within the original source string.
|
---|
| 351 |
|
---|
| 352 | Within the following CSS, the `.baz` class node will have a `sourceIndex` of `12`.
|
---|
| 353 |
|
---|
| 354 | ```css
|
---|
| 355 | .foo, .bar, .baz {}
|
---|
| 356 | ```
|
---|
| 357 |
|
---|
| 358 | ## Container types
|
---|
| 359 |
|
---|
| 360 | The `root`, `selector`, and `pseudo` nodes have some helper methods for working
|
---|
| 361 | with their children.
|
---|
| 362 |
|
---|
| 363 | ### `container.nodes`
|
---|
| 364 |
|
---|
| 365 | An array of the container's children.
|
---|
| 366 |
|
---|
| 367 | ```js
|
---|
| 368 | // Input: h1 h2
|
---|
| 369 | selectors.at(0).nodes.length // => 3
|
---|
| 370 | selectors.at(0).nodes[0].value // => 'h1'
|
---|
| 371 | selectors.at(0).nodes[1].value // => ' '
|
---|
| 372 | ```
|
---|
| 373 |
|
---|
| 374 | ### `container.first` & `container.last`
|
---|
| 375 |
|
---|
| 376 | The first/last child of the container.
|
---|
| 377 |
|
---|
| 378 | ```js
|
---|
| 379 | selector.first === selector.nodes[0];
|
---|
| 380 | selector.last === selector.nodes[selector.nodes.length - 1];
|
---|
| 381 | ```
|
---|
| 382 |
|
---|
| 383 | ### `container.at(index)`
|
---|
| 384 |
|
---|
| 385 | Returns the node at position `index`.
|
---|
| 386 |
|
---|
| 387 | ```js
|
---|
| 388 | selector.at(0) === selector.first;
|
---|
| 389 | selector.at(0) === selector.nodes[0];
|
---|
| 390 | ```
|
---|
| 391 |
|
---|
| 392 | Arguments:
|
---|
| 393 |
|
---|
| 394 | * `index`: The index of the node to return.
|
---|
| 395 |
|
---|
| 396 | ### `container.atPosition(line, column)`
|
---|
| 397 |
|
---|
| 398 | Returns the node at the source position `index`.
|
---|
| 399 |
|
---|
| 400 | ```js
|
---|
| 401 | selector.at(0) === selector.first;
|
---|
| 402 | selector.at(0) === selector.nodes[0];
|
---|
| 403 | ```
|
---|
| 404 |
|
---|
| 405 | Arguments:
|
---|
| 406 |
|
---|
| 407 | * `index`: The index of the node to return.
|
---|
| 408 |
|
---|
| 409 | ### `container.index(node)`
|
---|
| 410 |
|
---|
| 411 | Return the index of the node within its container.
|
---|
| 412 |
|
---|
| 413 | ```js
|
---|
| 414 | selector.index(selector.nodes[2]) // => 2
|
---|
| 415 | ```
|
---|
| 416 |
|
---|
| 417 | Arguments:
|
---|
| 418 |
|
---|
| 419 | * `node`: A node within the current container.
|
---|
| 420 |
|
---|
| 421 | ### `container.length`
|
---|
| 422 |
|
---|
| 423 | Proxy to the length of the container's nodes.
|
---|
| 424 |
|
---|
| 425 | ```js
|
---|
| 426 | container.length === container.nodes.length
|
---|
| 427 | ```
|
---|
| 428 |
|
---|
| 429 | ### `container` Array iterators
|
---|
| 430 |
|
---|
| 431 | The container class provides proxies to certain Array methods; these are:
|
---|
| 432 |
|
---|
| 433 | * `container.map === container.nodes.map`
|
---|
| 434 | * `container.reduce === container.nodes.reduce`
|
---|
| 435 | * `container.every === container.nodes.every`
|
---|
| 436 | * `container.some === container.nodes.some`
|
---|
| 437 | * `container.filter === container.nodes.filter`
|
---|
| 438 | * `container.sort === container.nodes.sort`
|
---|
| 439 |
|
---|
| 440 | Note that these methods only work on a container's immediate children; recursive
|
---|
| 441 | iteration is provided by `container.walk`.
|
---|
| 442 |
|
---|
| 443 | ### `container.each(callback)`
|
---|
| 444 |
|
---|
| 445 | Iterate the container's immediate children, calling `callback` for each child.
|
---|
| 446 | You may return `false` within the callback to break the iteration.
|
---|
| 447 |
|
---|
| 448 | ```js
|
---|
| 449 | let className;
|
---|
| 450 | selectors.each((selector, index) => {
|
---|
| 451 | if (selector.type === 'class') {
|
---|
| 452 | className = selector.value;
|
---|
| 453 | return false;
|
---|
| 454 | }
|
---|
| 455 | });
|
---|
| 456 | ```
|
---|
| 457 |
|
---|
| 458 | Note that unlike `Array#forEach()`, this iterator is safe to use whilst adding
|
---|
| 459 | or removing nodes from the container.
|
---|
| 460 |
|
---|
| 461 | Arguments:
|
---|
| 462 |
|
---|
| 463 | * `callback (function)`: A function to call for each node, which receives `node`
|
---|
| 464 | and `index` arguments.
|
---|
| 465 |
|
---|
| 466 | ### `container.walk(callback)`
|
---|
| 467 |
|
---|
| 468 | Like `container#each`, but will also iterate child nodes as long as they are
|
---|
| 469 | `container` types.
|
---|
| 470 |
|
---|
| 471 | ```js
|
---|
| 472 | selectors.walk((selector, index) => {
|
---|
| 473 | // all nodes
|
---|
| 474 | });
|
---|
| 475 | ```
|
---|
| 476 |
|
---|
| 477 | Arguments:
|
---|
| 478 |
|
---|
| 479 | * `callback (function)`: A function to call for each node, which receives `node`
|
---|
| 480 | and `index` arguments.
|
---|
| 481 |
|
---|
| 482 | This iterator is safe to use whilst mutating `container.nodes`,
|
---|
| 483 | like `container#each`.
|
---|
| 484 |
|
---|
| 485 | ### `container.walk` proxies
|
---|
| 486 |
|
---|
| 487 | The container class provides proxy methods for iterating over types of nodes,
|
---|
| 488 | so that it is easier to write modules that target specific selectors. Those
|
---|
| 489 | methods are:
|
---|
| 490 |
|
---|
| 491 | * `container.walkAttributes`
|
---|
| 492 | * `container.walkClasses`
|
---|
| 493 | * `container.walkCombinators`
|
---|
| 494 | * `container.walkComments`
|
---|
| 495 | * `container.walkIds`
|
---|
| 496 | * `container.walkNesting`
|
---|
| 497 | * `container.walkPseudos`
|
---|
| 498 | * `container.walkTags`
|
---|
| 499 | * `container.walkUniversals`
|
---|
| 500 |
|
---|
| 501 | ### `container.split(callback)`
|
---|
| 502 |
|
---|
| 503 | This method allows you to split a group of nodes by returning `true` from
|
---|
| 504 | a callback. It returns an array of arrays, where each inner array corresponds
|
---|
| 505 | to the groups that you created via the callback.
|
---|
| 506 |
|
---|
| 507 | ```js
|
---|
| 508 | // (input) => h1 h2>>h3
|
---|
| 509 | const list = selectors.first.split(selector => {
|
---|
| 510 | return selector.type === 'combinator';
|
---|
| 511 | });
|
---|
| 512 |
|
---|
| 513 | // (node values) => [['h1', ' '], ['h2', '>>'], ['h3']]
|
---|
| 514 | ```
|
---|
| 515 |
|
---|
| 516 | Arguments:
|
---|
| 517 |
|
---|
| 518 | * `callback (function)`: A function to call for each node, which receives `node`
|
---|
| 519 | as an argument.
|
---|
| 520 |
|
---|
| 521 | ### `container.prepend(node)` & `container.append(node)`
|
---|
| 522 |
|
---|
| 523 | Add a node to the start/end of the container. Note that doing so will set
|
---|
| 524 | the parent property of the node to this container.
|
---|
| 525 |
|
---|
| 526 | ```js
|
---|
| 527 | const id = parser.id({value: 'search'});
|
---|
| 528 | selector.append(id);
|
---|
| 529 | ```
|
---|
| 530 |
|
---|
| 531 | Arguments:
|
---|
| 532 |
|
---|
| 533 | * `node`: The node to add.
|
---|
| 534 |
|
---|
| 535 | ### `container.insertBefore(old, new)` & `container.insertAfter(old, new)`
|
---|
| 536 |
|
---|
| 537 | Add a node before or after an existing node in a container:
|
---|
| 538 |
|
---|
| 539 | ```js
|
---|
| 540 | selectors.walk(selector => {
|
---|
| 541 | if (selector.type !== 'class') {
|
---|
| 542 | const className = parser.className({value: 'theme-name'});
|
---|
| 543 | selector.parent.insertAfter(selector, className);
|
---|
| 544 | }
|
---|
| 545 | });
|
---|
| 546 | ```
|
---|
| 547 |
|
---|
| 548 | Arguments:
|
---|
| 549 |
|
---|
| 550 | * `old`: The existing node in the container.
|
---|
| 551 | * `new`: The new node to add before/after the existing node.
|
---|
| 552 |
|
---|
| 553 | ### `container.removeChild(node)`
|
---|
| 554 |
|
---|
| 555 | Remove the node from the container. Note that you can also use
|
---|
| 556 | `node.remove()` if you would like to remove just a single node.
|
---|
| 557 |
|
---|
| 558 | ```js
|
---|
| 559 | selector.length // => 2
|
---|
| 560 | selector.remove(id)
|
---|
| 561 | selector.length // => 1;
|
---|
| 562 | id.parent // undefined
|
---|
| 563 | ```
|
---|
| 564 |
|
---|
| 565 | Arguments:
|
---|
| 566 |
|
---|
| 567 | * `node`: The node to remove.
|
---|
| 568 |
|
---|
| 569 | ### `container.removeAll()` or `container.empty()`
|
---|
| 570 |
|
---|
| 571 | Remove all children from the container.
|
---|
| 572 |
|
---|
| 573 | ```js
|
---|
| 574 | selector.removeAll();
|
---|
| 575 | selector.length // => 0
|
---|
| 576 | ```
|
---|
| 577 |
|
---|
| 578 | ## Root nodes
|
---|
| 579 |
|
---|
| 580 | A root node represents a comma separated list of selectors. Indeed, all
|
---|
| 581 | a root's `toString()` method does is join its selector children with a ','.
|
---|
| 582 | Other than this, it has no special functionality and acts like a container.
|
---|
| 583 |
|
---|
| 584 | ### `root.trailingComma`
|
---|
| 585 |
|
---|
| 586 | This will be set to `true` if the input has a trailing comma, in order to
|
---|
| 587 | support parsing of legacy CSS hacks.
|
---|
| 588 |
|
---|
| 589 | ## Selector nodes
|
---|
| 590 |
|
---|
| 591 | A selector node represents a single complex selector. For example, this
|
---|
| 592 | selector string `h1 h2 h3, [href] > p`, is represented as two selector nodes.
|
---|
| 593 | It has no special functionality of its own.
|
---|
| 594 |
|
---|
| 595 | ## Pseudo nodes
|
---|
| 596 |
|
---|
| 597 | A pseudo selector extends a container node; if it has any parameters of its
|
---|
| 598 | own (such as `h1:not(h2, h3)`), they will be its children. Note that the pseudo
|
---|
| 599 | `value` will always contain the colons preceding the pseudo identifier. This
|
---|
| 600 | is so that both `:before` and `::before` are properly represented in the AST.
|
---|
| 601 |
|
---|
| 602 | ## Attribute nodes
|
---|
| 603 |
|
---|
| 604 | ### `attribute.quoted`
|
---|
| 605 |
|
---|
| 606 | Returns `true` if the attribute's value is wrapped in quotation marks, false if it is not.
|
---|
| 607 | Remains `undefined` if there is no attribute value.
|
---|
| 608 |
|
---|
| 609 | ```css
|
---|
| 610 | [href=foo] /* false */
|
---|
| 611 | [href='foo'] /* true */
|
---|
| 612 | [href="foo"] /* true */
|
---|
| 613 | [href] /* undefined */
|
---|
| 614 | ```
|
---|
| 615 |
|
---|
| 616 | ### `attribute.qualifiedAttribute`
|
---|
| 617 |
|
---|
| 618 | Returns the attribute name qualified with the namespace if one is given.
|
---|
| 619 |
|
---|
| 620 | ### `attribute.offsetOf(part)`
|
---|
| 621 |
|
---|
| 622 | Returns the offset of the attribute part specified relative to the
|
---|
| 623 | start of the node of the output string. This is useful in raising
|
---|
| 624 | error messages about a specific part of the attribute, especially
|
---|
| 625 | in combination with `attribute.sourceIndex`.
|
---|
| 626 |
|
---|
| 627 | Returns `-1` if the name is invalid or the value doesn't exist in this
|
---|
| 628 | attribute.
|
---|
| 629 |
|
---|
| 630 | The legal values for `part` are:
|
---|
| 631 |
|
---|
| 632 | * `"ns"` - alias for "namespace"
|
---|
| 633 | * `"namespace"` - the namespace if it exists.
|
---|
| 634 | * `"attribute"` - the attribute name
|
---|
| 635 | * `"attributeNS"` - the start of the attribute or its namespace
|
---|
| 636 | * `"operator"` - the match operator of the attribute
|
---|
| 637 | * `"value"` - The value (string or identifier)
|
---|
| 638 | * `"insensitive"` - the case insensitivity flag
|
---|
| 639 |
|
---|
| 640 | ### `attribute.raws.unquoted`
|
---|
| 641 |
|
---|
| 642 | Returns the unquoted content of the attribute's value.
|
---|
| 643 | Remains `undefined` if there is no attribute value.
|
---|
| 644 |
|
---|
| 645 | ```css
|
---|
| 646 | [href=foo] /* foo */
|
---|
| 647 | [href='foo'] /* foo */
|
---|
| 648 | [href="foo"] /* foo */
|
---|
| 649 | [href] /* undefined */
|
---|
| 650 | ```
|
---|
| 651 |
|
---|
| 652 | ### `attribute.spaces`
|
---|
| 653 |
|
---|
| 654 | Like `node.spaces` with the `before` and `after` values containing the spaces
|
---|
| 655 | around the element, the parts of the attribute can also have spaces before
|
---|
| 656 | and after them. The for each of `attribute`, `operator`, `value` and
|
---|
| 657 | `insensitive` there is corresponding property of the same nam in
|
---|
| 658 | `node.spaces` that has an optional `before` or `after` string containing only
|
---|
| 659 | whitespace.
|
---|
| 660 |
|
---|
| 661 | Note that corresponding values in `attributes.raws.spaces` contain values
|
---|
| 662 | including any comments. If set, these values will override the
|
---|
| 663 | `attribute.spaces` value. Take care to remove them if changing
|
---|
| 664 | `attribute.spaces`.
|
---|
| 665 |
|
---|
| 666 | ### `attribute.raws`
|
---|
| 667 |
|
---|
| 668 | The raws object stores comments and other information necessary to re-render
|
---|
| 669 | the node exactly as it was in the source.
|
---|
| 670 |
|
---|
| 671 | If a comment is embedded within the identifiers for the `namespace`, `attribute`
|
---|
| 672 | or `value` then a property is placed in the raws for that value containing the full source of the propery including comments.
|
---|
| 673 |
|
---|
| 674 | If a comment is embedded within the space between parts of the attribute
|
---|
| 675 | then the raw for that space is set accordingly.
|
---|
| 676 |
|
---|
| 677 | Setting an attribute's property `raws` value to be deleted.
|
---|
| 678 |
|
---|
| 679 | For now, changing the spaces required also updating or removing any of the
|
---|
| 680 | raws values that override them.
|
---|
| 681 |
|
---|
| 682 | Example: `[ /*before*/ href /* after-attr */ = /* after-operator */ te/*inside-value*/st/* wow */ /*omg*/i/*bbq*/ /*whodoesthis*/]` would parse as:
|
---|
| 683 |
|
---|
| 684 | ```js
|
---|
| 685 | {
|
---|
| 686 | attribute: "href",
|
---|
| 687 | operator: "=",
|
---|
| 688 | value: "test",
|
---|
| 689 | spaces: {
|
---|
| 690 | before: '',
|
---|
| 691 | after: '',
|
---|
| 692 | attribute: { before: ' ', after: ' ' },
|
---|
| 693 | operator: { after: ' ' },
|
---|
| 694 | value: { after: ' ' },
|
---|
| 695 | insensitive: { after: ' ' }
|
---|
| 696 | },
|
---|
| 697 | raws: {
|
---|
| 698 | spaces: {
|
---|
| 699 | attribute: { before: ' /*before*/ ', after: ' /* after-attr */ ' },
|
---|
| 700 | operator: { after: ' /* after-operator */ ' },
|
---|
| 701 | value: { after: '/* wow */ /*omg*/' },
|
---|
| 702 | insensitive: { after: '/*bbq*/ /*whodoesthis*/' }
|
---|
| 703 | },
|
---|
| 704 | unquoted: 'test',
|
---|
| 705 | value: 'te/*inside-value*/st'
|
---|
| 706 | }
|
---|
| 707 | }
|
---|
| 708 | ```
|
---|
| 709 |
|
---|
| 710 | ## `Processor`
|
---|
| 711 |
|
---|
| 712 | ### `ProcessorOptions`
|
---|
| 713 |
|
---|
| 714 | * `lossless` - When `true`, whitespace is preserved. Defaults to `true`.
|
---|
| 715 | * `updateSelector` - When `true`, if any processor methods are passed a postcss
|
---|
| 716 | `Rule` node instead of a string, then that Rule's selector is updated
|
---|
| 717 | with the results of the processing. Defaults to `true`.
|
---|
| 718 |
|
---|
| 719 | ### `process|processSync(selectors, [options])`
|
---|
| 720 |
|
---|
| 721 | Processes the `selectors`, returning a string from the result of processing.
|
---|
| 722 |
|
---|
| 723 | Note: when the `updateSelector` option is set, the rule's selector
|
---|
| 724 | will be updated with the resulting string.
|
---|
| 725 |
|
---|
| 726 | **Example:**
|
---|
| 727 |
|
---|
| 728 | ```js
|
---|
| 729 | const parser = require("postcss-selector-parser");
|
---|
| 730 | const processor = parser();
|
---|
| 731 |
|
---|
| 732 | let result = processor.processSync(' .class');
|
---|
| 733 | console.log(result);
|
---|
| 734 | // => .class
|
---|
| 735 |
|
---|
| 736 | // Asynchronous operation
|
---|
| 737 | let promise = processor.process(' .class').then(result => {
|
---|
| 738 | console.log(result)
|
---|
| 739 | // => .class
|
---|
| 740 | });
|
---|
| 741 |
|
---|
| 742 | // To have the parser normalize whitespace values, utilize the options
|
---|
| 743 | result = processor.processSync(' .class ', {lossless: false});
|
---|
| 744 | console.log(result);
|
---|
| 745 | // => .class
|
---|
| 746 |
|
---|
| 747 | // For better syntax errors, pass a PostCSS Rule node.
|
---|
| 748 | const postcss = require('postcss');
|
---|
| 749 | rule = postcss.rule({selector: ' #foo > a, .class '});
|
---|
| 750 | processor.process(rule, {lossless: false, updateSelector: true}).then(result => {
|
---|
| 751 | console.log(result);
|
---|
| 752 | // => #foo>a,.class
|
---|
| 753 | console.log("rule:", rule.selector);
|
---|
| 754 | // => rule: #foo>a,.class
|
---|
| 755 | })
|
---|
| 756 | ```
|
---|
| 757 |
|
---|
| 758 | Arguments:
|
---|
| 759 |
|
---|
| 760 | * `selectors (string|postcss.Rule)`: Either a selector string or a PostCSS Rule
|
---|
| 761 | node.
|
---|
| 762 | * `[options] (object)`: Process options
|
---|
| 763 |
|
---|
| 764 |
|
---|
| 765 | ### `ast|astSync(selectors, [options])`
|
---|
| 766 |
|
---|
| 767 | Like `process()` and `processSync()` but after
|
---|
| 768 | processing the `selectors` these methods return the `Root` node of the result
|
---|
| 769 | instead of a string.
|
---|
| 770 |
|
---|
| 771 | Note: when the `updateSelector` option is set, the rule's selector
|
---|
| 772 | will be updated with the resulting string.
|
---|
| 773 |
|
---|
| 774 | ### `transform|transformSync(selectors, [options])`
|
---|
| 775 |
|
---|
| 776 | Like `process()` and `processSync()` but after
|
---|
| 777 | processing the `selectors` these methods return the value returned by the
|
---|
| 778 | processor callback.
|
---|
| 779 |
|
---|
| 780 | Note: when the `updateSelector` option is set, the rule's selector
|
---|
| 781 | will be updated with the resulting string.
|
---|
| 782 |
|
---|
| 783 | ### Error Handling Within Selector Processors
|
---|
| 784 |
|
---|
| 785 | The root node passed to the selector processor callback
|
---|
| 786 | has a method `error(message, options)` that returns an
|
---|
| 787 | error object. This method should always be used to raise
|
---|
| 788 | errors relating to the syntax of selectors. The options
|
---|
| 789 | to this method are passed to postcss's error constructor
|
---|
| 790 | ([documentation](http://api.postcss.org/Container.html#error)).
|
---|
| 791 |
|
---|
| 792 | #### Async Error Example
|
---|
| 793 |
|
---|
| 794 | ```js
|
---|
| 795 | let processor = (root) => {
|
---|
| 796 | return new Promise((resolve, reject) => {
|
---|
| 797 | root.walkClasses((classNode) => {
|
---|
| 798 | if (/^(.*)[-_]/.test(classNode.value)) {
|
---|
| 799 | let msg = "classes may not have underscores or dashes in them";
|
---|
| 800 | reject(root.error(msg, {
|
---|
| 801 | index: classNode.sourceIndex + RegExp.$1.length + 1,
|
---|
| 802 | word: classNode.value
|
---|
| 803 | }));
|
---|
| 804 | }
|
---|
| 805 | });
|
---|
| 806 | resolve();
|
---|
| 807 | });
|
---|
| 808 | };
|
---|
| 809 |
|
---|
| 810 | const postcss = require("postcss");
|
---|
| 811 | const parser = require("postcss-selector-parser");
|
---|
| 812 | const selectorProcessor = parser(processor);
|
---|
| 813 | const plugin = postcss.plugin('classValidator', (options) => {
|
---|
| 814 | return (root) => {
|
---|
| 815 | let promises = [];
|
---|
| 816 | root.walkRules(rule => {
|
---|
| 817 | promises.push(selectorProcessor.process(rule));
|
---|
| 818 | });
|
---|
| 819 | return Promise.all(promises);
|
---|
| 820 | };
|
---|
| 821 | });
|
---|
| 822 | postcss(plugin()).process(`
|
---|
| 823 | .foo-bar {
|
---|
| 824 | color: red;
|
---|
| 825 | }
|
---|
| 826 | `.trim(), {from: 'test.css'}).catch((e) => console.error(e.toString()));
|
---|
| 827 |
|
---|
| 828 | // CssSyntaxError: classValidator: ./test.css:1:5: classes may not have underscores or dashes in them
|
---|
| 829 | //
|
---|
| 830 | // > 1 | .foo-bar {
|
---|
| 831 | // | ^
|
---|
| 832 | // 2 | color: red;
|
---|
| 833 | // 3 | }
|
---|
| 834 | ```
|
---|
| 835 |
|
---|
| 836 | #### Synchronous Error Example
|
---|
| 837 |
|
---|
| 838 | ```js
|
---|
| 839 | let processor = (root) => {
|
---|
| 840 | root.walkClasses((classNode) => {
|
---|
| 841 | if (/.*[-_]/.test(classNode.value)) {
|
---|
| 842 | let msg = "classes may not have underscores or dashes in them";
|
---|
| 843 | throw root.error(msg, {
|
---|
| 844 | index: classNode.sourceIndex,
|
---|
| 845 | word: classNode.value
|
---|
| 846 | });
|
---|
| 847 | }
|
---|
| 848 | });
|
---|
| 849 | };
|
---|
| 850 |
|
---|
| 851 | const postcss = require("postcss");
|
---|
| 852 | const parser = require("postcss-selector-parser");
|
---|
| 853 | const selectorProcessor = parser(processor);
|
---|
| 854 | const plugin = postcss.plugin('classValidator', (options) => {
|
---|
| 855 | return (root) => {
|
---|
| 856 | root.walkRules(rule => {
|
---|
| 857 | selectorProcessor.processSync(rule);
|
---|
| 858 | });
|
---|
| 859 | };
|
---|
| 860 | });
|
---|
| 861 | postcss(plugin()).process(`
|
---|
| 862 | .foo-bar {
|
---|
| 863 | color: red;
|
---|
| 864 | }
|
---|
| 865 | `.trim(), {from: 'test.css'}).catch((e) => console.error(e.toString()));
|
---|
| 866 |
|
---|
| 867 | // CssSyntaxError: classValidator: ./test.css:1:5: classes may not have underscores or dashes in them
|
---|
| 868 | //
|
---|
| 869 | // > 1 | .foo-bar {
|
---|
| 870 | // | ^
|
---|
| 871 | // 2 | color: red;
|
---|
| 872 | // 3 | }
|
---|
| 873 | ```
|
---|