test(compiler): test v-slot transform
This commit is contained in:
parent
96749e0178
commit
1c410205de
@ -1234,6 +1234,50 @@ describe('compiler: parse', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test('v-slot shorthand', () => {
|
||||
const ast = parse('<Comp #a="{ b }" />')
|
||||
const directive = (ast.children[0] as ElementNode).props[0]
|
||||
|
||||
expect(directive).toStrictEqual({
|
||||
type: NodeTypes.DIRECTIVE,
|
||||
name: 'slot',
|
||||
arg: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'a',
|
||||
isStatic: true,
|
||||
loc: {
|
||||
source: 'a',
|
||||
start: {
|
||||
column: 8,
|
||||
line: 1,
|
||||
offset: 7
|
||||
},
|
||||
end: {
|
||||
column: 9,
|
||||
line: 1,
|
||||
offset: 8
|
||||
}
|
||||
}
|
||||
},
|
||||
modifiers: [],
|
||||
exp: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: '{ b }',
|
||||
isStatic: false,
|
||||
loc: {
|
||||
start: { offset: 10, line: 1, column: 11 },
|
||||
end: { offset: 15, line: 1, column: 16 },
|
||||
source: '{ b }'
|
||||
}
|
||||
},
|
||||
loc: {
|
||||
start: { offset: 6, line: 1, column: 7 },
|
||||
end: { offset: 16, line: 1, column: 17 },
|
||||
source: '#a="{ b }"'
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
test('end tags are case-insensitive.', () => {
|
||||
const ast = parse('<div>hello</DIV>after')
|
||||
const element = ast.children[0] as ElementNode
|
||||
|
@ -0,0 +1,97 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`compiler: transform component slots dynamically named slots 1`] = `
|
||||
"const { resolveComponent, createVNode, toString } = Vue
|
||||
|
||||
return function render() {
|
||||
const _ctx = this
|
||||
const _component_Comp = resolveComponent(\\"Comp\\")
|
||||
|
||||
return createVNode(_component_Comp, 0, {
|
||||
[_ctx.one]: ({ foo }) => [
|
||||
toString(foo),
|
||||
toString(_ctx.bar)
|
||||
],
|
||||
[_ctx.two]: ({ bar }) => [
|
||||
toString(_ctx.foo),
|
||||
toString(bar)
|
||||
]
|
||||
})
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: transform component slots explicit default slot 1`] = `
|
||||
"const { resolveComponent, createVNode, toString } = Vue
|
||||
|
||||
return function render() {
|
||||
const _ctx = this
|
||||
const _component_Comp = resolveComponent(\\"Comp\\")
|
||||
|
||||
return createVNode(_component_Comp, 0, {
|
||||
default: ({ foo }) => [
|
||||
toString(foo),
|
||||
toString(_ctx.bar)
|
||||
]
|
||||
})
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: transform component slots implicit default slot 1`] = `
|
||||
"const { resolveComponent, createVNode } = Vue
|
||||
|
||||
return function render() {
|
||||
const _ctx = this
|
||||
const _component_Comp = resolveComponent(\\"Comp\\")
|
||||
|
||||
return createVNode(_component_Comp, 0, {
|
||||
default: () => [
|
||||
createVNode(\\"div\\")
|
||||
]
|
||||
})
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: transform component slots named slots 1`] = `
|
||||
"const { resolveComponent, createVNode, toString } = Vue
|
||||
|
||||
return function render() {
|
||||
const _ctx = this
|
||||
const _component_Comp = resolveComponent(\\"Comp\\")
|
||||
|
||||
return createVNode(_component_Comp, 0, {
|
||||
one: ({ foo }) => [
|
||||
toString(foo),
|
||||
toString(_ctx.bar)
|
||||
],
|
||||
two: ({ bar }) => [
|
||||
toString(_ctx.foo),
|
||||
toString(bar)
|
||||
]
|
||||
})
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: transform component slots nested slots scoping 1`] = `
|
||||
"const { resolveComponent, createVNode, toString } = Vue
|
||||
|
||||
return function render() {
|
||||
const _ctx = this
|
||||
const _component_Comp = resolveComponent(\\"Comp\\")
|
||||
const _component_Inner = resolveComponent(\\"Inner\\")
|
||||
|
||||
return createVNode(_component_Comp, 0, {
|
||||
default: ({ foo }) => [
|
||||
createVNode(_component_Inner, 0, {
|
||||
default: ({ bar }) => [
|
||||
toString(foo),
|
||||
toString(bar),
|
||||
toString(_ctx.baz)
|
||||
]
|
||||
}),
|
||||
toString(foo),
|
||||
toString(_ctx.bar),
|
||||
toString(_ctx.baz)
|
||||
]
|
||||
})
|
||||
}"
|
||||
`;
|
@ -3,7 +3,8 @@ import {
|
||||
parse,
|
||||
transform,
|
||||
ElementNode,
|
||||
NodeTypes
|
||||
NodeTypes,
|
||||
ErrorCodes
|
||||
} from '../../src'
|
||||
import { transformElement } from '../../src/transforms/transformElement'
|
||||
import { transformOn } from '../../src/transforms/vOn'
|
||||
@ -321,4 +322,27 @@ describe('compiler: transform <slot> outlets', () => {
|
||||
]
|
||||
})
|
||||
})
|
||||
|
||||
test(`error on unexpected custom directive on <slot>`, () => {
|
||||
const onError = jest.fn()
|
||||
const source = `<slot v-foo />`
|
||||
parseWithSlots(source, { onError })
|
||||
const index = source.indexOf('v-foo')
|
||||
expect(onError.mock.calls[0][0]).toMatchObject({
|
||||
code: ErrorCodes.X_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET,
|
||||
loc: {
|
||||
source: `v-foo`,
|
||||
start: {
|
||||
offset: index,
|
||||
line: 1,
|
||||
column: index + 1
|
||||
},
|
||||
end: {
|
||||
offset: index + 5,
|
||||
line: 1,
|
||||
column: index + 6
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -1,4 +1,12 @@
|
||||
import { CompilerOptions, parse, transform, generate } from '../../src'
|
||||
import {
|
||||
CompilerOptions,
|
||||
parse,
|
||||
transform,
|
||||
generate,
|
||||
ElementNode,
|
||||
NodeTypes,
|
||||
ErrorCodes
|
||||
} from '../../src'
|
||||
import { transformElement } from '../../src/transforms/transformElement'
|
||||
import { transformOn } from '../../src/transforms/vOn'
|
||||
import { transformBind } from '../../src/transforms/vBind'
|
||||
@ -20,22 +28,411 @@ function parseWithSlots(template: string, options: CompilerOptions = {}) {
|
||||
},
|
||||
...options
|
||||
})
|
||||
return ast
|
||||
return {
|
||||
root: ast,
|
||||
slots: (ast.children[0] as ElementNode).codegenNode!.arguments[2]
|
||||
}
|
||||
}
|
||||
|
||||
function createSlotMatcher(obj: Record<string, any>) {
|
||||
return {
|
||||
type: NodeTypes.JS_OBJECT_EXPRESSION,
|
||||
properties: Object.keys(obj).map(key => {
|
||||
return {
|
||||
type: NodeTypes.JS_PROPERTY,
|
||||
key: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
isStatic: !/^\[/.test(key),
|
||||
content: key.replace(/^\[|\]$/g, '')
|
||||
},
|
||||
value: obj[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
describe('compiler: transform component slots', () => {
|
||||
test('generate slot', () => {
|
||||
const ast = parseWithSlots(
|
||||
`
|
||||
<Comp>
|
||||
<Comp v-slot="{ dur }">
|
||||
hello {{ dur }}
|
||||
</Comp>
|
||||
</Comp>
|
||||
`,
|
||||
test('implicit default slot', () => {
|
||||
const { root, slots } = parseWithSlots(`<Comp><div/></Comp>`, {
|
||||
prefixIdentifiers: true
|
||||
})
|
||||
expect(slots).toMatchObject(
|
||||
createSlotMatcher({
|
||||
default: {
|
||||
type: NodeTypes.JS_SLOT_FUNCTION,
|
||||
params: undefined,
|
||||
returns: [
|
||||
{
|
||||
type: NodeTypes.ELEMENT,
|
||||
tag: `div`
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
)
|
||||
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('explicit default slot', () => {
|
||||
const { root, slots } = parseWithSlots(
|
||||
`<Comp v-slot="{ foo }">{{ foo }}{{ bar }}</Comp>`,
|
||||
{ prefixIdentifiers: true }
|
||||
)
|
||||
const { code } = generate(ast, { prefixIdentifiers: true })
|
||||
console.log(code)
|
||||
expect(slots).toMatchObject(
|
||||
createSlotMatcher({
|
||||
default: {
|
||||
type: NodeTypes.JS_SLOT_FUNCTION,
|
||||
params: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: `{ foo }`,
|
||||
isStatic: false
|
||||
},
|
||||
returns: [
|
||||
{
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
content: `foo`
|
||||
}
|
||||
},
|
||||
{
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
content: `_ctx.bar`
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
)
|
||||
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('named slots', () => {
|
||||
const { root, slots } = parseWithSlots(
|
||||
`<Comp>
|
||||
<template v-slot:one="{ foo }">
|
||||
{{ foo }}{{ bar }}
|
||||
</template>
|
||||
<template #two="{ bar }">
|
||||
{{ foo }}{{ bar }}
|
||||
</template>
|
||||
</Comp>`,
|
||||
{ prefixIdentifiers: true }
|
||||
)
|
||||
expect(slots).toMatchObject(
|
||||
createSlotMatcher({
|
||||
one: {
|
||||
type: NodeTypes.JS_SLOT_FUNCTION,
|
||||
params: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: `{ foo }`,
|
||||
isStatic: false
|
||||
},
|
||||
returns: [
|
||||
{
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
content: `foo`
|
||||
}
|
||||
},
|
||||
{
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
content: `_ctx.bar`
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
two: {
|
||||
type: NodeTypes.JS_SLOT_FUNCTION,
|
||||
params: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: `{ bar }`,
|
||||
isStatic: false
|
||||
},
|
||||
returns: [
|
||||
{
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
content: `_ctx.foo`
|
||||
}
|
||||
},
|
||||
{
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
content: `bar`
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
)
|
||||
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('dynamically named slots', () => {
|
||||
const { root, slots } = parseWithSlots(
|
||||
`<Comp>
|
||||
<template v-slot:[one]="{ foo }">
|
||||
{{ foo }}{{ bar }}
|
||||
</template>
|
||||
<template #[two]="{ bar }">
|
||||
{{ foo }}{{ bar }}
|
||||
</template>
|
||||
</Comp>`,
|
||||
{ prefixIdentifiers: true }
|
||||
)
|
||||
expect(slots).toMatchObject(
|
||||
createSlotMatcher({
|
||||
'[_ctx.one]': {
|
||||
type: NodeTypes.JS_SLOT_FUNCTION,
|
||||
params: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: `{ foo }`,
|
||||
isStatic: false
|
||||
},
|
||||
returns: [
|
||||
{
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
content: `foo`
|
||||
}
|
||||
},
|
||||
{
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
content: `_ctx.bar`
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
'[_ctx.two]': {
|
||||
type: NodeTypes.JS_SLOT_FUNCTION,
|
||||
params: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: `{ bar }`,
|
||||
isStatic: false
|
||||
},
|
||||
returns: [
|
||||
{
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
content: `_ctx.foo`
|
||||
}
|
||||
},
|
||||
{
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
content: `bar`
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
)
|
||||
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('nested slots scoping', () => {
|
||||
const { root, slots } = parseWithSlots(
|
||||
`<Comp>
|
||||
<template #default="{ foo }">
|
||||
<Inner v-slot="{ bar }">
|
||||
{{ foo }}{{ bar }}{{ baz }}
|
||||
</Inner>
|
||||
{{ foo }}{{ bar }}{{ baz }}
|
||||
</template>
|
||||
</Comp>`,
|
||||
{ prefixIdentifiers: true }
|
||||
)
|
||||
expect(slots).toMatchObject(
|
||||
createSlotMatcher({
|
||||
default: {
|
||||
type: NodeTypes.JS_SLOT_FUNCTION,
|
||||
params: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: `{ foo }`,
|
||||
isStatic: false
|
||||
},
|
||||
returns: [
|
||||
{
|
||||
type: NodeTypes.ELEMENT,
|
||||
codegenNode: {
|
||||
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||
arguments: [
|
||||
`_component_Inner`,
|
||||
`0`,
|
||||
createSlotMatcher({
|
||||
default: {
|
||||
type: NodeTypes.JS_SLOT_FUNCTION,
|
||||
params: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: `{ bar }`,
|
||||
isStatic: false
|
||||
},
|
||||
returns: [
|
||||
{
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
content: `foo`
|
||||
}
|
||||
},
|
||||
{
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
content: `bar`
|
||||
}
|
||||
},
|
||||
{
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
content: `_ctx.baz`
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
},
|
||||
// test scope
|
||||
{
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
content: `foo`
|
||||
}
|
||||
},
|
||||
{
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
content: `_ctx.bar`
|
||||
}
|
||||
},
|
||||
{
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
content: `_ctx.baz`
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
)
|
||||
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('error on extraneous children w/ named slots', () => {
|
||||
const onError = jest.fn()
|
||||
const source = `<Comp><template #default>foo</template>bar</Comp>`
|
||||
parseWithSlots(source, { onError })
|
||||
const index = source.indexOf('bar')
|
||||
expect(onError.mock.calls[0][0]).toMatchObject({
|
||||
code: ErrorCodes.X_EXTRANEOUS_NON_SLOT_CHILDREN,
|
||||
loc: {
|
||||
source: `bar`,
|
||||
start: {
|
||||
offset: index,
|
||||
line: 1,
|
||||
column: index + 1
|
||||
},
|
||||
end: {
|
||||
offset: index + 3,
|
||||
line: 1,
|
||||
column: index + 4
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
test('error on duplicated slot names', () => {
|
||||
const onError = jest.fn()
|
||||
const source = `<Comp><template #foo></template><template #foo></template></Comp>`
|
||||
parseWithSlots(source, { onError })
|
||||
const index = source.lastIndexOf('#foo')
|
||||
expect(onError.mock.calls[0][0]).toMatchObject({
|
||||
code: ErrorCodes.X_DUPLICATE_SLOT_NAMES,
|
||||
loc: {
|
||||
source: `#foo`,
|
||||
start: {
|
||||
offset: index,
|
||||
line: 1,
|
||||
column: index + 1
|
||||
},
|
||||
end: {
|
||||
offset: index + 4,
|
||||
line: 1,
|
||||
column: index + 5
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
test('error on invalid mixed slot usage', () => {
|
||||
const onError = jest.fn()
|
||||
const source = `<Comp v-slot="foo"><template #foo></template></Comp>`
|
||||
parseWithSlots(source, { onError })
|
||||
const index = source.lastIndexOf('#foo')
|
||||
expect(onError.mock.calls[0][0]).toMatchObject({
|
||||
code: ErrorCodes.X_MIXED_SLOT_USAGE,
|
||||
loc: {
|
||||
source: `#foo`,
|
||||
start: {
|
||||
offset: index,
|
||||
line: 1,
|
||||
column: index + 1
|
||||
},
|
||||
end: {
|
||||
offset: index + 4,
|
||||
line: 1,
|
||||
column: index + 5
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
test('error on v-slot usage on plain elements', () => {
|
||||
const onError = jest.fn()
|
||||
const source = `<div v-slot/>`
|
||||
parseWithSlots(source, { onError })
|
||||
const index = source.indexOf('v-slot')
|
||||
expect(onError.mock.calls[0][0]).toMatchObject({
|
||||
code: ErrorCodes.X_MISPLACED_V_SLOT,
|
||||
loc: {
|
||||
source: `v-slot`,
|
||||
start: {
|
||||
offset: index,
|
||||
line: 1,
|
||||
column: index + 1
|
||||
},
|
||||
end: {
|
||||
offset: index + 6,
|
||||
line: 1,
|
||||
column: index + 7
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
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_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
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -73,6 +73,7 @@ export const enum ErrorCodes {
|
||||
X_MIXED_SLOT_USAGE,
|
||||
X_DUPLICATE_SLOT_NAMES,
|
||||
X_EXTRANEOUS_NON_SLOT_CHILDREN,
|
||||
X_MISPLACED_V_SLOT,
|
||||
|
||||
// generic errors
|
||||
X_PREFIX_ID_NOT_SUPPORTED,
|
||||
@ -155,6 +156,8 @@ export const errorMessages: { [code: number]: string } = {
|
||||
[ErrorCodes.X_EXTRANEOUS_NON_SLOT_CHILDREN]:
|
||||
`Extraneous children found when component has explicit slots. ` +
|
||||
`These children will be ignored.`,
|
||||
[ErrorCodes.X_MISPLACED_V_SLOT]: `v-slot can only be used on components or <template> tags.`,
|
||||
|
||||
// generic errors
|
||||
[ErrorCodes.X_PREFIX_ID_NOT_SUPPORTED]: `"prefixIdentifiers" option is not supported in this build of compiler.`,
|
||||
[ErrorCodes.X_MODULE_MODE_NOT_SUPPORTED]: `ES module mode is not supported in this build of compiler.`
|
||||
|
@ -384,6 +384,11 @@ function parseTag(
|
||||
const props = []
|
||||
const ns = context.options.getNamespace(tag, parent)
|
||||
|
||||
let tagType = ElementTypes.ELEMENT
|
||||
if (tag === 'slot') tagType = ElementTypes.SLOT
|
||||
else if (tag === 'template') tagType = ElementTypes.TEMPLATE
|
||||
else if (/[A-Z-]/.test(tag)) tagType = ElementTypes.COMPONENT
|
||||
|
||||
advanceBy(context, match[0].length)
|
||||
advanceSpaces(context)
|
||||
|
||||
@ -427,12 +432,6 @@ function parseTag(
|
||||
advanceBy(context, isSelfClosing ? 2 : 1)
|
||||
}
|
||||
|
||||
let tagType = ElementTypes.ELEMENT
|
||||
|
||||
if (tag === 'slot') tagType = ElementTypes.SLOT
|
||||
else if (tag === 'template') tagType = ElementTypes.TEMPLATE
|
||||
else if (/[A-Z-]/.test(tag)) tagType = ElementTypes.COMPONENT
|
||||
|
||||
return {
|
||||
type: NodeTypes.ELEMENT,
|
||||
ns,
|
||||
|
@ -39,7 +39,7 @@ export const transformElement: NodeTransform = (node, context) => {
|
||||
node.tagType === ElementTypes.COMPONENT
|
||||
) {
|
||||
const isComponent = node.tagType === ElementTypes.COMPONENT
|
||||
const hasProps = node.props.length > 0
|
||||
let hasProps = node.props.length > 0
|
||||
const hasChildren = node.children.length > 0
|
||||
let runtimeDirectives: DirectiveNode[] | undefined
|
||||
let componentIdentifier: string | undefined
|
||||
@ -58,9 +58,18 @@ export const transformElement: NodeTransform = (node, context) => {
|
||||
]
|
||||
// props
|
||||
if (hasProps) {
|
||||
const { props, directives } = buildProps(node.props, node.loc, context)
|
||||
args.push(props)
|
||||
const { props, directives } = buildProps(
|
||||
node.props,
|
||||
node.loc,
|
||||
context,
|
||||
isComponent
|
||||
)
|
||||
runtimeDirectives = directives
|
||||
if (!props) {
|
||||
hasProps = false
|
||||
} else {
|
||||
args.push(props)
|
||||
}
|
||||
}
|
||||
// children
|
||||
if (hasChildren) {
|
||||
@ -104,9 +113,10 @@ type PropsExpression = ObjectExpression | CallExpression | ExpressionNode
|
||||
export function buildProps(
|
||||
props: ElementNode['props'],
|
||||
elementLoc: SourceLocation,
|
||||
context: TransformContext
|
||||
context: TransformContext,
|
||||
isComponent: boolean = false
|
||||
): {
|
||||
props: PropsExpression
|
||||
props: PropsExpression | undefined
|
||||
directives: DirectiveNode[]
|
||||
} {
|
||||
let isStatic = true
|
||||
@ -141,6 +151,11 @@ export function buildProps(
|
||||
|
||||
// skip v-slot - it is handled by its dedicated transform.
|
||||
if (name === 'slot') {
|
||||
if (!isComponent) {
|
||||
context.onError(
|
||||
createCompilerError(ErrorCodes.X_MISPLACED_V_SLOT, loc)
|
||||
)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
@ -197,7 +212,7 @@ export function buildProps(
|
||||
}
|
||||
}
|
||||
|
||||
let propsExpression: PropsExpression
|
||||
let propsExpression: PropsExpression | undefined = undefined
|
||||
|
||||
// has v-bind="object" or v-on="object", wrap with mergeProps
|
||||
if (mergeArgs.length) {
|
||||
@ -216,7 +231,7 @@ export function buildProps(
|
||||
// single v-bind with nothing else - no need for a mergeProps call
|
||||
propsExpression = mergeArgs[0]
|
||||
}
|
||||
} else {
|
||||
} else if (properties.length) {
|
||||
propsExpression = createObjectExpression(
|
||||
dedupeProperties(properties),
|
||||
elementLoc
|
||||
@ -224,7 +239,7 @@ export function buildProps(
|
||||
}
|
||||
|
||||
// hoist the object if it's fully static
|
||||
if (isStatic) {
|
||||
if (isStatic && propsExpression) {
|
||||
propsExpression = context.hoist(propsExpression)
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ export const transformSlotOutlet: NodeTransform = (node, context) => {
|
||||
nameIndex > -1
|
||||
? props.slice(0, nameIndex).concat(props.slice(nameIndex + 1))
|
||||
: props
|
||||
const hasProps = propsWithoutName.length
|
||||
let hasProps = propsWithoutName.length > 0
|
||||
if (hasProps) {
|
||||
const { props: propsExpression, directives } = buildProps(
|
||||
propsWithoutName,
|
||||
@ -79,7 +79,11 @@ export const transformSlotOutlet: NodeTransform = (node, context) => {
|
||||
)
|
||||
)
|
||||
}
|
||||
slotArgs.push(propsExpression)
|
||||
if (propsExpression) {
|
||||
slotArgs.push(propsExpression)
|
||||
} else {
|
||||
hasProps = false
|
||||
}
|
||||
}
|
||||
|
||||
if (children.length) {
|
||||
|
Loading…
Reference in New Issue
Block a user