Fix 8.5.17 visitor regression

This commit is contained in:
Andrey Sitnik
2026-07-29 12:56:11 +00:00
parent 2b83c89558
commit f121ccda0f
2 changed files with 67 additions and 2 deletions
+23 -2
View File
@@ -503,14 +503,24 @@ class LazyResult {
if (visit.iterator !== 0) {
let iterator = visit.iterator
// Advance past the child we just finished visiting. Like
// `Container#each`, the index is incremented only after a child has
// been fully processed, so a node inserted right after the current
// child is not skipped by the `existIndex < index` adjustment in
// `Container#insertAfter()` (which would fire exit events too early).
if (visit.descending) {
visit.descending = false
node.indexes[iterator] += 1
}
let child
while ((child = node.nodes[node.indexes[iterator]])) {
node.indexes[iterator] += 1
if (!child[isClean]) {
child[isClean] = true
visit.descending = true
stack.push(toStack(child))
return
}
node.indexes[iterator] += 1
}
visit.iterator = 0
delete node.indexes[iterator]
@@ -548,12 +558,22 @@ class LazyResult {
if (visit.iterator !== 0) {
let iterator = visit.iterator
// Advance past the child we just finished visiting. Like
// `Container#each`, the index is incremented only after a child has
// been fully processed. Incrementing before (as this loop used to)
// makes a node inserted right after the current child get skipped by
// the `existIndex < index` adjustment in `Container#insertAfter()`,
// which fires exit events before those new siblings are visited.
if (visit.descending) {
visit.descending = false
visitNode.indexes[iterator] += 1
}
let child
let descended = false
while ((child = visitNode.nodes[visitNode.indexes[iterator]])) {
visitNode.indexes[iterator] += 1
if (!child[isClean]) {
child[isClean] = true
visit.descending = true
stack.push({
eventIndex: 0,
events: getEvents(child),
@@ -563,6 +583,7 @@ class LazyResult {
descended = true
break
}
visitNode.indexes[iterator] += 1
}
if (descended) continue
visit.iterator = 0
+44
View File
@@ -324,6 +324,50 @@ test('works with at-rule params', () => {
is(css, '@media (mobile) {}')
})
function trackUnwrap(): [string[], Plugin] {
let order: string[] = []
let plugin: Plugin = {
postcssPlugin: 'unwrap-nested',
Rule(rule) {
order.push('Rule ' + rule.selector)
rule.each(child => {
if (child.type === 'rule') {
child.selector = rule.selector + ' ' + child.selector
rule.after(child)
}
})
},
RootExit() {
order.push('RootExit')
}
}
return [order, plugin]
}
test('visits nodes inserted after the current one before exit events', () => {
let [order, plugin] = trackUnwrap()
postcss([plugin]).process('a { b { c {} } }', { from: 'a.css' }).css
equal(order, [
'Rule a',
'Rule a b',
'Rule a b c',
'RootExit',
'Rule a',
'Rule a b',
'RootExit'
])
})
test('visits inserted siblings equally in sync and async walks', async () => {
let [syncOrder, syncPlugin] = trackUnwrap()
postcss([syncPlugin]).process('a { b { c {} } }', { from: 'a.css' }).css
let [asyncOrder, asyncPlugin] = trackUnwrap()
await postcss([asyncPlugin]).process('a { b { c {} } }', { from: 'a.css' })
equal(asyncOrder, syncOrder)
})
test('wraps node to proxies', () => {
let proxy: any
let root: Root | undefined