Compare commits
2 commits
main
...
feat/walk-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f7382fedbc | ||
|
|
1ceea2ff77 |
2 changed files with 37 additions and 22 deletions
|
|
@ -447,6 +447,7 @@ export function createVariants(theme: Theme): Variants {
|
||||||
if (variant.modifier) return null
|
if (variant.modifier) return null
|
||||||
|
|
||||||
let didApply = false
|
let didApply = false
|
||||||
|
let replacement: AstNode | null = null
|
||||||
|
|
||||||
walk([ruleNode], (node, ctx) => {
|
walk([ruleNode], (node, ctx) => {
|
||||||
if (node.kind !== 'rule' && node.kind !== 'at-rule') return WalkAction.Continue
|
if (node.kind !== 'rule' && node.kind !== 'at-rule') return WalkAction.Continue
|
||||||
|
|
@ -492,7 +493,7 @@ export function createVariants(theme: Theme): Variants {
|
||||||
rules.push(negatedAtRule)
|
rules.push(negatedAtRule)
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.assign(ruleNode, styleRule('&', rules))
|
replacement = styleRule('&', rules)
|
||||||
|
|
||||||
// Track that the variant was actually applied
|
// Track that the variant was actually applied
|
||||||
didApply = true
|
didApply = true
|
||||||
|
|
@ -500,6 +501,10 @@ export function createVariants(theme: Theme): Variants {
|
||||||
return WalkAction.Skip
|
return WalkAction.Skip
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (replacement) {
|
||||||
|
Object.assign(ruleNode, replacement)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Tweak group, peer, has to ignore intermediate `&` selectors (maybe?)
|
// TODO: Tweak group, peer, has to ignore intermediate `&` selectors (maybe?)
|
||||||
if (ruleNode.kind === 'rule' && ruleNode.selector === '&' && ruleNode.nodes.length === 1) {
|
if (ruleNode.kind === 'rule' && ruleNode.selector === '&' && ruleNode.nodes.length === 1) {
|
||||||
Object.assign(ruleNode, ruleNode.nodes[0])
|
Object.assign(ruleNode, ruleNode.nodes[0])
|
||||||
|
|
|
||||||
|
|
@ -57,36 +57,44 @@ function walkImplementation<T extends { nodes?: T[] }>(
|
||||||
enter: (node: T, ctx: VisitContext<T>) => EnterResult<T> | void = () => WalkAction.Continue,
|
enter: (node: T, ctx: VisitContext<T>) => EnterResult<T> | void = () => WalkAction.Continue,
|
||||||
exit: (node: T, ctx: VisitContext<T>) => ExitResult<T> | void = () => WalkAction.Continue,
|
exit: (node: T, ctx: VisitContext<T>) => ExitResult<T> | void = () => WalkAction.Continue,
|
||||||
) {
|
) {
|
||||||
let stack: [nodes: T[], offset: number, parent: Parent<T> | null][] = [[ast, 0, null]]
|
let surrogate = { nodes: ast } as Parent<T>
|
||||||
|
|
||||||
|
// Reduce memory usage by tracking 2 different objects instead of a single
|
||||||
|
// stack data structure. We could use 2 arrays, but objects are faster in Bun.
|
||||||
|
// In Node.js the 2 arrays or 2 objects have similar performance.
|
||||||
|
//
|
||||||
|
// Used indexing to prevent `push()` / `pop()` overhead.
|
||||||
|
let offsets: Record<number, number> = { 0: 0 }
|
||||||
|
let parents: Record<number, Parent<T>> = { 0: surrogate }
|
||||||
|
|
||||||
|
let depth = 0
|
||||||
|
|
||||||
let ctx: VisitContext<T> = {
|
let ctx: VisitContext<T> = {
|
||||||
parent: null,
|
parent: null,
|
||||||
depth: 0,
|
depth: 0,
|
||||||
path() {
|
path() {
|
||||||
let path: T[] = []
|
let path: T[] = []
|
||||||
|
|
||||||
for (let i = 1; i < stack.length; i++) {
|
for (let i = 1; i <= depth; i++) {
|
||||||
let parent = stack[i][2]
|
path.push(parents[i])
|
||||||
if (parent) path.push(parent)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return path
|
return path
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
while (stack.length > 0) {
|
while (depth >= 0) {
|
||||||
let depth = stack.length - 1
|
let offset = offsets[depth]
|
||||||
let frame = stack[depth]
|
let parent = parents[depth]
|
||||||
let nodes = frame[0]
|
let nodes = parent.nodes
|
||||||
let offset = frame[1]
|
|
||||||
let parent = frame[2]
|
|
||||||
|
|
||||||
// Done with this level
|
// Done with this level
|
||||||
if (offset >= nodes.length) {
|
if (offset >= nodes.length) {
|
||||||
stack.pop()
|
depth--
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.parent = parent
|
ctx.parent = depth === 0 ? null : parent
|
||||||
ctx.depth = depth
|
ctx.depth = depth
|
||||||
|
|
||||||
// Enter phase (offsets are positive)
|
// Enter phase (offsets are positive)
|
||||||
|
|
@ -96,11 +104,13 @@ function walkImplementation<T extends { nodes?: T[] }>(
|
||||||
|
|
||||||
switch (result.kind) {
|
switch (result.kind) {
|
||||||
case WalkKind.Continue: {
|
case WalkKind.Continue: {
|
||||||
if (node.nodes && node.nodes.length > 0) {
|
offsets[depth] = ~offset // Prepare for exit phase, same offset
|
||||||
stack.push([node.nodes, 0, node as Parent<T>])
|
|
||||||
}
|
|
||||||
|
|
||||||
frame[1] = ~offset // Prepare for exit phase, same offset
|
if (node.nodes && node.nodes.length > 0) {
|
||||||
|
depth++
|
||||||
|
offsets[depth] = 0
|
||||||
|
parents[depth] = node as Parent<T>
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -108,7 +118,7 @@ function walkImplementation<T extends { nodes?: T[] }>(
|
||||||
return // Stop immediately
|
return // Stop immediately
|
||||||
|
|
||||||
case WalkKind.Skip: {
|
case WalkKind.Skip: {
|
||||||
frame[1] = ~offset // Prepare for exit phase, same offset
|
offsets[depth] = ~offset // Prepare for exit phase, same offset
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -124,7 +134,7 @@ function walkImplementation<T extends { nodes?: T[] }>(
|
||||||
|
|
||||||
case WalkKind.ReplaceSkip: {
|
case WalkKind.ReplaceSkip: {
|
||||||
nodes.splice(offset, 1, ...result.nodes)
|
nodes.splice(offset, 1, ...result.nodes)
|
||||||
frame[1] += result.nodes.length // Advance to next sibling past replacements
|
offsets[depth] += result.nodes.length // Advance to next sibling past replacements
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -146,7 +156,7 @@ function walkImplementation<T extends { nodes?: T[] }>(
|
||||||
|
|
||||||
switch (result.kind) {
|
switch (result.kind) {
|
||||||
case WalkKind.Continue:
|
case WalkKind.Continue:
|
||||||
frame[1] = index + 1 // Advance to next sibling
|
offsets[depth] = index + 1 // Advance to next sibling
|
||||||
continue
|
continue
|
||||||
|
|
||||||
case WalkKind.Stop:
|
case WalkKind.Stop:
|
||||||
|
|
@ -154,7 +164,7 @@ function walkImplementation<T extends { nodes?: T[] }>(
|
||||||
|
|
||||||
case WalkKind.Replace: {
|
case WalkKind.Replace: {
|
||||||
nodes.splice(index, 1, ...result.nodes)
|
nodes.splice(index, 1, ...result.nodes)
|
||||||
frame[1] = index + result.nodes.length // Advance to next sibling past replacements
|
offsets[depth] = index + result.nodes.length // Advance to next sibling past replacements
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,7 +175,7 @@ function walkImplementation<T extends { nodes?: T[] }>(
|
||||||
|
|
||||||
case WalkKind.ReplaceSkip: {
|
case WalkKind.ReplaceSkip: {
|
||||||
nodes.splice(index, 1, ...result.nodes)
|
nodes.splice(index, 1, ...result.nodes)
|
||||||
frame[1] = index + result.nodes.length // Advance to next sibling past replacements
|
offsets[depth] = index + result.nodes.length // Advance to next sibling past replacements
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue