From c50fb0e951f6d3e0c979f6722c3f7834c7a4ef31 Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Tue, 18 Mar 2025 00:04:29 +0800 Subject: [PATCH 1/2] fix(compiler-vapor): move setInsertionState to the end to prevent nthChild mismatch --- .../__tests__/__snapshots__/compile.spec.ts.snap | 8 ++++---- .../__snapshots__/transformChildren.spec.ts.snap | 4 ++-- .../__tests__/transforms/__snapshots__/vFor.spec.ts.snap | 4 ++-- .../__tests__/transforms/__snapshots__/vOnce.spec.ts.snap | 4 ++-- .../__tests__/transforms/transformChildren.spec.ts | 4 ++-- packages/compiler-vapor/src/generators/operation.ts | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap index e56676d8706..7614fc3c4bc 100644 --- a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap @@ -26,7 +26,7 @@ export function render(_ctx) { `; exports[`compile > custom directive > component 1`] = ` -"import { resolveComponent as _resolveComponent, resolveDirective as _resolveDirective, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, withVaporDirectives as _withVaporDirectives, createIf as _createIf, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, resolveDirective as _resolveDirective, createComponentWithFallback as _createComponentWithFallback, withVaporDirectives as _withVaporDirectives, setInsertionState as _setInsertionState, createIf as _createIf, template as _template } from 'vue'; const t0 = _template("<div></div>") export function render(_ctx) { @@ -38,9 +38,9 @@ export function render(_ctx) { "default": () => { const n0 = _createIf(() => (true), () => { const n3 = t0() - _setInsertionState(n3) const n2 = _createComponentWithFallback(_component_Bar) _withVaporDirectives(n2, [[_directive_hello, void 0, void 0, { world: true }]]) + _setInsertionState(n3) return n3 }) return n0 @@ -149,7 +149,7 @@ export function render(_ctx, $props, $emit, $attrs, $slots) { `; exports[`compile > directives > v-pre > should not affect siblings after it 1`] = ` -"import { resolveComponent as _resolveComponent, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, child as _child, toDisplayString as _toDisplayString, setText as _setText, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, setInsertionState as _setInsertionState, child as _child, toDisplayString as _toDisplayString, setText as _setText, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue'; const t0 = _template("<div :id=\\"foo\\"><Comp></Comp>{{ bar }}</div>") const t1 = _template("<div> </div>") @@ -157,8 +157,8 @@ export function render(_ctx, $props, $emit, $attrs, $slots) { const _component_Comp = _resolveComponent("Comp") const n0 = t0() const n3 = t1() - _setInsertionState(n3, 0) const n1 = _createComponentWithFallback(_component_Comp) + _setInsertionState(n3, 0) const n2 = _child(n3) _renderEffect(() => { _setText(n2, _toDisplayString(_ctx.bar)) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap index 5ae8a94f5b1..884fb633ae9 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap @@ -1,18 +1,18 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`compiler: children transform > anchor insertion in middle 1`] = ` -"import { child as _child, next as _next, setInsertionState as _setInsertionState, createIf as _createIf, template as _template } from 'vue'; +"import { child as _child, next as _next, createIf as _createIf, setInsertionState as _setInsertionState, template as _template } from 'vue'; const t0 = _template("<div></div>") const t1 = _template("<div><div></div><!><div></div></div>", true) export function render(_ctx) { const n4 = t1() const n3 = _next(_child(n4)) - _setInsertionState(n4, n3) const n0 = _createIf(() => (1), () => { const n2 = t0() return n2 }, null, true) + _setInsertionState(n4, n3) return n4 }" `; diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap index cb14f56afdb..dbb0ddb3f26 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap @@ -65,20 +65,20 @@ export function render(_ctx) { `; exports[`compiler: v-for > nested v-for 1`] = ` -"import { setInsertionState as _setInsertionState, child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue'; +"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, setInsertionState as _setInsertionState, template as _template } from 'vue'; const t0 = _template("<span> </span>") const t1 = _template("<div></div>", true) export function render(_ctx) { const n0 = _createFor(() => (_ctx.list), (_for_item0) => { const n5 = t1() - _setInsertionState(n5) const n2 = _createFor(() => (_for_item0.value), (_for_item1) => { const n4 = t0() const x4 = _child(n4) _renderEffect(() => _setText(x4, _toDisplayString(_for_item1.value+_for_item0.value))) return n4 }, null, 1) + _setInsertionState(n5) return n5 }) return n0 diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap index ab3ade45b60..bb35b44e3f1 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap @@ -36,14 +36,14 @@ export function render(_ctx) { `; exports[`compiler: v-once > on component 1`] = ` -"import { resolveComponent as _resolveComponent, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, setInsertionState as _setInsertionState, template as _template } from 'vue'; const t0 = _template("<div></div>", true) export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") const n1 = t0() - _setInsertionState(n1) const n0 = _createComponentWithFallback(_component_Comp, { id: () => (_ctx.foo) }, null, null, true) + _setInsertionState(n1) return n1 }" `; diff --git a/packages/compiler-vapor/__tests__/transforms/transformChildren.spec.ts b/packages/compiler-vapor/__tests__/transforms/transformChildren.spec.ts index e656312356c..2d8ae8c960d 100644 --- a/packages/compiler-vapor/__tests__/transforms/transformChildren.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/transformChildren.spec.ts @@ -69,8 +69,8 @@ describe('compiler: children transform', () => { </div>`, ) // ensure the insertion anchor is generated before the insertion statement - expect(code).toMatch(`const n3 = _next(_child(n4)) - _setInsertionState(n4, n3)`) + expect(code).toMatch(`const n3 = _next(_child(n4))`) + expect(code).toMatch(`_setInsertionState(n4, n3)`) expect(code).toMatchSnapshot() }) }) diff --git a/packages/compiler-vapor/src/generators/operation.ts b/packages/compiler-vapor/src/generators/operation.ts index 4247bc6feca..8b0bc90c369 100644 --- a/packages/compiler-vapor/src/generators/operation.ts +++ b/packages/compiler-vapor/src/generators/operation.ts @@ -43,10 +43,10 @@ export function genOperationWithInsertionState( context: CodegenContext, ): CodeFragment[] { const [frag, push] = buildCodeFragment() + push(...genOperation(oper, context)) if (isBlockOperation(oper) && oper.parent) { push(...genInsertionstate(oper, context)) } - push(...genOperation(oper, context)) return frag } From 015e1eed26a7b815ac402ab1ca10a52d771b6743 Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Tue, 18 Mar 2025 12:32:24 +0800 Subject: [PATCH 2/2] fix(compiler-vapor): move `next`, `child` and `nthChild` before the setInsertionState --- .../__snapshots__/compile.spec.ts.snap | 34 ++++++++++++++++--- .../compiler-vapor/__tests__/compile.spec.ts | 17 ++++++++++ .../transformChildren.spec.ts.snap | 4 +-- .../__snapshots__/vFor.spec.ts.snap | 4 +-- .../__snapshots__/vOnce.spec.ts.snap | 4 +-- .../compiler-vapor/src/generators/block.ts | 2 +- .../src/generators/operation.ts | 2 +- .../compiler-vapor/src/generators/template.ts | 13 +++---- 8 files changed, 61 insertions(+), 19 deletions(-) diff --git a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap index 7614fc3c4bc..d4a8b6827bb 100644 --- a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap @@ -26,7 +26,7 @@ export function render(_ctx) { `; exports[`compile > custom directive > component 1`] = ` -"import { resolveComponent as _resolveComponent, resolveDirective as _resolveDirective, createComponentWithFallback as _createComponentWithFallback, withVaporDirectives as _withVaporDirectives, setInsertionState as _setInsertionState, createIf as _createIf, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, resolveDirective as _resolveDirective, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, withVaporDirectives as _withVaporDirectives, createIf as _createIf, template as _template } from 'vue'; const t0 = _template("<div></div>") export function render(_ctx) { @@ -38,9 +38,9 @@ export function render(_ctx) { "default": () => { const n0 = _createIf(() => (true), () => { const n3 = t0() + _setInsertionState(n3) const n2 = _createComponentWithFallback(_component_Bar) _withVaporDirectives(n2, [[_directive_hello, void 0, void 0, { world: true }]]) - _setInsertionState(n3) return n3 }) return n0 @@ -149,7 +149,7 @@ export function render(_ctx, $props, $emit, $attrs, $slots) { `; exports[`compile > directives > v-pre > should not affect siblings after it 1`] = ` -"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, setInsertionState as _setInsertionState, child as _child, toDisplayString as _toDisplayString, setText as _setText, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, child as _child, toDisplayString as _toDisplayString, setText as _setText, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue'; const t0 = _template("<div :id=\\"foo\\"><Comp></Comp>{{ bar }}</div>") const t1 = _template("<div> </div>") @@ -157,9 +157,9 @@ export function render(_ctx, $props, $emit, $attrs, $slots) { const _component_Comp = _resolveComponent("Comp") const n0 = t0() const n3 = t1() - const n1 = _createComponentWithFallback(_component_Comp) - _setInsertionState(n3, 0) const n2 = _child(n3) + _setInsertionState(n3, 0) + const n1 = _createComponentWithFallback(_component_Comp) _renderEffect(() => { _setText(n2, _toDisplayString(_ctx.bar)) _setProp(n3, "id", _ctx.foo) @@ -230,6 +230,30 @@ export function render(_ctx) { }" `; +exports[`compile > setInsertionState > next, child and nthChild should be above the setInsertionState 1`] = ` +"import { resolveComponent as _resolveComponent, child as _child, next as _next, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, nthChild as _nthChild, createIf as _createIf, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue'; +const t0 = _template("<div></div>") +const t1 = _template("<div><div></div><!><div></div><!><div><button></button></div></div>", true) + +export function render(_ctx) { + const _component_Comp = _resolveComponent("Comp") + const n6 = t1() + const n5 = _next(_child(n6)) + const n7 = _nthChild(n6, 3) + const p0 = _next(n7) + const n4 = _child(p0) + _setInsertionState(n6, n5) + const n0 = _createComponentWithFallback(_component_Comp) + _setInsertionState(n6, n7) + const n1 = _createIf(() => (true), () => { + const n3 = t0() + return n3 + }) + _renderEffect(() => _setProp(n4, "disabled", _ctx.foo)) + return n6 +}" +`; + exports[`compile > static + dynamic root 1`] = ` "import { toDisplayString as _toDisplayString, setText as _setText, template as _template } from 'vue'; const t0 = _template(" ") diff --git a/packages/compiler-vapor/__tests__/compile.spec.ts b/packages/compiler-vapor/__tests__/compile.spec.ts index 33f399caa77..3a2ce41f0cd 100644 --- a/packages/compiler-vapor/__tests__/compile.spec.ts +++ b/packages/compiler-vapor/__tests__/compile.spec.ts @@ -220,4 +220,21 @@ describe('compile', () => { expect(code).matchSnapshot() }) }) + + describe('setInsertionState', () => { + test('next, child and nthChild should be above the setInsertionState', () => { + const code = compile(` + <div> + <div /> + <Comp /> + <div /> + <div v-if="true" /> + <div> + <button :disabled="foo" /> + </div> + </div> + `) + expect(code).toMatchSnapshot() + }) + }) }) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap index 884fb633ae9..5ae8a94f5b1 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap @@ -1,18 +1,18 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`compiler: children transform > anchor insertion in middle 1`] = ` -"import { child as _child, next as _next, createIf as _createIf, setInsertionState as _setInsertionState, template as _template } from 'vue'; +"import { child as _child, next as _next, setInsertionState as _setInsertionState, createIf as _createIf, template as _template } from 'vue'; const t0 = _template("<div></div>") const t1 = _template("<div><div></div><!><div></div></div>", true) export function render(_ctx) { const n4 = t1() const n3 = _next(_child(n4)) + _setInsertionState(n4, n3) const n0 = _createIf(() => (1), () => { const n2 = t0() return n2 }, null, true) - _setInsertionState(n4, n3) return n4 }" `; diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap index dbb0ddb3f26..cb14f56afdb 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap @@ -65,20 +65,20 @@ export function render(_ctx) { `; exports[`compiler: v-for > nested v-for 1`] = ` -"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, setInsertionState as _setInsertionState, template as _template } from 'vue'; +"import { setInsertionState as _setInsertionState, child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue'; const t0 = _template("<span> </span>") const t1 = _template("<div></div>", true) export function render(_ctx) { const n0 = _createFor(() => (_ctx.list), (_for_item0) => { const n5 = t1() + _setInsertionState(n5) const n2 = _createFor(() => (_for_item0.value), (_for_item1) => { const n4 = t0() const x4 = _child(n4) _renderEffect(() => _setText(x4, _toDisplayString(_for_item1.value+_for_item0.value))) return n4 }, null, 1) - _setInsertionState(n5) return n5 }) return n0 diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap index bb35b44e3f1..ab3ade45b60 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap @@ -36,14 +36,14 @@ export function render(_ctx) { `; exports[`compiler: v-once > on component 1`] = ` -"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, setInsertionState as _setInsertionState, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template("<div></div>", true) export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") const n1 = t0() - const n0 = _createComponentWithFallback(_component_Comp, { id: () => (_ctx.foo) }, null, null, true) _setInsertionState(n1) + const n0 = _createComponentWithFallback(_component_Comp, { id: () => (_ctx.foo) }, null, null, true) return n1 }" `; diff --git a/packages/compiler-vapor/src/generators/block.ts b/packages/compiler-vapor/src/generators/block.ts index b161b8f45d1..48e4b5cb890 100644 --- a/packages/compiler-vapor/src/generators/block.ts +++ b/packages/compiler-vapor/src/generators/block.ts @@ -52,7 +52,7 @@ export function genBlockContent( push(...genSelf(child, context)) } for (const child of dynamic.children) { - push(...genChildren(child, context, `n${child.id!}`)) + push(...genChildren(child, context, push, `n${child.id!}`)) } push(...genOperations(operation, context)) diff --git a/packages/compiler-vapor/src/generators/operation.ts b/packages/compiler-vapor/src/generators/operation.ts index 8b0bc90c369..4247bc6feca 100644 --- a/packages/compiler-vapor/src/generators/operation.ts +++ b/packages/compiler-vapor/src/generators/operation.ts @@ -43,10 +43,10 @@ export function genOperationWithInsertionState( context: CodegenContext, ): CodeFragment[] { const [frag, push] = buildCodeFragment() - push(...genOperation(oper, context)) if (isBlockOperation(oper) && oper.parent) { push(...genInsertionstate(oper, context)) } + push(...genOperation(oper, context)) return frag } diff --git a/packages/compiler-vapor/src/generators/template.ts b/packages/compiler-vapor/src/generators/template.ts index 356c1ccbe15..5a066b09e9a 100644 --- a/packages/compiler-vapor/src/generators/template.ts +++ b/packages/compiler-vapor/src/generators/template.ts @@ -41,6 +41,7 @@ export function genSelf( export function genChildren( dynamic: IRDynamicInfo, context: CodegenContext, + pushBlock: (...items: CodeFragment[]) => number, from: string = `n${dynamic.id}`, ): CodeFragment[] { const { helper } = context @@ -72,17 +73,17 @@ export function genChildren( // p for "placeholder" variables that are meant for possible reuse by // other access paths const variable = id === undefined ? `p${context.block.tempId++}` : `n${id}` - push(NEWLINE, `const ${variable} = `) + pushBlock(NEWLINE, `const ${variable} = `) if (prev) { if (elementIndex - prev[1] === 1) { - push(...genCall(helper('next'), prev[0])) + pushBlock(...genCall(helper('next'), prev[0])) } else { - push(...genCall(helper('nthChild'), from, String(elementIndex))) + pushBlock(...genCall(helper('nthChild'), from, String(elementIndex))) } } else { if (elementIndex === 0) { - push(...genCall(helper('child'), from)) + pushBlock(...genCall(helper('child'), from)) } else { // check if there's a node that we can reuse from let init = genCall(helper('child'), from) @@ -91,7 +92,7 @@ export function genChildren( } else if (elementIndex > 1) { init = genCall(helper('nthChild'), from, String(elementIndex)) } - push(...init) + pushBlock(...init) } } @@ -109,7 +110,7 @@ export function genChildren( if (childrenToGen.length) { for (const [child, from] of childrenToGen) { - push(...genChildren(child, context, from)) + push(...genChildren(child, context, pushBlock, from)) } }