| # API Documentation |
| |
| *Please use only this documented API when working with the parser. Methods |
| not documented here are subject to change at any point.* |
| |
| ## `parser` function |
| |
| This is the module's main entry point. |
| |
| ```js |
| const parser = require('postcss-selector-parser'); |
| ``` |
| |
| ### `parser([transform], [options])` |
| |
| Creates a new `processor` instance |
| |
| ```js |
| const processor = parser(); |
| ``` |
| |
| Or, with optional transform function |
| |
| ```js |
| const transform = selectors => { |
| selectors.walkUniversals(selector => { |
| selector.remove(); |
| }); |
| }; |
| |
| const processor = parser(transform) |
| |
| // Example |
| const result = processor.processSync('*.class'); |
| // => .class |
| ``` |
| |
| [See processor documentation](#processor) |
| |
| Arguments: |
| |
| * `transform (function)`: Provide a function to work with the parsed AST. |
| * `options (object)`: Provide default options for all calls on the returned `Processor`. |
| |
| ### `parser.attribute([props])` |
| |
| Creates a new attribute selector. |
| |
| ```js |
| parser.attribute({attribute: 'href'}); |
| // => [href] |
| ``` |
| |
| Arguments: |
| |
| * `props (object)`: The new node's properties. |
| |
| ### `parser.className([props])` |
| |
| Creates a new class selector. |
| |
| ```js |
| parser.className({value: 'button'}); |
| // => .button |
| ``` |
| |
| Arguments: |
| |
| * `props (object)`: The new node's properties. |
| |
| ### `parser.combinator([props])` |
| |
| Creates a new selector combinator. |
| |
| ```js |
| parser.combinator({value: '+'}); |
| // => + |
| ``` |
| |
| Arguments: |
| |
| * `props (object)`: The new node's properties. |
| |
| Notes: |
| * **Descendant Combinators** The value of descendant combinators created by the |
| parser always just a single space (`" "`). For descendant selectors with no |
| comments, additional space is now stored in `node.spaces.before`. Depending |
| on the location of comments, additional spaces may be stored in |
| `node.raws.spaces.before`, `node.raws.spaces.after`, or `node.raws.value`. |
| * **Named Combinators** Although, nonstandard and unlikely to ever become a standard, |
| named combinators like `/deep/` and `/for/` are parsed as combinators. The |
| `node.value` is name after being unescaped and normalized as lowercase. The |
| original value for the combinator name is stored in `node.raws.value`. |
| |
| |
| ### `parser.comment([props])` |
| |
| Creates a new comment. |
| |
| ```js |
| parser.comment({value: '/* Affirmative, Dave. I read you. */'}); |
| // => /* Affirmative, Dave. I read you. */ |
| ``` |
| |
| Arguments: |
| |
| * `props (object)`: The new node's properties. |
| |
| ### `parser.id([props])` |
| |
| Creates a new id selector. |
| |
| ```js |
| parser.id({value: 'search'}); |
| // => #search |
| ``` |
| |
| Arguments: |
| |
| * `props (object)`: The new node's properties. |
| |
| ### `parser.nesting([props])` |
| |
| Creates a new nesting selector. |
| |
| ```js |
| parser.nesting(); |
| // => & |
| ``` |
| |
| Arguments: |
| |
| * `props (object)`: The new node's properties. |
| |
| ### `parser.pseudo([props])` |
| |
| Creates a new pseudo selector. |
| |
| ```js |
| parser.pseudo({value: '::before'}); |
| // => ::before |
| ``` |
| |
| Arguments: |
| |
| * `props (object)`: The new node's properties. |
| |
| ### `parser.root([props])` |
| |
| Creates a new root node. |
| |
| ```js |
| parser.root(); |
| // => (empty) |
| ``` |
| |
| Arguments: |
| |
| * `props (object)`: The new node's properties. |
| |
| ### `parser.selector([props])` |
| |
| Creates a new selector node. |
| |
| ```js |
| parser.selector(); |
| // => (empty) |
| ``` |
| |
| Arguments: |
| |
| * `props (object)`: The new node's properties. |
| |
| ### `parser.string([props])` |
| |
| Creates a new string node. |
| |
| ```js |
| parser.string(); |
| // => (empty) |
| ``` |
| |
| Arguments: |
| |
| * `props (object)`: The new node's properties. |
| |
| ### `parser.tag([props])` |
| |
| Creates a new tag selector. |
| |
| ```js |
| parser.tag({value: 'button'}); |
| // => button |
| ``` |
| |
| Arguments: |
| |
| * `props (object)`: The new node's properties. |
| |
| ### `parser.universal([props])` |
| |
| Creates a new universal selector. |
| |
| ```js |
| parser.universal(); |
| // => * |
| ``` |
| |
| Arguments: |
| |
| * `props (object)`: The new node's properties. |
| |
| ## Node types |
| |
| ### `node.type` |
| |
| A string representation of the selector type. It can be one of the following; |
| `attribute`, `class`, `combinator`, `comment`, `id`, `nesting`, `pseudo`, |
| `root`, `selector`, `string`, `tag`, or `universal`. Note that for convenience, |
| these constants are exposed on the main `parser` as uppercased keys. So for |
| example you can get `id` by querying `parser.ID`. |
| |
| ```js |
| parser.attribute({attribute: 'href'}).type; |
| // => 'attribute' |
| ``` |
| |
| ### `node.parent` |
| |
| Returns the parent node. |
| |
| ```js |
| root.nodes[0].parent === root; |
| ``` |
| |
| ### `node.toString()`, `String(node)`, or `'' + node` |
| |
| Returns a string representation of the node. |
| |
| ```js |
| const id = parser.id({value: 'search'}); |
| console.log(String(id)); |
| // => #search |
| ``` |
| |
| ### `node.next()` & `node.prev()` |
| |
| Returns the next/previous child of the parent node. |
| |
| ```js |
| const next = id.next(); |
| if (next && next.type !== 'combinator') { |
| throw new Error('Qualified IDs are not allowed!'); |
| } |
| ``` |
| |
| ### `node.replaceWith(node)` |
| |
| Replace a node with another. |
| |
| ```js |
| const attr = selectors.first.first; |
| const className = parser.className({value: 'test'}); |
| attr.replaceWith(className); |
| ``` |
| |
| Arguments: |
| |
| * `node`: The node to substitute the original with. |
| |
| ### `node.remove()` |
| |
| Removes the node from its parent node. |
| |
| ```js |
| if (node.type === 'id') { |
| node.remove(); |
| } |
| ``` |
| |
| ### `node.clone()` |
| |
| Returns a copy of a node, detached from any parent containers that the |
| original might have had. |
| |
| ```js |
| const cloned = parser.id({value: 'search'}); |
| String(cloned); |
| |
| // => #search |
| ``` |
| |
| ### `node.isAtPosition(line, column)` |
| |
| Return a `boolean` indicating whether this node includes the character at the |
| position of the given line and column. Returns `undefined` if the nodes lack |
| sufficient source metadata to determine the position. |
| |
| Arguments: |
| |
| * `line`: 1-index based line number relative to the start of the selector. |
| * `column`: 1-index based column number relative to the start of the selector. |
| |
| ### `node.spaces` |
| |
| Extra whitespaces around the node will be moved into `node.spaces.before` and |
| `node.spaces.after`. So for example, these spaces will be moved as they have |
| no semantic meaning: |
| |
| ```css |
| h1 , h2 {} |
| ``` |
| |
| For descendent selectors, the value is always a single space. |
| |
| ```css |
| h1 h2 {} |
| ``` |
| |
| 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. |
| |
| ### `node.source` |
| |
| An object describing the node's start/end, line/column source position. |
| |
| Within the following CSS, the `.bar` class node ... |
| |
| ```css |
| .foo, |
| .bar {} |
| ``` |
| |
| ... will contain the following `source` object. |
| |
| ```js |
| source: { |
| start: { |
| line: 2, |
| column: 3 |
| }, |
| end: { |
| line: 2, |
| column: 6 |
| } |
| } |
| ``` |
| |
| ### `node.sourceIndex` |
| |
| The zero-based index of the node within the original source string. |
| |
| Within the following CSS, the `.baz` class node will have a `sourceIndex` of `12`. |
| |
| ```css |
| .foo, .bar, .baz {} |
| ``` |
| |
| ## Container types |
| |
| The `root`, `selector`, and `pseudo` nodes have some helper methods for working |
| with their children. |
| |
| ### `container.nodes` |
| |
| An array of the container's children. |
| |
| ```js |
| // Input: h1 h2 |
| selectors.at(0).nodes.length // => 3 |
| selectors.at(0).nodes[0].value // => 'h1' |
| selectors.at(0).nodes[1].value // => ' ' |
| ``` |
| |
| ### `container.first` & `container.last` |
| |
| The first/last child of the container. |
| |
| ```js |
| selector.first === selector.nodes[0]; |
| selector.last === selector.nodes[selector.nodes.length - 1]; |
| ``` |
| |
| ### `container.at(index)` |
| |
| Returns the node at position `index`. |
| |
| ```js |
| selector.at(0) === selector.first; |
| selector.at(0) === selector.nodes[0]; |
| ``` |
| |
| Arguments: |
| |
| * `index`: The index of the node to return. |
| |
| ### `container.atPosition(line, column)` |
| |
| Returns the node at the source position `index`. |
| |
| ```js |
| selector.at(0) === selector.first; |
| selector.at(0) === selector.nodes[0]; |
| ``` |
| |
| Arguments: |
| |
| * `index`: The index of the node to return. |
| |
| ### `container.index(node)` |
| |
| Return the index of the node within its container. |
| |
| ```js |
| selector.index(selector.nodes[2]) // => 2 |
| ``` |
| |
| Arguments: |
| |
| * `node`: A node within the current container. |
| |
| ### `container.length` |
| |
| Proxy to the length of the container's nodes. |
| |
| ```js |
| container.length === container.nodes.length |
| ``` |
| |
| ### `container` Array iterators |
| |
| The container class provides proxies to certain Array methods; these are: |
| |
| * `container.map === container.nodes.map` |
| * `container.reduce === container.nodes.reduce` |
| * `container.every === container.nodes.every` |
| * `container.some === container.nodes.some` |
| * `container.filter === container.nodes.filter` |
| * `container.sort === container.nodes.sort` |
| |
| Note that these methods only work on a container's immediate children; recursive |
| iteration is provided by `container.walk`. |
| |
| ### `container.each(callback)` |
| |
| Iterate the container's immediate children, calling `callback` for each child. |
| You may return `false` within the callback to break the iteration. |
| |
| ```js |
| let className; |
| selectors.each((selector, index) => { |
| if (selector.type === 'class') { |
| className = selector.value; |
| return false; |
| } |
| }); |
| ``` |
| |
| Note that unlike `Array#forEach()`, this iterator is safe to use whilst adding |
| or removing nodes from the container. |
| |
| Arguments: |
| |
| * `callback (function)`: A function to call for each node, which receives `node` |
| and `index` arguments. |
| |
| ### `container.walk(callback)` |
| |
| Like `container#each`, but will also iterate child nodes as long as they are |
| `container` types. |
| |
| ```js |
| selectors.walk((selector, index) => { |
| // all nodes |
| }); |
| ``` |
| |
| Arguments: |
| |
| * `callback (function)`: A function to call for each node, which receives `node` |
| and `index` arguments. |
| |
| This iterator is safe to use whilst mutating `container.nodes`, |
| like `container#each`. |
| |
| ### `container.walk` proxies |
| |
| The container class provides proxy methods for iterating over types of nodes, |
| so that it is easier to write modules that target specific selectors. Those |
| methods are: |
| |
| * `container.walkAttributes` |
| * `container.walkClasses` |
| * `container.walkCombinators` |
| * `container.walkComments` |
| * `container.walkIds` |
| * `container.walkNesting` |
| * `container.walkPseudos` |
| * `container.walkTags` |
| * `container.walkUniversals` |
| |
| ### `container.split(callback)` |
| |
| This method allows you to split a group of nodes by returning `true` from |
| a callback. It returns an array of arrays, where each inner array corresponds |
| to the groups that you created via the callback. |
| |
| ```js |
| // (input) => h1 h2>>h3 |
| const list = selectors.first.split(selector => { |
| return selector.type === 'combinator'; |
| }); |
| |
| // (node values) => [['h1', ' '], ['h2', '>>'], ['h3']] |
| ``` |
| |
| Arguments: |
| |
| * `callback (function)`: A function to call for each node, which receives `node` |
| as an argument. |
| |
| ### `container.prepend(node)` & `container.append(node)` |
| |
| Add a node to the start/end of the container. Note that doing so will set |
| the parent property of the node to this container. |
| |
| ```js |
| const id = parser.id({value: 'search'}); |
| selector.append(id); |
| ``` |
| |
| Arguments: |
| |
| * `node`: The node to add. |
| |
| ### `container.insertBefore(old, new)` & `container.insertAfter(old, new)` |
| |
| Add a node before or after an existing node in a container: |
| |
| ```js |
| selectors.walk(selector => { |
| if (selector.type !== 'class') { |
| const className = parser.className({value: 'theme-name'}); |
| selector.parent.insertAfter(selector, className); |
| } |
| }); |
| ``` |
| |
| Arguments: |
| |
| * `old`: The existing node in the container. |
| * `new`: The new node to add before/after the existing node. |
| |
| ### `container.removeChild(node)` |
| |
| Remove the node from the container. Note that you can also use |
| `node.remove()` if you would like to remove just a single node. |
| |
| ```js |
| selector.length // => 2 |
| selector.remove(id) |
| selector.length // => 1; |
| id.parent // undefined |
| ``` |
| |
| Arguments: |
| |
| * `node`: The node to remove. |
| |
| ### `container.removeAll()` or `container.empty()` |
| |
| Remove all children from the container. |
| |
| ```js |
| selector.removeAll(); |
| selector.length // => 0 |
| ``` |
| |
| ## Root nodes |
| |
| A root node represents a comma separated list of selectors. Indeed, all |
| a root's `toString()` method does is join its selector children with a ','. |
| Other than this, it has no special functionality and acts like a container. |
| |
| ### `root.trailingComma` |
| |
| This will be set to `true` if the input has a trailing comma, in order to |
| support parsing of legacy CSS hacks. |
| |
| ## Selector nodes |
| |
| A selector node represents a single complex selector. For example, this |
| selector string `h1 h2 h3, [href] > p`, is represented as two selector nodes. |
| It has no special functionality of its own. |
| |
| ## Pseudo nodes |
| |
| A pseudo selector extends a container node; if it has any parameters of its |
| own (such as `h1:not(h2, h3)`), they will be its children. Note that the pseudo |
| `value` will always contain the colons preceding the pseudo identifier. This |
| is so that both `:before` and `::before` are properly represented in the AST. |
| |
| ## Attribute nodes |
| |
| ### `attribute.quoted` |
| |
| Returns `true` if the attribute's value is wrapped in quotation marks, false if it is not. |
| Remains `undefined` if there is no attribute value. |
| |
| ```css |
| [href=foo] /* false */ |
| [href='foo'] /* true */ |
| [href="foo"] /* true */ |
| [href] /* undefined */ |
| ``` |
| |
| ### `attribute.qualifiedAttribute` |
| |
| Returns the attribute name qualified with the namespace if one is given. |
| |
| ### `attribute.offsetOf(part)` |
| |
| Returns the offset of the attribute part specified relative to the |
| start of the node of the output string. This is useful in raising |
| error messages about a specific part of the attribute, especially |
| in combination with `attribute.sourceIndex`. |
| |
| Returns `-1` if the name is invalid or the value doesn't exist in this |
| attribute. |
| |
| The legal values for `part` are: |
| |
| * `"ns"` - alias for "namespace" |
| * `"namespace"` - the namespace if it exists. |
| * `"attribute"` - the attribute name |
| * `"attributeNS"` - the start of the attribute or its namespace |
| * `"operator"` - the match operator of the attribute |
| * `"value"` - The value (string or identifier) |
| * `"insensitive"` - the case insensitivity flag |
| |
| ### `attribute.raws.unquoted` |
| |
| Returns the unquoted content of the attribute's value. |
| Remains `undefined` if there is no attribute value. |
| |
| ```css |
| [href=foo] /* foo */ |
| [href='foo'] /* foo */ |
| [href="foo"] /* foo */ |
| [href] /* undefined */ |
| ``` |
| |
| ### `attribute.spaces` |
| |
| Like `node.spaces` with the `before` and `after` values containing the spaces |
| around the element, the parts of the attribute can also have spaces before |
| and after them. The for each of `attribute`, `operator`, `value` and |
| `insensitive` there is corresponding property of the same nam in |
| `node.spaces` that has an optional `before` or `after` string containing only |
| whitespace. |
| |
| Note that corresponding values in `attributes.raws.spaces` contain values |
| including any comments. If set, these values will override the |
| `attribute.spaces` value. Take care to remove them if changing |
| `attribute.spaces`. |
| |
| ### `attribute.raws` |
| |
| The raws object stores comments and other information necessary to re-render |
| the node exactly as it was in the source. |
| |
| If a comment is embedded within the identifiers for the `namespace`, `attribute` |
| or `value` then a property is placed in the raws for that value containing the full source of the propery including comments. |
| |
| If a comment is embedded within the space between parts of the attribute |
| then the raw for that space is set accordingly. |
| |
| Setting an attribute's property `raws` value to be deleted. |
| |
| For now, changing the spaces required also updating or removing any of the |
| raws values that override them. |
| |
| Example: `[ /*before*/ href /* after-attr */ = /* after-operator */ te/*inside-value*/st/* wow */ /*omg*/i/*bbq*/ /*whodoesthis*/]` would parse as: |
| |
| ```js |
| { |
| attribute: "href", |
| operator: "=", |
| value: "test", |
| spaces: { |
| before: '', |
| after: '', |
| attribute: { before: ' ', after: ' ' }, |
| operator: { after: ' ' }, |
| value: { after: ' ' }, |
| insensitive: { after: ' ' } |
| }, |
| raws: { |
| spaces: { |
| attribute: { before: ' /*before*/ ', after: ' /* after-attr */ ' }, |
| operator: { after: ' /* after-operator */ ' }, |
| value: { after: '/* wow */ /*omg*/' }, |
| insensitive: { after: '/*bbq*/ /*whodoesthis*/' } |
| }, |
| unquoted: 'test', |
| value: 'te/*inside-value*/st' |
| } |
| } |
| ``` |
| |
| ## `Processor` |
| |
| ### `ProcessorOptions` |
| |
| * `lossless` - When `true`, whitespace is preserved. Defaults to `true`. |
| * `updateSelector` - When `true`, if any processor methods are passed a postcss |
| `Rule` node instead of a string, then that Rule's selector is updated |
| with the results of the processing. Defaults to `true`. |
| |
| ### `process|processSync(selectors, [options])` |
| |
| Processes the `selectors`, returning a string from the result of processing. |
| |
| Note: when the `updateSelector` option is set, the rule's selector |
| will be updated with the resulting string. |
| |
| **Example:** |
| |
| ```js |
| const parser = require("postcss-selector-parser"); |
| const processor = parser(); |
| |
| let result = processor.processSync(' .class'); |
| console.log(result); |
| // => .class |
| |
| // Asynchronous operation |
| let promise = processor.process(' .class').then(result => { |
| console.log(result) |
| // => .class |
| }); |
| |
| // To have the parser normalize whitespace values, utilize the options |
| result = processor.processSync(' .class ', {lossless: false}); |
| console.log(result); |
| // => .class |
| |
| // For better syntax errors, pass a PostCSS Rule node. |
| const postcss = require('postcss'); |
| rule = postcss.rule({selector: ' #foo > a, .class '}); |
| processor.process(rule, {lossless: false, updateSelector: true}).then(result => { |
| console.log(result); |
| // => #foo>a,.class |
| console.log("rule:", rule.selector); |
| // => rule: #foo>a,.class |
| }) |
| ``` |
| |
| Arguments: |
| |
| * `selectors (string|postcss.Rule)`: Either a selector string or a PostCSS Rule |
| node. |
| * `[options] (object)`: Process options |
| |
| |
| ### `ast|astSync(selectors, [options])` |
| |
| Like `process()` and `processSync()` but after |
| processing the `selectors` these methods return the `Root` node of the result |
| instead of a string. |
| |
| Note: when the `updateSelector` option is set, the rule's selector |
| will be updated with the resulting string. |
| |
| ### `transform|transformSync(selectors, [options])` |
| |
| Like `process()` and `processSync()` but after |
| processing the `selectors` these methods return the value returned by the |
| processor callback. |
| |
| Note: when the `updateSelector` option is set, the rule's selector |
| will be updated with the resulting string. |
| |
| ### Error Handling Within Selector Processors |
| |
| The root node passed to the selector processor callback |
| has a method `error(message, options)` that returns an |
| error object. This method should always be used to raise |
| errors relating to the syntax of selectors. The options |
| to this method are passed to postcss's error constructor |
| ([documentation](http://api.postcss.org/Container.html#error)). |
| |
| #### Async Error Example |
| |
| ```js |
| let processor = (root) => { |
| return new Promise((resolve, reject) => { |
| root.walkClasses((classNode) => { |
| if (/^(.*)[-_]/.test(classNode.value)) { |
| let msg = "classes may not have underscores or dashes in them"; |
| reject(root.error(msg, { |
| index: classNode.sourceIndex + RegExp.$1.length + 1, |
| word: classNode.value |
| })); |
| } |
| }); |
| resolve(); |
| }); |
| }; |
| |
| const postcss = require("postcss"); |
| const parser = require("postcss-selector-parser"); |
| const selectorProcessor = parser(processor); |
| const plugin = postcss.plugin('classValidator', (options) => { |
| return (root) => { |
| let promises = []; |
| root.walkRules(rule => { |
| promises.push(selectorProcessor.process(rule)); |
| }); |
| return Promise.all(promises); |
| }; |
| }); |
| postcss(plugin()).process(` |
| .foo-bar { |
| color: red; |
| } |
| `.trim(), {from: 'test.css'}).catch((e) => console.error(e.toString())); |
| |
| // CssSyntaxError: classValidator: ./test.css:1:5: classes may not have underscores or dashes in them |
| // |
| // > 1 | .foo-bar { |
| // | ^ |
| // 2 | color: red; |
| // 3 | } |
| ``` |
| |
| #### Synchronous Error Example |
| |
| ```js |
| let processor = (root) => { |
| root.walkClasses((classNode) => { |
| if (/.*[-_]/.test(classNode.value)) { |
| let msg = "classes may not have underscores or dashes in them"; |
| throw root.error(msg, { |
| index: classNode.sourceIndex, |
| word: classNode.value |
| }); |
| } |
| }); |
| }; |
| |
| const postcss = require("postcss"); |
| const parser = require("postcss-selector-parser"); |
| const selectorProcessor = parser(processor); |
| const plugin = postcss.plugin('classValidator', (options) => { |
| return (root) => { |
| root.walkRules(rule => { |
| selectorProcessor.processSync(rule); |
| }); |
| }; |
| }); |
| postcss(plugin()).process(` |
| .foo-bar { |
| color: red; |
| } |
| `.trim(), {from: 'test.css'}).catch((e) => console.error(e.toString())); |
| |
| // CssSyntaxError: classValidator: ./test.css:1:5: classes may not have underscores or dashes in them |
| // |
| // > 1 | .foo-bar { |
| // | ^ |
| // 2 | color: red; |
| // 3 | } |
| ``` |