From 5c41ad9e824d0e2007aaec75ed62a66664174cb1 Mon Sep 17 00:00:00 2001 From: Alex Snezhko Date: Sat, 12 Jul 2025 22:40:07 -0700 Subject: [PATCH] feat(compiler-core): consider component import names for resolving setup component references --- packages/compiler-core/src/codegen.ts | 5 ++- packages/compiler-core/src/options.ts | 6 ++++ packages/compiler-core/src/transform.ts | 2 ++ .../src/transforms/transformElement.ts | 35 ++++++++++++------- .../__snapshots__/compileScript.spec.ts.snap | 27 ++++++++++++++ .../__tests__/compileScript.spec.ts | 26 ++++++++++++++ packages/compiler-sfc/src/compileScript.ts | 12 +++++-- 7 files changed, 97 insertions(+), 16 deletions(-) diff --git a/packages/compiler-core/src/codegen.ts b/packages/compiler-core/src/codegen.ts index 6b4559fabb2..1b5918c4af1 100644 --- a/packages/compiler-core/src/codegen.ts +++ b/packages/compiler-core/src/codegen.ts @@ -120,7 +120,10 @@ enum NewlineType { } export interface CodegenContext - extends Omit, 'bindingMetadata' | 'inline'> { + extends Omit< + Required, + 'bindingMetadata' | 'knownComponents' | 'inline' + > { source: string code: string line: number diff --git a/packages/compiler-core/src/options.ts b/packages/compiler-core/src/options.ts index 1de865f42eb..37a57b12c5f 100644 --- a/packages/compiler-core/src/options.ts +++ b/packages/compiler-core/src/options.ts @@ -199,6 +199,12 @@ interface SharedTransformCodegenOptions { * binding access when `prefixIdentifiers` is enabled. */ bindingMetadata?: BindingMetadata + /** + * Optional metadata analyzed from script - used to help disambiguate + * template component references when other bindings in the script use + * different name casing + */ + knownComponents?: Set /** * Compile the function for inlining inside setup(). * This allows the function to directly access setup() local bindings. diff --git a/packages/compiler-core/src/transform.ts b/packages/compiler-core/src/transform.ts index 9d8fd842935..5fc7ecc6bf0 100644 --- a/packages/compiler-core/src/transform.ts +++ b/packages/compiler-core/src/transform.ts @@ -143,6 +143,7 @@ export function createTransformContext( inSSR = false, ssrCssVars = ``, bindingMetadata = EMPTY_OBJ, + knownComponents = new Set(), inline = false, isTS = false, onError = defaultOnError, @@ -171,6 +172,7 @@ export function createTransformContext( inSSR, ssrCssVars, bindingMetadata, + knownComponents, inline, isTS, onError, diff --git a/packages/compiler-core/src/transforms/transformElement.ts b/packages/compiler-core/src/transforms/transformElement.ts index 1dca0c514c1..fed76b09a0b 100644 --- a/packages/compiler-core/src/transforms/transformElement.ts +++ b/packages/compiler-core/src/transforms/transformElement.ts @@ -286,13 +286,13 @@ export function resolveComponentType( // this is skipped in browser build since browser builds do not perform // binding analysis. if (!__BROWSER__) { - const fromSetup = resolveSetupReference(tag, context) + const fromSetup = resolveSetupReference(tag, context, true) if (fromSetup) { return fromSetup } const dotIndex = tag.indexOf('.') if (dotIndex > 0) { - const ns = resolveSetupReference(tag.slice(0, dotIndex), context) + const ns = resolveSetupReference(tag.slice(0, dotIndex), context, true) if (ns) { return ns + tag.slice(dotIndex) } @@ -319,7 +319,11 @@ export function resolveComponentType( return toValidAssetId(tag, `component`) } -function resolveSetupReference(name: string, context: TransformContext) { +function resolveSetupReference( + name: string, + context: TransformContext, + isComponent: boolean, +) { const bindings = context.bindingMetadata if (!bindings || bindings.__isScriptSetup === false) { return @@ -327,16 +331,23 @@ function resolveSetupReference(name: string, context: TransformContext) { const camelName = camelize(name) const PascalName = capitalize(camelName) + const names = [name, camelName, PascalName] + const isKnownComponentPerName = names.map(name => ({ + name, + isKnownComponent: + context.knownComponents && context.knownComponents.has(name), + })) const checkType = (type: BindingTypes) => { - if (bindings[name] === type) { - return name - } - if (bindings[camelName] === type) { - return camelName - } - if (bindings[PascalName] === type) { - return PascalName + if (isComponent) { + // If there is a known component with this name then prioritize it + const foundWithKnownComponent = isKnownComponentPerName.find( + x => bindings[x.name] === type && x.isKnownComponent, + ) + if (foundWithKnownComponent) { + return foundWithKnownComponent.name + } } + return names.find(x => bindings[x] === type) } const fromConst = @@ -885,7 +896,7 @@ export function buildDirectiveArgs( // user directive. // see if we have directives exposed via + + `, + { inlineTemplate: true }, + ) + expect(content).toMatch('_createVNode(First)') + expect(content).toMatch('_createVNode(SecondComp)') + expect(content).toMatch('_createVNode(thirdComp)') + assertCode(content) + }) + test('avoid unref() when necessary', () => { // function, const, component import const { content } = compile( diff --git a/packages/compiler-sfc/src/compileScript.ts b/packages/compiler-sfc/src/compileScript.ts index 3bfc90dfaac..5bf96e1d92f 100644 --- a/packages/compiler-sfc/src/compileScript.ts +++ b/packages/compiler-sfc/src/compileScript.ts @@ -726,16 +726,21 @@ export function compileScript( if (scriptAst) { Object.assign(ctx.bindingMetadata, analyzeScriptBindings(scriptAst.body)) } + const knownComponents = new Set() for (const [key, { isType, imported, source }] of Object.entries( ctx.userImports, )) { if (isType) continue - ctx.bindingMetadata[key] = + const isKnownComponent = imported === '*' || (imported === 'default' && source.endsWith('.vue')) || source === 'vue' - ? BindingTypes.SETUP_CONST - : BindingTypes.SETUP_MAYBE_REF + if (isKnownComponent) { + knownComponents.add(key) + ctx.bindingMetadata[key] = BindingTypes.SETUP_CONST + } else { + ctx.bindingMetadata[key] = BindingTypes.SETUP_MAYBE_REF + } } for (const key in scriptBindings) { ctx.bindingMetadata[key] = scriptBindings[key] @@ -880,6 +885,7 @@ export function compileScript( inline: true, isTS: ctx.isTS, bindingMetadata: ctx.bindingMetadata, + knownComponents: knownComponents, }, }) templateMap = map