wip: source location for props

This commit is contained in:
Evan You 2023-11-15 17:45:42 +08:00
parent 08038a938c
commit b81415ceac
2 changed files with 88 additions and 161 deletions

View File

@ -475,10 +475,10 @@ export default class Tokenizer {
} else if (c === CharCodes.Slash) { } else if (c === CharCodes.Slash) {
this.state = State.InSelfClosingTag this.state = State.InSelfClosingTag
} else if (!isWhitespace(c)) { } else if (!isWhitespace(c)) {
this.enterAttribute(c) this.handleAttributeStart(c)
} }
} }
private enterAttribute(c: number) { private handleAttributeStart(c: number) {
if ( if (
c === CharCodes.LowerV && c === CharCodes.LowerV &&
this.buffer.charCodeAt(this.index + 1) === CharCodes.Dash this.buffer.charCodeAt(this.index + 1) === CharCodes.Dash
@ -576,7 +576,7 @@ export default class Tokenizer {
this.stateBeforeAttributeName(c) this.stateBeforeAttributeName(c)
} else if (!isWhitespace(c)) { } else if (!isWhitespace(c)) {
this.cbs.onattribend(QuoteType.NoValue, this.sectionStart) this.cbs.onattribend(QuoteType.NoValue, this.sectionStart)
this.enterAttribute(c) this.handleAttributeStart(c)
} }
} }
private stateBeforeAttributeValue(c: number): void { private stateBeforeAttributeValue(c: number): void {

View File

@ -5,10 +5,10 @@ import {
DirectiveNode, DirectiveNode,
ElementNode, ElementNode,
ElementTypes, ElementTypes,
ExpressionNode,
Namespaces, Namespaces,
NodeTypes, NodeTypes,
RootNode, RootNode,
SourceLocation,
TemplateChildNode, TemplateChildNode,
createRoot createRoot
} from '../ast' } from '../ast'
@ -55,9 +55,6 @@ export const defaultParserOptions: MergedParserOptions = {
comments: __DEV__ comments: __DEV__
} }
const directiveParseRE =
/(?:^v-([a-z0-9-]+))?(?:(?::|^\.|^@|^#)(\[[^\]]+\]|[^\.]+))?(.+)?$/i
const foreignContextElements = new Set(['math', 'svg']) const foreignContextElements = new Set(['math', 'svg'])
const htmlIntegrationElements = new Set([ const htmlIntegrationElements = new Set([
@ -81,9 +78,11 @@ let currentInput = ''
let currentElement: ElementNode | null = null let currentElement: ElementNode | null = null
let currentProp: AttributeNode | DirectiveNode | null = null let currentProp: AttributeNode | DirectiveNode | null = null
let currentAttrValue = '' let currentAttrValue = ''
let currentAttrStartIndex = -1
let currentAttrEndIndex = -1
let currentAttrs: Set<string> = new Set() let currentAttrs: Set<string> = new Set()
let inPre = 0 let inPre = 0
let inVPre = 0 // let inVPre = 0
const stack: ElementNode[] = [] const stack: ElementNode[] = []
const foreignContext: boolean[] = [false] const foreignContext: boolean[] = [false]
@ -141,114 +140,57 @@ const tokenizer = new Tokenizer(
}, },
onattribname(start, end) { onattribname(start, end) {
const name = getSlice(start, end) // plain attribute
if (!inVPre && isDirective(name)) { currentProp = {
// directive type: NodeTypes.ATTRIBUTE,
const match = directiveParseRE.exec(name)! name: getSlice(start, end),
const firstChar = name[0] value: undefined,
const isPropShorthand = firstChar === '.' loc: getLoc(start)
const dirName =
match[1] ||
(isPropShorthand || firstChar === ':'
? 'bind'
: firstChar === '@'
? 'on'
: 'slot')
let arg: ExpressionNode | undefined
if (match[2]) {
const isSlot = dirName === 'slot'
// const startOffset = name.lastIndexOf(
// match[2],
// name.length - (match[3]?.length || 0)
// )
let content = match[2]
let isStatic = true
if (content.startsWith('[')) {
isStatic = false
if (!content.endsWith(']')) {
// TODO emitError(
// context,
// ErrorCodes.X_MISSING_DYNAMIC_DIRECTIVE_ARGUMENT_END
// )
content = content.slice(1)
} else {
content = content.slice(1, content.length - 1)
}
} else if (isSlot) {
// #1241 special case for v-slot: vuetify relies extensively on slot
// names containing dots. v-slot doesn't have any modifiers and Vue 2.x
// supports such usage so we are keeping it consistent with 2.x.
content += match[3] || ''
}
arg = {
type: NodeTypes.SIMPLE_EXPRESSION,
content,
isStatic,
constType: isStatic
? ConstantTypes.CAN_STRINGIFY
: ConstantTypes.NOT_CONSTANT,
// @ts-expect-error TODO
loc: {}
}
}
const modifiers = match[3] ? match[3].slice(1).split('.') : []
if (isPropShorthand) modifiers.push('prop')
// 2.x compat v-bind:foo.sync -> v-model:foo
if (__COMPAT__ && dirName === 'bind' && arg) {
// TODO
// if (
// modifiers.includes('sync') &&
// checkCompatEnabled(
// CompilerDeprecationTypes.COMPILER_V_BIND_SYNC,
// context,
// loc,
// arg.loc.source
// )
// ) {
// dirName = 'model'
// modifiers.splice(modifiers.indexOf('sync'), 1)
// }
// if (__DEV__ && modifiers.includes('prop')) {
// checkCompatEnabled(
// CompilerDeprecationTypes.COMPILER_V_BIND_PROP,
// context,
// loc
// )
// }
}
currentProp = {
type: NodeTypes.DIRECTIVE,
name: dirName,
exp: undefined,
arg,
modifiers,
// @ts-expect-error TODO
loc: {}
}
} else {
// plain attribute
currentProp = {
type: NodeTypes.ATTRIBUTE,
name,
value: undefined,
loc: {
start: tokenizer.getPositionForIndex(start),
// @ts-expect-error to be attached on attribute end
end: undefined,
source: ''
}
}
} }
}, },
ondirname(start, end) {
// console.log('name ' + getSlice(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)
}
},
ondirarg(start, end) {
// console.log('arg ' + getSlice(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)
}
},
ondirmodifier(start, end) {
// console.log('.' + getSlice(start, end))
},
onattribdata(start, end) { onattribdata(start, end) {
currentAttrValue += getSlice(start, end) currentAttrValue += getSlice(start, end)
if (currentAttrStartIndex < 0) currentAttrStartIndex = start - 1
currentAttrEndIndex = end + 1
}, },
onattribentity(codepoint) { onattribentity(codepoint) {
currentAttrValue += fromCodePoint(codepoint) currentAttrValue += fromCodePoint(codepoint)
@ -260,45 +202,34 @@ const tokenizer = new Tokenizer(
// } else { // } else {
// currentAttrs.add(name) // currentAttrs.add(name)
// } // }
// if (currentElement) { if (currentElement) {
// if (currentAttrValue) { if (currentAttrValue) {
// if (currentProp!.type === NodeTypes.ATTRIBUTE) { if (currentProp!.type === NodeTypes.ATTRIBUTE) {
// // assign value // assign value
// currentProp!.value = { currentProp!.value = {
// type: NodeTypes.TEXT, type: NodeTypes.TEXT,
// content: currentAttrValue, content: currentAttrValue,
// // @ts-expect-error TODO loc: getLoc(currentAttrStartIndex, currentAttrEndIndex)
// loc: {} }
// } } else {
// } else { // directive
// // directive currentProp!.exp = {
// currentProp!.exp = { type: NodeTypes.SIMPLE_EXPRESSION,
// type: NodeTypes.SIMPLE_EXPRESSION, content: currentAttrValue,
// content: currentAttrValue, isStatic: false,
// isStatic: false, // Treat as non-constant by default. This can be potentially set
// // Treat as non-constant by default. This can be potentially set to // to other values by `transformExpression` to make it eligible
// // other values by `transformExpression` to make it eligible for hoisting. // for hoisting.
// constType: ConstantTypes.NOT_CONSTANT, constType: ConstantTypes.NOT_CONSTANT,
// // @ts-expect-error TODO loc: getLoc(currentAttrStartIndex, currentAttrEndIndex)
// loc: {} }
// } }
// } }
// } currentProp!.loc.end = tokenizer.getPositionForIndex(end)
// currentProp!.loc.end = tokenizer.getPositionForIndex(end) currentElement.props.push(currentProp!)
// currentElement.props.push(currentProp!) }
// }
currentAttrValue = '' currentAttrValue = ''
}, currentAttrStartIndex = currentAttrEndIndex = -1
ondirname(start, end) {
// console.log('name ' + getSlice(start, end))
currentProp
},
ondirarg(start, end) {
// console.log('arg ' + getSlice(start, end))
},
ondirmodifier(start, end) {
// console.log('.' + getSlice(start, end))
}, },
oncomment(start, end, offset) { oncomment(start, end, offset) {
@ -493,17 +424,11 @@ function getParent() {
return stack[0] || currentRoot return stack[0] || currentRoot
} }
function isDirective(name: string) { function getLoc(start: number, end?: number): SourceLocation {
switch (name[0]) { return {
case ':': start: tokenizer.getPositionForIndex(start),
case '.': // @ts-expect-error allow late attachment
case '@': end: end && tokenizer.getPositionForIndex(end)
case '#':
return true
case 'v':
return name[1] === '-'
default:
return false
} }
} }
@ -513,6 +438,8 @@ function reset() {
currentProp = null currentProp = null
currentAttrs.clear() currentAttrs.clear()
currentAttrValue = '' currentAttrValue = ''
currentAttrStartIndex = -1
currentAttrEndIndex = -1
stack.length = 0 stack.length = 0
foreignContext.length = 1 foreignContext.length = 1
foreignContext[0] = false foreignContext[0] = false