Skip to content

Commit 2a490fb

Browse files
committed
chore: move lengthy source code comments to API.md
1 parent 02571e2 commit 2a490fb

File tree

3 files changed

+173
-71
lines changed

3 files changed

+173
-71
lines changed

API.md

Lines changed: 166 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -580,15 +580,32 @@ console.log(nodes[0].text) // "(min-width: 768px)"
580580
581581
---
582582
583-
## `walk(ast, callback)`
583+
## `walk(ast, callback, depth?)`
584584
585-
Walk the AST in depth-first order.
585+
Walk the AST in depth-first order, calling the callback for each node.
586586
587587
```typescript
588-
function walk(node: CSSNode, callback: (node: CSSNode, depth: number) => void): void
588+
function walk(
589+
node: CSSNode,
590+
callback: (node: CSSNode, depth: number) => void | typeof SKIP | typeof BREAK,
591+
depth?: number
592+
): boolean
589593
```
590594

591-
**Example:**
595+
### Parameters
596+
597+
- **`node`** - The root node to start walking from
598+
- **`callback`** - Function to call for each node visited. Receives the node and its depth (0 for root).
599+
- Return `SKIP` to skip children of current node
600+
- Return `BREAK` to stop traversal entirely
601+
- Return nothing to continue normal traversal
602+
- **`depth`** - Starting depth (default: 0)
603+
604+
### Returns
605+
606+
`boolean` - Returns `false` if traversal was stopped with `BREAK`, otherwise `true`
607+
608+
### Example 1: Basic Walking
592609

593610
```typescript
594611
import { parse, walk } from '@projectwallace/css-parser'
@@ -607,6 +624,151 @@ walk(ast, (node, depth) => {
607624
// IDENTIFIER
608625
```
609626
627+
### Example 2: Skip Nested Rules
628+
629+
```typescript
630+
import { parse, walk, SKIP, STYLE_RULE } from '@projectwallace/css-parser'
631+
632+
const ast = parse('.a { .b { .c { color: red; } } }')
633+
634+
walk(ast, (node) => {
635+
if (node.type === STYLE_RULE) {
636+
console.log(node.text)
637+
return SKIP // Don't visit nested rules
638+
}
639+
})
640+
// Output: .a { ... }, but not .b or .c
641+
```
642+
643+
### Example 3: Stop on First Declaration
644+
645+
```typescript
646+
import { parse, walk, BREAK, DECLARATION } from '@projectwallace/css-parser'
647+
648+
const ast = parse('.a { color: red; margin: 10px; }')
649+
650+
walk(ast, (node) => {
651+
if (node.type === DECLARATION) {
652+
console.log(node.name)
653+
return BREAK // Stop traversal
654+
}
655+
})
656+
// Output: "color" (stops before "margin")
657+
```
658+
659+
---
660+
661+
## `traverse(ast, options?)`
662+
663+
Walk the AST in depth-first order, calling enter before visiting children and leave after.
664+
665+
```typescript
666+
function traverse(
667+
node: CSSNode,
668+
options?: {
669+
enter?: (node: CSSNode) => void | typeof SKIP | typeof BREAK
670+
leave?: (node: CSSNode) => void | typeof SKIP | typeof BREAK
671+
}
672+
): boolean
673+
```
674+
675+
### Parameters
676+
677+
- **`node`** - The root node to start walking from
678+
- **`options`** - Object with optional enter and leave callback functions
679+
- **`enter`** - Called before visiting children
680+
- Return `SKIP` to skip children (leave still called)
681+
- Return `BREAK` to stop traversal entirely (leave NOT called)
682+
- **`leave`** - Called after visiting children
683+
- Return `BREAK` to stop traversal
684+
685+
### Returns
686+
687+
`boolean` - Returns `false` if traversal was stopped with `BREAK`, otherwise `true`
688+
689+
### Example 1: Track Context with Enter/Leave
690+
691+
```typescript
692+
import { parse, traverse, AT_RULE } from '@projectwallace/css-parser'
693+
694+
const ast = parse('@media screen { .a { color: red; } }')
695+
696+
let depth = 0
697+
traverse(ast, {
698+
enter(node) {
699+
depth++
700+
console.log(`${' '.repeat(depth)}Entering ${node.type_name}`)
701+
},
702+
leave(node) {
703+
console.log(`${' '.repeat(depth)}Leaving ${node.type_name}`)
704+
depth--
705+
}
706+
})
707+
```
708+
709+
### Example 2: Skip Media Query Contents
710+
711+
```typescript
712+
import { parse, traverse, SKIP, AT_RULE } from '@projectwallace/css-parser'
713+
714+
const ast = parse('@media screen { .a { color: red; } }')
715+
716+
let depth = 0
717+
traverse(ast, {
718+
enter(node) {
719+
depth++
720+
if (node.type === AT_RULE) {
721+
console.log('Entering media query at depth', depth)
722+
return SKIP // Skip contents but still call leave
723+
}
724+
},
725+
leave(node) {
726+
if (node.type === AT_RULE) {
727+
console.log('Leaving media query at depth', depth)
728+
}
729+
depth--
730+
}
731+
})
732+
// Output:
733+
// Entering media query at depth 2
734+
// Leaving media query at depth 2
735+
```
736+
737+
### Example 3: Context-Aware Processing
738+
739+
```typescript
740+
import { parse, traverse, STYLE_RULE, AT_RULE } from '@projectwallace/css-parser'
741+
742+
const ast = parse(`
743+
.top { color: red; }
744+
@media screen {
745+
.nested { color: blue; }
746+
}
747+
`)
748+
749+
const context = []
750+
751+
traverse(ast, {
752+
enter(node) {
753+
if (node.type === AT_RULE) {
754+
context.push(`@${node.name}`)
755+
} else if (node.type === STYLE_RULE) {
756+
const selector = node.first_child.text
757+
const ctx = context.length ? ` in ${context.join(' ')}` : ''
758+
console.log(`Rule: ${selector}${ctx}`)
759+
}
760+
},
761+
leave(node) {
762+
if (node.type === AT_RULE) {
763+
context.pop()
764+
}
765+
}
766+
})
767+
// Output:
768+
// Rule: .top
769+
// Rule: .nested in @media
770+
```
771+
610772
---
611773
612774
## `tokenize(source, skip_comments?)`

src/css-node.ts

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -696,25 +696,12 @@ export class CSSNode {
696696
// --- Node Cloning ---
697697

698698
/**
699-
* Clone this node as a mutable plain JavaScript object
700-
*
701-
* Extracts all properties from the arena into a plain object with children as an array.
702-
* The resulting object can be freely modified.
699+
* Clone this node as a mutable plain JavaScript object with children as arrays.
700+
* See API.md for examples.
703701
*
704702
* @param options - Cloning configuration
705703
* @param options.deep - Recursively clone children (default: true)
706704
* @param options.locations - Include line/column/start/length (default: false)
707-
* @returns Plain object with children as array
708-
*
709-
* @example
710-
* const ast = parse('div { color: red; }')
711-
* const decl = ast.first_child.block.first_child
712-
* const plain = decl.clone()
713-
*
714-
* // Access children as array
715-
* plain.children.length
716-
* plain.children[0]
717-
* plain.children.push(newChild)
718705
*/
719706
clone(options: CloneOptions = {}): PlainCSSNode {
720707
const { deep = true, locations = false } = options

src/walk.ts

Lines changed: 5 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -8,34 +8,12 @@ export const BREAK = Symbol('BREAK')
88
type WalkCallback = (node: CSSNode, depth: number) => void | typeof SKIP | typeof BREAK
99

1010
/**
11-
* Walk the AST in depth-first order, calling the callback for each node
11+
* Walk the AST in depth-first order, calling the callback for each node.
12+
* Return SKIP to skip children, BREAK to stop traversal. See API.md for examples.
1213
*
1314
* @param node - The root node to start walking from
14-
* @param callback - Function to call for each node visited. Receives the node and its depth (0 for root).
15-
* Return SKIP to skip children of current node, or BREAK to stop traversal entirely.
15+
* @param callback - Function called for each node. Receives the node and its depth (0 for root).
1616
* @param depth - Starting depth (default: 0)
17-
*
18-
* @example
19-
* import { parse, walk, SKIP, BREAK } from '@projectwallace/css-parser'
20-
*
21-
* const ast = parse('.a { .b { .c { color: red; } } }')
22-
*
23-
* // Skip nested rules
24-
* walk(ast, (node) => {
25-
* if (node.type === STYLE_RULE) {
26-
* console.log(node.text)
27-
* return SKIP // Don't visit nested rules
28-
* }
29-
* })
30-
* // Output: .a { ... }, but not .b or .c
31-
*
32-
* // Stop on first declaration
33-
* walk(ast, (node) => {
34-
* if (node.type === DECLARATION) {
35-
* console.log(node.name)
36-
* return BREAK // Stop traversal
37-
* }
38-
* })
3917
*/
4018
export function walk(node: CSSNode, callback: WalkCallback, depth = 0): boolean {
4119
// Call callback for current node
@@ -74,36 +52,11 @@ interface WalkEnterLeaveOptions {
7452
}
7553

7654
/**
77-
* Walk the AST in depth-first order, calling enter before visiting children and leave after
55+
* Walk the AST in depth-first order, calling enter before visiting children and leave after.
56+
* Return SKIP in enter to skip children (leave still called), BREAK to stop (leave NOT called). See API.md for examples.
7857
*
7958
* @param node - The root node to start walking from
8059
* @param options - Object with optional enter and leave callback functions
81-
* @param options.enter - Called before visiting children. Return SKIP to skip children (leave still called),
82-
* or BREAK to stop traversal entirely (leave NOT called).
83-
* @param options.leave - Called after visiting children. Return BREAK to stop traversal.
84-
*
85-
* @example
86-
* import { parse, traverse, SKIP, BREAK } from '@projectwallace/css-parser'
87-
*
88-
* const ast = parse('@media screen { .a { color: red; } }')
89-
*
90-
* // Track context with skip
91-
* let depth = 0
92-
* traverse(ast, {
93-
* enter(node) {
94-
* depth++
95-
* if (node.type === AT_RULE) {
96-
* console.log('Entering media query at depth', depth)
97-
* return SKIP // Skip contents but still call leave
98-
* }
99-
* },
100-
* leave(node) {
101-
* if (node.type === AT_RULE) {
102-
* console.log('Leaving media query at depth', depth)
103-
* }
104-
* depth--
105-
* }
106-
* })
10760
*/
10861
export function traverse(node: CSSNode, { enter = NOOP, leave = NOOP }: WalkEnterLeaveOptions = {}): boolean {
10962
// Call enter callback before processing children

0 commit comments

Comments
 (0)