]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compiler-core/slots): should support on-component named slots
authorEvan You <yyx990803@gmail.com>
Thu, 2 Apr 2020 00:44:53 +0000 (20:44 -0400)
committerEvan You <yyx990803@gmail.com>
Thu, 2 Apr 2020 00:44:53 +0000 (20:44 -0400)
packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
packages/compiler-core/__tests__/transforms/vSlot.spec.ts
packages/compiler-core/src/errors.ts
packages/compiler-core/src/transforms/vSlot.ts

index e6d0e11d5e4bd5421bdde434359b55df1e490e37..2eb27e83ec58775be9ab6b6a6fa55257da8f3b87 100644 (file)
@@ -113,20 +113,6 @@ return function render(_ctx, _cache) {
 }"
 `;
 
-exports[`compiler: transform component slots named slots 1`] = `
-"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
-
-return function render(_ctx, _cache) {
-  const _component_Comp = _resolveComponent(\\"Comp\\")
-
-  return (_openBlock(), _createBlock(_component_Comp, null, {
-    one: _withCtx(({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)]),
-    two: _withCtx(({ bar }) => [_toDisplayString(_ctx.foo), _toDisplayString(bar)]),
-    _: 1
-  }))
-}"
-`;
-
 exports[`compiler: transform component slots named slots w/ implicit default slot 1`] = `
 "const _Vue = Vue
 
@@ -171,6 +157,32 @@ return function render(_ctx, _cache) {
 }"
 `;
 
+exports[`compiler: transform component slots on component dynamically named slot 1`] = `
+"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
+
+return function render(_ctx, _cache) {
+  const _component_Comp = _resolveComponent(\\"Comp\\")
+
+  return (_openBlock(), _createBlock(_component_Comp, null, {
+    [_ctx.named]: _withCtx(({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)]),
+    _: 1
+  }))
+}"
+`;
+
+exports[`compiler: transform component slots on component named slot 1`] = `
+"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
+
+return function render(_ctx, _cache) {
+  const _component_Comp = _resolveComponent(\\"Comp\\")
+
+  return (_openBlock(), _createBlock(_component_Comp, null, {
+    named: _withCtx(({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)]),
+    _: 1
+  }))
+}"
+`;
+
 exports[`compiler: transform component slots on-component default slot 1`] = `
 "const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
 
@@ -183,3 +195,17 @@ return function render(_ctx, _cache) {
   }))
 }"
 `;
+
+exports[`compiler: transform component slots template named slots 1`] = `
+"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
+
+return function render(_ctx, _cache) {
+  const _component_Comp = _resolveComponent(\\"Comp\\")
+
+  return (_openBlock(), _createBlock(_component_Comp, null, {
+    one: _withCtx(({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)]),
+    two: _withCtx(({ bar }) => [_toDisplayString(_ctx.foo), _toDisplayString(bar)]),
+    _: 1
+  }))
+}"
+`;
index c6c9c8324ea29b302ec82a4ab7abee329e232d7a..c1cb651d9c98c4d73a9142051496af748f949475 100644 (file)
@@ -130,7 +130,40 @@ describe('compiler: transform component slots', () => {
     expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
   })
 
-  test('named slots', () => {
+  test('on component named slot', () => {
+    const { root, slots } = parseWithSlots(
+      `<Comp v-slot:named="{ foo }">{{ foo }}{{ bar }}</Comp>`,
+      { prefixIdentifiers: true }
+    )
+    expect(slots).toMatchObject(
+      createSlotMatcher({
+        named: {
+          type: NodeTypes.JS_FUNCTION_EXPRESSION,
+          params: {
+            type: NodeTypes.COMPOUND_EXPRESSION,
+            children: [`{ `, { content: `foo` }, ` }`]
+          },
+          returns: [
+            {
+              type: NodeTypes.INTERPOLATION,
+              content: {
+                content: `foo`
+              }
+            },
+            {
+              type: NodeTypes.INTERPOLATION,
+              content: {
+                content: `_ctx.bar`
+              }
+            }
+          ]
+        }
+      })
+    )
+    expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
+  })
+
+  test('template named slots', () => {
     const { root, slots } = parseWithSlots(
       `<Comp>
         <template v-slot:one="{ foo }">
@@ -191,6 +224,39 @@ describe('compiler: transform component slots', () => {
     expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
   })
 
+  test('on component dynamically named slot', () => {
+    const { root, slots } = parseWithSlots(
+      `<Comp v-slot:[named]="{ foo }">{{ foo }}{{ bar }}</Comp>`,
+      { prefixIdentifiers: true }
+    )
+    expect(slots).toMatchObject(
+      createSlotMatcher({
+        '[_ctx.named]': {
+          type: NodeTypes.JS_FUNCTION_EXPRESSION,
+          params: {
+            type: NodeTypes.COMPOUND_EXPRESSION,
+            children: [`{ `, { content: `foo` }, ` }`]
+          },
+          returns: [
+            {
+              type: NodeTypes.INTERPOLATION,
+              content: {
+                content: `foo`
+              }
+            },
+            {
+              type: NodeTypes.INTERPOLATION,
+              content: {
+                content: `_ctx.bar`
+              }
+            }
+          ]
+        }
+      })
+    )
+    expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
+  })
+
   test('named slots w/ implicit default slot', () => {
     const { root, slots } = parseWithSlots(
       `<Comp>
@@ -736,28 +802,5 @@ describe('compiler: transform component slots', () => {
         }
       })
     })
-
-    test('error on named slot on component', () => {
-      const onError = jest.fn()
-      const source = `<Comp v-slot:foo>foo</Comp>`
-      parseWithSlots(source, { onError })
-      const index = source.indexOf('v-slot')
-      expect(onError.mock.calls[0][0]).toMatchObject({
-        code: ErrorCodes.X_V_SLOT_NAMED_SLOT_ON_COMPONENT,
-        loc: {
-          source: `v-slot:foo`,
-          start: {
-            offset: index,
-            line: 1,
-            column: index + 1
-          },
-          end: {
-            offset: index + 10,
-            line: 1,
-            column: index + 11
-          }
-        }
-      })
-    })
   })
 })
index 408847a5bdc8fc06d381fdfd6cf7e6e29dfe2787..5d66ddcf18033b01e5ef2f52a80729100799114a 100644 (file)
@@ -76,7 +76,6 @@ export const enum ErrorCodes {
   X_V_BIND_NO_EXPRESSION,
   X_V_ON_NO_EXPRESSION,
   X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET,
-  X_V_SLOT_NAMED_SLOT_ON_COMPONENT,
   X_V_SLOT_MIXED_SLOT_USAGE,
   X_V_SLOT_DUPLICATE_SLOT_NAMES,
   X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN,
@@ -163,13 +162,10 @@ export const errorMessages: { [code: number]: string } = {
   [ErrorCodes.X_V_BIND_NO_EXPRESSION]: `v-bind is missing expression.`,
   [ErrorCodes.X_V_ON_NO_EXPRESSION]: `v-on is missing expression.`,
   [ErrorCodes.X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET]: `Unexpected custom directive on <slot> outlet.`,
-  [ErrorCodes.X_V_SLOT_NAMED_SLOT_ON_COMPONENT]:
-    `Named v-slot on component. ` +
-    `Named slots should use <template v-slot> syntax nested inside the component.`,
   [ErrorCodes.X_V_SLOT_MIXED_SLOT_USAGE]:
     `Mixed v-slot usage on both the component and nested <template>.` +
-    `The default slot should also use <template> syntax when there are other ` +
-    `named slots to avoid scope ambiguity.`,
+    `When there are multiple named slots, all slots should use <template> ` +
+    `syntax to avoid scope ambiguity.`,
   [ErrorCodes.X_V_SLOT_DUPLICATE_SLOT_NAMES]: `Duplicate slot names found. `,
   [ErrorCodes.X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN]:
     `Extraneous children found when component already has explicitly named ` +
index d02cda50dadc5478f506c8caed787384095397ed..548e90fd521ec88138c7976bd3ac4e5493aa774f 100644 (file)
@@ -139,17 +139,17 @@ export function buildSlots(
     hasDynamicSlots = hasScopeRef(node, context.identifiers)
   }
 
-  // 1. Check for default slot with slotProps on component itself.
+  // 1. Check for slot with slotProps on component itself.
   //    <Comp v-slot="{ prop }"/>
-  const onComponentDefaultSlot = findDir(node, 'slot', true)
-  if (onComponentDefaultSlot) {
-    const { arg, exp, loc } = onComponentDefaultSlot
-    if (arg) {
-      context.onError(
-        createCompilerError(ErrorCodes.X_V_SLOT_NAMED_SLOT_ON_COMPONENT, loc)
+  const onComponentSlot = findDir(node, 'slot', true)
+  if (onComponentSlot) {
+    const { arg, exp } = onComponentSlot
+    slotsProperties.push(
+      createObjectProperty(
+        arg || createSimpleExpression('default', true),
+        buildSlotFn(exp, children, loc)
       )
-    }
-    slotsProperties.push(buildDefaultSlotProperty(exp, children))
+    )
   }
 
   // 2. Iterate through children and check for template slots
@@ -174,8 +174,8 @@ export function buildSlots(
       continue
     }
 
-    if (onComponentDefaultSlot) {
-      // already has on-component default slot - this is incorrect usage.
+    if (onComponentSlot) {
+      // already has on-component slot - this is incorrect usage.
       context.onError(
         createCompilerError(ErrorCodes.X_V_SLOT_MIXED_SLOT_USAGE, slotDir.loc)
       )
@@ -294,7 +294,7 @@ export function buildSlots(
     }
   }
 
-  if (!onComponentDefaultSlot) {
+  if (!onComponentSlot) {
     if (!hasTemplateSlots) {
       // implicit default slot (on component)
       slotsProperties.push(buildDefaultSlotProperty(undefined, children))