diff --git a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts
index bf3510a052d..e4ab776a764 100644
--- a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts
+++ b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts
@@ -99,6 +99,16 @@ describe('compiler: element transform', () => {
expect(node.tag).toBe(`$setup["Example"]`)
})
+ test('resolve component from setup bindings & component', () => {
+ const { root, node } = parseWithElementTransform(``, {
+ bindingMetadata: {
+ Example: BindingTypes.SETUP_CONST,
+ },
+ })
+ expect(root.helpers).not.toContain(RESOLVE_COMPONENT)
+ expect(node.tag).toBe(`_resolveSetupReturned("Example", $setup)`)
+ })
+
test('resolve component from setup bindings (inline)', () => {
const { root, node } = parseWithElementTransform(``, {
inline: true,
diff --git a/packages/compiler-core/src/ast.ts b/packages/compiler-core/src/ast.ts
index bae13372a98..de0569e8edd 100644
--- a/packages/compiler-core/src/ast.ts
+++ b/packages/compiler-core/src/ast.ts
@@ -9,6 +9,7 @@ import {
OPEN_BLOCK,
type RENDER_LIST,
type RENDER_SLOT,
+ RESOLVE_SETUP_RETURNED,
WITH_DIRECTIVES,
type WITH_MEMO,
} from './runtimeHelpers'
@@ -875,6 +876,10 @@ export function getVNodeBlockHelper(
return ssr || isComponent ? CREATE_BLOCK : CREATE_ELEMENT_BLOCK
}
+export function getSetupReturnedHelper(): typeof RESOLVE_SETUP_RETURNED {
+ return RESOLVE_SETUP_RETURNED
+}
+
export function convertToBlock(
node: VNodeCall,
{ helper, removeHelper, inSSR }: TransformContext,
diff --git a/packages/compiler-core/src/codegen.ts b/packages/compiler-core/src/codegen.ts
index 99020bcf1ae..fe600e5e044 100644
--- a/packages/compiler-core/src/codegen.ts
+++ b/packages/compiler-core/src/codegen.ts
@@ -24,6 +24,7 @@ import {
type TemplateLiteral,
type TextNode,
type VNodeCall,
+ getSetupReturnedHelper,
getVNodeBlockHelper,
getVNodeHelper,
locStub,
@@ -336,6 +337,8 @@ export function generate(
if (!__BROWSER__ && options.bindingMetadata && !options.inline) {
// binding optimization args
args.push('$props', '$setup', '$data', '$options')
+ // Add helper 'getSetupReturnedHelper' for $setup
+ context.helper(getSetupReturnedHelper())
}
const signature =
!__BROWSER__ && options.isTS
diff --git a/packages/compiler-core/src/runtimeHelpers.ts b/packages/compiler-core/src/runtimeHelpers.ts
index 7cf3757b249..410ee29bd4c 100644
--- a/packages/compiler-core/src/runtimeHelpers.ts
+++ b/packages/compiler-core/src/runtimeHelpers.ts
@@ -26,6 +26,9 @@ export const CREATE_STATIC: unique symbol = Symbol(
export const RESOLVE_COMPONENT: unique symbol = Symbol(
__DEV__ ? `resolveComponent` : ``,
)
+export const RESOLVE_SETUP_RETURNED: unique symbol = Symbol(
+ __DEV__ ? `resolveSetupReturned` : ``,
+)
export const RESOLVE_DYNAMIC_COMPONENT: unique symbol = Symbol(
__DEV__ ? `resolveDynamicComponent` : ``,
)
@@ -98,6 +101,7 @@ export const helperNameMap: Record = {
[CREATE_TEXT]: `createTextVNode`,
[CREATE_STATIC]: `createStaticVNode`,
[RESOLVE_COMPONENT]: `resolveComponent`,
+ [RESOLVE_SETUP_RETURNED]: `resolveSetupReturned`,
[RESOLVE_DYNAMIC_COMPONENT]: `resolveDynamicComponent`,
[RESOLVE_DIRECTIVE]: `resolveDirective`,
[RESOLVE_FILTER]: `resolveFilter`,
diff --git a/packages/compiler-core/src/transforms/transformElement.ts b/packages/compiler-core/src/transforms/transformElement.ts
index 1dca0c514c1..50938a318cf 100644
--- a/packages/compiler-core/src/transforms/transformElement.ts
+++ b/packages/compiler-core/src/transforms/transformElement.ts
@@ -21,6 +21,7 @@ import {
createObjectProperty,
createSimpleExpression,
createVNodeCall,
+ getSetupReturnedHelper,
} from '../ast'
import {
PatchFlags,
@@ -344,10 +345,13 @@ function resolveSetupReference(name: string, context: TransformContext) {
checkType(BindingTypes.SETUP_REACTIVE_CONST) ||
checkType(BindingTypes.LITERAL_CONST)
if (fromConst) {
+ const helper = context.helperString
return context.inline
? // in inline mode, const setup bindings (e.g. imports) can be used as-is
fromConst
- : `$setup[${JSON.stringify(fromConst)}]`
+ : `${helper(getSetupReturnedHelper())}(${JSON.stringify(
+ fromConst,
+ )}, $setup)`
}
const fromMaybeRef =
diff --git a/packages/runtime-core/src/helpers/resolveAssets.ts b/packages/runtime-core/src/helpers/resolveAssets.ts
index 910fab33424..986c2e96e9c 100644
--- a/packages/runtime-core/src/helpers/resolveAssets.ts
+++ b/packages/runtime-core/src/helpers/resolveAssets.ts
@@ -1,5 +1,6 @@
import {
type ComponentInternalInstance,
+ type ComponentInternalOptions,
type ComponentOptions,
type ConcreteComponent,
currentInstance,
@@ -7,7 +8,7 @@ import {
} from '../component'
import { currentRenderingInstance } from '../componentRenderContext'
import type { Directive } from '../directives'
-import { camelize, capitalize, isString } from '@vue/shared'
+import { camelize, capitalize, isLateTag, isString } from '@vue/shared'
import { warn } from '../warning'
import type { VNodeTypes } from '../vnode'
@@ -118,12 +119,21 @@ function resolveAsset(
return Component
}
- if (__DEV__ && warnMissing && !res) {
- const extra =
- type === COMPONENTS
- ? `\nIf this is a native custom element, make sure to exclude it from ` +
+ if (
+ __DEV__ &&
+ warnMissing &&
+ ((!res && !isLateTag(name)) || (res && isLateTag(name)))
+ ) {
+ let extra = ''
+ if (type === COMPONENTS) {
+ if (isLateTag(name)) {
+ extra = `\nplease do not use built-in tag names as component names.`
+ } else {
+ extra =
+ `\nIf this is a native custom element, make sure to exclude it from ` +
`component resolution via compilerOptions.isCustomElement.`
- : ``
+ }
+ }
warn(`Failed to resolve ${type.slice(0, -1)}: ${name}${extra}`)
}
@@ -144,3 +154,23 @@ function resolve(registry: Record | undefined, name: string) {
registry[capitalize(camelize(name))])
)
}
+
+/**
+ * @private
+ */
+export function resolveSetupReturned(
+ name: string,
+ setupReturn: Record,
+): unknown {
+ if (!setupReturn) return name
+ const returnValue = setupReturn[name]
+ if (
+ returnValue &&
+ (returnValue as ComponentInternalOptions).__file &&
+ isLateTag(name as string)
+ ) {
+ const extra = `\nplease do not use built-in tag names as component names.`
+ warn(`Failed to resolve component: ${name},${extra}`)
+ }
+ return returnValue
+}
diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts
index 1ed6f21df77..bdf41cf08b4 100644
--- a/packages/runtime-core/src/index.ts
+++ b/packages/runtime-core/src/index.ts
@@ -144,6 +144,7 @@ export {
resolveComponent,
resolveDirective,
resolveDynamicComponent,
+ resolveSetupReturned,
} from './helpers/resolveAssets'
// For integration with runtime compiler
export { registerRuntimeCompiler, isRuntimeOnly } from './component'
diff --git a/packages/shared/src/domTagConfig.ts b/packages/shared/src/domTagConfig.ts
index 7f9d198e569..da23b36e3dc 100644
--- a/packages/shared/src/domTagConfig.ts
+++ b/packages/shared/src/domTagConfig.ts
@@ -14,6 +14,8 @@ const HTML_TAGS =
'option,output,progress,select,textarea,details,dialog,menu,' +
'summary,template,blockquote,iframe,tfoot'
+const LATE_ADDED_TAGS = 'search'
+
// https://developer.mozilla.org/en-US/docs/Web/SVG/Element
const SVG_TAGS =
'svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,' +
@@ -62,3 +64,6 @@ export const isMathMLTag: (key: string) => boolean =
*/
export const isVoidTag: (key: string) => boolean =
/*@__PURE__*/ makeMap(VOID_TAGS)
+
+export const isLateTag: (key: string) => boolean =
+ /*#__PURE__*/ makeMap(LATE_ADDED_TAGS)