Skip to content

feat(compiler-core): consider component import names for resolving setup component references #13624

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion packages/compiler-core/src/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,10 @@ enum NewlineType {
}

export interface CodegenContext
extends Omit<Required<CodegenOptions>, 'bindingMetadata' | 'inline'> {
extends Omit<
Required<CodegenOptions>,
'bindingMetadata' | 'knownComponents' | 'inline'
> {
source: string
code: string
line: number
Expand Down
6 changes: 6 additions & 0 deletions packages/compiler-core/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>
/**
* Compile the function for inlining inside setup().
* This allows the function to directly access setup() local bindings.
Expand Down
2 changes: 2 additions & 0 deletions packages/compiler-core/src/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ export function createTransformContext(
inSSR = false,
ssrCssVars = ``,
bindingMetadata = EMPTY_OBJ,
knownComponents = new Set<string>(),
inline = false,
isTS = false,
onError = defaultOnError,
Expand Down Expand Up @@ -171,6 +172,7 @@ export function createTransformContext(
inSSR,
ssrCssVars,
bindingMetadata,
knownComponents,
inline,
isTS,
onError,
Expand Down
35 changes: 23 additions & 12 deletions packages/compiler-core/src/transforms/transformElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -319,24 +319,35 @@ 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
}

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 =
Expand Down Expand Up @@ -885,7 +896,7 @@ export function buildDirectiveArgs(
// user directive.
// see if we have directives exposed via <script setup>
const fromSetup =
!__BROWSER__ && resolveSetupReference('v-' + dir.name, context)
!__BROWSER__ && resolveSetupReference('v-' + dir.name, context, false)
if (fromSetup) {
dirArgs.push(fromSetup)
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,33 @@ return (_ctx, _cache) => {
}"
`;

exports[`SFC compile <script setup> > inlineTemplate mode > referencing scope components casing 1`] = `
"import { createVNode as _createVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"

import First from './First.vue'
import SecondComp from './Second.vue'
import thirdComp from './Fifth.vue'


export default {
setup(__props) {

const first = () => 1
const secondComp = () => 2
const ThirdComp = () => 3

return (_ctx, _cache) => {
return (_openBlock(), _createElementBlock(_Fragment, null, [
_createVNode(First),
_createVNode(SecondComp),
_createVNode(thirdComp)
], 64 /* STABLE_FRAGMENT */))
}
}

}"
`;

exports[`SFC compile <script setup> > inlineTemplate mode > should work 1`] = `
"import { toDisplayString as _toDisplayString, createElementVNode as _createElementVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"

Expand Down
26 changes: 26 additions & 0 deletions packages/compiler-sfc/__tests__/compileScript.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,32 @@ describe('SFC compile <script setup>', () => {
assertCode(content)
})

test('referencing scope components casing', () => {
const { content } = compile(
`
<script setup>
import First from './First.vue'
import SecondComp from './Second.vue'
import thirdComp from './Fifth.vue'

const first = () => 1
const secondComp = () => 2
const ThirdComp = () => 3
</script>
<template>
<first/>
<second-comp/>
<third-comp/>
</template>
`,
{ 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(
Expand Down
12 changes: 9 additions & 3 deletions packages/compiler-sfc/src/compileScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -726,16 +726,21 @@ export function compileScript(
if (scriptAst) {
Object.assign(ctx.bindingMetadata, analyzeScriptBindings(scriptAst.body))
}
const knownComponents = new Set<string>()
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]
Expand Down Expand Up @@ -880,6 +885,7 @@ export function compileScript(
inline: true,
isTS: ctx.isTS,
bindingMetadata: ctx.bindingMetadata,
knownComponents: knownComponents,
},
})
templateMap = map
Expand Down