• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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