fix(compiler-core/compat): fix is prop usage on components

also fix v-bind:is usage on plain element in compat mode

fix #3934
This commit is contained in:
Evan You
2021-06-21 16:16:49 -04:00
parent 4de5d24aa7
commit 08e93220f1
4 changed files with 144 additions and 57 deletions

View File

@@ -10,7 +10,8 @@ import {
assert,
advancePositionWithMutation,
advancePositionWithClone,
isCoreComponent
isCoreComponent,
isBindKey
} from './utils'
import {
Namespaces,
@@ -596,52 +597,20 @@ function parseTag(
}
let tagType = ElementTypes.ELEMENT
const options = context.options
if (!context.inVPre && !options.isCustomElement(tag)) {
const hasVIs = props.some(p => {
if (p.name !== 'is') return
// v-is="xxx" (TODO: deprecate)
if (p.type === NodeTypes.DIRECTIVE) {
return true
}
// is="vue:xxx"
if (p.value && p.value.content.startsWith('vue:')) {
return true
}
// in compat mode, any is usage is considered a component
if (
__COMPAT__ &&
checkCompatEnabled(
CompilerDeprecationTypes.COMPILER_IS_ON_ELEMENT,
context,
p.loc
)
) {
return true
}
})
if (options.isNativeTag && !hasVIs) {
if (!options.isNativeTag(tag)) tagType = ElementTypes.COMPONENT
} else if (
hasVIs ||
isCoreComponent(tag) ||
(options.isBuiltInComponent && options.isBuiltInComponent(tag)) ||
/^[A-Z]/.test(tag) ||
tag === 'component'
) {
tagType = ElementTypes.COMPONENT
}
if (!context.inVPre) {
if (tag === 'slot') {
tagType = ElementTypes.SLOT
} else if (
tag === 'template' &&
props.some(
p =>
p.type === NodeTypes.DIRECTIVE && isSpecialTemplateDirective(p.name)
)
) {
tagType = ElementTypes.TEMPLATE
} else if (tag === 'template') {
if (
props.some(
p =>
p.type === NodeTypes.DIRECTIVE && isSpecialTemplateDirective(p.name)
)
) {
tagType = ElementTypes.TEMPLATE
}
} else if (isComponent(tag, props, context)) {
tagType = ElementTypes.COMPONENT
}
}
@@ -658,6 +627,65 @@ function parseTag(
}
}
function isComponent(
tag: string,
props: (AttributeNode | DirectiveNode)[],
context: ParserContext
) {
const options = context.options
if (options.isCustomElement(tag)) {
return false
}
if (
tag === 'component' ||
/^[A-Z]/.test(tag) ||
isCoreComponent(tag) ||
(options.isBuiltInComponent && options.isBuiltInComponent(tag)) ||
(options.isNativeTag && !options.isNativeTag(tag))
) {
return true
}
// at this point the tag should be a native tag, but check for potential "is"
// casting
for (let i = 0; i < props.length; i++) {
const p = props[i]
if (p.type === NodeTypes.ATTRIBUTE) {
if (p.name === 'is' && p.value) {
if (p.value.content.startsWith('vue:')) {
return true
} else if (
__COMPAT__ &&
checkCompatEnabled(
CompilerDeprecationTypes.COMPILER_IS_ON_ELEMENT,
context,
p.loc
)
) {
return true
}
}
} else {
// directive
// v-is (TODO Deprecate)
if (p.name === 'is') {
return true
} else if (
// :is on plain element - only treat as component in compat mode
p.name === 'bind' &&
isBindKey(p.arg, 'is') &&
__COMPAT__ &&
checkCompatEnabled(
CompilerDeprecationTypes.COMPILER_IS_ON_ELEMENT,
context,
p.loc
)
) {
return true
}
}
}
}
function parseAttributes(
context: ParserContext,
type: TagType