Skip to content

Commit 76a5ea1

Browse files
committed
simplify walk
1. Use single function with noop `enter` and `exit` functions instead of 3 separate functions. 2. Instead of repeating some logic with sub-switches, just toggle the offset and let the loop go around. 3. Cleanup some comments
1 parent e864cca commit 76a5ea1

File tree

1 file changed

+16
-297
lines changed

1 file changed

+16
-297
lines changed

packages/tailwindcss/src/walk.ts

Lines changed: 16 additions & 297 deletions
Original file line numberDiff line numberDiff line change
@@ -44,234 +44,18 @@ export function walk<T extends object>(
4444
hooks:
4545
| ((node: T, ctx: VisitContext<T>) => EnterResult<T> | void) // Old API, enter only
4646
| {
47-
enter: (node: T, ctx: VisitContext<T>) => EnterResult<T> | void
48-
exit: (node: T, ctx: VisitContext<T>) => ExitResult<T> | void
49-
}
50-
| {
51-
enter: (node: T, ctx: VisitContext<T>) => EnterResult<T> | void
52-
exit?: never
53-
}
54-
| {
55-
enter?: never
56-
exit: (node: T, ctx: VisitContext<T>) => ExitResult<T> | void
47+
enter?: (node: T, ctx: VisitContext<T>) => EnterResult<T> | void
48+
exit?: (node: T, ctx: VisitContext<T>) => ExitResult<T> | void
5749
},
5850
): void {
59-
if (typeof hooks === 'function') walkEnter(ast, hooks)
60-
else if (hooks.enter && hooks.exit) walkEnterExit(ast, hooks.enter, hooks.exit)
61-
else if (hooks.enter) walkEnter(ast, hooks.enter)
62-
else if (hooks.exit) walkExit(ast, hooks.exit)
63-
}
64-
65-
function walkEnter<T extends { nodes?: T[] }>(
66-
ast: T[],
67-
enter: (node: T, ctx: VisitContext<T>) => EnterResult<T> | void,
68-
) {
69-
let stack: [nodes: T[], offset: number, parent: Parent<T> | null][] = [[ast, 0, null]]
70-
let ctx: VisitContext<T> = {
71-
parent: null,
72-
depth: 0,
73-
path() {
74-
let path: T[] = []
75-
76-
for (let i = 1; i < stack.length; i++) {
77-
let parent = stack[i][2]
78-
if (parent) path.push(parent)
79-
}
80-
81-
return path
82-
},
83-
}
84-
85-
while (stack.length > 0) {
86-
let depth = stack.length - 1
87-
let frame = stack[depth]
88-
let nodes = frame[0]
89-
let offset = frame[1]
90-
let parent = frame[2]
91-
92-
// Done with this level
93-
if (offset >= nodes.length) {
94-
stack.pop()
95-
continue
96-
}
97-
98-
let node = nodes[offset]
99-
100-
ctx.parent = parent
101-
ctx.depth = depth
102-
103-
let result = enter(node, ctx) ?? WalkAction.Continue
104-
105-
switch (result.kind) {
106-
case WalkKind.Continue: {
107-
if (node.nodes && node.nodes.length > 0) {
108-
stack.push([node.nodes, 0, node as Parent<T>])
109-
}
110-
111-
stack[depth][1]++ // Advance to next sibling
112-
continue
113-
}
114-
115-
case WalkKind.Stop:
116-
return // Stop immediately
117-
118-
case WalkKind.Skip:
119-
stack[depth][1]++ // Advance to next sibling
120-
continue
121-
122-
case WalkKind.Replace: {
123-
// Replace current node, with new nodes. No need to change the offset
124-
// because we want to re-visit the current index, which now contains the
125-
// new nodes.
126-
nodes.splice(offset, 1, ...result.nodes)
127-
continue
128-
}
129-
130-
case WalkKind.ReplaceStop: {
131-
nodes.splice(offset, 1, ...result.nodes) // Replace current node
132-
return // Stop immediately
133-
}
134-
135-
case WalkKind.ReplaceSkip: {
136-
nodes.splice(offset, 1, ...result.nodes) // Replace current node
137-
stack[depth][1] += result.nodes.length // Advance to next sibling past replacements
138-
continue
139-
}
140-
141-
default: {
142-
result satisfies never
143-
144-
throw new Error(
145-
// @ts-expect-error `result.kind` could still be filled in with an invalid value
146-
`Invalid \`WalkAction.${WalkKind[result.kind] ?? `Unknown(${result.kind})`}\` in enter.`,
147-
)
148-
}
149-
}
150-
}
51+
if (typeof hooks === 'function') walkImplementation(ast, hooks)
52+
else walkImplementation(ast, hooks.enter, hooks.exit)
15153
}
15254

153-
function walkExit<T extends { nodes?: T[] }>(
55+
function walkImplementation<T extends { nodes?: T[] }>(
15456
ast: T[],
155-
exit: (node: T, ctx: VisitContext<T>) => ExitResult<T> | void,
156-
) {
157-
let stack: [nodes: T[], offset: number, parent: Parent<T> | null][] = [[ast, 0, null]]
158-
let ctx: VisitContext<T> = {
159-
parent: null,
160-
depth: 0,
161-
path() {
162-
let path: T[] = []
163-
164-
for (let i = 1; i < stack.length; i++) {
165-
let parent = stack[i][2]
166-
if (parent) path.push(parent)
167-
}
168-
169-
return path
170-
},
171-
}
172-
173-
while (stack.length > 0) {
174-
let depth = stack.length - 1
175-
let frame = stack[depth]
176-
let nodes = frame[0]
177-
let offset = frame[1]
178-
let parent = frame[2]
179-
180-
// Done with this level
181-
if (offset >= nodes.length) {
182-
stack.pop()
183-
continue
184-
}
185-
186-
ctx.parent = parent
187-
ctx.depth = depth
188-
189-
// "Enter" phase. We need to enter the AST so we can go back up again
190-
if (offset >= 0) {
191-
let node = nodes[offset]
192-
193-
if (node.nodes && node.nodes.length > 0) {
194-
stack[depth][1] = ~offset // Prepare for actual exit phase
195-
stack.push([node.nodes, 0, node as Parent<T>])
196-
continue
197-
}
198-
199-
// Leaf node: run exit immediately
200-
let result = exit(node, ctx) ?? WalkAction.Continue
201-
202-
switch (result.kind) {
203-
case WalkKind.Continue: {
204-
stack[depth][1]++ // Advance to next sibling
205-
continue
206-
}
207-
208-
case WalkKind.Stop:
209-
return // Stop immediately
210-
211-
case WalkKind.ReplaceStop: {
212-
nodes.splice(offset, 1, ...result.nodes)
213-
return // Stop immediately
214-
}
215-
216-
case WalkKind.Replace:
217-
case WalkKind.ReplaceSkip: {
218-
nodes.splice(offset, 1, ...result.nodes)
219-
stack[depth][1] += result.nodes.length
220-
continue
221-
}
222-
223-
default: {
224-
result satisfies never
225-
throw new Error(
226-
// @ts-expect-error `result.kind` could still be filled in with an invalid value
227-
`Invalid \`WalkAction.${WalkKind[result.kind] ?? `Unknown(${result.kind})`}\` in exit (leaf).`,
228-
)
229-
}
230-
}
231-
}
232-
233-
// Actual exit phase for nodes[~offset]
234-
let index = ~offset
235-
let node = nodes[index]
236-
237-
let result = exit(node, ctx) ?? WalkAction.Continue
238-
239-
switch (result.kind) {
240-
case WalkKind.Continue: {
241-
stack[depth][1] = index + 1 // Advance to next sibling
242-
continue
243-
}
244-
245-
case WalkKind.Stop:
246-
return // Stop immediately
247-
248-
case WalkKind.ReplaceStop: {
249-
nodes.splice(index, 1, ...result.nodes)
250-
return // Stop immediately
251-
}
252-
253-
case WalkKind.Replace:
254-
case WalkKind.ReplaceSkip: {
255-
nodes.splice(index, 1, ...result.nodes)
256-
stack[depth][1] = index + result.nodes.length // Advance to next sibling past replacements
257-
continue
258-
}
259-
260-
default: {
261-
result satisfies never
262-
throw new Error(
263-
// @ts-expect-error `result.kind` could still be filled in with an invalid value
264-
`Invalid \`WalkAction.${WalkKind[result.kind] ?? `Unknown(${result.kind})`}\` in exit.`,
265-
)
266-
}
267-
}
268-
}
269-
}
270-
271-
function walkEnterExit<T extends { nodes?: T[] }>(
272-
ast: T[],
273-
enter: (node: T, ctx: VisitContext<T>) => EnterResult<T> | void,
274-
exit: (node: T, ctx: VisitContext<T>) => EnterResult<T> | void,
57+
enter: (node: T, ctx: VisitContext<T>) => EnterResult<T> | void = () => WalkAction.Continue,
58+
exit: (node: T, ctx: VisitContext<T>) => ExitResult<T> | void = () => WalkAction.Continue,
27559
) {
27660
let stack: [nodes: T[], offset: number, parent: Parent<T> | null][] = [[ast, 0, null]]
27761
let ctx: VisitContext<T> = {
@@ -313,88 +97,24 @@ function walkEnterExit<T extends { nodes?: T[] }>(
31397
switch (result.kind) {
31498
case WalkKind.Continue: {
31599
if (node.nodes && node.nodes.length > 0) {
316-
stack[depth][1] = ~offset // Prepare for exit phase, same offset
317100
stack.push([node.nodes, 0, node as Parent<T>])
318-
continue
319101
}
320102

321-
// Already a leaf node, can immediately exit
322-
{
323-
let result = exit(node, ctx) ?? WalkAction.Continue
324-
325-
switch (result.kind) {
326-
case WalkKind.Continue:
327-
case WalkKind.Skip:
328-
stack[depth][1]++ // Advance to next sibling
329-
continue
330-
331-
case WalkKind.Stop:
332-
return // Stop immediately
333-
334-
case WalkKind.ReplaceStop:
335-
nodes.splice(offset, 1, ...result.nodes)
336-
return // Stop immediately
337-
338-
case WalkKind.Replace:
339-
case WalkKind.ReplaceSkip:
340-
nodes.splice(offset, 1, ...result.nodes)
341-
stack[depth][1] += result.nodes.length // Advance to next sibling past replacements
342-
continue
343-
344-
default: {
345-
result satisfies never
346-
throw new Error(
347-
// @ts-expect-error r.kind may be invalid
348-
`Invalid \`WalkAction.${WalkKind[result.kind] ?? `Unknown(${result.kind})`}\` in exit (leaf).`,
349-
)
350-
}
351-
}
352-
}
103+
frame[1] = ~offset // Prepare for exit phase, same offset
104+
continue
353105
}
354106

355107
case WalkKind.Stop:
356108
return // Stop immediately
357109

358110
case WalkKind.Skip: {
359-
let result = exit(node, ctx) ?? WalkAction.Continue
360-
361-
switch (result.kind) {
362-
case WalkKind.Continue:
363-
case WalkKind.Skip:
364-
stack[depth][1]++
365-
continue
366-
367-
case WalkKind.Stop:
368-
return // Stop immediately
369-
370-
case WalkKind.Replace:
371-
nodes.splice(offset, 1, ...result.nodes)
372-
stack[depth][1] += result.nodes.length // don't visit replacements' exits
373-
continue
374-
375-
case WalkKind.ReplaceStop:
376-
nodes.splice(offset, 1, ...result.nodes)
377-
return // Stop immediately
378-
379-
case WalkKind.ReplaceSkip:
380-
nodes.splice(offset, 1, ...result.nodes)
381-
stack[depth][1] += result.nodes.length // don't visit replacements' exits
382-
continue
383-
384-
default: {
385-
result satisfies never
386-
throw new Error(
387-
// @ts-expect-error r.kind may be invalid
388-
`Invalid \`WalkAction.${WalkKind[result.kind] ?? `Unknown(${result.kind})`}\` in exit (skip).`,
389-
)
390-
}
391-
}
111+
frame[1] = ~offset // Prepare for exit phase, same offset
112+
continue
392113
}
393114

394115
case WalkKind.Replace: {
395-
// Replace current node; re-visit current index (enter on first replacement)
396116
nodes.splice(offset, 1, ...result.nodes)
397-
continue
117+
continue // Re-process at same offset
398118
}
399119

400120
case WalkKind.ReplaceStop: {
@@ -404,7 +124,7 @@ function walkEnterExit<T extends { nodes?: T[] }>(
404124

405125
case WalkKind.ReplaceSkip: {
406126
nodes.splice(offset, 1, ...result.nodes)
407-
stack[depth][1] += result.nodes.length // Advance to next sibling past replacements
127+
frame[1] += result.nodes.length // Advance to next sibling past replacements
408128
continue
409129
}
410130

@@ -426,16 +146,15 @@ function walkEnterExit<T extends { nodes?: T[] }>(
426146

427147
switch (result.kind) {
428148
case WalkKind.Continue:
429-
case WalkKind.Skip:
430-
stack[depth][1] = index + 1 // Advance to next sibling
149+
frame[1] = index + 1 // Advance to next sibling
431150
continue
432151

433152
case WalkKind.Stop:
434153
return // Stop immediately
435154

436155
case WalkKind.Replace: {
437156
nodes.splice(index, 1, ...result.nodes)
438-
stack[depth][1] = index + result.nodes.length // Advance to next sibling past replacements
157+
frame[1] = index + result.nodes.length // Advance to next sibling past replacements
439158
continue
440159
}
441160

@@ -446,7 +165,7 @@ function walkEnterExit<T extends { nodes?: T[] }>(
446165

447166
case WalkKind.ReplaceSkip: {
448167
nodes.splice(index, 1, ...result.nodes)
449-
stack[depth][1] = index + result.nodes.length // Advance to next sibling past replacements
168+
frame[1] = index + result.nodes.length // Advance to next sibling past replacements
450169
continue
451170
}
452171

0 commit comments

Comments
 (0)