1# API Documentation 2 3*Please use only this documented API when working with the parser. Methods 4not documented here are subject to change at any point.* 5 6## `parser` function 7 8This is the module's main entry point. 9 10```js 11const parser = require('postcss-selector-parser'); 12``` 13 14### `parser([transform], [options])` 15 16Creates a new `processor` instance 17 18```js 19const processor = parser(); 20``` 21 22Or, with optional transform function 23 24```js 25const transform = selectors => { 26 selectors.walkUniversals(selector => { 27 selector.remove(); 28 }); 29}; 30 31const processor = parser(transform) 32 33// Example 34const result = processor.processSync('*.class'); 35// => .class 36``` 37 38[See processor documentation](#processor) 39 40Arguments: 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 47Creates a new attribute selector. 48 49```js 50parser.attribute({attribute: 'href'}); 51// => [href] 52``` 53 54Arguments: 55 56* `props (object)`: The new node's properties. 57 58### `parser.className([props])` 59 60Creates a new class selector. 61 62```js 63parser.className({value: 'button'}); 64// => .button 65``` 66 67Arguments: 68 69* `props (object)`: The new node's properties. 70 71### `parser.combinator([props])` 72 73Creates a new selector combinator. 74 75```js 76parser.combinator({value: '+'}); 77// => + 78``` 79 80Arguments: 81 82* `props (object)`: The new node's properties. 83 84Notes: 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 98Creates a new comment. 99 100```js 101parser.comment({value: '/* Affirmative, Dave. I read you. */'}); 102// => /* Affirmative, Dave. I read you. */ 103``` 104 105Arguments: 106 107* `props (object)`: The new node's properties. 108 109### `parser.id([props])` 110 111Creates a new id selector. 112 113```js 114parser.id({value: 'search'}); 115// => #search 116``` 117 118Arguments: 119 120* `props (object)`: The new node's properties. 121 122### `parser.nesting([props])` 123 124Creates a new nesting selector. 125 126```js 127parser.nesting(); 128// => & 129``` 130 131Arguments: 132 133* `props (object)`: The new node's properties. 134 135### `parser.pseudo([props])` 136 137Creates a new pseudo selector. 138 139```js 140parser.pseudo({value: '::before'}); 141// => ::before 142``` 143 144Arguments: 145 146* `props (object)`: The new node's properties. 147 148### `parser.root([props])` 149 150Creates a new root node. 151 152```js 153parser.root(); 154// => (empty) 155``` 156 157Arguments: 158 159* `props (object)`: The new node's properties. 160 161### `parser.selector([props])` 162 163Creates a new selector node. 164 165```js 166parser.selector(); 167// => (empty) 168``` 169 170Arguments: 171 172* `props (object)`: The new node's properties. 173 174### `parser.string([props])` 175 176Creates a new string node. 177 178```js 179parser.string(); 180// => (empty) 181``` 182 183Arguments: 184 185* `props (object)`: The new node's properties. 186 187### `parser.tag([props])` 188 189Creates a new tag selector. 190 191```js 192parser.tag({value: 'button'}); 193// => button 194``` 195 196Arguments: 197 198* `props (object)`: The new node's properties. 199 200### `parser.universal([props])` 201 202Creates a new universal selector. 203 204```js 205parser.universal(); 206// => * 207``` 208 209Arguments: 210 211* `props (object)`: The new node's properties. 212 213## Node types 214 215### `node.type` 216 217A 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, 220these constants are exposed on the main `parser` as uppercased keys. So for 221example you can get `id` by querying `parser.ID`. 222 223```js 224parser.attribute({attribute: 'href'}).type; 225// => 'attribute' 226``` 227 228### `node.parent` 229 230Returns the parent node. 231 232```js 233root.nodes[0].parent === root; 234``` 235 236### `node.toString()`, `String(node)`, or `'' + node` 237 238Returns a string representation of the node. 239 240```js 241const id = parser.id({value: 'search'}); 242console.log(String(id)); 243// => #search 244``` 245 246### `node.next()` & `node.prev()` 247 248Returns the next/previous child of the parent node. 249 250```js 251const next = id.next(); 252if (next && next.type !== 'combinator') { 253 throw new Error('Qualified IDs are not allowed!'); 254} 255``` 256 257### `node.replaceWith(node)` 258 259Replace a node with another. 260 261```js 262const attr = selectors.first.first; 263const className = parser.className({value: 'test'}); 264attr.replaceWith(className); 265``` 266 267Arguments: 268 269* `node`: The node to substitute the original with. 270 271### `node.remove()` 272 273Removes the node from its parent node. 274 275```js 276if (node.type === 'id') { 277 node.remove(); 278} 279``` 280 281### `node.clone([opts])` 282 283Returns a copy of a node, detached from any parent containers that the 284original might have had. 285 286```js 287const cloned = node.clone(); 288``` 289 290### `node.isAtPosition(line, column)` 291 292Return a `boolean` indicating whether this node includes the character at the 293position of the given line and column. Returns `undefined` if the nodes lack 294sufficient source metadata to determine the position. 295 296Arguments: 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 303Extra 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 305no semantic meaning: 306 307```css 308 h1 , h2 {} 309``` 310 311For descendent selectors, the value is always a single space. 312 313```css 314h1 h2 {} 315``` 316 317Additional 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 321An object describing the node's start/end, line/column source position. 322 323Within 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 333source: { 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 347The zero-based index of the node within the original source string. 348 349Within 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 357The `root`, `selector`, and `pseudo` nodes have some helper methods for working 358with their children. 359 360### `container.nodes` 361 362An array of the container's children. 363 364```js 365// Input: h1 h2 366selectors.at(0).nodes.length // => 3 367selectors.at(0).nodes[0].value // => 'h1' 368selectors.at(0).nodes[1].value // => ' ' 369``` 370 371### `container.first` & `container.last` 372 373The first/last child of the container. 374 375```js 376selector.first === selector.nodes[0]; 377selector.last === selector.nodes[selector.nodes.length - 1]; 378``` 379 380### `container.at(index)` 381 382Returns the node at position `index`. 383 384```js 385selector.at(0) === selector.first; 386selector.at(0) === selector.nodes[0]; 387``` 388 389Arguments: 390 391* `index`: The index of the node to return. 392 393### `container.atPosition(line, column)` 394 395Returns the node at the source position `line` and `column`. 396 397```js 398// Input: :not(.foo),\n#foo > :matches(ol, ul) 399selector.atPosition(1, 1); // => :not(.foo) 400selector.atPosition(2, 1); // => \n#foo 401``` 402 403Arguments: 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 410Return the index of the node within its container. 411 412```js 413selector.index(selector.nodes[2]) // => 2 414``` 415 416Arguments: 417 418* `node`: A node within the current container. 419 420### `container.length` 421 422Proxy to the length of the container's nodes. 423 424```js 425container.length === container.nodes.length 426``` 427 428### `container` Array iterators 429 430The 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 439Note that these methods only work on a container's immediate children; recursive 440iteration is provided by `container.walk`. 441 442### `container.each(callback)` 443 444Iterate the container's immediate children, calling `callback` for each child. 445You may return `false` within the callback to break the iteration. 446 447```js 448let className; 449selectors.each((selector, index) => { 450 if (selector.type === 'class') { 451 className = selector.value; 452 return false; 453 } 454}); 455``` 456 457Note that unlike `Array#forEach()`, this iterator is safe to use whilst adding 458or removing nodes from the container. 459 460Arguments: 461 462* `callback (function)`: A function to call for each node, which receives `node` 463 and `index` arguments. 464 465### `container.walk(callback)` 466 467Like `container#each`, but will also iterate child nodes as long as they are 468`container` types. 469 470```js 471selectors.walk((selector, index) => { 472 // all nodes 473}); 474``` 475 476Arguments: 477 478* `callback (function)`: A function to call for each node, which receives `node` 479 and `index` arguments. 480 481This iterator is safe to use whilst mutating `container.nodes`, 482like `container#each`. 483 484### `container.walk` proxies 485 486The container class provides proxy methods for iterating over types of nodes, 487so that it is easier to write modules that target specific selectors. Those 488methods 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 502This method allows you to split a group of nodes by returning `true` from 503a callback. It returns an array of arrays, where each inner array corresponds 504to the groups that you created via the callback. 505 506```js 507// (input) => h1 h2>>h3 508const list = selectors.first.split(selector => { 509 return selector.type === 'combinator'; 510}); 511 512// (node values) => [['h1', ' '], ['h2', '>>'], ['h3']] 513``` 514 515Arguments: 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 522Add a node to the start/end of the container. Note that doing so will set 523the parent property of the node to this container. 524 525```js 526const id = parser.id({value: 'search'}); 527selector.append(id); 528``` 529 530Arguments: 531 532* `node`: The node to add. 533 534### `container.insertBefore(old, new)` & `container.insertAfter(old, new)` 535 536Add a node before or after an existing node in a container: 537 538```js 539selectors.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 547Arguments: 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 554Remove 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 558selector.length // => 2 559selector.remove(id) 560selector.length // => 1; 561id.parent // undefined 562``` 563 564Arguments: 565 566* `node`: The node to remove. 567 568### `container.removeAll()` or `container.empty()` 569 570Remove all children from the container. 571 572```js 573selector.removeAll(); 574selector.length // => 0 575``` 576 577## Root nodes 578 579A root node represents a comma separated list of selectors. Indeed, all 580a root's `toString()` method does is join its selector children with a ','. 581Other than this, it has no special functionality and acts like a container. 582 583### `root.trailingComma` 584 585This will be set to `true` if the input has a trailing comma, in order to 586support parsing of legacy CSS hacks. 587 588## Selector nodes 589 590A selector node represents a single complex selector. For example, this 591selector string `h1 h2 h3, [href] > p`, is represented as two selector nodes. 592It has no special functionality of its own. 593 594## Pseudo nodes 595 596A pseudo selector extends a container node; if it has any parameters of its 597own (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 599is so that both `:before` and `::before` are properly represented in the AST. 600 601## Attribute nodes 602 603### `attribute.quoted` 604 605Returns `true` if the attribute's value is wrapped in quotation marks, false if it is not. 606Remains `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 617Returns 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 641Returns the unquoted content of the attribute's value. 642Remains `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 653Like `node.spaces` with the `before` and `after` values containing the spaces 654around the element, the parts of the attribute can also have spaces before 655and 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 658whitespace. 659 660Note that corresponding values in `attributes.raws.spaces` contain values 661including 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 667The raws object stores comments and other information necessary to re-render 668the node exactly as it was in the source. 669 670If a comment is embedded within the identifiers for the `namespace`, `attribute` 671or `value` then a property is placed in the raws for that value containing the full source of the propery including comments. 672 673If a comment is embedded within the space between parts of the attribute 674then the raw for that space is set accordingly. 675 676Setting an attribute's property `raws` value to be deleted. 677 678For now, changing the spaces required also updating or removing any of the 679raws values that override them. 680 681Example: `[ /*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 720Processes the `selectors`, returning a string from the result of processing. 721 722Note: when the `updateSelector` option is set, the rule's selector 723will be updated with the resulting string. 724 725**Example:** 726 727```js 728const parser = require("postcss-selector-parser"); 729const processor = parser(); 730 731let result = processor.processSync(' .class'); 732console.log(result); 733// => .class 734 735// Asynchronous operation 736let 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 742result = processor.processSync(' .class ', {lossless: false}); 743console.log(result); 744// => .class 745 746// For better syntax errors, pass a PostCSS Rule node. 747const postcss = require('postcss'); 748rule = postcss.rule({selector: ' #foo > a, .class '}); 749processor.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 757Arguments: 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 766Like `process()` and `processSync()` but after 767processing the `selectors` these methods return the `Root` node of the result 768instead of a string. 769 770Note: when the `updateSelector` option is set, the rule's selector 771will be updated with the resulting string. 772 773### `transform|transformSync(selectors, [options])` 774 775Like `process()` and `processSync()` but after 776processing the `selectors` these methods return the value returned by the 777processor callback. 778 779Note: when the `updateSelector` option is set, the rule's selector 780will be updated with the resulting string. 781 782### Error Handling Within Selector Processors 783 784The root node passed to the selector processor callback 785has a method `error(message, options)` that returns an 786error object. This method should always be used to raise 787errors relating to the syntax of selectors. The options 788to this method are passed to postcss's error constructor 789([documentation](http://api.postcss.org/Container.html#error)). 790 791#### Async Error Example 792 793```js 794let 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 809const postcss = require("postcss"); 810const parser = require("postcss-selector-parser"); 811const selectorProcessor = parser(processor); 812const 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}); 821postcss(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 838let 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 850const postcss = require("postcss"); 851const parser = require("postcss-selector-parser"); 852const selectorProcessor = parser(processor); 853const plugin = postcss.plugin('classValidator', (options) => { 854 return (root) => { 855 root.walkRules(rule => { 856 selectorProcessor.processSync(rule); 857 }); 858 }; 859}); 860postcss(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``` 873