diff --git a/src/html/parser.ts b/src/html/parser.ts index bc94e25b..91f9c286 100644 --- a/src/html/parser.ts +++ b/src/html/parser.ts @@ -151,6 +151,7 @@ export class Parser { private parserOptions: any private document: VDocumentFragment private elementStack: VElement[] + private vPreElement: VElement | null /** * The source code text. @@ -211,6 +212,13 @@ export class Parser { return last(this.elementStack) || this.document } + /** + * Check if the current location is in a v-pre element. + */ + private get isInVPreElement(): boolean { + return this.vPreElement != null + } + /** * Initialize this parser. * @param tokenizer The tokenizer to parse. @@ -237,6 +245,7 @@ export class Parser { errors: this.errors, } this.elementStack = [] + this.vPreElement = null } /** @@ -277,7 +286,7 @@ export class Parser { private popElementStack(): void { assert(this.elementStack.length >= 1) - const element = this.elementStack.pop() as VElement + const element = this.elementStack.pop()! propagateEndLocation(element) // Update the current namespace. @@ -285,6 +294,12 @@ export class Parser { this.namespace = current.type === "VElement" ? current.namespace : NS.HTML + // Update v-pre state. + if (this.vPreElement === element) { + this.vPreElement = null + this.expressionEnabled = true + } + // Update expression flag. if (this.elementStack.length === 0) { this.expressionEnabled = false @@ -384,9 +399,11 @@ export class Parser { const attrName = node.key.name if ( - DIRECTIVE_NAME.test(attrName) || - attrName === "slot-scope" || - (tagName === "template" && attrName === "scope") + (this.expressionEnabled || + (attrName === "v-pre" && !this.isInVPreElement)) && + (DIRECTIVE_NAME.test(attrName) || + attrName === "slot-scope" || + (tagName === "template" && attrName === "scope")) ) { convertToDirective( this.text, @@ -442,6 +459,14 @@ export class Parser { endTag: null, variables: [], } + const hasVPre = + !this.isInVPreElement && + token.attributes.some(a => a.key.name === "v-pre") + + // Disable expression if v-pre + if (hasVPre) { + this.expressionEnabled = false + } // Setup relations. parent.children.push(element) @@ -470,11 +495,16 @@ export class Parser { // Vue.js supports self-closing elements even if it's not one of void elements. if (token.selfClosing || isVoid) { + this.expressionEnabled = !this.isInVPreElement return } // Push to stack. this.elementStack.push(element) + if (hasVPre) { + assert(this.vPreElement === null) + this.vPreElement = element + } this.namespace = namespace // Update the content type of this element. diff --git a/test/fixtures/ast/v-pre/ast.json b/test/fixtures/ast/v-pre/ast.json new file mode 100644 index 00000000..9d4c399f --- /dev/null +++ b/test/fixtures/ast/v-pre/ast.json @@ -0,0 +1,1245 @@ +{ + "type": "Program", + "start": 0, + "end": 0, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 0 + } + }, + "range": [ + 0, + 0 + ], + "body": [], + "sourceType": "script", + "comments": [], + "tokens": [], + "templateBody": { + "type": "VElement", + "range": [ + 0, + 128 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 8, + "column": 11 + } + }, + "name": "template", + "rawName": "template", + "namespace": "http://www.w3.org/1999/xhtml", + "startTag": { + "type": "VStartTag", + "range": [ + 0, + 10 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 10 + } + }, + "selfClosing": false, + "attributes": [] + }, + "children": [ + { + "type": "VText", + "range": [ + 10, + 15 + ], + "loc": { + "start": { + "line": 1, + "column": 10 + }, + "end": { + "line": 2, + "column": 4 + } + }, + "value": "\n " + }, + { + "type": "VExpressionContainer", + "range": [ + 15, + 20 + ], + "loc": { + "start": { + "line": 2, + "column": 4 + }, + "end": { + "line": 2, + "column": 9 + } + }, + "expression": { + "type": "Identifier", + "start": 17, + "end": 18, + "loc": { + "start": { + "line": 2, + "column": 6 + }, + "end": { + "line": 2, + "column": 7 + } + }, + "range": [ + 17, + 18 + ], + "name": "a" + }, + "references": [ + { + "id": { + "type": "Identifier", + "start": 17, + "end": 18, + "loc": { + "start": { + "line": 2, + "column": 6 + }, + "end": { + "line": 2, + "column": 7 + } + }, + "range": [ + 17, + 18 + ], + "name": "a" + }, + "mode": "r" + } + ] + }, + { + "type": "VText", + "range": [ + 20, + 25 + ], + "loc": { + "start": { + "line": 2, + "column": 9 + }, + "end": { + "line": 3, + "column": 4 + } + }, + "value": "\n " + }, + { + "type": "VElement", + "range": [ + 25, + 106 + ], + "loc": { + "start": { + "line": 3, + "column": 4 + }, + "end": { + "line": 6, + "column": 10 + } + }, + "name": "div", + "rawName": "div", + "namespace": "http://www.w3.org/1999/xhtml", + "startTag": { + "type": "VStartTag", + "range": [ + 25, + 46 + ], + "loc": { + "start": { + "line": 3, + "column": 4 + }, + "end": { + "line": 3, + "column": 25 + } + }, + "selfClosing": false, + "attributes": [ + { + "type": "VAttribute", + "range": [ + 30, + 39 + ], + "loc": { + "start": { + "line": 3, + "column": 9 + }, + "end": { + "line": 3, + "column": 18 + } + }, + "directive": false, + "key": { + "type": "VIdentifier", + "range": [ + 30, + 35 + ], + "loc": { + "start": { + "line": 3, + "column": 9 + }, + "end": { + "line": 3, + "column": 14 + } + }, + "name": ":attr", + "rawName": ":attr" + }, + "value": { + "type": "VLiteral", + "range": [ + 36, + 39 + ], + "loc": { + "start": { + "line": 3, + "column": 15 + }, + "end": { + "line": 3, + "column": 18 + } + }, + "value": "a" + } + }, + { + "type": "VAttribute", + "range": [ + 40, + 45 + ], + "loc": { + "start": { + "line": 3, + "column": 19 + }, + "end": { + "line": 3, + "column": 24 + } + }, + "directive": true, + "key": { + "type": "VDirectiveKey", + "range": [ + 40, + 45 + ], + "loc": { + "start": { + "line": 3, + "column": 19 + }, + "end": { + "line": 3, + "column": 24 + } + }, + "name": "pre", + "argument": null, + "modifiers": [], + "shorthand": false, + "raw": { + "name": "pre", + "argument": null, + "modifiers": [] + } + }, + "value": null + } + ] + }, + "children": [ + { + "type": "VText", + "range": [ + 46, + 69 + ], + "loc": { + "start": { + "line": 3, + "column": 25 + }, + "end": { + "line": 5, + "column": 8 + } + }, + "value": "\n {{a}}\n " + }, + { + "type": "VElement", + "range": [ + 69, + 95 + ], + "loc": { + "start": { + "line": 5, + "column": 8 + }, + "end": { + "line": 5, + "column": 34 + } + }, + "name": "div", + "rawName": "div", + "namespace": "http://www.w3.org/1999/xhtml", + "startTag": { + "type": "VStartTag", + "range": [ + 69, + 84 + ], + "loc": { + "start": { + "line": 5, + "column": 8 + }, + "end": { + "line": 5, + "column": 23 + } + }, + "selfClosing": false, + "attributes": [ + { + "type": "VAttribute", + "range": [ + 74, + 83 + ], + "loc": { + "start": { + "line": 5, + "column": 13 + }, + "end": { + "line": 5, + "column": 22 + } + }, + "directive": false, + "key": { + "type": "VIdentifier", + "range": [ + 74, + 79 + ], + "loc": { + "start": { + "line": 5, + "column": 13 + }, + "end": { + "line": 5, + "column": 18 + } + }, + "name": ":attr", + "rawName": ":attr" + }, + "value": { + "type": "VLiteral", + "range": [ + 80, + 83 + ], + "loc": { + "start": { + "line": 5, + "column": 19 + }, + "end": { + "line": 5, + "column": 22 + } + }, + "value": "a" + } + } + ] + }, + "children": [ + { + "type": "VText", + "range": [ + 84, + 89 + ], + "loc": { + "start": { + "line": 5, + "column": 23 + }, + "end": { + "line": 5, + "column": 28 + } + }, + "value": "{{a}}" + } + ], + "endTag": { + "type": "VEndTag", + "range": [ + 89, + 95 + ], + "loc": { + "start": { + "line": 5, + "column": 28 + }, + "end": { + "line": 5, + "column": 34 + } + } + }, + "variables": [] + }, + { + "type": "VText", + "range": [ + 95, + 100 + ], + "loc": { + "start": { + "line": 5, + "column": 34 + }, + "end": { + "line": 6, + "column": 4 + } + }, + "value": "\n " + } + ], + "endTag": { + "type": "VEndTag", + "range": [ + 100, + 106 + ], + "loc": { + "start": { + "line": 6, + "column": 4 + }, + "end": { + "line": 6, + "column": 10 + } + } + }, + "variables": [] + }, + { + "type": "VText", + "range": [ + 106, + 111 + ], + "loc": { + "start": { + "line": 6, + "column": 10 + }, + "end": { + "line": 7, + "column": 4 + } + }, + "value": "\n " + }, + { + "type": "VExpressionContainer", + "range": [ + 111, + 116 + ], + "loc": { + "start": { + "line": 7, + "column": 4 + }, + "end": { + "line": 7, + "column": 9 + } + }, + "expression": { + "type": "Identifier", + "start": 113, + "end": 114, + "loc": { + "start": { + "line": 7, + "column": 6 + }, + "end": { + "line": 7, + "column": 7 + } + }, + "range": [ + 113, + 114 + ], + "name": "a" + }, + "references": [ + { + "id": { + "type": "Identifier", + "start": 113, + "end": 114, + "loc": { + "start": { + "line": 7, + "column": 6 + }, + "end": { + "line": 7, + "column": 7 + } + }, + "range": [ + 113, + 114 + ], + "name": "a" + }, + "mode": "r" + } + ] + }, + { + "type": "VText", + "range": [ + 116, + 117 + ], + "loc": { + "start": { + "line": 7, + "column": 9 + }, + "end": { + "line": 8, + "column": 0 + } + }, + "value": "\n" + } + ], + "endTag": { + "type": "VEndTag", + "range": [ + 117, + 128 + ], + "loc": { + "start": { + "line": 8, + "column": 0 + }, + "end": { + "line": 8, + "column": 11 + } + } + }, + "variables": [], + "tokens": [ + { + "type": "HTMLTagOpen", + "range": [ + 0, + 9 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 9 + } + }, + "value": "template" + }, + { + "type": "HTMLTagClose", + "range": [ + 9, + 10 + ], + "loc": { + "start": { + "line": 1, + "column": 9 + }, + "end": { + "line": 1, + "column": 10 + } + }, + "value": "" + }, + { + "type": "HTMLWhitespace", + "range": [ + 10, + 15 + ], + "loc": { + "start": { + "line": 1, + "column": 10 + }, + "end": { + "line": 2, + "column": 4 + } + }, + "value": "\n " + }, + { + "type": "VExpressionStart", + "range": [ + 15, + 17 + ], + "loc": { + "start": { + "line": 2, + "column": 4 + }, + "end": { + "line": 2, + "column": 6 + } + }, + "value": "{{" + }, + { + "type": "Identifier", + "value": "a", + "start": 17, + "end": 18, + "loc": { + "start": { + "line": 2, + "column": 6 + }, + "end": { + "line": 2, + "column": 7 + } + }, + "range": [ + 17, + 18 + ] + }, + { + "type": "VExpressionEnd", + "range": [ + 18, + 20 + ], + "loc": { + "start": { + "line": 2, + "column": 7 + }, + "end": { + "line": 2, + "column": 9 + } + }, + "value": "}}" + }, + { + "type": "HTMLWhitespace", + "range": [ + 20, + 25 + ], + "loc": { + "start": { + "line": 2, + "column": 9 + }, + "end": { + "line": 3, + "column": 4 + } + }, + "value": "\n " + }, + { + "type": "HTMLTagOpen", + "range": [ + 25, + 29 + ], + "loc": { + "start": { + "line": 3, + "column": 4 + }, + "end": { + "line": 3, + "column": 8 + } + }, + "value": "div" + }, + { + "type": "HTMLIdentifier", + "range": [ + 30, + 35 + ], + "loc": { + "start": { + "line": 3, + "column": 9 + }, + "end": { + "line": 3, + "column": 14 + } + }, + "value": ":attr" + }, + { + "type": "HTMLAssociation", + "range": [ + 35, + 36 + ], + "loc": { + "start": { + "line": 3, + "column": 14 + }, + "end": { + "line": 3, + "column": 15 + } + }, + "value": "" + }, + { + "type": "HTMLLiteral", + "range": [ + 36, + 39 + ], + "loc": { + "start": { + "line": 3, + "column": 15 + }, + "end": { + "line": 3, + "column": 18 + } + }, + "value": "a" + }, + { + "type": "HTMLIdentifier", + "range": [ + 40, + 45 + ], + "loc": { + "start": { + "line": 3, + "column": 19 + }, + "end": { + "line": 3, + "column": 24 + } + }, + "value": "v-pre" + }, + { + "type": "HTMLTagClose", + "range": [ + 45, + 46 + ], + "loc": { + "start": { + "line": 3, + "column": 24 + }, + "end": { + "line": 3, + "column": 25 + } + }, + "value": "" + }, + { + "type": "HTMLWhitespace", + "range": [ + 46, + 55 + ], + "loc": { + "start": { + "line": 3, + "column": 25 + }, + "end": { + "line": 4, + "column": 8 + } + }, + "value": "\n " + }, + { + "type": "HTMLText", + "range": [ + 55, + 60 + ], + "loc": { + "start": { + "line": 4, + "column": 8 + }, + "end": { + "line": 4, + "column": 13 + } + }, + "value": "{{a}}" + }, + { + "type": "HTMLWhitespace", + "range": [ + 60, + 69 + ], + "loc": { + "start": { + "line": 4, + "column": 13 + }, + "end": { + "line": 5, + "column": 8 + } + }, + "value": "\n " + }, + { + "type": "HTMLTagOpen", + "range": [ + 69, + 73 + ], + "loc": { + "start": { + "line": 5, + "column": 8 + }, + "end": { + "line": 5, + "column": 12 + } + }, + "value": "div" + }, + { + "type": "HTMLIdentifier", + "range": [ + 74, + 79 + ], + "loc": { + "start": { + "line": 5, + "column": 13 + }, + "end": { + "line": 5, + "column": 18 + } + }, + "value": ":attr" + }, + { + "type": "HTMLAssociation", + "range": [ + 79, + 80 + ], + "loc": { + "start": { + "line": 5, + "column": 18 + }, + "end": { + "line": 5, + "column": 19 + } + }, + "value": "" + }, + { + "type": "HTMLLiteral", + "range": [ + 80, + 83 + ], + "loc": { + "start": { + "line": 5, + "column": 19 + }, + "end": { + "line": 5, + "column": 22 + } + }, + "value": "a" + }, + { + "type": "HTMLTagClose", + "range": [ + 83, + 84 + ], + "loc": { + "start": { + "line": 5, + "column": 22 + }, + "end": { + "line": 5, + "column": 23 + } + }, + "value": "" + }, + { + "type": "HTMLText", + "range": [ + 84, + 89 + ], + "loc": { + "start": { + "line": 5, + "column": 23 + }, + "end": { + "line": 5, + "column": 28 + } + }, + "value": "{{a}}" + }, + { + "type": "HTMLEndTagOpen", + "range": [ + 89, + 94 + ], + "loc": { + "start": { + "line": 5, + "column": 28 + }, + "end": { + "line": 5, + "column": 33 + } + }, + "value": "div" + }, + { + "type": "HTMLTagClose", + "range": [ + 94, + 95 + ], + "loc": { + "start": { + "line": 5, + "column": 33 + }, + "end": { + "line": 5, + "column": 34 + } + }, + "value": "" + }, + { + "type": "HTMLWhitespace", + "range": [ + 95, + 100 + ], + "loc": { + "start": { + "line": 5, + "column": 34 + }, + "end": { + "line": 6, + "column": 4 + } + }, + "value": "\n " + }, + { + "type": "HTMLEndTagOpen", + "range": [ + 100, + 105 + ], + "loc": { + "start": { + "line": 6, + "column": 4 + }, + "end": { + "line": 6, + "column": 9 + } + }, + "value": "div" + }, + { + "type": "HTMLTagClose", + "range": [ + 105, + 106 + ], + "loc": { + "start": { + "line": 6, + "column": 9 + }, + "end": { + "line": 6, + "column": 10 + } + }, + "value": "" + }, + { + "type": "HTMLWhitespace", + "range": [ + 106, + 111 + ], + "loc": { + "start": { + "line": 6, + "column": 10 + }, + "end": { + "line": 7, + "column": 4 + } + }, + "value": "\n " + }, + { + "type": "VExpressionStart", + "range": [ + 111, + 113 + ], + "loc": { + "start": { + "line": 7, + "column": 4 + }, + "end": { + "line": 7, + "column": 6 + } + }, + "value": "{{" + }, + { + "type": "Identifier", + "value": "a", + "start": 113, + "end": 114, + "loc": { + "start": { + "line": 7, + "column": 6 + }, + "end": { + "line": 7, + "column": 7 + } + }, + "range": [ + 113, + 114 + ] + }, + { + "type": "VExpressionEnd", + "range": [ + 114, + 116 + ], + "loc": { + "start": { + "line": 7, + "column": 7 + }, + "end": { + "line": 7, + "column": 9 + } + }, + "value": "}}" + }, + { + "type": "HTMLWhitespace", + "range": [ + 116, + 117 + ], + "loc": { + "start": { + "line": 7, + "column": 9 + }, + "end": { + "line": 8, + "column": 0 + } + }, + "value": "\n" + }, + { + "type": "HTMLEndTagOpen", + "range": [ + 117, + 127 + ], + "loc": { + "start": { + "line": 8, + "column": 0 + }, + "end": { + "line": 8, + "column": 10 + } + }, + "value": "template" + }, + { + "type": "HTMLTagClose", + "range": [ + 127, + 128 + ], + "loc": { + "start": { + "line": 8, + "column": 10 + }, + "end": { + "line": 8, + "column": 11 + } + }, + "value": "" + }, + { + "type": "HTMLWhitespace", + "range": [ + 128, + 129 + ], + "loc": { + "start": { + "line": 8, + "column": 11 + }, + "end": { + "line": 9, + "column": 0 + } + }, + "value": "\n" + } + ], + "comments": [], + "errors": [] + } +} \ No newline at end of file diff --git a/test/fixtures/ast/v-pre/source.vue b/test/fixtures/ast/v-pre/source.vue new file mode 100644 index 00000000..64ee40a7 --- /dev/null +++ b/test/fixtures/ast/v-pre/source.vue @@ -0,0 +1,8 @@ + diff --git a/test/fixtures/ast/v-pre/token-ranges.json b/test/fixtures/ast/v-pre/token-ranges.json new file mode 100644 index 00000000..cb63475b --- /dev/null +++ b/test/fixtures/ast/v-pre/token-ranges.json @@ -0,0 +1,37 @@ +[ + "", + "\n ", + "{{", + "a", + "}}", + "\n ", + "", + "\n ", + "{{a}}", + "\n ", + "", + "{{a}}", + "", + "\n ", + "", + "\n ", + "{{", + "a", + "}}", + "\n", + "", + "\n" +] \ No newline at end of file diff --git a/test/fixtures/ast/v-pre/tree.json b/test/fixtures/ast/v-pre/tree.json new file mode 100644 index 00000000..f38fe443 --- /dev/null +++ b/test/fixtures/ast/v-pre/tree.json @@ -0,0 +1,152 @@ +[ + { + "type": "VElement", + "text": "", + "children": [ + { + "type": "VStartTag", + "text": "", + "children": [] + } + ] + } +] \ No newline at end of file