This commit is contained in:
disservin 2025-05-05 20:38:29 +00:00 committed by GitHub
commit ba95fdca8d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 111 additions and 2 deletions

View File

@ -3453,4 +3453,96 @@ describe('compiler: parse', () => {
}) })
} }
}) })
describe('vue specific errors', () => {
test('error when v-slot used on non-root level <template>', () => {
const onError = vi.fn()
baseParse(
`<Bar><template><template #header> Header </template></template></Bar>`,
{ onError },
)
expect(onError.mock.calls[0]).toMatchObject([
{
code: ErrorCodes.X_SLOT_TEMPLATE_NOT_ROOT,
loc: {
start: { column: 16, line: 1, offset: 15 },
end: { column: 53, line: 1, offset: 52 },
source: '<template #header> Header </template>',
},
},
])
})
test('error when v-slot used on non-root level <template> with v-if', () => {
const onError = vi.fn()
baseParse(
`<Bar><template v-if="true"><template #header> Header </template></template></Bar>`,
{ onError },
)
expect(onError.mock.calls[0]).toMatchObject([
{
code: ErrorCodes.X_SLOT_TEMPLATE_NOT_ROOT,
loc: {
start: { column: 28, line: 1, offset: 27 },
end: { column: 65, line: 1, offset: 64 },
source: '<template #header> Header </template>',
},
},
])
})
test('error when v-slot used on non-root level <template> together with v-if', () => {
const onError = vi.fn()
baseParse(
` <div><template v-if="true"><template v-if="true" #item> Header </template></template></div>`,
{ onError },
)
expect(onError.mock.calls[0]).toMatchObject([
{
code: ErrorCodes.X_SLOT_TEMPLATE_NOT_ROOT,
loc: {
start: { column: 30, line: 1, offset: 29 },
end: { column: 77, line: 1, offset: 76 },
source: '<template v-if="true" #item> Header </template>',
},
},
])
})
test('error when v-slot used on non-root level <template> inside dynamic component', () => {
const onError = vi.fn()
baseParse(
`<component is="MyComp"><template><template #item>bar</template></template></component>`,
{ onError },
)
expect(onError.mock.calls[0]).toMatchObject([
{
code: ErrorCodes.X_SLOT_TEMPLATE_NOT_ROOT,
loc: {
start: { column: 34, line: 1, offset: 33 },
end: { column: 64, line: 1, offset: 63 },
source: '<template #item>bar</template>',
},
},
])
})
test('shouldnt error when v-slot used on non-root level <template> inside dynamic component with casting', () => {
const onError = vi.fn()
baseParse(`<div is="vue:MyComp"><template #item>bar</template></div>`, {
onError,
})
expect(onError.mock.calls[0]).toEqual(undefined)
})
})
}) })

View File

@ -69,6 +69,7 @@ export enum ErrorCodes {
X_MISSING_INTERPOLATION_END, X_MISSING_INTERPOLATION_END,
X_MISSING_DIRECTIVE_NAME, X_MISSING_DIRECTIVE_NAME,
X_MISSING_DYNAMIC_DIRECTIVE_ARGUMENT_END, X_MISSING_DYNAMIC_DIRECTIVE_ARGUMENT_END,
X_SLOT_TEMPLATE_NOT_ROOT,
// transform errors // transform errors
X_V_IF_NO_EXPRESSION, X_V_IF_NO_EXPRESSION,
@ -151,6 +152,9 @@ export const errorMessages: Record<ErrorCodes, string> = {
'End bracket for dynamic directive argument was not found. ' + 'End bracket for dynamic directive argument was not found. ' +
'Note that dynamic directive argument cannot contain spaces.', 'Note that dynamic directive argument cannot contain spaces.',
[ErrorCodes.X_MISSING_DIRECTIVE_NAME]: 'Legal directive name was expected.', [ErrorCodes.X_MISSING_DIRECTIVE_NAME]: 'Legal directive name was expected.',
[ErrorCodes.X_SLOT_TEMPLATE_NOT_ROOT]:
`<template v-slot> can only appear at the root level inside ` +
`the receiving component`,
// transform errors // transform errors
[ErrorCodes.X_V_IF_NO_EXPRESSION]: `v-if/v-else-if is missing expression.`, [ErrorCodes.X_V_IF_NO_EXPRESSION]: `v-if/v-else-if is missing expression.`,

View File

@ -763,13 +763,26 @@ function backTrack(index: number, c: number) {
} }
const specialTemplateDir = new Set(['if', 'else', 'else-if', 'for', 'slot']) const specialTemplateDir = new Set(['if', 'else', 'else-if', 'for', 'slot'])
function isFragmentTemplate({ tag, props }: ElementNode): boolean { function isFragmentTemplate({ tag, props, loc }: ElementNode): boolean {
if (tag === 'template') { if (tag === 'template') {
for (let i = 0; i < props.length; i++) { for (let i = 0; i < props.length; i++) {
if ( if (
props[i].type === NodeTypes.DIRECTIVE && props[i].type === NodeTypes.DIRECTIVE &&
specialTemplateDir.has((props[i] as DirectiveNode).name) specialTemplateDir.has((props[i] as DirectiveNode).name)
) { ) {
if (
stack[0] &&
'tagType' in stack[0] &&
!isComponent(stack[0]) &&
props.find(p => p.name === 'slot')
) {
currentOptions.onError(
createCompilerError(
ErrorCodes.X_SLOT_TEMPLATE_NOT_ROOT,
getLoc(loc.start.offset, loc.end.offset),
),
)
}
return true return true
} }
} }

View File

@ -21,7 +21,7 @@ export function createDOMCompilerError(
} }
export enum DOMErrorCodes { export enum DOMErrorCodes {
X_V_HTML_NO_EXPRESSION = 53 /* ErrorCodes.__EXTEND_POINT__ */, X_V_HTML_NO_EXPRESSION = 54 /* ErrorCodes.__EXTEND_POINT__ */,
X_V_HTML_WITH_CHILDREN, X_V_HTML_WITH_CHILDREN,
X_V_TEXT_NO_EXPRESSION, X_V_TEXT_NO_EXPRESSION,
X_V_TEXT_WITH_CHILDREN, X_V_TEXT_WITH_CHILDREN,