1 | # API Documentation
|
---|
2 |
|
---|
3 | *Please use only this documented API when working with the parser. Methods
|
---|
4 | not documented here are subject to change at any point.*
|
---|
5 |
|
---|
6 | ## `parser` function
|
---|
7 |
|
---|
8 | This is the module's main entry point.
|
---|
9 |
|
---|
10 | ```js
|
---|
11 | const parser = require('postcss-selector-parser');
|
---|
12 | ```
|
---|
13 |
|
---|
14 | ### `parser([transform], [options])`
|
---|
15 |
|
---|
16 | Creates a new `processor` instance
|
---|
17 |
|
---|
18 | ```js
|
---|
19 | const processor = parser();
|
---|
20 | ```
|
---|
21 |
|
---|
22 | Or, with optional transform function
|
---|
23 |
|
---|
24 | ```js
|
---|
25 | const transform = selectors => {
|
---|
26 | selectors.walkUniversals(selector => {
|
---|
27 | selector.remove();
|
---|
28 | });
|
---|
29 | };
|
---|
30 |
|
---|
31 | const processor = parser(transform)
|
---|
32 |
|
---|
33 | // Example
|
---|
34 | const result = processor.processSync('*.class');
|
---|
35 | // => .class
|
---|
36 | ```
|
---|
37 |
|
---|
38 | [See processor documentation](#processor)
|
---|
39 |
|
---|
40 | Arguments:
|
---|
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 |
|
---|
47 | Creates a new attribute selector.
|
---|
48 |
|
---|
49 | ```js
|
---|
50 | parser.attribute({attribute: 'href'});
|
---|
51 | // => [href]
|
---|
52 | ```
|
---|
53 |
|
---|
54 | Arguments:
|
---|
55 |
|
---|
56 | * `props (object)`: The new node's properties.
|
---|
57 |
|
---|
58 | ### `parser.className([props])`
|
---|
59 |
|
---|
60 | Creates a new class selector.
|
---|
61 |
|
---|
62 | ```js
|
---|
63 | parser.className({value: 'button'});
|
---|
64 | // => .button
|
---|
65 | ```
|
---|
66 |
|
---|
67 | Arguments:
|
---|
68 |
|
---|
69 | * `props (object)`: The new node's properties.
|
---|
70 |
|
---|
71 | ### `parser.combinator([props])`
|
---|
72 |
|
---|
73 | Creates a new selector combinator.
|
---|
74 |
|
---|
75 | ```js
|
---|
76 | parser.combinator({value: '+'});
|
---|
77 | // => +
|
---|
78 | ```
|
---|
79 |
|
---|
80 | Arguments:
|
---|
81 |
|
---|
82 | * `props (object)`: The new node's properties.
|
---|
83 |
|
---|
84 | Notes:
|
---|
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 |
|
---|
98 | Creates a new comment.
|
---|
99 |
|
---|
100 | ```js
|
---|
101 | parser.comment({value: '/* Affirmative, Dave. I read you. */'});
|
---|
102 | // => /* Affirmative, Dave. I read you. */
|
---|
103 | ```
|
---|
104 |
|
---|
105 | Arguments:
|
---|
106 |
|
---|
107 | * `props (object)`: The new node's properties.
|
---|
108 |
|
---|
109 | ### `parser.id([props])`
|
---|
110 |
|
---|
111 | Creates a new id selector.
|
---|
112 |
|
---|
113 | ```js
|
---|
114 | parser.id({value: 'search'});
|
---|
115 | // => #search
|
---|
116 | ```
|
---|
117 |
|
---|
118 | Arguments:
|
---|
119 |
|
---|
120 | * `props (object)`: The new node's properties.
|
---|
121 |
|
---|
122 | ### `parser.nesting([props])`
|
---|
123 |
|
---|
124 | Creates a new nesting selector.
|
---|
125 |
|
---|
126 | ```js
|
---|
127 | parser.nesting();
|
---|
128 | // => &
|
---|
129 | ```
|
---|
130 |
|
---|
131 | Arguments:
|
---|
132 |
|
---|
133 | * `props (object)`: The new node's properties.
|
---|
134 |
|
---|
135 | ### `parser.pseudo([props])`
|
---|
136 |
|
---|
137 | Creates a new pseudo selector.
|
---|
138 |
|
---|
139 | ```js
|
---|
140 | parser.pseudo({value: '::before'});
|
---|
141 | // => ::before
|
---|
142 | ```
|
---|
143 |
|
---|
144 | Arguments:
|
---|
145 |
|
---|
146 | * `props (object)`: The new node's properties.
|
---|
147 |
|
---|
148 | ### `parser.root([props])`
|
---|
149 |
|
---|
150 | Creates a new root node.
|
---|
151 |
|
---|
152 | ```js
|
---|
153 | parser.root();
|
---|
154 | // => (empty)
|
---|
155 | ```
|
---|
156 |
|
---|
157 | Arguments:
|
---|
158 |
|
---|
159 | * `props (object)`: The new node's properties.
|
---|
160 |
|
---|
161 | ### `parser.selector([props])`
|
---|
162 |
|
---|
163 | Creates a new selector node.
|
---|
164 |
|
---|
165 | ```js
|
---|
166 | parser.selector();
|
---|
167 | // => (empty)
|
---|
168 | ```
|
---|
169 |
|
---|
170 | Arguments:
|
---|
171 |
|
---|
172 | * `props (object)`: The new node's properties.
|
---|
173 |
|
---|
174 | ### `parser.string([props])`
|
---|
175 |
|
---|
176 | Creates a new string node.
|
---|
177 |
|
---|
178 | ```js
|
---|
179 | parser.string();
|
---|
180 | // => (empty)
|
---|
181 | ```
|
---|
182 |
|
---|
183 | Arguments:
|
---|
184 |
|
---|
185 | * `props (object)`: The new node's properties.
|
---|
186 |
|
---|
187 | ### `parser.tag([props])`
|
---|
188 |
|
---|
189 | Creates a new tag selector.
|
---|
190 |
|
---|
191 | ```js
|
---|
192 | parser.tag({value: 'button'});
|
---|
193 | // => button
|
---|
194 | ```
|
---|
195 |
|
---|
196 | Arguments:
|
---|
197 |
|
---|
198 | * `props (object)`: The new node's properties.
|
---|
199 |
|
---|
200 | ### `parser.universal([props])`
|
---|
201 |
|
---|
202 | Creates a new universal selector.
|
---|
203 |
|
---|
204 | ```js
|
---|
205 | parser.universal();
|
---|
206 | // => *
|
---|
207 | ```
|
---|
208 |
|
---|
209 | Arguments:
|
---|
210 |
|
---|
211 | * `props (object)`: The new node's properties.
|
---|
212 |
|
---|
213 | ## Node types
|
---|
214 |
|
---|
215 | ### `node.type`
|
---|
216 |
|
---|
217 | A 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,
|
---|
220 | these constants are exposed on the main `parser` as uppercased keys. So for
|
---|
221 | example you can get `id` by querying `parser.ID`.
|
---|
222 |
|
---|
223 | ```js
|
---|
224 | parser.attribute({attribute: 'href'}).type;
|
---|
225 | // => 'attribute'
|
---|
226 | ```
|
---|
227 |
|
---|
228 | ### `node.parent`
|
---|
229 |
|
---|
230 | Returns the parent node.
|
---|
231 |
|
---|
232 | ```js
|
---|
233 | root.nodes[0].parent === root;
|
---|
234 | ```
|
---|
235 |
|
---|
236 | ### `node.toString()`, `String(node)`, or `'' + node`
|
---|
237 |
|
---|
238 | Returns a string representation of the node.
|
---|
239 |
|
---|
240 | ```js
|
---|
241 | const id = parser.id({value: 'search'});
|
---|
242 | console.log(String(id));
|
---|
243 | // => #search
|
---|
244 | ```
|
---|
245 |
|
---|
246 | ### `node.next()` & `node.prev()`
|
---|
247 |
|
---|
248 | Returns the next/previous child of the parent node.
|
---|
249 |
|
---|
250 | ```js
|
---|
251 | const next = id.next();
|
---|
252 | if (next && next.type !== 'combinator') {
|
---|
253 | throw new Error('Qualified IDs are not allowed!');
|
---|
254 | }
|
---|
255 | ```
|
---|
256 |
|
---|
257 | ### `node.replaceWith(node)`
|
---|
258 |
|
---|
259 | Replace a node with another.
|
---|
260 |
|
---|
261 | ```js
|
---|
262 | const attr = selectors.first.first;
|
---|
263 | const className = parser.className({value: 'test'});
|
---|
264 | attr.replaceWith(className);
|
---|
265 | ```
|
---|
266 |
|
---|
267 | Arguments:
|
---|
268 |
|
---|
269 | * `node`: The node to substitute the original with.
|
---|
270 |
|
---|
271 | ### `node.remove()`
|
---|
272 |
|
---|
273 | Removes the node from its parent node.
|
---|
274 |
|
---|
275 | ```js
|
---|
276 | if (node.type === 'id') {
|
---|
277 | node.remove();
|
---|
278 | }
|
---|
279 | ```
|
---|
280 |
|
---|
281 | ### `node.clone()`
|
---|
282 |
|
---|
283 | Returns a copy of a node, detached from any parent containers that the
|
---|
284 | original might have had.
|
---|
285 |
|
---|
286 | ```js
|
---|
287 | const cloned = parser.id({value: 'search'});
|
---|
288 | String(cloned);
|
---|
289 |
|
---|
290 | // => #search
|
---|
291 | ```
|
---|
292 |
|
---|
293 | ### `node.isAtPosition(line, column)`
|
---|
294 |
|
---|
295 | Return a `boolean` indicating whether this node includes the character at the
|
---|
296 | position of the given line and column. Returns `undefined` if the nodes lack
|
---|
297 | sufficient source metadata to determine the position.
|
---|
298 |
|
---|
299 | Arguments:
|
---|
300 |
|
---|
301 | * `line`: 1-index based line number relative to the start of the selector.
|
---|
302 | * `column`: 1-index based column number relative to the start of the selector.
|
---|
303 |
|
---|
304 | ### `node.spaces`
|
---|
305 |
|
---|
306 | Extra whitespaces around the node will be moved into `node.spaces.before` and
|
---|
307 | `node.spaces.after`. So for example, these spaces will be moved as they have
|
---|
308 | no semantic meaning:
|
---|
309 |
|
---|
310 | ```css
|
---|
311 | h1 , h2 {}
|
---|
312 | ```
|
---|
313 |
|
---|
314 | For descendent selectors, the value is always a single space.
|
---|
315 |
|
---|
316 | ```css
|
---|
317 | h1 h2 {}
|
---|
318 | ```
|
---|
319 |
|
---|
320 | 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.
|
---|
321 |
|
---|
322 | ### `node.source`
|
---|
323 |
|
---|
324 | An object describing the node's start/end, line/column source position.
|
---|
325 |
|
---|
326 | Within the following CSS, the `.bar` class node ...
|
---|
327 |
|
---|
328 | ```css
|
---|
329 | .foo,
|
---|
330 | .bar {}
|
---|
331 | ```
|
---|
332 |
|
---|
333 | ... will contain the following `source` object.
|
---|
334 |
|
---|
335 | ```js
|
---|
336 | source: {
|
---|
337 | start: {
|
---|
338 | line: 2,
|
---|
339 | column: 3
|
---|
340 | },
|
---|
341 | end: {
|
---|
342 | line: 2,
|
---|
343 | column: 6
|
---|
344 | }
|
---|
345 | }
|
---|
346 | ```
|
---|
347 |
|
---|
348 | ### `node.sourceIndex`
|
---|
349 |
|
---|
350 | The zero-based index of the node within the original source string.
|
---|
351 |
|
---|
352 | Within the following CSS, the `.baz` class node will have a `sourceIndex` of `12`.
|
---|
353 |
|
---|
354 | ```css
|
---|
355 | .foo, .bar, .baz {}
|
---|
356 | ```
|
---|
357 |
|
---|
358 | ## Container types
|
---|
359 |
|
---|
360 | The `root`, `selector`, and `pseudo` nodes have some helper methods for working
|
---|
361 | with their children.
|
---|
362 |
|
---|
363 | ### `container.nodes`
|
---|
364 |
|
---|
365 | An array of the container's children.
|
---|
366 |
|
---|
367 | ```js
|
---|
368 | // Input: h1 h2
|
---|
369 | selectors.at(0).nodes.length // => 3
|
---|
370 | selectors.at(0).nodes[0].value // => 'h1'
|
---|
371 | selectors.at(0).nodes[1].value // => ' '
|
---|
372 | ```
|
---|
373 |
|
---|
374 | ### `container.first` & `container.last`
|
---|
375 |
|
---|
376 | The first/last child of the container.
|
---|
377 |
|
---|
378 | ```js
|
---|
379 | selector.first === selector.nodes[0];
|
---|
380 | selector.last === selector.nodes[selector.nodes.length - 1];
|
---|
381 | ```
|
---|
382 |
|
---|
383 | ### `container.at(index)`
|
---|
384 |
|
---|
385 | Returns the node at position `index`.
|
---|
386 |
|
---|
387 | ```js
|
---|
388 | selector.at(0) === selector.first;
|
---|
389 | selector.at(0) === selector.nodes[0];
|
---|
390 | ```
|
---|
391 |
|
---|
392 | Arguments:
|
---|
393 |
|
---|
394 | * `index`: The index of the node to return.
|
---|
395 |
|
---|
396 | ### `container.atPosition(line, column)`
|
---|
397 |
|
---|
398 | Returns the node at the source position `index`.
|
---|
399 |
|
---|
400 | ```js
|
---|
401 | selector.at(0) === selector.first;
|
---|
402 | selector.at(0) === selector.nodes[0];
|
---|
403 | ```
|
---|
404 |
|
---|
405 | Arguments:
|
---|
406 |
|
---|
407 | * `index`: The index of the node to return.
|
---|
408 |
|
---|
409 | ### `container.index(node)`
|
---|
410 |
|
---|
411 | Return the index of the node within its container.
|
---|
412 |
|
---|
413 | ```js
|
---|
414 | selector.index(selector.nodes[2]) // => 2
|
---|
415 | ```
|
---|
416 |
|
---|
417 | Arguments:
|
---|
418 |
|
---|
419 | * `node`: A node within the current container.
|
---|
420 |
|
---|
421 | ### `container.length`
|
---|
422 |
|
---|
423 | Proxy to the length of the container's nodes.
|
---|
424 |
|
---|
425 | ```js
|
---|
426 | container.length === container.nodes.length
|
---|
427 | ```
|
---|
428 |
|
---|
429 | ### `container` Array iterators
|
---|
430 |
|
---|
431 | The container class provides proxies to certain Array methods; these are:
|
---|
432 |
|
---|
433 | * `container.map === container.nodes.map`
|
---|
434 | * `container.reduce === container.nodes.reduce`
|
---|
435 | * `container.every === container.nodes.every`
|
---|
436 | * `container.some === container.nodes.some`
|
---|
437 | * `container.filter === container.nodes.filter`
|
---|
438 | * `container.sort === container.nodes.sort`
|
---|
439 |
|
---|
440 | Note that these methods only work on a container's immediate children; recursive
|
---|
441 | iteration is provided by `container.walk`.
|
---|
442 |
|
---|
443 | ### `container.each(callback)`
|
---|
444 |
|
---|
445 | Iterate the container's immediate children, calling `callback` for each child.
|
---|
446 | You may return `false` within the callback to break the iteration.
|
---|
447 |
|
---|
448 | ```js
|
---|
449 | let className;
|
---|
450 | selectors.each((selector, index) => {
|
---|
451 | if (selector.type === 'class') {
|
---|
452 | className = selector.value;
|
---|
453 | return false;
|
---|
454 | }
|
---|
455 | });
|
---|
456 | ```
|
---|
457 |
|
---|
458 | Note that unlike `Array#forEach()`, this iterator is safe to use whilst adding
|
---|
459 | or removing nodes from the container.
|
---|
460 |
|
---|
461 | Arguments:
|
---|
462 |
|
---|
463 | * `callback (function)`: A function to call for each node, which receives `node`
|
---|
464 | and `index` arguments.
|
---|
465 |
|
---|
466 | ### `container.walk(callback)`
|
---|
467 |
|
---|
468 | Like `container#each`, but will also iterate child nodes as long as they are
|
---|
469 | `container` types.
|
---|
470 |
|
---|
471 | ```js
|
---|
472 | selectors.walk((selector, index) => {
|
---|
473 | // all nodes
|
---|
474 | });
|
---|
475 | ```
|
---|
476 |
|
---|
477 | Arguments:
|
---|
478 |
|
---|
479 | * `callback (function)`: A function to call for each node, which receives `node`
|
---|
480 | and `index` arguments.
|
---|
481 |
|
---|
482 | This iterator is safe to use whilst mutating `container.nodes`,
|
---|
483 | like `container#each`.
|
---|
484 |
|
---|
485 | ### `container.walk` proxies
|
---|
486 |
|
---|
487 | The container class provides proxy methods for iterating over types of nodes,
|
---|
488 | so that it is easier to write modules that target specific selectors. Those
|
---|
489 | methods are:
|
---|
490 |
|
---|
491 | * `container.walkAttributes`
|
---|
492 | * `container.walkClasses`
|
---|
493 | * `container.walkCombinators`
|
---|
494 | * `container.walkComments`
|
---|
495 | * `container.walkIds`
|
---|
496 | * `container.walkNesting`
|
---|
497 | * `container.walkPseudos`
|
---|
498 | * `container.walkTags`
|
---|
499 | * `container.walkUniversals`
|
---|
500 |
|
---|
501 | ### `container.split(callback)`
|
---|
502 |
|
---|
503 | This method allows you to split a group of nodes by returning `true` from
|
---|
504 | a callback. It returns an array of arrays, where each inner array corresponds
|
---|
505 | to the groups that you created via the callback.
|
---|
506 |
|
---|
507 | ```js
|
---|
508 | // (input) => h1 h2>>h3
|
---|
509 | const list = selectors.first.split(selector => {
|
---|
510 | return selector.type === 'combinator';
|
---|
511 | });
|
---|
512 |
|
---|
513 | // (node values) => [['h1', ' '], ['h2', '>>'], ['h3']]
|
---|
514 | ```
|
---|
515 |
|
---|
516 | Arguments:
|
---|
517 |
|
---|
518 | * `callback (function)`: A function to call for each node, which receives `node`
|
---|
519 | as an argument.
|
---|
520 |
|
---|
521 | ### `container.prepend(node)` & `container.append(node)`
|
---|
522 |
|
---|
523 | Add a node to the start/end of the container. Note that doing so will set
|
---|
524 | the parent property of the node to this container.
|
---|
525 |
|
---|
526 | ```js
|
---|
527 | const id = parser.id({value: 'search'});
|
---|
528 | selector.append(id);
|
---|
529 | ```
|
---|
530 |
|
---|
531 | Arguments:
|
---|
532 |
|
---|
533 | * `node`: The node to add.
|
---|
534 |
|
---|
535 | ### `container.insertBefore(old, new)` & `container.insertAfter(old, new)`
|
---|
536 |
|
---|
537 | Add a node before or after an existing node in a container:
|
---|
538 |
|
---|
539 | ```js
|
---|
540 | selectors.walk(selector => {
|
---|
541 | if (selector.type !== 'class') {
|
---|
542 | const className = parser.className({value: 'theme-name'});
|
---|
543 | selector.parent.insertAfter(selector, className);
|
---|
544 | }
|
---|
545 | });
|
---|
546 | ```
|
---|
547 |
|
---|
548 | Arguments:
|
---|
549 |
|
---|
550 | * `old`: The existing node in the container.
|
---|
551 | * `new`: The new node to add before/after the existing node.
|
---|
552 |
|
---|
553 | ### `container.removeChild(node)`
|
---|
554 |
|
---|
555 | Remove the node from the container. Note that you can also use
|
---|
556 | `node.remove()` if you would like to remove just a single node.
|
---|
557 |
|
---|
558 | ```js
|
---|
559 | selector.length // => 2
|
---|
560 | selector.remove(id)
|
---|
561 | selector.length // => 1;
|
---|
562 | id.parent // undefined
|
---|
563 | ```
|
---|
564 |
|
---|
565 | Arguments:
|
---|
566 |
|
---|
567 | * `node`: The node to remove.
|
---|
568 |
|
---|
569 | ### `container.removeAll()` or `container.empty()`
|
---|
570 |
|
---|
571 | Remove all children from the container.
|
---|
572 |
|
---|
573 | ```js
|
---|
574 | selector.removeAll();
|
---|
575 | selector.length // => 0
|
---|
576 | ```
|
---|
577 |
|
---|
578 | ## Root nodes
|
---|
579 |
|
---|
580 | A root node represents a comma separated list of selectors. Indeed, all
|
---|
581 | a root's `toString()` method does is join its selector children with a ','.
|
---|
582 | Other than this, it has no special functionality and acts like a container.
|
---|
583 |
|
---|
584 | ### `root.trailingComma`
|
---|
585 |
|
---|
586 | This will be set to `true` if the input has a trailing comma, in order to
|
---|
587 | support parsing of legacy CSS hacks.
|
---|
588 |
|
---|
589 | ## Selector nodes
|
---|
590 |
|
---|
591 | A selector node represents a single complex selector. For example, this
|
---|
592 | selector string `h1 h2 h3, [href] > p`, is represented as two selector nodes.
|
---|
593 | It has no special functionality of its own.
|
---|
594 |
|
---|
595 | ## Pseudo nodes
|
---|
596 |
|
---|
597 | A pseudo selector extends a container node; if it has any parameters of its
|
---|
598 | own (such as `h1:not(h2, h3)`), they will be its children. Note that the pseudo
|
---|
599 | `value` will always contain the colons preceding the pseudo identifier. This
|
---|
600 | is so that both `:before` and `::before` are properly represented in the AST.
|
---|
601 |
|
---|
602 | ## Attribute nodes
|
---|
603 |
|
---|
604 | ### `attribute.quoted`
|
---|
605 |
|
---|
606 | Returns `true` if the attribute's value is wrapped in quotation marks, false if it is not.
|
---|
607 | Remains `undefined` if there is no attribute value.
|
---|
608 |
|
---|
609 | ```css
|
---|
610 | [href=foo] /* false */
|
---|
611 | [href='foo'] /* true */
|
---|
612 | [href="foo"] /* true */
|
---|
613 | [href] /* undefined */
|
---|
614 | ```
|
---|
615 |
|
---|
616 | ### `attribute.qualifiedAttribute`
|
---|
617 |
|
---|
618 | Returns the attribute name qualified with the namespace if one is given.
|
---|
619 |
|
---|
620 | ### `attribute.offsetOf(part)`
|
---|
621 |
|
---|
622 | Returns the offset of the attribute part specified relative to the
|
---|
623 | start of the node of the output string. This is useful in raising
|
---|
624 | error messages about a specific part of the attribute, especially
|
---|
625 | in combination with `attribute.sourceIndex`.
|
---|
626 |
|
---|
627 | Returns `-1` if the name is invalid or the value doesn't exist in this
|
---|
628 | attribute.
|
---|
629 |
|
---|
630 | The legal values for `part` are:
|
---|
631 |
|
---|
632 | * `"ns"` - alias for "namespace"
|
---|
633 | * `"namespace"` - the namespace if it exists.
|
---|
634 | * `"attribute"` - the attribute name
|
---|
635 | * `"attributeNS"` - the start of the attribute or its namespace
|
---|
636 | * `"operator"` - the match operator of the attribute
|
---|
637 | * `"value"` - The value (string or identifier)
|
---|
638 | * `"insensitive"` - the case insensitivity flag
|
---|
639 |
|
---|
640 | ### `attribute.raws.unquoted`
|
---|
641 |
|
---|
642 | Returns the unquoted content of the attribute's value.
|
---|
643 | Remains `undefined` if there is no attribute value.
|
---|
644 |
|
---|
645 | ```css
|
---|
646 | [href=foo] /* foo */
|
---|
647 | [href='foo'] /* foo */
|
---|
648 | [href="foo"] /* foo */
|
---|
649 | [href] /* undefined */
|
---|
650 | ```
|
---|
651 |
|
---|
652 | ### `attribute.spaces`
|
---|
653 |
|
---|
654 | Like `node.spaces` with the `before` and `after` values containing the spaces
|
---|
655 | around the element, the parts of the attribute can also have spaces before
|
---|
656 | and after them. The for each of `attribute`, `operator`, `value` and
|
---|
657 | `insensitive` there is corresponding property of the same nam in
|
---|
658 | `node.spaces` that has an optional `before` or `after` string containing only
|
---|
659 | whitespace.
|
---|
660 |
|
---|
661 | Note that corresponding values in `attributes.raws.spaces` contain values
|
---|
662 | including any comments. If set, these values will override the
|
---|
663 | `attribute.spaces` value. Take care to remove them if changing
|
---|
664 | `attribute.spaces`.
|
---|
665 |
|
---|
666 | ### `attribute.raws`
|
---|
667 |
|
---|
668 | The raws object stores comments and other information necessary to re-render
|
---|
669 | the node exactly as it was in the source.
|
---|
670 |
|
---|
671 | If a comment is embedded within the identifiers for the `namespace`, `attribute`
|
---|
672 | or `value` then a property is placed in the raws for that value containing the full source of the propery including comments.
|
---|
673 |
|
---|
674 | If a comment is embedded within the space between parts of the attribute
|
---|
675 | then the raw for that space is set accordingly.
|
---|
676 |
|
---|
677 | Setting an attribute's property `raws` value to be deleted.
|
---|
678 |
|
---|
679 | For now, changing the spaces required also updating or removing any of the
|
---|
680 | raws values that override them.
|
---|
681 |
|
---|
682 | Example: `[ /*before*/ href /* after-attr */ = /* after-operator */ te/*inside-value*/st/* wow */ /*omg*/i/*bbq*/ /*whodoesthis*/]` would parse as:
|
---|
683 |
|
---|
684 | ```js
|
---|
685 | {
|
---|
686 | attribute: "href",
|
---|
687 | operator: "=",
|
---|
688 | value: "test",
|
---|
689 | spaces: {
|
---|
690 | before: '',
|
---|
691 | after: '',
|
---|
692 | attribute: { before: ' ', after: ' ' },
|
---|
693 | operator: { after: ' ' },
|
---|
694 | value: { after: ' ' },
|
---|
695 | insensitive: { after: ' ' }
|
---|
696 | },
|
---|
697 | raws: {
|
---|
698 | spaces: {
|
---|
699 | attribute: { before: ' /*before*/ ', after: ' /* after-attr */ ' },
|
---|
700 | operator: { after: ' /* after-operator */ ' },
|
---|
701 | value: { after: '/* wow */ /*omg*/' },
|
---|
702 | insensitive: { after: '/*bbq*/ /*whodoesthis*/' }
|
---|
703 | },
|
---|
704 | unquoted: 'test',
|
---|
705 | value: 'te/*inside-value*/st'
|
---|
706 | }
|
---|
707 | }
|
---|
708 | ```
|
---|
709 |
|
---|
710 | ## `Processor`
|
---|
711 |
|
---|
712 | ### `ProcessorOptions`
|
---|
713 |
|
---|
714 | * `lossless` - When `true`, whitespace is preserved. Defaults to `true`.
|
---|
715 | * `updateSelector` - When `true`, if any processor methods are passed a postcss
|
---|
716 | `Rule` node instead of a string, then that Rule's selector is updated
|
---|
717 | with the results of the processing. Defaults to `true`.
|
---|
718 |
|
---|
719 | ### `process|processSync(selectors, [options])`
|
---|
720 |
|
---|
721 | Processes the `selectors`, returning a string from the result of processing.
|
---|
722 |
|
---|
723 | Note: when the `updateSelector` option is set, the rule's selector
|
---|
724 | will be updated with the resulting string.
|
---|
725 |
|
---|
726 | **Example:**
|
---|
727 |
|
---|
728 | ```js
|
---|
729 | const parser = require("postcss-selector-parser");
|
---|
730 | const processor = parser();
|
---|
731 |
|
---|
732 | let result = processor.processSync(' .class');
|
---|
733 | console.log(result);
|
---|
734 | // => .class
|
---|
735 |
|
---|
736 | // Asynchronous operation
|
---|
737 | let promise = processor.process(' .class').then(result => {
|
---|
738 | console.log(result)
|
---|
739 | // => .class
|
---|
740 | });
|
---|
741 |
|
---|
742 | // To have the parser normalize whitespace values, utilize the options
|
---|
743 | result = processor.processSync(' .class ', {lossless: false});
|
---|
744 | console.log(result);
|
---|
745 | // => .class
|
---|
746 |
|
---|
747 | // For better syntax errors, pass a PostCSS Rule node.
|
---|
748 | const postcss = require('postcss');
|
---|
749 | rule = postcss.rule({selector: ' #foo > a, .class '});
|
---|
750 | processor.process(rule, {lossless: false, updateSelector: true}).then(result => {
|
---|
751 | console.log(result);
|
---|
752 | // => #foo>a,.class
|
---|
753 | console.log("rule:", rule.selector);
|
---|
754 | // => rule: #foo>a,.class
|
---|
755 | })
|
---|
756 | ```
|
---|
757 |
|
---|
758 | Arguments:
|
---|
759 |
|
---|
760 | * `selectors (string|postcss.Rule)`: Either a selector string or a PostCSS Rule
|
---|
761 | node.
|
---|
762 | * `[options] (object)`: Process options
|
---|
763 |
|
---|
764 |
|
---|
765 | ### `ast|astSync(selectors, [options])`
|
---|
766 |
|
---|
767 | Like `process()` and `processSync()` but after
|
---|
768 | processing the `selectors` these methods return the `Root` node of the result
|
---|
769 | instead of a string.
|
---|
770 |
|
---|
771 | Note: when the `updateSelector` option is set, the rule's selector
|
---|
772 | will be updated with the resulting string.
|
---|
773 |
|
---|
774 | ### `transform|transformSync(selectors, [options])`
|
---|
775 |
|
---|
776 | Like `process()` and `processSync()` but after
|
---|
777 | processing the `selectors` these methods return the value returned by the
|
---|
778 | processor callback.
|
---|
779 |
|
---|
780 | Note: when the `updateSelector` option is set, the rule's selector
|
---|
781 | will be updated with the resulting string.
|
---|
782 |
|
---|
783 | ### Error Handling Within Selector Processors
|
---|
784 |
|
---|
785 | The root node passed to the selector processor callback
|
---|
786 | has a method `error(message, options)` that returns an
|
---|
787 | error object. This method should always be used to raise
|
---|
788 | errors relating to the syntax of selectors. The options
|
---|
789 | to this method are passed to postcss's error constructor
|
---|
790 | ([documentation](http://api.postcss.org/Container.html#error)).
|
---|
791 |
|
---|
792 | #### Async Error Example
|
---|
793 |
|
---|
794 | ```js
|
---|
795 | let processor = (root) => {
|
---|
796 | return new Promise((resolve, reject) => {
|
---|
797 | root.walkClasses((classNode) => {
|
---|
798 | if (/^(.*)[-_]/.test(classNode.value)) {
|
---|
799 | let msg = "classes may not have underscores or dashes in them";
|
---|
800 | reject(root.error(msg, {
|
---|
801 | index: classNode.sourceIndex + RegExp.$1.length + 1,
|
---|
802 | word: classNode.value
|
---|
803 | }));
|
---|
804 | }
|
---|
805 | });
|
---|
806 | resolve();
|
---|
807 | });
|
---|
808 | };
|
---|
809 |
|
---|
810 | const postcss = require("postcss");
|
---|
811 | const parser = require("postcss-selector-parser");
|
---|
812 | const selectorProcessor = parser(processor);
|
---|
813 | const plugin = postcss.plugin('classValidator', (options) => {
|
---|
814 | return (root) => {
|
---|
815 | let promises = [];
|
---|
816 | root.walkRules(rule => {
|
---|
817 | promises.push(selectorProcessor.process(rule));
|
---|
818 | });
|
---|
819 | return Promise.all(promises);
|
---|
820 | };
|
---|
821 | });
|
---|
822 | postcss(plugin()).process(`
|
---|
823 | .foo-bar {
|
---|
824 | color: red;
|
---|
825 | }
|
---|
826 | `.trim(), {from: 'test.css'}).catch((e) => console.error(e.toString()));
|
---|
827 |
|
---|
828 | // CssSyntaxError: classValidator: ./test.css:1:5: classes may not have underscores or dashes in them
|
---|
829 | //
|
---|
830 | // > 1 | .foo-bar {
|
---|
831 | // | ^
|
---|
832 | // 2 | color: red;
|
---|
833 | // 3 | }
|
---|
834 | ```
|
---|
835 |
|
---|
836 | #### Synchronous Error Example
|
---|
837 |
|
---|
838 | ```js
|
---|
839 | let processor = (root) => {
|
---|
840 | root.walkClasses((classNode) => {
|
---|
841 | if (/.*[-_]/.test(classNode.value)) {
|
---|
842 | let msg = "classes may not have underscores or dashes in them";
|
---|
843 | throw root.error(msg, {
|
---|
844 | index: classNode.sourceIndex,
|
---|
845 | word: classNode.value
|
---|
846 | });
|
---|
847 | }
|
---|
848 | });
|
---|
849 | };
|
---|
850 |
|
---|
851 | const postcss = require("postcss");
|
---|
852 | const parser = require("postcss-selector-parser");
|
---|
853 | const selectorProcessor = parser(processor);
|
---|
854 | const plugin = postcss.plugin('classValidator', (options) => {
|
---|
855 | return (root) => {
|
---|
856 | root.walkRules(rule => {
|
---|
857 | selectorProcessor.processSync(rule);
|
---|
858 | });
|
---|
859 | };
|
---|
860 | });
|
---|
861 | postcss(plugin()).process(`
|
---|
862 | .foo-bar {
|
---|
863 | color: red;
|
---|
864 | }
|
---|
865 | `.trim(), {from: 'test.css'}).catch((e) => console.error(e.toString()));
|
---|
866 |
|
---|
867 | // CssSyntaxError: classValidator: ./test.css:1:5: classes may not have underscores or dashes in them
|
---|
868 | //
|
---|
869 | // > 1 | .foo-bar {
|
---|
870 | // | ^
|
---|
871 | // 2 | color: red;
|
---|
872 | // 3 | }
|
---|
873 | ```
|
---|