Skip to content

feat(compiler): compiler supports search tag #9249

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 7 commits into
base: minor
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
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,40 @@ describe('compiler: element transform', () => {
expect(node.tag).toBe(`$setup["Example"]`)
})

test('resolve component from setup bindings & component', () => {
const { root, node } = parseWithElementTransform(`<search/>`, {
bindingMetadata: {
search: BindingTypes.SETUP_CONST,
},
isNativeTag: (tag: string) => tag !== 'search',
})
expect(root.helpers).not.toContain(RESOLVE_COMPONENT)
expect(node.tag).toBe(`_resolveLateAddedTag("search", 'setupState')`)

const { root: root2, node: node2 } = parseWithElementTransform(
`<search/>`,
{
bindingMetadata: {
search: BindingTypes.SETUP_LET,
},
isNativeTag: (tag: string) => tag !== 'search',
},
)
expect(root2.helpers).not.toContain(RESOLVE_COMPONENT)
expect(node2.tag).toBe(`_resolveLateAddedTag("search", 'setupState')`)
})

test('resolve component from props', () => {
const { root, node } = parseWithElementTransform(`<search/>`, {
bindingMetadata: {
search: BindingTypes.PROPS,
},
isNativeTag: (tag: string) => tag !== 'search',
})
expect(root.helpers).not.toContain(RESOLVE_COMPONENT)
expect(node.tag).toBe(`_unref(_resolveLateAddedTag("search", 'props'))`)
})

test('resolve component from setup bindings (inline)', () => {
const { root, node } = parseWithElementTransform(`<Example/>`, {
inline: true,
Expand Down
5 changes: 5 additions & 0 deletions packages/compiler-core/src/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
OPEN_BLOCK,
type RENDER_LIST,
type RENDER_SLOT,
RESOLVE_LATE_ADDED_TAG,
WITH_DIRECTIVES,
type WITH_MEMO,
} from './runtimeHelpers'
Expand Down Expand Up @@ -875,6 +876,10 @@ export function getVNodeBlockHelper(
return ssr || isComponent ? CREATE_BLOCK : CREATE_ELEMENT_BLOCK
}

export function getResolveLateAddedTagHelper(): typeof RESOLVE_LATE_ADDED_TAG {
return RESOLVE_LATE_ADDED_TAG
}

export function convertToBlock(
node: VNodeCall,
{ helper, removeHelper, inSSR }: TransformContext,
Expand Down
3 changes: 3 additions & 0 deletions packages/compiler-core/src/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
type TemplateLiteral,
type TextNode,
type VNodeCall,
getResolveLateAddedTagHelper,
getVNodeBlockHelper,
getVNodeHelper,
locStub,
Expand Down Expand Up @@ -336,6 +337,8 @@ export function generate(
if (!__BROWSER__ && options.bindingMetadata && !options.inline) {
// binding optimization args
args.push('$props', '$setup', '$data', '$options')
// Add helper 'getResolveLateAddedTagHelper' for $setup
context.helper(getResolveLateAddedTagHelper())
}
const signature =
!__BROWSER__ && options.isTS
Expand Down
4 changes: 4 additions & 0 deletions packages/compiler-core/src/runtimeHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ export const CREATE_STATIC: unique symbol = Symbol(
export const RESOLVE_COMPONENT: unique symbol = Symbol(
__DEV__ ? `resolveComponent` : ``,
)
export const RESOLVE_LATE_ADDED_TAG: unique symbol = Symbol(
__DEV__ ? `resolveLateAddedTag` : ``,
)
export const RESOLVE_DYNAMIC_COMPONENT: unique symbol = Symbol(
__DEV__ ? `resolveDynamicComponent` : ``,
)
Expand Down Expand Up @@ -98,6 +101,7 @@ export const helperNameMap: Record<symbol, string> = {
[CREATE_TEXT]: `createTextVNode`,
[CREATE_STATIC]: `createStaticVNode`,
[RESOLVE_COMPONENT]: `resolveComponent`,
[RESOLVE_LATE_ADDED_TAG]: `resolveLateAddedTag`,
[RESOLVE_DYNAMIC_COMPONENT]: `resolveDynamicComponent`,
[RESOLVE_DIRECTIVE]: `resolveDirective`,
[RESOLVE_FILTER]: `resolveFilter`,
Expand Down
24 changes: 18 additions & 6 deletions packages/compiler-core/src/transforms/transformElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ import {
createObjectProperty,
createSimpleExpression,
createVNodeCall,
getResolveLateAddedTagHelper,
} from '../ast'
import {
PatchFlags,
camelize,
capitalize,
isBuiltInDirective,
isLateTag,
isObject,
isOn,
isReservedProp,
Expand Down Expand Up @@ -85,7 +87,6 @@ export const transformElement: NodeTransform = (node, context) => {
) {
return
}

const { tag, props } = node
const isComponent = node.tagType === ElementTypes.COMPONENT

Expand Down Expand Up @@ -344,28 +345,39 @@ 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)}]`
: isLateTag(fromConst)
? `${helper(getResolveLateAddedTagHelper())}(${JSON.stringify(fromConst)}, 'setupState')`
: `$setup[${JSON.stringify(fromConst)}]`
}

const fromMaybeRef =
checkType(BindingTypes.SETUP_LET) ||
checkType(BindingTypes.SETUP_REF) ||
checkType(BindingTypes.SETUP_MAYBE_REF)
if (fromMaybeRef) {
const helper = context.helperString
return context.inline
? // setup scope bindings that may be refs need to be unrefed
`${context.helperString(UNREF)}(${fromMaybeRef})`
: `$setup[${JSON.stringify(fromMaybeRef)}]`
: isLateTag(fromMaybeRef)
? `${helper(getResolveLateAddedTagHelper())}(${JSON.stringify(fromMaybeRef)}, 'setupState')`
: `$setup[${JSON.stringify(fromMaybeRef)}]`
}

const fromProps = checkType(BindingTypes.PROPS)
if (fromProps) {
return `${context.helperString(UNREF)}(${
context.inline ? '__props' : '$props'
}[${JSON.stringify(fromProps)}])`
const helper = context.helperString
const fromPropsStr = JSON.stringify(fromProps)
let propsCode = context.inline
? `__props[${fromPropsStr}]`
: isLateTag(fromProps)
? `${helper(getResolveLateAddedTagHelper())}(${fromPropsStr}, 'props')`
: `$props[${fromPropsStr}]`
return `${helper(UNREF)}(${propsCode})`
}
}

Expand Down
45 changes: 39 additions & 6 deletions packages/runtime-core/src/helpers/resolveAssets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,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'

Expand Down Expand Up @@ -118,12 +118,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}`)
}

Expand All @@ -144,3 +153,27 @@ function resolve(registry: Record<string, any> | undefined, name: string) {
registry[capitalize(camelize(name))])
)
}

/**
* @private
*/
export function resolveLateAddedTag(
name: string,
key: 'setupState' | 'props',
): unknown {
if (!currentRenderingInstance || !currentRenderingInstance[key]) return name
const data = currentRenderingInstance[key]
const value = data[name]
// Only the render function for the value is parsed as a component
// and a warning is reported
if (
__DEV__ &&
value &&
(value as ComponentInternalInstance).render &&
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the 'setup script', the component value comes from the 'props' or 'setupState', and when it is an object with a render function, can it be considered a component? If it can be judged that it is a component in this way, then report a warning, otherwise leave it as is

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 value
}
1 change: 1 addition & 0 deletions packages/runtime-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ export {
resolveComponent,
resolveDirective,
resolveDynamicComponent,
resolveLateAddedTag,
} from './helpers/resolveAssets'
// For integration with runtime compiler
export { registerRuntimeCompiler, isRuntimeOnly } from './component'
Expand Down
5 changes: 5 additions & 0 deletions packages/shared/src/domTagConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,' +
Expand Down Expand Up @@ -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)