diff --git a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts index bf3510a052d..4d1f8261709 100644 --- a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts +++ b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts @@ -5,6 +5,8 @@ import { type NodeTransform, baseCompile, baseParse as parse, + trackSlotScopes, + trackVForSlotScopes, transform, transformExpression, } from '../../src' @@ -74,6 +76,19 @@ function parseWithBind(template: string, options?: CompilerOptions) { }) } +function parseWithSlot(template: string, options?: CompilerOptions) { + return parseWithElementTransform(template, { + nodeTransforms: [ + ...(options?.prefixIdentifiers + ? [trackVForSlotScopes, transformExpression] + : []), + transformElement, + trackSlotScopes, + ], + ...options, + }) +} + describe('compiler: element transform', () => { test('import + resolve component', () => { const { root } = parseWithElementTransform(``) @@ -1381,4 +1396,44 @@ describe('compiler: element transform', () => { ], }) }) + + test('v-for scope var name conflict with component name', () => { + const onError = vi.fn() + parseWithForTransform(``, { + onError, + prefixIdentifiers: true, + bindingMetadata: { + Comp: BindingTypes.SETUP_CONST, + }, + }) + expect(onError.mock.calls[0]).toMatchObject([ + { + code: ErrorCodes.X_VAR_NAME_CONFLICT_WITH_COMPONENT_NAME, + }, + ]) + }) + + test('slot scope var name conflict with component name', () => { + const onError = vi.fn() + parseWithSlot( + ` + + `, + { + onError, + prefixIdentifiers: true, + bindingMetadata: { + Comp: BindingTypes.SETUP_CONST, + CompB: BindingTypes.SETUP_CONST, + }, + }, + ) + expect(onError.mock.calls[0]).toMatchObject([ + { + code: ErrorCodes.X_VAR_NAME_CONFLICT_WITH_COMPONENT_NAME, + }, + ]) + }) }) diff --git a/packages/compiler-core/src/errors.ts b/packages/compiler-core/src/errors.ts index 58e113ab19e..c70bc7544bf 100644 --- a/packages/compiler-core/src/errors.ts +++ b/packages/compiler-core/src/errors.ts @@ -90,6 +90,7 @@ export enum ErrorCodes { X_V_MODEL_ON_PROPS, X_INVALID_EXPRESSION, X_KEEP_ALIVE_INVALID_CHILDREN, + X_VAR_NAME_CONFLICT_WITH_COMPONENT_NAME, // generic errors X_PREFIX_ID_NOT_SUPPORTED, @@ -179,6 +180,7 @@ export const errorMessages: Record = { [ErrorCodes.X_INVALID_EXPRESSION]: `Error parsing JavaScript expression: `, [ErrorCodes.X_KEEP_ALIVE_INVALID_CHILDREN]: ` expects exactly one child component.`, [ErrorCodes.X_VNODE_HOOKS]: `@vnode-* hooks in templates are no longer supported. Use the vue: prefix instead. For example, @vnode-mounted should be changed to @vue:mounted. @vnode-* hooks support has been removed in 3.4.`, + [ErrorCodes.X_VAR_NAME_CONFLICT_WITH_COMPONENT_NAME]: `the variable name conflicts with the component name.`, // generic errors [ErrorCodes.X_PREFIX_ID_NOT_SUPPORTED]: `"prefixIdentifiers" option is not supported in this build of compiler.`, diff --git a/packages/compiler-core/src/transforms/transformElement.ts b/packages/compiler-core/src/transforms/transformElement.ts index c917436ea91..47b3c92c694 100644 --- a/packages/compiler-core/src/transforms/transformElement.ts +++ b/packages/compiler-core/src/transforms/transformElement.ts @@ -319,6 +319,20 @@ export function resolveComponentType( return toValidAssetId(tag, `component`) } +/** + * dev only + */ +const checkName = (name: string, context: TransformContext) => { + if (context.identifiers[name]) { + context.onError( + createCompilerError( + ErrorCodes.X_VAR_NAME_CONFLICT_WITH_COMPONENT_NAME, + context.currentNode!.loc, + ), + ) + } +} + function resolveSetupReference(name: string, context: TransformContext) { const bindings = context.bindingMetadata if (!bindings || bindings.__isScriptSetup === false) { @@ -344,6 +358,7 @@ function resolveSetupReference(name: string, context: TransformContext) { checkType(BindingTypes.SETUP_REACTIVE_CONST) || checkType(BindingTypes.LITERAL_CONST) if (fromConst) { + __DEV__ && checkName(fromConst, context) return context.inline ? // in inline mode, const setup bindings (e.g. imports) can be used as-is fromConst @@ -355,6 +370,7 @@ function resolveSetupReference(name: string, context: TransformContext) { checkType(BindingTypes.SETUP_REF) || checkType(BindingTypes.SETUP_MAYBE_REF) if (fromMaybeRef) { + __DEV__ && checkName(fromMaybeRef, context) return context.inline ? // setup scope bindings that may be refs need to be unrefed `${context.helperString(UNREF)}(${fromMaybeRef})` @@ -363,6 +379,7 @@ function resolveSetupReference(name: string, context: TransformContext) { const fromProps = checkType(BindingTypes.PROPS) if (fromProps) { + __DEV__ && checkName(fromProps, context) return `${context.helperString(UNREF)}(${ context.inline ? '__props' : '$props' }[${JSON.stringify(fromProps)}])` diff --git a/packages/compiler-dom/src/errors.ts b/packages/compiler-dom/src/errors.ts index b47624840ab..5bffa4e53a1 100644 --- a/packages/compiler-dom/src/errors.ts +++ b/packages/compiler-dom/src/errors.ts @@ -21,7 +21,7 @@ export function createDOMCompilerError( } 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_TEXT_NO_EXPRESSION, X_V_TEXT_WITH_CHILDREN,