]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: check duplicated attributes
authorEvan You <yyx990803@gmail.com>
Wed, 15 Nov 2023 11:36:05 +0000 (19:36 +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 1cb8ab99d6b6c0399da36e2f7d518da827e53348..3ec0d0c8cafd16ccc68fb444626d32fbe98f9115 100644 (file)
@@ -143,6 +143,7 @@ export interface Callbacks {
   onattribentity(codepoint: number): void
   onattribend(quote: QuoteType, endIndex: number): void
   onattribname(start: number, endIndex: number): void
+  onattribnameend(endIndex: number): void
 
   ondirname(start: number, endIndex: number): void
   ondirarg(start: number, endIndex: number): void
@@ -218,7 +219,7 @@ export default class Tokenizer {
    * processed index, so all the newlines up to this index should have been
    * recorded.
    */
-  public getPositionForIndex(index: number): Position {
+  public getPos(index: number): Position {
     let line = 1
     let column = index + 1
     for (let i = this.newlines.length - 1; i >= 0; i--) {
@@ -513,17 +514,13 @@ export default class Tokenizer {
   private stateInAttributeName(c: number): void {
     if (c === CharCodes.Eq || isEndOfTagSection(c)) {
       this.cbs.onattribname(this.sectionStart, this.index)
-      this.sectionStart = this.index
-      this.state = State.AfterAttributeName
-      this.stateAfterAttributeName(c)
+      this.handleAttributeNameEnd(c)
     }
   }
   private stateInDirectiveName(c: number): void {
     if (c === CharCodes.Eq || isEndOfTagSection(c)) {
       this.cbs.ondirname(this.sectionStart, this.index)
-      this.sectionStart = this.index
-      this.state = State.AfterAttributeName
-      this.stateAfterAttributeName(c)
+      this.handleAttributeNameEnd(c)
     } else if (c === CharCodes.Colon) {
       this.cbs.ondirname(this.sectionStart, this.index)
       this.state = State.InDirectiveArg
@@ -537,9 +534,7 @@ export default class Tokenizer {
   private stateInDirectiveArg(c: number): void {
     if (c === CharCodes.Eq || isEndOfTagSection(c)) {
       this.cbs.ondirarg(this.sectionStart, this.index)
-      this.sectionStart = this.index
-      this.state = State.AfterAttributeName
-      this.stateAfterAttributeName(c)
+      this.handleAttributeNameEnd(c)
     } else if (c === CharCodes.LeftSqaure) {
       this.state = State.InDirectiveDynamicArg
     } else if (c === CharCodes.Dot) {
@@ -558,14 +553,18 @@ export default class Tokenizer {
   private stateInDirectiveModifier(c: number): void {
     if (c === CharCodes.Eq || isEndOfTagSection(c)) {
       this.cbs.ondirmodifier(this.sectionStart, this.index)
-      this.sectionStart = this.index
-      this.state = State.AfterAttributeName
-      this.stateAfterAttributeName(c)
+      this.handleAttributeNameEnd(c)
     } else if (c === CharCodes.Dot) {
       this.cbs.ondirmodifier(this.sectionStart, this.index)
       this.sectionStart = this.index + 1
     }
   }
+  private handleAttributeNameEnd(c: number): void {
+    this.sectionStart = this.index
+    this.state = State.AfterAttributeName
+    this.cbs.onattribnameend(this.index)
+    this.stateAfterAttributeName(c)
+  }
   private stateAfterAttributeName(c: number): void {
     if (c === CharCodes.Eq) {
       this.state = State.BeforeAttributeValue
index eb4cd618ebfb36fff921ccd13a919aaca52ec9c2..926ba94f3c0a729798d0bfbb9b2a8ac6f24b00ae 100644 (file)
@@ -116,7 +116,6 @@ const tokenizer = new Tokenizer(
     },
 
     ondirname(start, end) {
-      // console.log('name ' + getSlice(start, end))
       const raw = getSlice(start, end)
       const name =
         raw === '.' || raw === ':'
@@ -135,8 +134,8 @@ const tokenizer = new Tokenizer(
         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 = {
@@ -158,19 +157,28 @@ const tokenizer = new Tokenizer(
       if (currentAttrStartIndex < 0) currentAttrStartIndex = start
       currentAttrEndIndex = end
     },
+
     onattribentity(codepoint) {
       currentAttrValue += fromCodePoint(codepoint)
     },
+
+    onattribnameend(end) {
+      // check duplicate attrs
+      const start = currentProp!.loc.start.offset
+      const name = getSlice(start, end)
+      if (currentAttrs.has(name)) {
+        currentProp = null
+        // TODO emit error DUPLICATE_ATTRIBUTE
+        throw new Error(`duplicate attr ${name}`)
+      } else {
+        currentAttrs.add(name)
+      }
+    },
+
     onattribend(quote, end) {
-      // TODO check duplicate
-      // if (currentAttrs.has(name)) {
-      //   // emit error DUPLICATE_ATTRIBUTE
-      // } else {
-      //   currentAttrs.add(name)
-      // }
-      if (currentElement) {
+      if (currentElement && currentProp) {
         if (currentAttrValue) {
-          if (currentProp!.type === NodeTypes.ATTRIBUTE) {
+          if (currentProp.type === NodeTypes.ATTRIBUTE) {
             // assign value
             currentProp!.value = {
               type: NodeTypes.TEXT,
@@ -182,7 +190,7 @@ const tokenizer = new Tokenizer(
             }
           } else {
             // directive
-            currentProp!.exp = {
+            currentProp.exp = {
               type: NodeTypes.SIMPLE_EXPRESSION,
               content: currentAttrValue,
               isStatic: false,
@@ -194,7 +202,7 @@ const tokenizer = new Tokenizer(
             }
           }
         }
-        currentProp!.loc.end = tokenizer.getPositionForIndex(end)
+        currentProp.loc.end = tokenizer.getPos(end)
         currentElement.props.push(currentProp!)
       }
       currentAttrValue = ''
@@ -232,7 +240,7 @@ function emitOpenTag(name: string, start: number) {
     props: [],
     children: [],
     loc: {
-      start: tokenizer.getPositionForIndex(start - 1),
+      start: tokenizer.getPos(start - 1),
       // @ts-expect-error to be attached on tag close
       end: undefined,
       source: ''
@@ -273,8 +281,8 @@ function onText(content: string, start: number, end: number) {
       type: NodeTypes.TEXT,
       content,
       loc: {
-        start: tokenizer.getPositionForIndex(start),
-        end: tokenizer.getPositionForIndex(end),
+        start: tokenizer.getPos(start),
+        end: tokenizer.getPos(end),
         source: content
       }
     })
@@ -287,7 +295,7 @@ function onCloseTag(el: ElementNode, end: number) {
   while (currentInput.charCodeAt(end + offset) !== CharCodes.Gt) {
     offset++
   }
-  el.loc.end = tokenizer.getPositionForIndex(end + offset + 1)
+  el.loc.end = tokenizer.getPos(end + offset + 1)
   // whitepsace management
   el.children = condenseWhitespace(el.children)
 }
@@ -388,9 +396,9 @@ function getParent() {
 
 function getLoc(start: number, end?: number): SourceLocation {
   return {
-    start: tokenizer.getPositionForIndex(start),
+    start: tokenizer.getPos(start),
     // @ts-expect-error allow late attachment
-    end: end && tokenizer.getPositionForIndex(end)
+    end: end && tokenizer.getPos(end)
   }
 }
 
@@ -411,6 +419,7 @@ export function baseParse(input: string, options?: ParserOptions): RootNode {
   currentOptions = extend({}, defaultParserOptions, options)
   const root = (currentRoot = createRoot([]))
   tokenizer.parse(currentInput)
+  root.loc.end = tokenizer.getPos(input.length)
   root.children = condenseWhitespace(root.children)
   return root
 }