From a12abe72bc94436232e17deecce4ac2b881186e9 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 16 Nov 2023 10:54:54 +0800 Subject: [PATCH] wip: refine element type --- packages/compiler-core/src/parser/index.ts | 93 ++++++++++++++++++++-- packages/compiler-core/src/utils.ts | 21 +++-- 2 files changed, 100 insertions(+), 14 deletions(-) diff --git a/packages/compiler-core/src/parser/index.ts b/packages/compiler-core/src/parser/index.ts index de697e4ff..97937d434 100644 --- a/packages/compiler-core/src/parser/index.ts +++ b/packages/compiler-core/src/parser/index.ts @@ -18,6 +18,7 @@ import Tokenizer, { CharCodes, QuoteType, isWhitespace } from './Tokenizer' import { CompilerCompatOptions } from '../compat/compatConfig' import { NO, extend } from '@vue/shared' import { defaultOnError, defaultOnWarn } from '../errors' +import { isCoreComponent } from '../utils' type OptionalOptions = | 'getTextMode' // TODO @@ -113,8 +114,7 @@ const tokenizer = new Tokenizer({ type: NodeTypes.ELEMENT, tag: name, ns: currentOptions.getNamespace(name, getParent()), - // TODO refine tag type - tagType: ElementTypes.ELEMENT, + tagType: ElementTypes.ELEMENT, // will be refined on tag close props: [], children: [], loc: { @@ -188,8 +188,6 @@ const tokenizer = new Tokenizer({ if (name === 'pre') { inVPre = true currentElementIsVPreBoundary = true - // force current element type - currentElement!.tagType = ElementTypes.ELEMENT // convert dirs before this one to attributes const props = currentElement!.props for (let i = 0; i < props.length; i++) { @@ -362,9 +360,23 @@ function onCloseTag(el: ElementNode, end: number) { offset++ } el.loc.end = tokenizer.getPos(end + offset + 1) + + // refine element type + const tag = el.tag + if (!inVPre) { + if (tag === 'slot') { + el.tagType = ElementTypes.SLOT + } else if (isFragmentTemplate(el)) { + el.tagType = ElementTypes.TEMPLATE + } else if (isComponent(el)) { + el.tagType = ElementTypes.COMPONENT + } + } + // whitepsace management el.children = condenseWhitespace(el.children) - if (currentOptions.isPreTag(el.tag)) { + + if (currentOptions.isPreTag(tag)) { inPre-- } if (currentElementIsVPreBoundary) { @@ -373,8 +385,77 @@ function onCloseTag(el: ElementNode, end: number) { } } -const windowsNewlineRE = /\r\n/g +const specialTemplateDir = new Set(['if', 'else', 'else-if', 'for', 'slot']) +function isFragmentTemplate({ tag, props }: ElementNode): boolean { + if (tag === 'template') { + for (let i = 0; i < props.length; i++) { + if ( + props[i].type === NodeTypes.DIRECTIVE && + specialTemplateDir.has(props[i].name) + ) { + return true + } + } + } + return false +} +function isComponent({ tag, props }: ElementNode): boolean { + if (currentOptions.isCustomElement(tag)) { + return false + } + if ( + tag === 'component' || + isUpperCase(tag.charCodeAt(0)) || + isCoreComponent(tag) || + currentOptions.isBuiltInComponent?.(tag) || + !currentOptions.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 + } + // TODO else if ( + // __COMPAT__ && + // checkCompatEnabled( + // CompilerDeprecationTypes.COMPILER_IS_ON_ELEMENT, + // context, + // p.loc + // ) + // ) { + // return true + // } + } + } + // TODO else if ( + // __COMPAT__ && + // // :is on plain element - only treat as component in compat mode + // p.name === 'bind' && + // isStaticArgOf(p.arg, 'is') && + // checkCompatEnabled( + // CompilerDeprecationTypes.COMPILER_IS_ON_ELEMENT, + // context, + // p.loc + // ) + // ) { + // return true + // } + } + return false +} + +function isUpperCase(c: number) { + return c > 64 && c < 91 +} + +const windowsNewlineRE = /\r\n/g function condenseWhitespace(nodes: TemplateChildNode[]): TemplateChildNode[] { const shouldCondense = currentOptions.whitespace !== 'preserve' let removedWhitespace = false diff --git a/packages/compiler-core/src/utils.ts b/packages/compiler-core/src/utils.ts index baffe5086..ee6beadbb 100644 --- a/packages/compiler-core/src/utils.ts +++ b/packages/compiler-core/src/utils.ts @@ -49,14 +49,19 @@ export const isBuiltInType = (tag: string, expected: string): boolean => tag === expected || tag === hyphenate(expected) export function isCoreComponent(tag: string): symbol | void { - if (isBuiltInType(tag, 'Teleport')) { - return TELEPORT - } else if (isBuiltInType(tag, 'Suspense')) { - return SUSPENSE - } else if (isBuiltInType(tag, 'KeepAlive')) { - return KEEP_ALIVE - } else if (isBuiltInType(tag, 'BaseTransition')) { - return BASE_TRANSITION + switch (tag) { + case 'Teleport': + case 'teleport': + return TELEPORT + case 'Suspense': + case 'suspense': + return SUSPENSE + case 'KeepAlive': + case 'keep-alive': + return KEEP_ALIVE + case 'BaseTransition': + case 'base-transition': + return BASE_TRANSITION } }