]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip(vapor): adjust children and block generation order for hydration
authorEvan You <evan@vuejs.org>
Tue, 11 Mar 2025 07:09:36 +0000 (15:09 +0800)
committerEvan You <evan@vuejs.org>
Tue, 11 Mar 2025 07:09:36 +0000 (15:09 +0800)
26 files changed:
packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vIf.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts
packages/compiler-vapor/__tests__/transforms/transformSlotOutlet.spec.ts
packages/compiler-vapor/__tests__/transforms/transformTemplateRef.spec.ts
packages/compiler-vapor/__tests__/transforms/vFor.spec.ts
packages/compiler-vapor/__tests__/transforms/vIf.spec.ts
packages/compiler-vapor/__tests__/transforms/vModel.spec.ts
packages/compiler-vapor/__tests__/transforms/vOnce.spec.ts
packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts
packages/compiler-vapor/src/generators/block.ts
packages/compiler-vapor/src/generators/operation.ts
packages/compiler-vapor/src/generators/template.ts
packages/compiler-vapor/src/ir/index.ts
packages/compiler-vapor/src/transforms/transformChildren.ts
packages/compiler-vapor/src/transforms/transformElement.ts
packages/compiler-vapor/src/transforms/transformSlotOutlet.ts
packages/compiler-vapor/src/transforms/vFor.ts
packages/compiler-vapor/src/transforms/vIf.ts
packages/runtime-vapor/__tests__/hydration.spec.ts
packages/runtime-vapor/src/component.ts
packages/runtime-vapor/src/dom/hydration.ts
packages/runtime-vapor/src/insertionState.ts

index fff7dd1272b4e3d8da880552c87103979607356e..e56676d87068a758a3992eb347b6582b5c549ff3 100644 (file)
@@ -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("<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 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)
index 5f0121b5e9173ff6495754915530587f3794977f..40c4096ce2938a102cc5399eeca2285510abfa55 100644 (file)
@@ -40,10 +40,10 @@ const t0 = _template("<div><div>x</div><div><span> </span></div><div><span> </sp
 export function render(_ctx) {
   const n3 = t0()
   const p0 = _next(_child(n3))
-  const n0 = _child(p0)
   const p1 = _next(p0)
-  const n1 = _child(p1)
   const p2 = _next(p1)
+  const n0 = _child(p0)
+  const n1 = _child(p1)
   const n2 = _child(p2)
   const x0 = _child(n0)
   const x1 = _child(n1)
index 6454ff1e22e1d8a9bfc57ed1e44ec67776d8cd53..69527c0b100bb81e9da44a010f4e3e44b1583699 100644 (file)
@@ -29,8 +29,8 @@ exports[`compiler: element transform > 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("<form></form>")
 
 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)
index 360823cdcf69ac45dfa7b03e89a001e283f28d8a..16ab6ae37200c414d521b0a4e9d961da05b8e468 100644 (file)
@@ -25,7 +25,6 @@ const t4 = _template("fine")
 const t5 = _template("<div> </div>")
 
 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]
index 6649e333bf1eb998ec6966b13c187379b5e6e83e..4ecd8c76a7ee1478cc5e059b289ef2bad056e103 100644 (file)
@@ -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]
     }
index 5421623a509a8394367328a133879f6edf32f311..adaad182cf3445dbac55fb012f4fba8b8ab49d4e 100644 (file)
@@ -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(`<component :is />`)
       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', () => {
       `<Foo :[foo-bar]="bar" :[baz]="qux" />`,
     )
     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', () => {
       `<Foo @[foo-bar]="bar" @[baz]="qux" />`,
     )
     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', () => {
index defc7c77f92164e98b0c4ff5bc5ad7865722b258..5993511323bf78ffc6c6a988170888a1d845f664 100644 (file)
@@ -31,67 +31,59 @@ describe('compiler: transform <slot> 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(`<slot name="foo" />`)
     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(`<slot :name="foo + bar" />`)
     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(`<slot :name />`)
     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 <slot> outlets', () => {
       `<slot foo="bar" :baz="qux" :foo-bar="foo-bar" />`,
     )
     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 <slot> outlets', () => {
       `<slot name="foo" foo="bar" :baz="qux" />`,
     )
     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 <slot> outlets', () => {
       `<slot name="foo" foo="bar" v-bind="obj" :baz="qux" />`,
     )
     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 <slot> outlets', () => {
       `<slot @click="foo" v-on="bar" :baz="qux" />`,
     )
     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(`<slot><div/></slot>`)
     expect(code).toMatchSnapshot()
     expect(ir.template[0]).toBe('<div></div>')
-    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 <slot> outlets', () => {
     )
     expect(code).toMatchSnapshot()
     expect(ir.template[0]).toBe('<div></div>')
-    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 <slot> outlets', () => {
     )
     expect(code).toMatchSnapshot()
     expect(ir.template[0]).toBe('<div></div>')
-    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 <slot> outlets', () => {
     )
     expect(code).toMatchSnapshot()
     expect(ir.template[0]).toBe('<div></div>')
-    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 <slot>', () => {
index a87c803ab04fc2c228d729d6dba4ae09e3a9d821..6be8f18779cc777e9f966105bf24eead7668d08b 100644 (file)
@@ -86,10 +86,10 @@ describe('compiler: template ref transform', () => {
       `<div ref="foo" v-if="true" />`,
     )
 
-    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', () => {
       `<div ref="foo" v-for="item in [1,2,3]" />`,
     )
 
-    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,
index 31a6da36908193e09452e20476a5929a273a8bc2..0008df7f4c77729e48cd48ee372dd630b7dde93e 100644 (file)
@@ -33,38 +33,38 @@ describe('compiler: v-for', () => {
     expect(code).matchSnapshot()
     expect(helpers).contains('createFor')
     expect(ir.template).toEqual(['<div> </div>'])
-    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(['<span> </span>', '<div></div>'])
-    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', () => {
       '<span v-for="({ id, value }) in items" :key="id">{{ id }}{{ value }}</span>',
     )
     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', () => {
       `<div v-for="([id, other], index) in list" :key="id">{{ id + other + index }}</div>`,
     )
     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,
index 8f2d572d0b75ac648a0f14ae6a94d6756873e75e..66cdf7b9306a39bafe2475c3ed504077e2ff886e 100644 (file)
@@ -33,23 +33,23 @@ describe('compiler: v-if', () => {
     expect(helpers).contains('createIf')
 
     expect(ir.template).toEqual(['<div> </div>'])
-    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(['<div></div>', 'hello', '<p> </p>'])
     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(['<div></div>', '<p></p>'])
 
-    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(['<div></div>', '<p></p>', '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', () => {
index 32ec9dd5c4dedf6e4df6eb09f1f1ce35a9a9365f..51eaa9e02306088f323abf0879d68fdde7a06d2a 100644 (file)
@@ -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(
-        '<Comp v-model:[foo].trim="foo" v-model:[bar].number="bar" />',
-      )
-      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(
+        '<Comp v-model:[foo].trim="foo" v-model:[bar].number="bar" />',
+      )
+      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'],
+          },
+        ],
+      })
     })
   })
 })
index 43077bf2eb98652f5f90f812d9ae00fc4695a623..97f6ee62a119d3587e452125bcd15f00fbb5db64 100644 (file)
@@ -126,15 +126,13 @@ describe('compiler: v-once', () => {
     const { ir, code } = compileWithOnce(`<div><Comp :id="foo" v-once /></div>`)
     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(`<div v-for="i in list" v-once />`)
     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,
+    })
   })
 })
index caa45681a24ad23fdca24aee403c677ca18ed14b..84ddb2e5d046d28ce3d5a36eb71fdb4720a92443 100644 (file)
@@ -36,27 +36,25 @@ describe('compiler: transform slot', () => {
     expect(code).toMatchSnapshot()
 
     expect(ir.template).toEqual(['<div></div>'])
-    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', '<span></span>'])
-    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', () => {
       </Comp>`,
     )
     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', () => {
       </Comp>`,
     )
     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(`<A><B/></A>`)
     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', () => {
index 77ba4bee8a7c087b22760989e2745771ab051831..b161b8f45d1b6565da791621b55c66c6c42a74f8 100644 (file)
@@ -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!}`))
   }
index 64b2a568ee1fa85b4578f9728892eb8c12ce83d6..ad5c43d6b949a4382782fb9a5d57c23fe95f20ac 100644 (file)
@@ -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
 }
 
index 90694447983faab2814268c4f9ac5c90b8edb018..dfd0082d7e1f132ffddf455769713d2831e4d266 100644 (file)
@@ -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
index d4beb1e3f0a19765a929115863bc14460f9a6602..27fc479fd2c599faff13a01d12da112d7d311c36 100644 (file)
@@ -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 ||
index 8952036c04bf50cde64fcfef2039092460b61326..790cd9d6fb19038110a5d91ca54f4d62cc7c87a6 100644 (file)
@@ -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
     }
   }
 }
index f42801ace6d72f257aa02aa6663881d7229451c1..dceb3fd6121f33da80dfd7121ede5ddbb9ad05bf 100644 (file)
@@ -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 = []
 }
 
index a3652cbacd9d55189f57001118d8f30a4f881237..83b4aa2d2e4a565d63f68fe97369f648f83258a2 100644 (file)
@@ -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,
-    })
+    }
   }
 }
 
index 69480672a99112a236e68a6428255bdf0e2be2ec..d4cf42dbd55b9b9e3e23d8f44019cd901a475dd6 100644 (file)
@@ -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,
-    })
+    }
   }
 }
index 8fad9c3146ec1df75bffd7ea404f454a9078be5a..088af62e1938873752bba8d03822fda942e813e0 100644 (file)
@@ -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
index 3345debc34dcd86130a2a2bab8e5dbdfa3db94b4..caa84b16b91e97102a42571388efd00e10b5e763 100644 (file)
@@ -257,6 +257,63 @@ describe('SSR hydration', () => {
     )
   })
 
+  test('nested fragment components', async () => {
+    const t0 = template('<div> </div>')
+    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('<div></div>')
+    const Parent = {
+      setup() {
+        const n0 = t2()
+        const n1 = createComponent(Comp)
+        const n2 = t2()
+        return [n0, n1, n2]
+      },
+    }
+
+    const t3 = template('<div><span></span></div>', true)
+    const { container } = mountWithHydration(
+      '<div><!--[-->' +
+        '<div></div><!--[--><div>foo</div>foo<!--]--><div></div>' +
+        '<!--]--><span></span></div>',
+      () => {
+        const n1 = t3() as Element
+        setInsertionState(n1, 0)
+        createComponent(Parent)
+        return n1
+      },
+    )
+
+    expect(container.innerHTML).toBe(
+      '<div><!--[-->' +
+        '<div></div><!--[--><div>foo</div>foo<!--]--><div></div>' +
+        '<!--]--><span></span></div>',
+    )
+
+    msg.value = 'bar'
+    await nextTick()
+    expect(container.innerHTML).toBe(
+      '<div><!--[-->' +
+        '<div></div><!--[--><div>bar</div>bar<!--]--><div></div>' +
+        '<!--]--><span></span></div>',
+    )
+  })
+
   // test('element with ref', () => {
   //   const el = ref()
   //   const { vnode, container } = mountWithHydration('<div></div>', () =>
index 17cbc0c3b9bdb1cd946355dacb34561d8d6701b5..4056caebd93ccce3b36498158eecb1e7666dd853 100644 (file)
@@ -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
index db16e61b2035212a0fc7d42b983e6db3d616f948..d34d9db7da58c4e2b2aaa9d5a417168ec316ab18 100644 (file)
@@ -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
 }
index 4b06c04a6b13ac3187a03164fc3ac660066b50e2..c8c7ffbcd1de3b1000cbc9fa62f50ee9c8c3a0b3 100644 (file)
@@ -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)
 }