0
# Node Manipulation
1
2
Base methods available on all AST nodes for manipulation, navigation, and tree modification. Includes specialized container methods for nodes that can have children.
3
4
## Capabilities
5
6
### Base Node Interface
7
8
Common properties and methods available on all AST nodes.
9
10
```javascript { .api }
11
/**
12
* Base interface for all AST nodes
13
*/
14
interface Base {
15
/** Node type identifier */
16
type: string;
17
/** Parent container node */
18
parent: Container;
19
/** Node value/content */
20
value: string;
21
/** Whitespace configuration */
22
spaces: Spaces;
23
/** Source location information */
24
source?: NodeSource;
25
/** Index in source string */
26
sourceIndex: number;
27
/** Raw whitespace before node */
28
rawSpaceBefore: string;
29
/** Raw whitespace after node */
30
rawSpaceAfter: string;
31
/** Raw values for precise output control */
32
raws?: NodeRaws;
33
34
/**
35
* Remove this node from its parent
36
* @returns The removed node
37
*/
38
remove(): Node;
39
40
/**
41
* Replace this node with other nodes
42
* @param nodes - Nodes to replace with
43
* @returns This node
44
*/
45
replaceWith(...nodes: Node[]): Node;
46
47
/**
48
* Get the next sibling node
49
* @returns Next sibling or undefined
50
*/
51
next(): Node | undefined;
52
53
/**
54
* Get the previous sibling node
55
* @returns Previous sibling or undefined
56
*/
57
prev(): Node | undefined;
58
59
/**
60
* Clone this node
61
* @param opts - Override options
62
* @returns Cloned node
63
*/
64
clone(opts?: {[key: string]: any}): this;
65
66
/**
67
* Check if node includes character at given position
68
* @param line - 1-based line number
69
* @param column - 1-based column number
70
* @returns Whether node is at position
71
*/
72
isAtPosition(line: number, column: number): boolean | undefined;
73
74
/**
75
* Set property with escaped value
76
* @param name - Property name
77
* @param value - Unescaped value
78
* @param valueEscaped - Escaped value
79
*/
80
setPropertyAndEscape(name: string, value: any, valueEscaped: string): void;
81
82
/**
83
* Set property without escaping
84
* @param name - Property name
85
* @param value - Value (both escaped and unescaped)
86
*/
87
setPropertyWithoutEscape(name: string, value: any): void;
88
89
/**
90
* Append to property with escaped value
91
* @param name - Property name
92
* @param value - Unescaped value
93
* @param valueEscaped - Escaped value
94
*/
95
appendToPropertyAndEscape(name: string, value: any, valueEscaped: string): void;
96
97
/**
98
* Convert node to string representation
99
* @returns String representation
100
*/
101
toString(): string;
102
}
103
104
interface NodeRaws {
105
/** Raw value as it appeared in source */
106
value?: string;
107
/** Raw spacing information */
108
spaces?: {
109
before?: string;
110
after?: string;
111
};
112
}
113
```
114
115
**Usage Examples:**
116
117
```javascript
118
const parser = require('postcss-selector-parser');
119
120
const ast = parser().astSync('.class1, .class2');
121
const firstSelector = ast.first;
122
const classNode = firstSelector.first;
123
124
// Navigate nodes
125
const nextNode = classNode.next();
126
const prevNode = classNode.prev();
127
128
// Clone node
129
const clonedNode = classNode.clone();
130
131
// Remove node
132
classNode.remove();
133
134
// Replace node
135
const newClass = parser.className({ value: 'new-class' });
136
classNode.replaceWith(newClass);
137
```
138
139
### Container Interface
140
141
Methods available on nodes that can contain children (Root, Selector, Pseudo).
142
143
```javascript { .api }
144
/**
145
* Interface for nodes that can contain children
146
*/
147
interface Container extends Base {
148
/** Array of child nodes */
149
nodes: Node[];
150
151
/**
152
* Append a child node
153
* @param node - Node to append
154
* @returns This container
155
*/
156
append(node: Node): this;
157
158
/**
159
* Prepend a child node
160
* @param node - Node to prepend
161
* @returns This container
162
*/
163
prepend(node: Node): this;
164
165
/**
166
* Get child node at index
167
* @param index - Child index
168
* @returns Child node
169
*/
170
at(index: number): Node;
171
172
/**
173
* Get most specific node at line/column position
174
* @param line - 1-based line number
175
* @param column - 1-based column number
176
* @returns Node at position
177
*/
178
atPosition(line: number, column: number): Node;
179
180
/**
181
* Get index of child node
182
* @param child - Child node
183
* @returns Index of child
184
*/
185
index(child: Node): number;
186
187
/** First child node */
188
readonly first: Node;
189
190
/** Last child node */
191
readonly last: Node;
192
193
/** Number of child nodes */
194
readonly length: number;
195
196
/**
197
* Remove specific child node
198
* @param child - Child to remove
199
* @returns This container
200
*/
201
removeChild(child: Node): this;
202
203
/**
204
* Remove all child nodes
205
* @returns This container
206
*/
207
removeAll(): this;
208
209
/**
210
* Remove all child nodes (alias for removeAll)
211
* @returns This container
212
*/
213
empty(): this;
214
215
/**
216
* Insert nodes after existing node
217
* @param oldNode - Reference node
218
* @param newNode - Node to insert
219
* @param restNodes - Additional nodes to insert
220
* @returns This container
221
*/
222
insertAfter(oldNode: Node, newNode: Node, ...restNodes: Node[]): this;
223
224
/**
225
* Insert nodes before existing node
226
* @param oldNode - Reference node
227
* @param newNode - Node to insert
228
* @param restNodes - Additional nodes to insert
229
* @returns This container
230
*/
231
insertBefore(oldNode: Node, newNode: Node, ...restNodes: Node[]): this;
232
}
233
```
234
235
**Usage Examples:**
236
237
```javascript
238
const parser = require('postcss-selector-parser');
239
240
const root = parser.root();
241
const selector = parser.selector();
242
243
// Append children
244
selector.append(parser.tag({ value: 'div' }));
245
selector.append(parser.className({ value: 'container' }));
246
root.append(selector);
247
248
// Access children
249
const firstChild = selector.first; // div
250
const lastChild = selector.last; // .container
251
const childCount = selector.length; // 2
252
253
// Insert nodes
254
const id = parser.id({ value: 'main' });
255
selector.insertBefore(selector.first, id);
256
// Result: #main div.container
257
258
// Remove children
259
selector.removeChild(selector.last);
260
selector.empty(); // Remove all children
261
```
262
263
### Container Iteration Methods
264
265
Methods for iterating over and traversing container nodes.
266
267
```javascript { .api }
268
interface Container {
269
/**
270
* Iterate over direct children
271
* @param callback - Function called for each child
272
* @returns False if iteration was stopped
273
*/
274
each(callback: (node: Node, index: number) => boolean | void): boolean | undefined;
275
276
/**
277
* Walk all descendant nodes
278
* @param callback - Function called for each descendant
279
* @returns False if traversal was stopped
280
*/
281
walk(callback: (node: Node, index: number) => boolean | void): boolean | undefined;
282
283
/**
284
* Walk all attribute nodes
285
* @param callback - Function called for each attribute
286
* @returns False if traversal was stopped
287
*/
288
walkAttributes(callback: (node: Attribute) => boolean | void): boolean | undefined;
289
290
/**
291
* Walk all class nodes
292
* @param callback - Function called for each class
293
* @returns False if traversal was stopped
294
*/
295
walkClasses(callback: (node: ClassName) => boolean | void): boolean | undefined;
296
297
/**
298
* Walk all combinator nodes
299
* @param callback - Function called for each combinator
300
* @returns False if traversal was stopped
301
*/
302
walkCombinators(callback: (node: Combinator) => boolean | void): boolean | undefined;
303
304
/**
305
* Walk all comment nodes
306
* @param callback - Function called for each comment
307
* @returns False if traversal was stopped
308
*/
309
walkComments(callback: (node: Comment) => boolean | void): boolean | undefined;
310
311
/**
312
* Walk all ID nodes
313
* @param callback - Function called for each ID
314
* @returns False if traversal was stopped
315
*/
316
walkIds(callback: (node: Identifier) => boolean | void): boolean | undefined;
317
318
/**
319
* Walk all nesting nodes
320
* @param callback - Function called for each nesting node
321
* @returns False if traversal was stopped
322
*/
323
walkNesting(callback: (node: Nesting) => boolean | void): boolean | undefined;
324
325
/**
326
* Walk all pseudo nodes
327
* @param callback - Function called for each pseudo
328
* @returns False if traversal was stopped
329
*/
330
walkPseudos(callback: (node: Pseudo) => boolean | void): boolean | undefined;
331
332
/**
333
* Walk all tag nodes
334
* @param callback - Function called for each tag
335
* @returns False if traversal was stopped
336
*/
337
walkTags(callback: (node: Tag) => boolean | void): boolean | undefined;
338
339
/**
340
* Walk all universal nodes
341
* @param callback - Function called for each universal
342
* @returns False if traversal was stopped
343
*/
344
walkUniversals(callback: (node: Universal) => boolean | void): boolean | undefined;
345
}
346
```
347
348
**Usage Examples:**
349
350
```javascript
351
const parser = require('postcss-selector-parser');
352
353
const ast = parser().astSync('.btn, #header > .nav .item');
354
355
// Iterate over selectors
356
ast.each((selector, index) => {
357
console.log(`Selector ${index}:`, selector.toString());
358
});
359
360
// Walk all nodes
361
ast.walk((node, index) => {
362
console.log(`Node ${index}:`, node.type, node.value);
363
});
364
365
// Walk specific node types
366
ast.walkClasses(classNode => {
367
console.log('Class:', classNode.value);
368
// Outputs: btn, nav, item
369
});
370
371
ast.walkIds(idNode => {
372
console.log('ID:', idNode.value);
373
// Outputs: header
374
});
375
376
// Stop traversal early
377
ast.walkUniversals(universalNode => {
378
console.log('Universal selector found');
379
universalNode.remove(); // Remove all * selectors
380
});
381
382
ast.walkTags(tagNode => {
383
if (tagNode.value === 'div') {
384
return false; // Stop walking
385
}
386
});
387
```
388
389
### Container Array Methods
390
391
Array-like methods for working with child nodes.
392
393
```javascript { .api }
394
interface Container {
395
/**
396
* Split children based on predicate
397
* @param callback - Predicate function
398
* @returns Tuple of [matching, non-matching] nodes
399
*/
400
split(callback: (node: Node) => boolean): [Node[], Node[]];
401
402
/**
403
* Map over children
404
* @param callback - Mapping function
405
* @returns Array of mapped values
406
*/
407
map<T>(callback: (node: Node) => T): T[];
408
409
/**
410
* Reduce children to single value
411
* @param callback - Reducer function
412
* @param initialValue - Initial value
413
* @returns Reduced value
414
*/
415
reduce<T>(
416
callback: (
417
previousValue: T,
418
currentValue: Node,
419
currentIndex: number,
420
array: readonly Node[]
421
) => T,
422
initialValue?: T
423
): T;
424
425
/**
426
* Test if all children match predicate
427
* @param callback - Test function
428
* @returns True if all children match
429
*/
430
every(callback: (node: Node) => boolean): boolean;
431
432
/**
433
* Test if some children match predicate
434
* @param callback - Test function
435
* @returns True if any children match
436
*/
437
some(callback: (node: Node) => boolean): boolean;
438
439
/**
440
* Filter children by predicate
441
* @param callback - Filter function
442
* @returns Array of matching children
443
*/
444
filter(callback: (node: Node) => boolean): Node[];
445
446
/**
447
* Sort children by comparison function
448
* @param callback - Comparison function
449
* @returns Array of sorted children
450
*/
451
sort(callback: (nodeA: Node, nodeB: Node) => number): Node[];
452
}
453
```
454
455
**Usage Examples:**
456
457
```javascript
458
const parser = require('postcss-selector-parser');
459
460
const selector = parser().astSync('div.btn#main').first;
461
462
// Map node values
463
const values = selector.map(node => node.value);
464
// Result: ['div', 'btn', 'main']
465
466
// Filter by type
467
const classes = selector.filter(node => node.type === 'class');
468
469
// Check conditions
470
const hasId = selector.some(node => node.type === 'id');
471
const allHaveValues = selector.every(node => node.value);
472
473
// Split by type
474
const [tags, others] = selector.split(node => node.type === 'tag');
475
476
// Sort by value
477
const sorted = selector.sort((a, b) => a.value.localeCompare(b.value));
478
```
479
480
## Types
481
482
```javascript { .api }
483
interface Spaces {
484
before: string;
485
after: string;
486
[spaceType: string]: string | Partial<SpaceAround> | undefined;
487
}
488
489
interface SpaceAround {
490
before: string;
491
after: string;
492
}
493
494
interface NodeSource {
495
start?: { line: number; column: number };
496
end?: { line: number; column: number };
497
}
498
```