AK-21/Graphite-Industrial-Intelligence
0
1# API Documentation2 3*Please use only this documented API when working with the parser. Methods4not documented here are subject to change at any point.*5 6## `parser` function7 8This is the module's main entry point.9 10```js11const parser = require('postcss-selector-parser');12```13 14### `parser([transform], [options])`15 16Creates a new `processor` instance17 18```js19const processor = parser();20```21 22Or, with optional transform function23 24```js25const transform = selectors => {26 selectors.walkUniversals(selector => {27 selector.remove();28 });29};30 31const processor = parser(transform)32 33// Example34const result = processor.processSync('*.class');35// => .class36```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```js50parser.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```js63parser.className({value: 'button'});64// => .button65```66 67Arguments:68 69* `props (object)`: The new node's properties.70 71### `parser.combinator([props])`72 73Creates a new selector combinator.74 75```js76parser.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 the86 parser always just a single space (`" "`). For descendant selectors with no87 comments, additional space is now stored in `node.spaces.before`. Depending88 on the location of comments, additional spaces may be stored in89 `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. The92 `node.value` is name after being unescaped and normalized as lowercase. The93 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```js101parser.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```js114parser.id({value: 'search'});115// => #search116```117 118Arguments:119 120* `props (object)`: The new node's properties.121 122### `parser.nesting([props])`123 124Creates a new nesting selector.125 126```js127parser.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```js140parser.pseudo({value: '::before'});141// => ::before142```143 144Arguments:145 146* `props (object)`: The new node's properties.147 148### `parser.root([props])`149 150Creates a new root node.151 152```js153parser.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```js166parser.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```js179parser.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```js192parser.tag({value: 'button'});193// => button194```195 196Arguments:197 198* `props (object)`: The new node's properties.199 200### `parser.universal([props])`201 202Creates a new universal selector.203 204```js205parser.universal();206// => *207```208 209Arguments:210 211* `props (object)`: The new node's properties.212 213## Node types214 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 for221example you can get `id` by querying `parser.ID`.222 223```js224parser.attribute({attribute: 'href'}).type;225// => 'attribute'226```227 228### `node.parent`229 230Returns the parent node.231 232```js233root.nodes[0].parent === root;234```235 236### `node.toString()`, `String(node)`, or `'' + node`237 238Returns a string representation of the node.239 240```js241const id = parser.id({value: 'search'});242console.log(String(id));243// => #search244```245 246### `node.next()` & `node.prev()`247 248Returns the next/previous child of the parent node.249 250```js251const 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```js262const 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```js276if (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 the284original might have had.285 286```js287const cloned = node.clone();288```289 290### `node.isAtPosition(line, column)`291 292Return a `boolean` indicating whether this node includes the character at the293position of the given line and column. Returns `undefined` if the nodes lack294sufficient 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` and304`node.spaces.after`. So for example, these spaces will be moved as they have305no semantic meaning:306 307```css308 h1 , h2 {}309```310 311For descendent selectors, the value is always a single space.312 313```css314h1 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```css326.foo,327 .bar {}328```329 330... will contain the following `source` object.331 332```js333source: {334 start: {335 line: 2,336 column: 3337 },338 end: {339 line: 2,340 column: 6341 }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```css352.foo, .bar, .baz {}353```354 355## Container types356 357The `root`, `selector`, and `pseudo` nodes have some helper methods for working358with their children.359 360### `container.nodes`361 362An array of the container's children.363 364```js365// Input: h1 h2366selectors.at(0).nodes.length // => 3367selectors.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```js376selector.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```js385selector.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```js398// Input: :not(.foo),\n#foo > :matches(ol, ul)399selector.atPosition(1, 1); // => :not(.foo)400selector.atPosition(2, 1); // => \n#foo401```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```js413selector.index(selector.nodes[2]) // => 2414```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```js425container.length === container.nodes.length426```427 428### `container` Array iterators429 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; recursive440iteration 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```js448let 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 adding458or 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 are468`container` types.469 470```js471selectors.walk((selector, index) => {472 // all nodes473});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` proxies485 486The container class provides proxy methods for iterating over types of nodes,487so that it is easier to write modules that target specific selectors. Those488methods 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` from503a callback. It returns an array of arrays, where each inner array corresponds504to the groups that you created via the callback.505 506```js507// (input) => h1 h2>>h3508const 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 set523the parent property of the node to this container.524 525```js526const 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```js539selectors.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 use555`node.remove()` if you would like to remove just a single node.556 557```js558selector.length // => 2559selector.remove(id)560selector.length // => 1;561id.parent // undefined562```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```js573selector.removeAll();574selector.length // => 0575```576 577## Root nodes578 579A root node represents a comma separated list of selectors. Indeed, all580a 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 to586support parsing of legacy CSS hacks.587 588## Selector nodes589 590A selector node represents a single complex selector. For example, this591selector string `h1 h2 h3, [href] > p`, is represented as two selector nodes.592It has no special functionality of its own.593 594## Pseudo nodes595 596A pseudo selector extends a container node; if it has any parameters of its597own (such as `h1:not(h2, h3)`), they will be its children. Note that the pseudo598`value` will always contain the colons preceding the pseudo identifier. This599is so that both `:before` and `::before` are properly represented in the AST.600 601## Attribute nodes602 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```css609[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 the622 start of the node of the output string. This is useful in raising623 error messages about a specific part of the attribute, especially624 in combination with `attribute.sourceIndex`.625 626 Returns `-1` if the name is invalid or the value doesn't exist in this627 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 name634 * `"attributeNS"` - the start of the attribute or its namespace635 * `"operator"` - the match operator of the attribute636 * `"value"` - The value (string or identifier)637 * `"insensitive"` - the case insensitivity flag638 639### `attribute.raws.unquoted`640 641Returns the unquoted content of the attribute's value.642Remains `undefined` if there is no attribute value.643 644```css645[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 spaces654around the element, the parts of the attribute can also have spaces before655and after them. The for each of `attribute`, `operator`, `value` and656`insensitive` there is corresponding property of the same nam in657`node.spaces` that has an optional `before` or `after` string containing only658whitespace.659 660Note that corresponding values in `attributes.raws.spaces` contain values661including any comments. If set, these values will override the662`attribute.spaces` value. Take care to remove them if changing663`attribute.spaces`.664 665### `attribute.raws`666 667The raws object stores comments and other information necessary to re-render668the 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 attribute674then 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 the679raws 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```js684{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 postcss715 `Rule` node instead of a string, then that Rule's selector is updated716 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 selector723will be updated with the resulting string.724 725**Example:**726 727```js728const parser = require("postcss-selector-parser");729const processor = parser();730 731let result = processor.processSync(' .class');732console.log(result);733// => .class734 735// Asynchronous operation736let promise = processor.process(' .class').then(result => {737 console.log(result)738 // => .class739});740 741// To have the parser normalize whitespace values, utilize the options742result = processor.processSync(' .class ', {lossless: false});743console.log(result);744// => .class745 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,.class752 console.log("rule:", rule.selector);753 // => rule: #foo>a,.class754})755```756 757Arguments:758 759* `selectors (string|postcss.Rule)`: Either a selector string or a PostCSS Rule760 node.761* `[options] (object)`: Process options762 763 764### `ast|astSync(selectors, [options])`765 766Like `process()` and `processSync()` but after767processing the `selectors` these methods return the `Root` node of the result768instead of a string.769 770Note: when the `updateSelector` option is set, the rule's selector771will be updated with the resulting string.772 773### `transform|transformSync(selectors, [options])`774 775Like `process()` and `processSync()` but after776processing the `selectors` these methods return the value returned by the777processor callback.778 779Note: when the `updateSelector` option is set, the rule's selector780will be updated with the resulting string.781 782### Error Handling Within Selector Processors783 784The root node passed to the selector processor callback785has a method `error(message, options)` that returns an786error object. This method should always be used to raise787errors relating to the syntax of selectors. The options788to this method are passed to postcss's error constructor789([documentation](http://postcss.org/api/#container-error)).790 791#### Async Error Example792 793```js794let 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.value802 }));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 them828//829// > 1 | .foo-bar {830// | ^831// 2 | color: red;832// 3 | }833```834 835#### Synchronous Error Example836 837```js838let 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.value845 });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 them867//868// > 1 | .foo-bar {869// | ^870// 2 | color: red;871// 3 | }872```873 