Spaces:
Running
Running
# 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 | } | |
``` | |