From: Evan You Date: Tue, 11 Mar 2025 07:09:36 +0000 (+0800) Subject: wip(vapor): adjust children and block generation order for hydration X-Git-Tag: v3.6.0-alpha.1~16^2~43 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=972257474479ea22a32085e4b2b879965bb1d749;p=thirdparty%2Fvuejs%2Fcore.git wip(vapor): adjust children and block generation order for hydration --- diff --git a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap index fff7dd1272..e56676d870 100644 --- a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap @@ -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, child as _child, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, 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("
{{ bar }}
") const t1 = _template("
") @@ -157,9 +157,9 @@ export function render(_ctx, $props, $emit, $attrs, $slots) { const _component_Comp = _resolveComponent("Comp") const n0 = t0() const n3 = t1() - const n2 = _child(n3) _setInsertionState(n3, 0) const n1 = _createComponentWithFallback(_component_Comp) + const n2 = _child(n3) _renderEffect(() => { _setText(n2, _toDisplayString(_ctx.bar)) _setProp(n3, "id", _ctx.foo) 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 5f0121b5e9..40c4096ce2 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap @@ -40,10 +40,10 @@ const t0 = _template("
x
component > generate multi root component const t0 = _template("123") export function render(_ctx, $props, $emit, $attrs, $slots) { - const n1 = t0() const n0 = _createComponent(_ctx.Comp) + const n1 = t0() return [n0, n1] }" `; @@ -321,8 +321,8 @@ const t2 = _template("
") export function render(_ctx) { const n1 = t1() - const n0 = t0() const n3 = t2() + const n0 = t0() const n2 = t2() _insert(n0, n1) _insert(n2, n3) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vIf.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vIf.spec.ts.snap index 360823cdcf..16ab6ae372 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vIf.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vIf.spec.ts.snap @@ -25,7 +25,6 @@ const t4 = _template("fine") const t5 = _template("
") export function render(_ctx) { - const n13 = t5() const n0 = _createIf(() => (_ctx.ok), () => { const n2 = t0() return n2 @@ -38,6 +37,7 @@ export function render(_ctx) { const n11 = t4() return [n10, n11] })) + const n13 = t5() const x13 = _child(n13) _renderEffect(() => _setText(x13, _toDisplayString(_ctx.text))) return [n0, n13] diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap index 6649e333bf..4ecd8c76a7 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap @@ -167,7 +167,6 @@ export function render(_ctx) { const _component_Comp = _resolveComponent("Comp") const n5 = _createComponentWithFallback(_component_Comp, null, { "default": (_slotProps0) => { - const n3 = t0() const n1 = _createComponentWithFallback(_component_Inner, null, { "default": (_slotProps1) => { const n0 = t0() @@ -175,6 +174,7 @@ export function render(_ctx) { return n0 } }) + const n3 = t0() _renderEffect(() => _setText(n3, " " + _toDisplayString(_slotProps0["foo"] + _ctx.bar + _ctx.baz))) return [n1, n3] } diff --git a/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts b/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts index 5421623a50..adaad182cf 100644 --- a/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts @@ -29,16 +29,14 @@ describe('compiler: element transform', () => { expect(code).toMatchSnapshot() expect(helpers).contains.all.keys('resolveComponent') expect(helpers).contains.all.keys('createComponentWithFallback') - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - id: 0, - tag: 'Foo', - asset: true, - root: true, - props: [[]], - }, - ]) + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + id: 0, + tag: 'Foo', + asset: true, + root: true, + props: [[]], + }) }) test.todo('resolve implicitly self-referencing component', () => { @@ -57,13 +55,11 @@ describe('compiler: element transform', () => { }) expect(code).toMatchSnapshot() expect(helpers).not.toContain('resolveComponent') - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Example', - asset: false, - }, - ]) + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Example', + asset: false, + }) }) test('resolve component from setup bindings (inline)', () => { @@ -149,14 +145,12 @@ describe('compiler: element transform', () => { }) expect(code).toMatchSnapshot() expect(helpers).toContain('resolveComponent') - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - id: 0, - tag: 'Example', - asset: true, - }, - ]) + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + id: 0, + tag: 'Example', + asset: true, + }) }) test('generate single root component', () => { @@ -186,46 +180,44 @@ describe('compiler: element transform', () => { class: () => ("bar") }`) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Foo', - asset: true, - root: true, - props: [ - [ - { - key: { + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Foo', + asset: true, + root: true, + props: [ + [ + { + key: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'id', + isStatic: true, + }, + values: [ + { type: NodeTypes.SIMPLE_EXPRESSION, - content: 'id', + content: 'foo', isStatic: true, }, - values: [ - { - type: NodeTypes.SIMPLE_EXPRESSION, - content: 'foo', - isStatic: true, - }, - ], + ], + }, + { + key: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'class', + isStatic: true, }, - { - key: { + values: [ + { type: NodeTypes.SIMPLE_EXPRESSION, - content: 'class', + content: 'bar', isStatic: true, }, - values: [ - { - type: NodeTypes.SIMPLE_EXPRESSION, - content: 'bar', - isStatic: true, - }, - ], - }, - ], + ], + }, ], - }, - ]) + ], + }) }) test('v-bind="obj"', () => { @@ -234,18 +226,16 @@ describe('compiler: element transform', () => { expect(code).contains(`[ () => (_ctx.obj) ]`) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Foo', - props: [ - { - kind: IRDynamicPropsKind.EXPRESSION, - value: { content: 'obj', isStatic: false }, - }, - ], - }, - ]) + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Foo', + props: [ + { + kind: IRDynamicPropsKind.EXPRESSION, + value: { content: 'obj', isStatic: false }, + }, + ], + }) }) test('v-bind="obj" after static prop', () => { @@ -259,19 +249,17 @@ describe('compiler: element transform', () => { () => (_ctx.obj) ] }`) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Foo', - props: [ - [{ key: { content: 'id' }, values: [{ content: 'foo' }] }], - { - kind: IRDynamicPropsKind.EXPRESSION, - value: { content: 'obj' }, - }, - ], - }, - ]) + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Foo', + props: [ + [{ key: { content: 'id' }, values: [{ content: 'foo' }] }], + { + kind: IRDynamicPropsKind.EXPRESSION, + value: { content: 'obj' }, + }, + ], + }) }) test('v-bind="obj" before static prop', () => { @@ -283,19 +271,17 @@ describe('compiler: element transform', () => { () => (_ctx.obj), { id: () => ("foo") } ]`) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Foo', - props: [ - { - kind: IRDynamicPropsKind.EXPRESSION, - value: { content: 'obj' }, - }, - [{ key: { content: 'id' }, values: [{ content: 'foo' }] }], - ], - }, - ]) + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Foo', + props: [ + { + kind: IRDynamicPropsKind.EXPRESSION, + value: { content: 'obj' }, + }, + [{ key: { content: 'id' }, values: [{ content: 'foo' }] }], + ], + }) }) test('v-bind="obj" between static props', () => { @@ -310,20 +296,18 @@ describe('compiler: element transform', () => { { class: () => ("bar") } ] }`) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Foo', - props: [ - [{ key: { content: 'id' }, values: [{ content: 'foo' }] }], - { - kind: IRDynamicPropsKind.EXPRESSION, - value: { content: 'obj' }, - }, - [{ key: { content: 'class' }, values: [{ content: 'bar' }] }], - ], - }, - ]) + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Foo', + props: [ + [{ key: { content: 'id' }, values: [{ content: 'foo' }] }], + { + kind: IRDynamicPropsKind.EXPRESSION, + value: { content: 'obj' }, + }, + [{ key: { content: 'class' }, values: [{ content: 'bar' }] }], + ], + }) }) test.todo('props merging: event handlers', () => { @@ -368,19 +352,17 @@ describe('compiler: element transform', () => { expect(code).contains(`[ () => (_toHandlers(_ctx.obj)) ]`) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Foo', - props: [ - { - kind: IRDynamicPropsKind.EXPRESSION, - value: { content: 'obj' }, - handler: true, - }, - ], - }, - ]) + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Foo', + props: [ + { + kind: IRDynamicPropsKind.EXPRESSION, + value: { content: 'obj' }, + handler: true, + }, + ], + }) }) test('v-on expression is inline statement', () => { @@ -390,21 +372,19 @@ describe('compiler: element transform', () => { expect(code).toMatchSnapshot() expect(code).contains(`onBar: () => _on_bar`) expect(code).contains(`const _on_bar = () => _ctx.handler`) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Foo', - props: [ - [ - { - key: { content: 'bar' }, - handler: true, - values: [{ content: '_on_bar' }], - }, - ], + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Foo', + props: [ + [ + { + key: { content: 'bar' }, + handler: true, + values: [{ content: '_on_bar' }], + }, ], - }, - ]) + ], + }) }) test('v-on expression is a function call', () => { @@ -416,21 +396,19 @@ describe('compiler: element transform', () => { expect(code).contains( `const _on_bar = $event => (_ctx.handleBar($event))`, ) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Foo', - props: [ - [ - { - key: { content: 'bar' }, - handler: true, - values: [{ content: '_on_bar' }], - }, - ], + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Foo', + props: [ + [ + { + key: { content: 'bar' }, + handler: true, + values: [{ content: '_on_bar' }], + }, ], - }, - ]) + ], + }) }) test('cache v-on expression with unique handler name', () => { @@ -444,34 +422,33 @@ describe('compiler: element transform', () => { ) expect(code).contains(`onBar: () => _on_bar1`) expect(code).contains(`const _on_bar1 = () => _ctx.handler`) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Foo', - props: [ - [ - { - key: { content: 'bar' }, - handler: true, - values: [{ content: '_on_bar' }], - }, - ], + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Foo', + props: [ + [ + { + key: { content: 'bar' }, + handler: true, + values: [{ content: '_on_bar' }], + }, ], - }, - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Bar', - props: [ - [ - { - key: { content: 'bar' }, - handler: true, - values: [{ content: '_on_bar1' }], - }, - ], + ], + }) + + expect(ir.block.dynamic.children[1].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Bar', + props: [ + [ + { + key: { content: 'bar' }, + handler: true, + values: [{ content: '_on_bar1' }], + }, ], - }, - ]) + ], + }) }) }) @@ -482,20 +459,18 @@ describe('compiler: element transform', () => { ) expect(code).toMatchSnapshot() expect(helpers).toContain('resolveDynamicComponent') - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'component', - asset: true, - root: true, - props: [[]], - dynamic: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: 'foo', - isStatic: true, - }, + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'component', + asset: true, + root: true, + props: [[]], + dynamic: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'foo', + isStatic: true, }, - ]) + }) }) test('capitalized version w/ static binding', () => { @@ -504,20 +479,18 @@ describe('compiler: element transform', () => { ) expect(code).toMatchSnapshot() expect(helpers).toContain('resolveDynamicComponent') - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Component', - asset: true, - root: true, - props: [[]], - dynamic: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: 'foo', - isStatic: true, - }, + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Component', + asset: true, + root: true, + props: [[]], + dynamic: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'foo', + isStatic: true, }, - ]) + }) }) test('dynamic binding', () => { @@ -526,20 +499,18 @@ describe('compiler: element transform', () => { ) expect(code).toMatchSnapshot() expect(helpers).toContain('createDynamicComponent') - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'component', - asset: true, - root: true, - props: [[]], - dynamic: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: 'foo', - isStatic: false, - }, + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'component', + asset: true, + root: true, + props: [[]], + dynamic: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'foo', + isStatic: false, }, - ]) + }) }) test('dynamic binding shorthand', () => { @@ -547,20 +518,18 @@ describe('compiler: element transform', () => { compileWithElementTransform(``) expect(code).toMatchSnapshot() expect(helpers).toContain('createDynamicComponent') - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'component', - asset: true, - root: true, - props: [[]], - dynamic: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: 'is', - isStatic: false, - }, + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'component', + asset: true, + root: true, + props: [[]], + dynamic: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'is', + isStatic: false, }, - ]) + }) }) // #3934 @@ -574,15 +543,13 @@ describe('compiler: element transform', () => { expect(code).toMatchSnapshot() expect(helpers).toContain('resolveComponent') expect(helpers).not.toContain('resolveDynamicComponent') - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'custom-input', - asset: true, - root: true, - props: [[{ key: { content: 'is' }, values: [{ content: 'foo' }] }]], - }, - ]) + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'custom-input', + asset: true, + root: true, + props: [[{ key: { content: 'is' }, values: [{ content: 'foo' }] }]], + }) }) }) @@ -893,24 +860,22 @@ describe('compiler: element transform', () => { ``, ) expect(code).toMatchSnapshot() - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Foo', - props: [ - { - kind: IRDynamicPropsKind.ATTRIBUTE, - key: { content: 'foo-bar' }, - values: [{ content: 'bar' }], - }, - { - kind: IRDynamicPropsKind.ATTRIBUTE, - key: { content: 'baz' }, - values: [{ content: 'qux' }], - }, - ], - }, - ]) + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Foo', + props: [ + { + kind: IRDynamicPropsKind.ATTRIBUTE, + key: { content: 'foo-bar' }, + values: [{ content: 'bar' }], + }, + { + kind: IRDynamicPropsKind.ATTRIBUTE, + key: { content: 'baz' }, + values: [{ content: 'qux' }], + }, + ], + }) }) test('component with dynamic event arguments', () => { @@ -918,26 +883,24 @@ describe('compiler: element transform', () => { ``, ) expect(code).toMatchSnapshot() - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Foo', - props: [ - { - kind: IRDynamicPropsKind.ATTRIBUTE, - key: { content: 'foo-bar' }, - values: [{ content: 'bar' }], - handler: true, - }, - { - kind: IRDynamicPropsKind.ATTRIBUTE, - key: { content: 'baz' }, - values: [{ content: 'qux' }], - handler: true, - }, - ], - }, - ]) + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Foo', + props: [ + { + kind: IRDynamicPropsKind.ATTRIBUTE, + key: { content: 'foo-bar' }, + values: [{ content: 'bar' }], + handler: true, + }, + { + kind: IRDynamicPropsKind.ATTRIBUTE, + key: { content: 'baz' }, + values: [{ content: 'qux' }], + handler: true, + }, + ], + }) }) test('component event with once modifier', () => { diff --git a/packages/compiler-vapor/__tests__/transforms/transformSlotOutlet.spec.ts b/packages/compiler-vapor/__tests__/transforms/transformSlotOutlet.spec.ts index defc7c77f9..5993511323 100644 --- a/packages/compiler-vapor/__tests__/transforms/transformSlotOutlet.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/transformSlotOutlet.spec.ts @@ -31,67 +31,59 @@ describe('compiler: transform outlets', () => { expect(code).toMatchSnapshot() expect(helpers).toContain('createSlot') expect(ir.block.effect).toEqual([]) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.SLOT_OUTLET_NODE, - id: 0, - name: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: 'default', - isStatic: true, - }, - props: [], - fallback: undefined, + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.SLOT_OUTLET_NODE, + id: 0, + name: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'default', + isStatic: true, }, - ]) + props: [], + fallback: undefined, + }) }) test('statically named slot outlet', () => { const { ir, code } = compileWithSlotsOutlet(``) expect(code).toMatchSnapshot() - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.SLOT_OUTLET_NODE, - id: 0, - name: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: 'foo', - isStatic: true, - }, + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.SLOT_OUTLET_NODE, + id: 0, + name: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'foo', + isStatic: true, }, - ]) + }) }) test('dynamically named slot outlet', () => { const { ir, code } = compileWithSlotsOutlet(``) expect(code).toMatchSnapshot() - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.SLOT_OUTLET_NODE, - id: 0, - name: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: 'foo + bar', - isStatic: false, - }, + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.SLOT_OUTLET_NODE, + id: 0, + name: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'foo + bar', + isStatic: false, }, - ]) + }) }) test('dynamically named slot outlet with v-bind shorthand', () => { const { ir, code } = compileWithSlotsOutlet(``) expect(code).toMatchSnapshot() - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.SLOT_OUTLET_NODE, - id: 0, - name: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: 'name', - isStatic: false, - }, + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.SLOT_OUTLET_NODE, + id: 0, + name: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'name', + isStatic: false, }, - ]) + }) }) test('default slot outlet with props', () => { @@ -99,19 +91,17 @@ describe('compiler: transform outlets', () => { ``, ) expect(code).toMatchSnapshot() - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.SLOT_OUTLET_NODE, - name: { content: 'default' }, - props: [ - [ - { key: { content: 'foo' }, values: [{ content: 'bar' }] }, - { key: { content: 'baz' }, values: [{ content: 'qux' }] }, - { key: { content: 'fooBar' }, values: [{ content: 'foo-bar' }] }, - ], + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.SLOT_OUTLET_NODE, + name: { content: 'default' }, + props: [ + [ + { key: { content: 'foo' }, values: [{ content: 'bar' }] }, + { key: { content: 'baz' }, values: [{ content: 'qux' }] }, + { key: { content: 'fooBar' }, values: [{ content: 'foo-bar' }] }, ], - }, - ]) + ], + }) }) test('statically named slot outlet with props', () => { @@ -119,18 +109,16 @@ describe('compiler: transform outlets', () => { ``, ) expect(code).toMatchSnapshot() - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.SLOT_OUTLET_NODE, - name: { content: 'foo' }, - props: [ - [ - { key: { content: 'foo' }, values: [{ content: 'bar' }] }, - { key: { content: 'baz' }, values: [{ content: 'qux' }] }, - ], + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.SLOT_OUTLET_NODE, + name: { content: 'foo' }, + props: [ + [ + { key: { content: 'foo' }, values: [{ content: 'bar' }] }, + { key: { content: 'baz' }, values: [{ content: 'qux' }] }, ], - }, - ]) + ], + }) }) test('statically named slot outlet with v-bind="obj"', () => { @@ -138,17 +126,15 @@ describe('compiler: transform outlets', () => { ``, ) expect(code).toMatchSnapshot() - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.SLOT_OUTLET_NODE, - name: { content: 'foo' }, - props: [ - [{ key: { content: 'foo' }, values: [{ content: 'bar' }] }], - { value: { content: 'obj', isStatic: false } }, - [{ key: { content: 'baz' }, values: [{ content: 'qux' }] }], - ], - }, - ]) + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.SLOT_OUTLET_NODE, + name: { content: 'foo' }, + props: [ + [{ key: { content: 'foo' }, values: [{ content: 'bar' }] }], + { value: { content: 'obj', isStatic: false } }, + [{ key: { content: 'baz' }, values: [{ content: 'qux' }] }], + ], + }) }) test('statically named slot outlet with v-on', () => { @@ -156,36 +142,32 @@ describe('compiler: transform outlets', () => { ``, ) expect(code).toMatchSnapshot() - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.SLOT_OUTLET_NODE, - props: [ - [{ key: { content: 'click' }, values: [{ content: 'foo' }] }], - { value: { content: 'bar' }, handler: true }, - [{ key: { content: 'baz' }, values: [{ content: 'qux' }] }], - ], - }, - ]) + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.SLOT_OUTLET_NODE, + props: [ + [{ key: { content: 'click' }, values: [{ content: 'foo' }] }], + { value: { content: 'bar' }, handler: true }, + [{ key: { content: 'baz' }, values: [{ content: 'qux' }] }], + ], + }) }) test('default slot outlet with fallback', () => { const { ir, code } = compileWithSlotsOutlet(`
`) expect(code).toMatchSnapshot() expect(ir.template[0]).toBe('
') - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.SLOT_OUTLET_NODE, - id: 0, - name: { content: 'default' }, - fallback: { - type: IRNodeTypes.BLOCK, - dynamic: { - children: [{ template: 0, id: 2 }], - }, - returns: [2], + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.SLOT_OUTLET_NODE, + id: 0, + name: { content: 'default' }, + fallback: { + type: IRNodeTypes.BLOCK, + dynamic: { + children: [{ template: 0, id: 2 }], }, + returns: [2], }, - ]) + }) }) test('named slot outlet with fallback', () => { @@ -194,20 +176,18 @@ describe('compiler: transform outlets', () => { ) expect(code).toMatchSnapshot() expect(ir.template[0]).toBe('
') - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.SLOT_OUTLET_NODE, - id: 0, - name: { content: 'foo' }, - fallback: { - type: IRNodeTypes.BLOCK, - dynamic: { - children: [{ template: 0, id: 2 }], - }, - returns: [2], + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.SLOT_OUTLET_NODE, + id: 0, + name: { content: 'foo' }, + fallback: { + type: IRNodeTypes.BLOCK, + dynamic: { + children: [{ template: 0, id: 2 }], }, + returns: [2], }, - ]) + }) }) test('default slot outlet with props & fallback', () => { @@ -216,21 +196,19 @@ describe('compiler: transform outlets', () => { ) expect(code).toMatchSnapshot() expect(ir.template[0]).toBe('
') - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.SLOT_OUTLET_NODE, - id: 0, - name: { content: 'default' }, - props: [[{ key: { content: 'foo' }, values: [{ content: 'bar' }] }]], - fallback: { - type: IRNodeTypes.BLOCK, - dynamic: { - children: [{ template: 0, id: 2 }], - }, - returns: [2], + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.SLOT_OUTLET_NODE, + id: 0, + name: { content: 'default' }, + props: [[{ key: { content: 'foo' }, values: [{ content: 'bar' }] }]], + fallback: { + type: IRNodeTypes.BLOCK, + dynamic: { + children: [{ template: 0, id: 2 }], }, + returns: [2], }, - ]) + }) }) test('named slot outlet with props & fallback', () => { @@ -239,21 +217,19 @@ describe('compiler: transform outlets', () => { ) expect(code).toMatchSnapshot() expect(ir.template[0]).toBe('
') - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.SLOT_OUTLET_NODE, - id: 0, - name: { content: 'foo' }, - props: [[{ key: { content: 'foo' }, values: [{ content: 'bar' }] }]], - fallback: { - type: IRNodeTypes.BLOCK, - dynamic: { - children: [{ template: 0, id: 2 }], - }, - returns: [2], + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.SLOT_OUTLET_NODE, + id: 0, + name: { content: 'foo' }, + props: [[{ key: { content: 'foo' }, values: [{ content: 'bar' }] }]], + fallback: { + type: IRNodeTypes.BLOCK, + dynamic: { + children: [{ template: 0, id: 2 }], }, + returns: [2], }, - ]) + }) }) test('error on unexpected custom directive on ', () => { diff --git a/packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts b/packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts index a87c803ab0..6be8f18779 100644 --- a/packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts @@ -86,10 +86,10 @@ describe('compiler: template ref transform', () => { `
`, ) - expect(ir.block.operation).lengthOf(1) - expect(ir.block.operation[0].type).toBe(IRNodeTypes.IF) + const op = ir.block.dynamic.children[0].operation as IfIRNode + expect(op.type).toBe(IRNodeTypes.IF) - const { positive } = ir.block.operation[0] as IfIRNode + const { positive } = op expect(positive.operation).toMatchObject([ { type: IRNodeTypes.SET_TEMPLATE_REF, @@ -111,7 +111,7 @@ describe('compiler: template ref transform', () => { `
`, ) - const { render } = ir.block.operation[0] as ForIRNode + const { render } = ir.block.dynamic.children[0].operation as ForIRNode expect(render.operation).toMatchObject([ { type: IRNodeTypes.SET_TEMPLATE_REF, diff --git a/packages/compiler-vapor/__tests__/transforms/vFor.spec.ts b/packages/compiler-vapor/__tests__/transforms/vFor.spec.ts index 31a6da3690..0008df7f4c 100644 --- a/packages/compiler-vapor/__tests__/transforms/vFor.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vFor.spec.ts @@ -33,38 +33,38 @@ describe('compiler: v-for', () => { expect(code).matchSnapshot() expect(helpers).contains('createFor') expect(ir.template).toEqual(['
']) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.FOR, - id: 0, - source: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: 'items', - }, - value: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: 'item', - }, - key: undefined, - index: undefined, - render: { - type: IRNodeTypes.BLOCK, - dynamic: { - children: [{ template: 0 }], - }, - }, - keyProp: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: 'item.id', + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.FOR, + id: 0, + source: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'items', + }, + value: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'item', + }, + key: undefined, + index: undefined, + render: { + type: IRNodeTypes.BLOCK, + dynamic: { + children: [{ template: 0 }], }, }, - ]) + keyProp: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'item.id', + }, + }) expect(ir.block.returns).toEqual([0]) expect(ir.block.dynamic).toMatchObject({ children: [{ id: 0 }], }) expect(ir.block.effect).toEqual([]) - expect((ir.block.operation[0] as ForIRNode).render.effect).lengthOf(1) + expect( + (ir.block.dynamic.children[0].operation as ForIRNode).render.effect, + ).lengthOf(1) }) test('multi effect', () => { @@ -90,21 +90,22 @@ describe('compiler: v-for', () => { ) expect(code).contains(`_for_item1.value+_for_item0.value`) expect(ir.template).toEqual([' ', '
']) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.FOR, - id: 0, - source: { content: 'list' }, - value: { content: 'i' }, - render: { - type: IRNodeTypes.BLOCK, - dynamic: { - children: [{ template: 1 }], - }, + const parentOp = ir.block.dynamic.children[0].operation + expect(parentOp).toMatchObject({ + type: IRNodeTypes.FOR, + id: 0, + source: { content: 'list' }, + value: { content: 'i' }, + render: { + type: IRNodeTypes.BLOCK, + dynamic: { + children: [{ template: 1 }], }, }, - ]) - expect((ir.block.operation[0] as any).render.operation[0]).toMatchObject({ + }) + expect( + (parentOp as any).render.dynamic.children[0].children[0].operation, + ).toMatchObject({ type: IRNodeTypes.FOR, id: 2, source: { content: 'i' }, @@ -123,7 +124,7 @@ describe('compiler: v-for', () => { '{{ id }}{{ value }}', ) expect(code).matchSnapshot() - expect(ir.block.operation[0]).toMatchObject({ + expect(ir.block.dynamic.children[0].operation).toMatchObject({ type: IRNodeTypes.FOR, source: { type: NodeTypes.SIMPLE_EXPRESSION, @@ -152,7 +153,7 @@ describe('compiler: v-for', () => { ) expect(code).matchSnapshot() expect(code).toContain('_getRestElement(_for_item0.value, ["id"])') - expect(ir.block.operation[0]).toMatchObject({ + expect(ir.block.dynamic.children[0].operation).toMatchObject({ type: IRNodeTypes.FOR, source: { type: NodeTypes.SIMPLE_EXPRESSION, @@ -183,7 +184,7 @@ describe('compiler: v-for', () => { `
{{ id + other + index }}
`, ) expect(code).matchSnapshot() - expect(ir.block.operation[0]).toMatchObject({ + expect(ir.block.dynamic.children[0].operation).toMatchObject({ type: IRNodeTypes.FOR, source: { type: NodeTypes.SIMPLE_EXPRESSION, @@ -215,7 +216,7 @@ describe('compiler: v-for', () => { ) expect(code).matchSnapshot() expect(code).toContain('_for_item0.value.slice(1)') - expect(ir.block.operation[0]).toMatchObject({ + expect(ir.block.dynamic.children[0].operation).toMatchObject({ type: IRNodeTypes.FOR, source: { type: NodeTypes.SIMPLE_EXPRESSION, @@ -252,7 +253,7 @@ describe('compiler: v-for', () => { expect(code).toContain( `_getDefaultValue(_for_item0.value.baz[0], _ctx.quux)`, ) - expect(ir.block.operation[0]).toMatchObject({ + expect(ir.block.dynamic.children[0].operation).toMatchObject({ type: IRNodeTypes.FOR, source: { type: NodeTypes.SIMPLE_EXPRESSION, diff --git a/packages/compiler-vapor/__tests__/transforms/vIf.spec.ts b/packages/compiler-vapor/__tests__/transforms/vIf.spec.ts index 8f2d572d0b..66cdf7b930 100644 --- a/packages/compiler-vapor/__tests__/transforms/vIf.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vIf.spec.ts @@ -33,23 +33,23 @@ describe('compiler: v-if', () => { expect(helpers).contains('createIf') expect(ir.template).toEqual(['
']) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.IF, - id: 0, - condition: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: 'ok', - isStatic: false, - }, - positive: { - type: IRNodeTypes.BLOCK, - dynamic: { - children: [{ template: 0 }], - }, + + const op = ir.block.dynamic.children[0].operation + expect(op).toMatchObject({ + type: IRNodeTypes.IF, + id: 0, + condition: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'ok', + isStatic: false, + }, + positive: { + type: IRNodeTypes.BLOCK, + dynamic: { + children: [{ template: 0 }], }, }, - ]) + }) expect(ir.block.returns).toEqual([0]) expect(ir.block.dynamic).toMatchObject({ @@ -57,7 +57,7 @@ describe('compiler: v-if', () => { }) expect(ir.block.effect).toEqual([]) - expect((ir.block.operation[0] as IfIRNode).positive.effect).lengthOf(1) + expect((op as IfIRNode).positive.effect).lengthOf(1) expect(code).matchSnapshot() }) @@ -70,7 +70,8 @@ describe('compiler: v-if', () => { expect(ir.template).toEqual(['
', 'hello', '

']) expect(ir.block.effect).toEqual([]) - expect((ir.block.operation[0] as IfIRNode).positive.effect).toMatchObject([ + const op = ir.block.dynamic.children[0].operation as IfIRNode + expect(op.positive.effect).toMatchObject([ { operations: [ { @@ -87,7 +88,7 @@ describe('compiler: v-if', () => { ], }, ]) - expect((ir.block.operation[0] as IfIRNode).positive.dynamic).toMatchObject({ + expect(op.positive.dynamic).toMatchObject({ id: 1, children: { 2: { @@ -116,29 +117,27 @@ describe('compiler: v-if', () => { expect(helpers).contains('createIf') expect(ir.block.effect).lengthOf(0) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.IF, - id: 0, - condition: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: 'ok', - isStatic: false, - }, - positive: { - type: IRNodeTypes.BLOCK, - dynamic: { - children: [{ template: 0 }], - }, + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.IF, + id: 0, + condition: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'ok', + isStatic: false, + }, + positive: { + type: IRNodeTypes.BLOCK, + dynamic: { + children: [{ template: 0 }], }, - negative: { - type: IRNodeTypes.BLOCK, - dynamic: { - children: [{ template: 1 }], - }, + }, + negative: { + type: IRNodeTypes.BLOCK, + dynamic: { + children: [{ template: 1 }], }, }, - ]) + }) expect(ir.block.returns).toEqual([0]) }) @@ -149,37 +148,35 @@ describe('compiler: v-if', () => { expect(code).matchSnapshot() expect(ir.template).toEqual(['
', '

']) - expect(ir.block.operation).toMatchObject([ - { + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.IF, + id: 0, + condition: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'ok', + isStatic: false, + }, + positive: { + type: IRNodeTypes.BLOCK, + dynamic: { + children: [{ template: 0 }], + }, + }, + negative: { type: IRNodeTypes.IF, - id: 0, condition: { type: NodeTypes.SIMPLE_EXPRESSION, - content: 'ok', + content: 'orNot', isStatic: false, }, positive: { type: IRNodeTypes.BLOCK, dynamic: { - children: [{ template: 0 }], - }, - }, - negative: { - type: IRNodeTypes.IF, - condition: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: 'orNot', - isStatic: false, - }, - positive: { - type: IRNodeTypes.BLOCK, - dynamic: { - children: [{ template: 1 }], - }, + children: [{ template: 1 }], }, }, }, - ]) + }) expect(ir.block.returns).toEqual([0]) }) @@ -191,33 +188,31 @@ describe('compiler: v-if', () => { expect(ir.template).toEqual(['
', '

', 'fine']) expect(ir.block.returns).toEqual([0]) - expect(ir.block.operation).toMatchObject([ - { + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.IF, + id: 0, + positive: { + type: IRNodeTypes.BLOCK, + dynamic: { + children: [{ template: 0 }], + }, + }, + negative: { type: IRNodeTypes.IF, - id: 0, positive: { type: IRNodeTypes.BLOCK, dynamic: { - children: [{ template: 0 }], + children: [{ template: 1 }], }, }, negative: { - type: IRNodeTypes.IF, - positive: { - type: IRNodeTypes.BLOCK, - dynamic: { - children: [{ template: 1 }], - }, - }, - negative: { - type: IRNodeTypes.BLOCK, - dynamic: { - children: [{ template: 2 }], - }, + type: IRNodeTypes.BLOCK, + dynamic: { + children: [{ template: 2 }], }, }, }, - ]) + }) }) test('comment between branches', () => { diff --git a/packages/compiler-vapor/__tests__/transforms/vModel.spec.ts b/packages/compiler-vapor/__tests__/transforms/vModel.spec.ts index 32ec9dd5c4..51eaa9e023 100644 --- a/packages/compiler-vapor/__tests__/transforms/vModel.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vModel.spec.ts @@ -206,22 +206,20 @@ describe('compiler: vModel transform', () => { expect(code).contains( `"onUpdate:modelValue": () => _value => (_ctx.foo = _value)`, ) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Comp', - props: [ - [ - { - key: { content: 'modelValue', isStatic: true }, - model: true, - modelModifiers: [], - values: [{ content: 'foo', isStatic: false }], - }, - ], + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Comp', + props: [ + [ + { + key: { content: 'modelValue', isStatic: true }, + model: true, + modelModifiers: [], + values: [{ content: 'foo', isStatic: false }], + }, ], - }, - ]) + ], + }) }) test('v-model with arguments for component should work', () => { @@ -231,22 +229,20 @@ describe('compiler: vModel transform', () => { expect(code).contains( `"onUpdate:bar": () => _value => (_ctx.foo = _value)`, ) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Comp', - props: [ - [ - { - key: { content: 'bar', isStatic: true }, - model: true, - modelModifiers: [], - values: [{ content: 'foo', isStatic: false }], - }, - ], + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Comp', + props: [ + [ + { + key: { content: 'bar', isStatic: true }, + model: true, + modelModifiers: [], + values: [{ content: 'foo', isStatic: false }], + }, ], - }, - ]) + ], + }) }) test('v-model with dynamic arguments for component should work', () => { @@ -256,20 +252,18 @@ describe('compiler: vModel transform', () => { `[_ctx.arg]: _ctx.foo, ["onUpdate:" + _ctx.arg]: () => _value => (_ctx.foo = _value)`, ) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Comp', - props: [ - { - key: { content: 'arg', isStatic: false }, - values: [{ content: 'foo', isStatic: false }], - model: true, - modelModifiers: [], - }, - ], - }, - ]) + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Comp', + props: [ + { + key: { content: 'arg', isStatic: false }, + values: [{ content: 'foo', isStatic: false }], + model: true, + modelModifiers: [], + }, + ], + }) }) test('v-model for component should generate modelModifiers', () => { @@ -280,22 +274,20 @@ describe('compiler: vModel transform', () => { expect(code).contain( `modelModifiers: () => ({ trim: true, "bar-baz": true })`, ) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Comp', - props: [ - [ - { - key: { content: 'modelValue', isStatic: true }, - values: [{ content: 'foo', isStatic: false }], - model: true, - modelModifiers: ['trim', 'bar-baz'], - }, - ], + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Comp', + props: [ + [ + { + key: { content: 'modelValue', isStatic: true }, + values: [{ content: 'foo', isStatic: false }], + model: true, + modelModifiers: ['trim', 'bar-baz'], + }, ], - }, - ]) + ], + }) }) test('v-model with arguments for component should generate modelModifiers', () => { @@ -305,57 +297,53 @@ describe('compiler: vModel transform', () => { expect(code).toMatchSnapshot() expect(code).contain(`fooModifiers: () => ({ trim: true })`) expect(code).contain(`barModifiers: () => ({ number: true })`) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Comp', - props: [ - [ - { - key: { content: 'foo', isStatic: true }, - values: [{ content: 'foo', isStatic: false }], - model: true, - modelModifiers: ['trim'], - }, - { - key: { content: 'bar', isStatic: true }, - values: [{ content: 'bar', isStatic: false }], - model: true, - modelModifiers: ['number'], - }, - ], - ], - }, - ]) - }) - - test('v-model with dynamic arguments for component should generate modelModifiers ', () => { - const { code, ir } = compileWithVModel( - '', - ) - expect(code).toMatchSnapshot() - expect(code).contain(`[_ctx.foo + "Modifiers"]: () => ({ trim: true })`) - expect(code).contain(`[_ctx.bar + "Modifiers"]: () => ({ number: true })`) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Comp', - props: [ + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Comp', + props: [ + [ { - key: { content: 'foo', isStatic: false }, + key: { content: 'foo', isStatic: true }, values: [{ content: 'foo', isStatic: false }], model: true, modelModifiers: ['trim'], }, { - key: { content: 'bar', isStatic: false }, + key: { content: 'bar', isStatic: true }, values: [{ content: 'bar', isStatic: false }], model: true, modelModifiers: ['number'], }, ], - }, - ]) + ], + }) + }) + + test('v-model with dynamic arguments for component should generate modelModifiers ', () => { + const { code, ir } = compileWithVModel( + '', + ) + expect(code).toMatchSnapshot() + expect(code).contain(`[_ctx.foo + "Modifiers"]: () => ({ trim: true })`) + expect(code).contain(`[_ctx.bar + "Modifiers"]: () => ({ number: true })`) + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Comp', + props: [ + { + key: { content: 'foo', isStatic: false }, + values: [{ content: 'foo', isStatic: false }], + model: true, + modelModifiers: ['trim'], + }, + { + key: { content: 'bar', isStatic: false }, + values: [{ content: 'bar', isStatic: false }], + model: true, + modelModifiers: ['number'], + }, + ], + }) }) }) }) diff --git a/packages/compiler-vapor/__tests__/transforms/vOnce.spec.ts b/packages/compiler-vapor/__tests__/transforms/vOnce.spec.ts index 43077bf2eb..97f6ee62a1 100644 --- a/packages/compiler-vapor/__tests__/transforms/vOnce.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vOnce.spec.ts @@ -126,15 +126,13 @@ describe('compiler: v-once', () => { const { ir, code } = compileWithOnce(`
`) expect(code).toMatchSnapshot() expect(ir.block.effect).lengthOf(0) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - id: 0, - tag: 'Comp', - once: true, - parent: 1, - }, - ]) + expect(ir.block.dynamic.children[0].children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + id: 0, + tag: 'Comp', + once: true, + parent: 1, + }) }) test.todo('on slot outlet') @@ -155,24 +153,22 @@ describe('compiler: v-once', () => { expect(code).toMatchSnapshot() expect(ir.block.effect).lengthOf(0) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.IF, - id: 0, - once: true, - condition: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: 'expr', - isStatic: false, - }, - positive: { - type: IRNodeTypes.BLOCK, - dynamic: { - children: [{ template: 0 }], - }, + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.IF, + id: 0, + once: true, + condition: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'expr', + isStatic: false, + }, + positive: { + type: IRNodeTypes.BLOCK, + dynamic: { + children: [{ template: 0 }], }, }, - ]) + }) }) test('with v-if/else', () => { @@ -182,42 +178,38 @@ describe('compiler: v-once', () => { expect(code).toMatchSnapshot() expect(ir.block.effect).lengthOf(0) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.IF, - id: 0, - once: true, - condition: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: 'expr', - isStatic: false, - }, - positive: { - type: IRNodeTypes.BLOCK, - dynamic: { - children: [{ template: 0 }], - }, + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.IF, + id: 0, + once: true, + condition: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'expr', + isStatic: false, + }, + positive: { + type: IRNodeTypes.BLOCK, + dynamic: { + children: [{ template: 0 }], }, - negative: { - type: IRNodeTypes.BLOCK, - dynamic: { - children: [{ template: 1 }], - }, + }, + negative: { + type: IRNodeTypes.BLOCK, + dynamic: { + children: [{ template: 1 }], }, }, - ]) + }) }) test('with v-for', () => { const { ir, code } = compileWithOnce(`
`) expect(code).toMatchSnapshot() expect(ir.block.effect).lengthOf(0) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.FOR, - id: 0, - once: true, - }, - ]) + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.FOR, + id: 0, + once: true, + }) }) }) diff --git a/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts b/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts index caa45681a2..84ddb2e5d0 100644 --- a/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts @@ -36,27 +36,25 @@ describe('compiler: transform slot', () => { expect(code).toMatchSnapshot() expect(ir.template).toEqual(['
']) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - id: 1, - tag: 'Comp', - props: [[]], - slots: [ - { - slotType: IRSlotType.STATIC, - slots: { - default: { - type: IRNodeTypes.BLOCK, - dynamic: { - children: [{ template: 0 }], - }, + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + id: 1, + tag: 'Comp', + props: [[]], + slots: [ + { + slotType: IRSlotType.STATIC, + slots: { + default: { + type: IRNodeTypes.BLOCK, + dynamic: { + children: [{ template: 0 }], }, }, }, - ], - }, - ]) + }, + ], + }) expect(ir.block.returns).toEqual([1]) expect(ir.block.dynamic).toMatchObject({ children: [{ id: 1 }], @@ -72,31 +70,29 @@ describe('compiler: transform slot', () => { expect(code).contains(`"default": (_slotProps0) =>`) expect(code).contains(`_slotProps0["foo"] + _ctx.bar`) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Comp', - props: [[]], - slots: [ - { - slotType: IRSlotType.STATIC, - slots: { - default: { - type: IRNodeTypes.BLOCK, - props: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: '{ foo }', - ast: { - type: 'ArrowFunctionExpression', - params: [{ type: 'ObjectPattern' }], - }, + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Comp', + props: [[]], + slots: [ + { + slotType: IRSlotType.STATIC, + slots: { + default: { + type: IRNodeTypes.BLOCK, + props: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: '{ foo }', + ast: { + type: 'ArrowFunctionExpression', + params: [{ type: 'ObjectPattern' }], }, }, }, }, - ], - }, - ]) + }, + ], + }) }) test('on component named slot', () => { @@ -108,26 +104,24 @@ describe('compiler: transform slot', () => { expect(code).contains(`"named": (_slotProps0) =>`) expect(code).contains(`_slotProps0["foo"] + _ctx.bar`) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Comp', - slots: [ - { - slotType: IRSlotType.STATIC, - slots: { - named: { - type: IRNodeTypes.BLOCK, - props: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: '{ foo }', - }, + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Comp', + slots: [ + { + slotType: IRSlotType.STATIC, + slots: { + named: { + type: IRNodeTypes.BLOCK, + props: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: '{ foo }', }, }, }, - ], - }, - ]) + }, + ], + }) }) test('on component dynamically named slot', () => { @@ -139,28 +133,26 @@ describe('compiler: transform slot', () => { expect(code).contains(`fn: (_slotProps0) =>`) expect(code).contains(`_slotProps0["foo"] + _ctx.bar`) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Comp', - slots: [ - { - name: { + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Comp', + slots: [ + { + name: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'named', + isStatic: false, + }, + fn: { + type: IRNodeTypes.BLOCK, + props: { type: NodeTypes.SIMPLE_EXPRESSION, - content: 'named', - isStatic: false, - }, - fn: { - type: IRNodeTypes.BLOCK, - props: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: '{ foo }', - }, + content: '{ foo }', }, }, - ], - }, - ]) + }, + ], + }) }) test('named slots w/ implicit default slot', () => { @@ -172,33 +164,31 @@ describe('compiler: transform slot', () => { expect(code).toMatchSnapshot() expect(ir.template).toEqual(['foo', 'bar', '']) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - id: 4, - tag: 'Comp', - props: [[]], - slots: [ - { - slotType: IRSlotType.STATIC, - slots: { - one: { - type: IRNodeTypes.BLOCK, - dynamic: { - children: [{ template: 0 }], - }, + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + id: 4, + tag: 'Comp', + props: [[]], + slots: [ + { + slotType: IRSlotType.STATIC, + slots: { + one: { + type: IRNodeTypes.BLOCK, + dynamic: { + children: [{ template: 0 }], }, - default: { - type: IRNodeTypes.BLOCK, - dynamic: { - children: [{}, { template: 1 }, { template: 2 }], - }, + }, + default: { + type: IRNodeTypes.BLOCK, + dynamic: { + children: [{}, { template: 1 }, { template: 2 }], }, }, }, - ], - }, - ]) + }, + ], + }) }) test('nested slots scoping', () => { @@ -219,29 +209,28 @@ describe('compiler: transform slot', () => { expect(code).contains(`_slotProps0["foo"] + _slotProps1["bar"] + _ctx.baz`) expect(code).contains(`_slotProps0["foo"] + _ctx.bar + _ctx.baz`) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Comp', - props: [[]], - slots: [ - { - slotType: IRSlotType.STATIC, - slots: { - default: { - type: IRNodeTypes.BLOCK, - props: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: '{ foo }', - }, + const outerOp = ir.block.dynamic.children[0].operation + expect(outerOp).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Comp', + props: [[]], + slots: [ + { + slotType: IRSlotType.STATIC, + slots: { + default: { + type: IRNodeTypes.BLOCK, + props: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: '{ foo }', }, }, }, - ], - }, - ]) + }, + ], + }) expect( - (ir.block.operation[0] as any).slots[0].slots.default.operation[0], + (outerOp as any).slots[0].slots.default.dynamic.children[0].operation, ).toMatchObject({ type: IRNodeTypes.CREATE_COMPONENT_NODE, tag: 'Inner', @@ -269,23 +258,20 @@ describe('compiler: transform slot', () => { `, ) expect(code).toMatchSnapshot() - expect(ir.block.operation[0].type).toBe(IRNodeTypes.CREATE_COMPONENT_NODE) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Comp', - slots: [ - { - name: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: 'name', - isStatic: false, - }, - fn: { type: IRNodeTypes.BLOCK }, + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Comp', + slots: [ + { + name: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'name', + isStatic: false, }, - ], - }, - ]) + fn: { type: IRNodeTypes.BLOCK }, + }, + ], + }) }) test('dynamic slots name w/ v-for', () => { @@ -299,28 +285,25 @@ describe('compiler: transform slot', () => { expect(code).contains(`fn: (_slotProps0) =>`) expect(code).contains(`_setText(n0, _toDisplayString(_slotProps0["bar"]))`) - expect(ir.block.operation[0].type).toBe(IRNodeTypes.CREATE_COMPONENT_NODE) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Comp', - slots: [ - { - name: { - type: NodeTypes.SIMPLE_EXPRESSION, - content: 'item', - isStatic: false, - }, - fn: { type: IRNodeTypes.BLOCK }, - loop: { - source: { content: 'list' }, - value: { content: 'item' }, - index: undefined, - }, + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Comp', + slots: [ + { + name: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'item', + isStatic: false, }, - ], - }, - ]) + fn: { type: IRNodeTypes.BLOCK }, + loop: { + source: { content: 'list' }, + value: { content: 'item' }, + index: undefined, + }, + }, + ], + }) }) test('dynamic slots name w/ v-for and provide absent key', () => { @@ -330,30 +313,27 @@ describe('compiler: transform slot', () => { `, ) expect(code).toMatchSnapshot() - expect(ir.block.operation[0].type).toBe(IRNodeTypes.CREATE_COMPONENT_NODE) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Comp', - slots: [ - { - name: { + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Comp', + slots: [ + { + name: { + type: NodeTypes.SIMPLE_EXPRESSION, + content: 'index', + isStatic: false, + }, + fn: { type: IRNodeTypes.BLOCK }, + loop: { + source: { content: 'list' }, + value: undefined, + index: { type: NodeTypes.SIMPLE_EXPRESSION, - content: 'index', - isStatic: false, - }, - fn: { type: IRNodeTypes.BLOCK }, - loop: { - source: { content: 'list' }, - value: undefined, - index: { - type: NodeTypes.SIMPLE_EXPRESSION, - }, }, }, - ], - }, - ]) + }, + ], + }) }) test('dynamic slots name w/ v-if / v-else[-if]', () => { @@ -368,30 +348,27 @@ describe('compiler: transform slot', () => { expect(code).contains(`fn: (_slotProps0) =>`) - expect(ir.block.operation[0].type).toBe(IRNodeTypes.CREATE_COMPONENT_NODE) - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'Comp', - slots: [ - { + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Comp', + slots: [ + { + slotType: IRSlotType.CONDITIONAL, + condition: { content: 'condition' }, + positive: { + slotType: IRSlotType.DYNAMIC, + }, + negative: { slotType: IRSlotType.CONDITIONAL, - condition: { content: 'condition' }, + condition: { content: 'anotherCondition' }, positive: { slotType: IRSlotType.DYNAMIC, }, - negative: { - slotType: IRSlotType.CONDITIONAL, - condition: { content: 'anotherCondition' }, - positive: { - slotType: IRSlotType.DYNAMIC, - }, - negative: { slotType: IRSlotType.DYNAMIC }, - }, + negative: { slotType: IRSlotType.DYNAMIC }, }, - ], - }, - ]) + }, + ], + }) }) test('quote slot name', () => { @@ -405,29 +382,31 @@ describe('compiler: transform slot', () => { test('nested component slot', () => { const { ir, code } = compileWithSlots(``) expect(code).toMatchSnapshot() - expect(ir.block.operation).toMatchObject([ - { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'A', - slots: [ - { - slotType: IRSlotType.STATIC, - slots: { - default: { - type: IRNodeTypes.BLOCK, - operation: [ + expect(ir.block.dynamic.children[0].operation).toMatchObject({ + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'A', + slots: [ + { + slotType: IRSlotType.STATIC, + slots: { + default: { + type: IRNodeTypes.BLOCK, + dynamic: { + children: [ { - type: IRNodeTypes.CREATE_COMPONENT_NODE, - tag: 'B', - slots: [], + operation: { + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'B', + slots: [], + }, }, ], }, }, }, - ], - }, - ]) + }, + ], + }) }) describe('errors', () => { diff --git a/packages/compiler-vapor/src/generators/block.ts b/packages/compiler-vapor/src/generators/block.ts index 77ba4bee8a..b161b8f45d 100644 --- a/packages/compiler-vapor/src/generators/block.ts +++ b/packages/compiler-vapor/src/generators/block.ts @@ -11,7 +11,7 @@ import { } from './utils' import type { CodegenContext } from '../generate' import { genEffects, genOperations } from './operation' -import { genChildren } from './template' +import { genChildren, genSelf } from './template' import { toValidAssetId } from '@vue/compiler-dom' export function genBlock( @@ -48,6 +48,9 @@ export function genBlockContent( genResolveAssets('directive', 'resolveDirective') } + for (const child of dynamic.children) { + push(...genSelf(child, context)) + } for (const child of dynamic.children) { push(...genChildren(child, context, `n${child.id!}`)) } diff --git a/packages/compiler-vapor/src/generators/operation.ts b/packages/compiler-vapor/src/generators/operation.ts index 64b2a568ee..ad5c43d6b9 100644 --- a/packages/compiler-vapor/src/generators/operation.ts +++ b/packages/compiler-vapor/src/generators/operation.ts @@ -3,7 +3,7 @@ import { IRNodeTypes, type InsertionStateTypes, type OperationNode, - isTypeThatNeedsInsertionState, + isBlockOperation, } from '../ir' import type { CodegenContext } from '../generate' import { genInsertNode, genPrependNode } from './dom' @@ -33,11 +33,20 @@ export function genOperations( ): CodeFragment[] { const [frag, push] = buildCodeFragment() for (const operation of opers) { - if (isTypeThatNeedsInsertionState(operation) && operation.parent) { - push(...genInsertionstate(operation, context)) - } - push(...genOperation(operation, context)) + push(...genOperationWithInsertionState(operation, context)) + } + return frag +} + +export function genOperationWithInsertionState( + oper: OperationNode, + context: CodegenContext, +): CodeFragment[] { + const [frag, push] = buildCodeFragment() + 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 9069444798..dfd0082d7e 100644 --- a/packages/compiler-vapor/src/generators/template.ts +++ b/packages/compiler-vapor/src/generators/template.ts @@ -1,6 +1,7 @@ import type { CodegenContext } from '../generate' import { DynamicFlag, type IRDynamicInfo } from '../ir' import { genDirectivesForElement } from './directive' +import { genOperationWithInsertionState } from './operation' import { type CodeFragment, NEWLINE, buildCodeFragment, genCall } from './utils' export function genTemplates( @@ -18,24 +19,38 @@ export function genTemplates( .join('') } -export function genChildren( +export function genSelf( dynamic: IRDynamicInfo, context: CodegenContext, - from: string, - path: number[] = [], - knownPaths: [id: string, path: number[]][] = [], ): CodeFragment[] { - const { helper } = context const [frag, push] = buildCodeFragment() - let offset = 0 - const { children, id, template } = dynamic + const { id, template, operation } = dynamic if (id !== undefined && template !== undefined) { push(NEWLINE, `const n${id} = t${template}()`) push(...genDirectivesForElement(id, context)) } + if (operation) { + push(...genOperationWithInsertionState(operation, context)) + } + + return frag +} + +export function genChildren( + dynamic: IRDynamicInfo, + context: CodegenContext, + from: string = `n${dynamic.id}`, +): CodeFragment[] { + const { helper } = context + const [frag, push] = buildCodeFragment() + const { children } = dynamic + + let offset = 0 let prev: [variable: string, elementIndex: number] | undefined + const childrenToGen: [IRDynamicInfo, string][] = [] + for (const [index, child] of children.entries()) { if (child.flags & DynamicFlag.NON_TEMPLATE) { offset-- @@ -49,17 +64,11 @@ export function genChildren( : undefined if (id === undefined && !child.hasDynamicChild) { - const { id, template } = child - if (id !== undefined && template !== undefined) { - push(NEWLINE, `const n${id} = t${template}()`) - push(...genDirectivesForElement(id, context)) - } + push(...genSelf(child, context)) continue } const elementIndex = Number(index) + offset - const newPath = [...path, elementIndex] - // p for "placeholder" variables that are meant for possible reuse by // other access paths const variable = id === undefined ? `p${context.block.tempId++}` : `n${id}` @@ -72,58 +81,28 @@ export function genChildren( push(...genCall(helper('nthChild'), from, String(elementIndex))) } } else { - if (newPath.length === 1 && newPath[0] === 0) { + if (elementIndex === 0) { push(...genCall(helper('child'), from)) } else { // check if there's a node that we can reuse from - let resolvedFrom = from - let resolvedPath = newPath - let skipFirstChild = false - outer: for (const [from, path] of knownPaths) { - const l = path.length - const tail = newPath.slice(l) - for (let i = 0; i < l; i++) { - const parentSeg = path[i] - const thisSeg = newPath[i] - if (parentSeg !== thisSeg) { - if (i === l - 1) { - // last bit is reusable - resolvedFrom = from - resolvedPath = [thisSeg - parentSeg, ...tail] - skipFirstChild = true - break outer - } - break - } else if (i === l - 1) { - // full overlap - resolvedFrom = from - resolvedPath = tail - break outer - } - } - } - let init - for (const i of resolvedPath) { - init = init - ? genCall(helper('child'), init) - : skipFirstChild - ? resolvedFrom - : genCall(helper('child'), resolvedFrom) - if (i === 1) { - init = genCall(helper('next'), init) - } else if (i > 1) { - init = genCall(helper('nthChild'), resolvedFrom, String(i)) - } + let init = genCall(helper('child'), from) + if (elementIndex === 1) { + init = genCall(helper('next'), init) + } else if (elementIndex > 1) { + init = genCall(helper('nthChild'), from, String(elementIndex)) } - push(...init!) + push(...init) } } if (id !== undefined) { push(...genDirectivesForElement(id, context)) } - knownPaths.unshift([variable, newPath]) prev = [variable, elementIndex] - push(...genChildren(child, context, variable)) + childrenToGen.push([child, variable]) + } + + for (const [child, from] of childrenToGen) { + push(...genChildren(child, context, from)) } return frag diff --git a/packages/compiler-vapor/src/ir/index.ts b/packages/compiler-vapor/src/ir/index.ts index d4beb1e3f0..27fc479fd2 100644 --- a/packages/compiler-vapor/src/ir/index.ts +++ b/packages/compiler-vapor/src/ir/index.ts @@ -270,6 +270,7 @@ export interface IRDynamicInfo { children: IRDynamicInfo[] template?: number hasDynamicChild?: boolean + operation?: OperationNode } export interface IREffect { @@ -304,9 +305,7 @@ export type InsertionStateTypes = | SlotOutletIRNode | CreateComponentIRNode -export function isTypeThatNeedsInsertionState( - op: OperationNode, -): op is InsertionStateTypes { +export function isBlockOperation(op: OperationNode): op is InsertionStateTypes { const type = op.type return ( type === IRNodeTypes.CREATE_COMPONENT_NODE || diff --git a/packages/compiler-vapor/src/transforms/transformChildren.ts b/packages/compiler-vapor/src/transforms/transformChildren.ts index 8952036c04..790cd9d6fb 100644 --- a/packages/compiler-vapor/src/transforms/transformChildren.ts +++ b/packages/compiler-vapor/src/transforms/transformChildren.ts @@ -8,7 +8,7 @@ import { DynamicFlag, type IRDynamicInfo, IRNodeTypes, - isTypeThatNeedsInsertionState as isBlockOperation, + isBlockOperation, } from '../ir' export const transformChildren: NodeTransform = (node, context) => { @@ -102,14 +102,10 @@ function registerInsertion( parent: context.reference(), anchor, }) - } else { + } else if (child.operation && isBlockOperation(child.operation)) { // block types - for (const op of context.block.operation) { - if (isBlockOperation(op) && op.id === child.id) { - op.parent = context.reference() - op.anchor = anchor - } - } + child.operation.parent = context.reference() + child.operation.anchor = anchor } } } diff --git a/packages/compiler-vapor/src/transforms/transformElement.ts b/packages/compiler-vapor/src/transforms/transformElement.ts index f42801ace6..dceb3fd612 100644 --- a/packages/compiler-vapor/src/transforms/transformElement.ts +++ b/packages/compiler-vapor/src/transforms/transformElement.ts @@ -124,7 +124,7 @@ function transformComponentElement( } context.dynamic.flags |= DynamicFlag.NON_TEMPLATE | DynamicFlag.INSERT - context.registerOperation({ + context.dynamic.operation = { type: IRNodeTypes.CREATE_COMPONENT_NODE, id: context.reference(), tag, @@ -134,7 +134,7 @@ function transformComponentElement( slots: [...context.slots], once: context.inVOnce, dynamic: dynamicComponent, - }) + } context.slots = [] } diff --git a/packages/compiler-vapor/src/transforms/transformSlotOutlet.ts b/packages/compiler-vapor/src/transforms/transformSlotOutlet.ts index a3652cbacd..83b4aa2d2e 100644 --- a/packages/compiler-vapor/src/transforms/transformSlotOutlet.ts +++ b/packages/compiler-vapor/src/transforms/transformSlotOutlet.ts @@ -100,13 +100,13 @@ export const transformSlotOutlet: NodeTransform = (node, context) => { return () => { exitBlock && exitBlock() - context.registerOperation({ + context.dynamic.operation = { type: IRNodeTypes.SLOT_OUTLET_NODE, id, name: slotName, props: irProps, fallback, - }) + } } } diff --git a/packages/compiler-vapor/src/transforms/vFor.ts b/packages/compiler-vapor/src/transforms/vFor.ts index 69480672a9..d4cf42dbd5 100644 --- a/packages/compiler-vapor/src/transforms/vFor.ts +++ b/packages/compiler-vapor/src/transforms/vFor.ts @@ -67,7 +67,7 @@ export function processFor( parent.block.node !== parent.node && parent.node.children.length === 1 - context.registerOperation({ + context.dynamic.operation = { type: IRNodeTypes.FOR, id, source: source as SimpleExpressionNode, @@ -84,6 +84,6 @@ export function processFor( ), component: isComponent, onlyChild: !!isOnlyChild, - }) + } } } diff --git a/packages/compiler-vapor/src/transforms/vIf.ts b/packages/compiler-vapor/src/transforms/vIf.ts index 8fad9c3146..088af62e19 100644 --- a/packages/compiler-vapor/src/transforms/vIf.ts +++ b/packages/compiler-vapor/src/transforms/vIf.ts @@ -46,7 +46,7 @@ export function processIf( return () => { onExit() - context.registerOperation({ + context.dynamic.operation = { type: IRNodeTypes.IF, id, condition: dir.exp!, @@ -54,14 +54,20 @@ export function processIf( once: context.inVOnce || isStaticExpression(dir.exp!, context.options.bindingMetadata), - }) + } } } else { // check the adjacent v-if const siblingIf = getSiblingIf(context, true) - const { operation } = context.block - let lastIfNode = operation[operation.length - 1] + const siblings = context.parent && context.parent.dynamic.children + let lastIfNode + if (siblings) { + let i = siblings.length + while (i--) { + if (siblings[i].operation) lastIfNode = siblings[i].operation + } + } if ( // check if v-if is the sibling node diff --git a/packages/runtime-vapor/__tests__/hydration.spec.ts b/packages/runtime-vapor/__tests__/hydration.spec.ts index 3345debc34..caa84b16b9 100644 --- a/packages/runtime-vapor/__tests__/hydration.spec.ts +++ b/packages/runtime-vapor/__tests__/hydration.spec.ts @@ -257,6 +257,63 @@ describe('SSR hydration', () => { ) }) + test('nested fragment components', async () => { + const t0 = template('
') + const t1 = template(' ') + const msg = ref('foo') + const Comp = { + setup() { + const n0 = t0() as Element + const n1 = t1() as Text + const x0 = child(n0) as Text + renderEffect(() => { + const _msg = msg.value + + setText(x0, toDisplayString(_msg)) + setText(n1, toDisplayString(_msg)) + }) + return [n0, n1] + }, + } + + const t2 = template('
') + const Parent = { + setup() { + const n0 = t2() + const n1 = createComponent(Comp) + const n2 = t2() + return [n0, n1, n2] + }, + } + + const t3 = template('
', true) + const { container } = mountWithHydration( + '
' + + '
foo
foo
' + + '
', + () => { + const n1 = t3() as Element + setInsertionState(n1, 0) + createComponent(Parent) + return n1 + }, + ) + + expect(container.innerHTML).toBe( + '
' + + '
foo
foo
' + + '
', + ) + + msg.value = 'bar' + await nextTick() + expect(container.innerHTML).toBe( + '
' + + '
bar
bar
' + + '
', + ) + }) + // test('element with ref', () => { // const el = ref() // const { vnode, container } = mountWithHydration('
', () => diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 17cbc0c3b9..4056caebd9 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -59,11 +59,7 @@ import { } from './componentSlots' import { hmrReload, hmrRerender } from './hmr' import { isHydrating, locateHydrationNode } from './dom/hydration' -import { - insertionAnchor, - insertionParent, - resetInsertionState, -} from './insertionState' +import { insertionAnchor, insertionParent } from './insertionState' export { currentInstance } from '@vue/runtime-dom' @@ -142,6 +138,8 @@ export function createComponent( currentInstance.appContext) || emptyContext, ): VaporComponentInstance { + const _insertionParent = insertionParent + const _insertionAnchor = insertionAnchor if (isHydrating) { locateHydrationNode() } @@ -263,9 +261,8 @@ export function createComponent( onScopeDispose(() => unmountComponent(instance), true) - if (!isHydrating && insertionParent) { - insert(instance.block, insertionParent, insertionAnchor) - resetInsertionState() + if (!isHydrating && _insertionParent) { + insert(instance.block, _insertionParent, _insertionAnchor) } return instance diff --git a/packages/runtime-vapor/src/dom/hydration.ts b/packages/runtime-vapor/src/dom/hydration.ts index db16e61b20..d34d9db7da 100644 --- a/packages/runtime-vapor/src/dom/hydration.ts +++ b/packages/runtime-vapor/src/dom/hydration.ts @@ -28,6 +28,7 @@ export function withHydration(container: ParentNode, fn: () => void): void { setInsertionState(container, 0) const res = fn() resetInsertionState() + currentHydrationNode = null isHydrating = false return res } @@ -75,10 +76,6 @@ function adoptTemplateImpl(node: Node, template: string): Node | null { } function locateHydrationNodeImpl() { - if (__DEV__ && !insertionParent) { - warn('Hydration error: missing insertion state.') - } - let node: Node | null // prepend / firstChild @@ -87,7 +84,9 @@ function locateHydrationNodeImpl() { } else { node = insertionAnchor ? insertionAnchor.previousSibling - : insertionParent!.lastChild + : insertionParent + ? insertionParent.lastChild + : currentHydrationNode if (node && isComment(node, ']')) { // fragment backward search @@ -120,10 +119,11 @@ function locateHydrationNodeImpl() { } } - currentHydrationNode = node - - if (__DEV__ && !currentHydrationNode) { + if (__DEV__ && !node) { // TODO more info warn('Hydration mismatch in ', insertionParent) } + + resetInsertionState() + currentHydrationNode = node } diff --git a/packages/runtime-vapor/src/insertionState.ts b/packages/runtime-vapor/src/insertionState.ts index 4b06c04a6b..c8c7ffbcd1 100644 --- a/packages/runtime-vapor/src/insertionState.ts +++ b/packages/runtime-vapor/src/insertionState.ts @@ -1,5 +1,3 @@ -import { setCurrentHydrationNode } from './dom/hydration' - export let insertionParent: ParentNode | undefined export let insertionAnchor: Node | 0 | undefined @@ -15,5 +13,4 @@ export function setInsertionState(parent: ParentNode, anchor?: Node | 0): void { export function resetInsertionState(): void { insertionParent = insertionAnchor = undefined - setCurrentHydrationNode(null) }