DirectiveNode,
ElementNode,
ElementTypes,
- ExpressionNode,
Namespaces,
NodeTypes,
RootNode,
+ SourceLocation,
TemplateChildNode,
createRoot
} from '../ast'
comments: __DEV__
}
-const directiveParseRE =
- /(?:^v-([a-z0-9-]+))?(?:(?::|^\.|^@|^#)(\[[^\]]+\]|[^\.]+))?(.+)?$/i
-
const foreignContextElements = new Set(['math', 'svg'])
const htmlIntegrationElements = new Set([
let currentElement: ElementNode | null = null
let currentProp: AttributeNode | DirectiveNode | null = null
let currentAttrValue = ''
+let currentAttrStartIndex = -1
+let currentAttrEndIndex = -1
let currentAttrs: Set<string> = new Set()
let inPre = 0
-let inVPre = 0
+// let inVPre = 0
const stack: ElementNode[] = []
const foreignContext: boolean[] = [false]
},
onattribname(start, end) {
- const name = getSlice(start, end)
- if (!inVPre && isDirective(name)) {
- // directive
- const match = directiveParseRE.exec(name)!
- const firstChar = name[0]
- const isPropShorthand = firstChar === '.'
- 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
- // )
- // }
- }
+ // plain attribute
+ currentProp = {
+ type: NodeTypes.ATTRIBUTE,
+ name: getSlice(start, end),
+ value: undefined,
+ loc: getLoc(start)
+ }
+ },
- 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) {
currentAttrValue += getSlice(start, end)
+ if (currentAttrStartIndex < 0) currentAttrStartIndex = start - 1
+ currentAttrEndIndex = end + 1
},
onattribentity(codepoint) {
currentAttrValue += fromCodePoint(codepoint)
// } else {
// currentAttrs.add(name)
// }
- // if (currentElement) {
- // if (currentAttrValue) {
- // if (currentProp!.type === NodeTypes.ATTRIBUTE) {
- // // assign value
- // currentProp!.value = {
- // type: NodeTypes.TEXT,
- // content: currentAttrValue,
- // // @ts-expect-error TODO
- // loc: {}
- // }
- // } else {
- // // directive
- // currentProp!.exp = {
- // type: NodeTypes.SIMPLE_EXPRESSION,
- // content: currentAttrValue,
- // isStatic: false,
- // // Treat as non-constant by default. This can be potentially set to
- // // other values by `transformExpression` to make it eligible for hoisting.
- // constType: ConstantTypes.NOT_CONSTANT,
- // // @ts-expect-error TODO
- // loc: {}
- // }
- // }
- // }
- // currentProp!.loc.end = tokenizer.getPositionForIndex(end)
- // currentElement.props.push(currentProp!)
- // }
+ if (currentElement) {
+ if (currentAttrValue) {
+ if (currentProp!.type === NodeTypes.ATTRIBUTE) {
+ // assign value
+ currentProp!.value = {
+ type: NodeTypes.TEXT,
+ content: currentAttrValue,
+ loc: getLoc(currentAttrStartIndex, currentAttrEndIndex)
+ }
+ } else {
+ // directive
+ currentProp!.exp = {
+ type: NodeTypes.SIMPLE_EXPRESSION,
+ content: currentAttrValue,
+ isStatic: false,
+ // Treat as non-constant by default. This can be potentially set
+ // to other values by `transformExpression` to make it eligible
+ // for hoisting.
+ constType: ConstantTypes.NOT_CONSTANT,
+ loc: getLoc(currentAttrStartIndex, currentAttrEndIndex)
+ }
+ }
+ }
+ currentProp!.loc.end = tokenizer.getPositionForIndex(end)
+ currentElement.props.push(currentProp!)
+ }
currentAttrValue = ''
- },
-
- 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))
+ currentAttrStartIndex = currentAttrEndIndex = -1
},
oncomment(start, end, offset) {
return stack[0] || currentRoot
}
-function isDirective(name: string) {
- switch (name[0]) {
- case ':':
- case '.':
- case '@':
- case '#':
- return true
- case 'v':
- return name[1] === '-'
- default:
- return false
+function getLoc(start: number, end?: number): SourceLocation {
+ return {
+ start: tokenizer.getPositionForIndex(start),
+ // @ts-expect-error allow late attachment
+ end: end && tokenizer.getPositionForIndex(end)
}
}
currentProp = null
currentAttrs.clear()
currentAttrValue = ''
+ currentAttrStartIndex = -1
+ currentAttrEndIndex = -1
stack.length = 0
foreignContext.length = 1
foreignContext[0] = false