From c342433cb5f19b770eaad3ae397532ef9faf5e79 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 16 Nov 2023 01:31:52 +0800 Subject: [PATCH] wip: v-pre handling --- packages/compiler-core/src/ast.ts | 1 + .../compiler-core/src/parser/Tokenizer.ts | 1 + packages/compiler-core/src/parser/index.ts | 169 +++++++++++++----- 3 files changed, 122 insertions(+), 49 deletions(-) diff --git a/packages/compiler-core/src/ast.ts b/packages/compiler-core/src/ast.ts index 131d80aaa..8c3a18ec4 100644 --- a/packages/compiler-core/src/ast.ts +++ b/packages/compiler-core/src/ast.ts @@ -191,6 +191,7 @@ export interface DirectiveNode extends Node { exp: ExpressionNode | undefined arg: ExpressionNode | undefined modifiers: string[] + raw?: string /** * optional property to cache the expression parse result for v-for */ diff --git a/packages/compiler-core/src/parser/Tokenizer.ts b/packages/compiler-core/src/parser/Tokenizer.ts index 2262aef54..53074c49d 100644 --- a/packages/compiler-core/src/parser/Tokenizer.ts +++ b/packages/compiler-core/src/parser/Tokenizer.ts @@ -282,6 +282,7 @@ export default class Tokenizer { this.cbs.oninterpolation(this.sectionStart, this.index) this.state = State.Text this.sectionStart = this.index + this.stateText(this.buffer.charCodeAt(this.index)) } } diff --git a/packages/compiler-core/src/parser/index.ts b/packages/compiler-core/src/parser/index.ts index 34da397a3..de697e4ff 100644 --- a/packages/compiler-core/src/parser/index.ts +++ b/packages/compiler-core/src/parser/index.ts @@ -8,6 +8,7 @@ import { Namespaces, NodeTypes, RootNode, + SimpleExpressionNode, SourceLocation, TemplateChildNode, createRoot @@ -67,7 +68,8 @@ let currentAttrStartIndex = -1 let currentAttrEndIndex = -1 let currentAttrs: Set = new Set() let inPre = 0 -// let inVPre = 0 +let inVPre = false +let currentElementIsVPreBoundary = false const stack: ElementNode[] = [] const tokenizer = new Tokenizer({ @@ -80,6 +82,9 @@ const tokenizer = new Tokenizer({ }, oninterpolation(start, end) { + if (inVPre) { + return onText(getSlice(start, end), start, end) + } let innerStart = start + tokenizer.delimiterOpen.length let innerEnd = end - tokenizer.delimiterClose.length while (isWhitespace(currentInput.charCodeAt(innerStart))) { @@ -103,7 +108,24 @@ const tokenizer = new Tokenizer({ }, onopentagname(start, end) { - emitOpenTag(getSlice(start, end), start) + const name = getSlice(start, end) + currentElement = { + type: NodeTypes.ELEMENT, + tag: name, + ns: currentOptions.getNamespace(name, getParent()), + // TODO refine tag type + tagType: ElementTypes.ELEMENT, + props: [], + children: [], + loc: { + start: tokenizer.getPos(start - 1), + // @ts-expect-error to be attached on tag close + end: undefined, + source: '' + }, + codegenNode: undefined + } + currentAttrs.clear() }, onopentagend(end) { @@ -138,40 +160,72 @@ const tokenizer = new Tokenizer({ ondirname(start, end) { const raw = getSlice(start, end) - const name = - raw === '.' || raw === ':' - ? 'bind' - : raw === '@' - ? 'on' - : raw === '#' - ? 'slot' - : raw.slice(2) - currentProp = { - type: NodeTypes.DIRECTIVE, - name, - exp: undefined, - arg: undefined, - modifiers: [], - loc: getLoc(start) + if (inVPre) { + currentProp = { + type: NodeTypes.ATTRIBUTE, + name: raw, + value: undefined, + loc: getLoc(start) + } + } else { + const name = + raw === '.' || raw === ':' + ? 'bind' + : raw === '@' + ? 'on' + : raw === '#' + ? 'slot' + : raw.slice(2) + currentProp = { + type: NodeTypes.DIRECTIVE, + name, + raw, + exp: undefined, + arg: undefined, + modifiers: [], + loc: getLoc(start) + } + 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++) { + if (props[i].type === NodeTypes.DIRECTIVE) { + props[i] = dirToAttr(props[i] as DirectiveNode) + } + } + } } }, ondirarg(start, end) { const arg = getSlice(start, end) - const isStatic = arg[0] !== `[` - ;(currentProp as DirectiveNode).arg = { - type: NodeTypes.SIMPLE_EXPRESSION, - content: arg, - isStatic, - constType: isStatic - ? ConstantTypes.CAN_STRINGIFY - : ConstantTypes.NOT_CONSTANT, - loc: getLoc(start, end) + if (inVPre) { + ;(currentProp as AttributeNode).name += arg + } else { + const isStatic = arg[0] !== `[` + ;(currentProp as DirectiveNode).arg = { + type: NodeTypes.SIMPLE_EXPRESSION, + content: arg, + isStatic, + constType: isStatic + ? ConstantTypes.CAN_STRINGIFY + : ConstantTypes.NOT_CONSTANT, + loc: getLoc(start, end) + } } }, ondirmodifier(start, end) { - ;(currentProp as DirectiveNode).modifiers.push(getSlice(start, end)) + const mod = getSlice(start, end) + if (inVPre) { + ;(currentProp as AttributeNode).name += '.' + mod + } else { + ;(currentProp as DirectiveNode).modifiers.push(mod) + } }, onattribdata(start, end) { @@ -188,6 +242,9 @@ const tokenizer = new Tokenizer({ // check duplicate attrs const start = currentProp!.loc.start.offset const name = getSlice(start, end) + if (currentProp!.type === NodeTypes.DIRECTIVE) { + currentProp!.raw = name + } if (currentAttrs.has(name)) { currentProp = null // TODO emit error DUPLICATE_ATTRIBUTE @@ -225,7 +282,12 @@ const tokenizer = new Tokenizer({ } } currentProp.loc.end = tokenizer.getPos(end) - currentElement.props.push(currentProp!) + if ( + currentProp.type !== NodeTypes.DIRECTIVE || + currentProp.name !== 'pre' + ) { + currentElement.props.push(currentProp) + } } currentAttrValue = '' currentAttrStartIndex = currentAttrEndIndex = -1 @@ -251,26 +313,6 @@ function getSlice(start: number, end: number) { return currentInput.slice(start, end) } -function emitOpenTag(name: string, start: number) { - currentElement = { - type: NodeTypes.ELEMENT, - tag: name, - ns: currentOptions.getNamespace(name, getParent()), - // TODO refine tag type - tagType: ElementTypes.ELEMENT, - props: [], - children: [], - loc: { - start: tokenizer.getPos(start - 1), - // @ts-expect-error to be attached on tag close - end: undefined, - source: '' - }, - codegenNode: undefined - } - currentAttrs.clear() -} - function endOpenTag(end: number) { addNode(currentElement!) const name = currentElement!.tag @@ -299,7 +341,7 @@ function onText(content: string, start: number, end: number) { if (lastNode?.type === NodeTypes.TEXT) { // merge lastNode.content += content - // TODO update loc + lastNode.loc.end = tokenizer.getPos(end) } else { parent.children.push({ type: NodeTypes.TEXT, @@ -325,6 +367,10 @@ function onCloseTag(el: ElementNode, end: number) { if (currentOptions.isPreTag(el.tag)) { inPre-- } + if (currentElementIsVPreBoundary) { + inVPre = false + currentElementIsVPreBoundary = false + } } const windowsNewlineRE = /\r\n/g @@ -429,6 +475,31 @@ function getLoc(start: number, end?: number): SourceLocation { } } +function dirToAttr(dir: DirectiveNode): AttributeNode { + const attr: AttributeNode = { + type: NodeTypes.ATTRIBUTE, + name: dir.raw!, + value: undefined, + loc: dir.loc + } + if (dir.exp) { + // account for quotes + const loc = dir.exp.loc + if (loc.end.offset < dir.loc.end.offset) { + loc.start.offset-- + loc.start.column-- + loc.end.offset++ + loc.end.column++ + } + attr.value = { + type: NodeTypes.TEXT, + content: (dir.exp as SimpleExpressionNode).content, + loc + } + } + return attr +} + function reset() { tokenizer.reset() currentElement = null