]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: source location for props
authorEvan You <yyx990803@gmail.com>
Wed, 15 Nov 2023 09:45:42 +0000 (17:45 +0800)
committerEvan You <yyx990803@gmail.com>
Sat, 25 Nov 2023 08:18:29 +0000 (16:18 +0800)
packages/compiler-core/src/parser/Tokenizer.ts
packages/compiler-core/src/parser/index.ts

index f8dd287bb91fb2516fef8dc618fb45890299893a..1cb8ab99d6b6c0399da36e2f7d518da827e53348 100644 (file)
@@ -475,10 +475,10 @@ export default class Tokenizer {
     } else if (c === CharCodes.Slash) {
       this.state = State.InSelfClosingTag
     } else if (!isWhitespace(c)) {
-      this.enterAttribute(c)
+      this.handleAttributeStart(c)
     }
   }
-  private enterAttribute(c: number) {
+  private handleAttributeStart(c: number) {
     if (
       c === CharCodes.LowerV &&
       this.buffer.charCodeAt(this.index + 1) === CharCodes.Dash
@@ -576,7 +576,7 @@ export default class Tokenizer {
       this.stateBeforeAttributeName(c)
     } else if (!isWhitespace(c)) {
       this.cbs.onattribend(QuoteType.NoValue, this.sectionStart)
-      this.enterAttribute(c)
+      this.handleAttributeStart(c)
     }
   }
   private stateBeforeAttributeValue(c: number): void {
index 450ae2263a3ff41e7483bd0f78dd8293676a22ca..84e2a7d7fdb3770f4cb36cd52fcd5b96699f30e2 100644 (file)
@@ -5,10 +5,10 @@ import {
   DirectiveNode,
   ElementNode,
   ElementTypes,
-  ExpressionNode,
   Namespaces,
   NodeTypes,
   RootNode,
+  SourceLocation,
   TemplateChildNode,
   createRoot
 } from '../ast'
@@ -55,9 +55,6 @@ export const defaultParserOptions: MergedParserOptions = {
   comments: __DEV__
 }
 
-const directiveParseRE =
-  /(?:^v-([a-z0-9-]+))?(?:(?::|^\.|^@|^#)(\[[^\]]+\]|[^\.]+))?(.+)?$/i
-
 const foreignContextElements = new Set(['math', 'svg'])
 
 const htmlIntegrationElements = new Set([
@@ -81,9 +78,11 @@ let currentInput = ''
 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]
 
@@ -141,114 +140,57 @@ const tokenizer = new Tokenizer(
     },
 
     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)
@@ -260,45 +202,34 @@ const tokenizer = new Tokenizer(
       // } 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) {
@@ -493,17 +424,11 @@ function getParent() {
   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)
   }
 }
 
@@ -513,6 +438,8 @@ function reset() {
   currentProp = null
   currentAttrs.clear()
   currentAttrValue = ''
+  currentAttrStartIndex = -1
+  currentAttrEndIndex = -1
   stack.length = 0
   foreignContext.length = 1
   foreignContext[0] = false