fix(compiler-core/slots): should support on-component named slots
This commit is contained in:
		
							parent
							
								
									20f4965b45
								
							
						
					
					
						commit
						a022b63605
					
				@ -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
 | 
			
		||||
  }))
 | 
			
		||||
}"
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
@ -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 ` +
 | 
			
		||||
 | 
			
		||||
@ -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))
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user