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(
+ `
+
+ {{Comp}}
+
+ `,
+ {
+ 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,