From 65a673a23fdaa36b7fd76163ba8ca187f5e60379 Mon Sep 17 00:00:00 2001 From: BJ Warshaw Date: Tue, 27 Jun 2017 04:52:02 -0400 Subject: [PATCH 001/345] Fix: avoid parse errors with foreign attributes (#9) Foreign attributes have prefix (e.g. `xlink:href`) or can be camelCased (e.g. viewBox). This updates `createVStartTag` to handle these variations by correctly finding their locations within the node hash. Fixes #8. --- lib/transform-html.js | 4 ++-- test/fixtures/svg-attrs-camel-case.vue | 9 +++++++++ test/fixtures/svg-attrs-colon.vue | 9 +++++++++ test/index.js | 28 ++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 test/fixtures/svg-attrs-camel-case.vue create mode 100644 test/fixtures/svg-attrs-colon.vue diff --git a/lib/transform-html.js b/lib/transform-html.js index 527feb45..922eb2ef 100644 --- a/lib/transform-html.js +++ b/lib/transform-html.js @@ -524,9 +524,9 @@ class HTMLTransformer { result.id = this.createVIdentifier(result, start + 1) for (const attr of attrs) { - const name = attr.name + const name = attr.prefix ? `${attr.prefix}:${attr.name}` : attr.name const value = attr.value - const attrLoc = attrLocs[name] + const attrLoc = attrLocs[name.toLowerCase()] const attribute = this.createVAttribute(result, name, value, attrLoc) diff --git a/test/fixtures/svg-attrs-camel-case.vue b/test/fixtures/svg-attrs-camel-case.vue new file mode 100644 index 00000000..175d69bc --- /dev/null +++ b/test/fixtures/svg-attrs-camel-case.vue @@ -0,0 +1,9 @@ + + + diff --git a/test/fixtures/svg-attrs-colon.vue b/test/fixtures/svg-attrs-colon.vue new file mode 100644 index 00000000..73ca3332 --- /dev/null +++ b/test/fixtures/svg-attrs-colon.vue @@ -0,0 +1,9 @@ + + + diff --git a/test/index.js b/test/index.js index 19941ea2..27090cfe 100644 --- a/test/index.js +++ b/test/index.js @@ -328,4 +328,32 @@ describe("Basic tests", () => { assert(actual === expected) }) }) + + describe("About fixtures/svg-attrs.vue", () => { + it("parses attributes with colons", () => { + const cli = new CLIEngine({ + cwd: FIXTURE_DIR, + envs: ["es6", "node"], + parser: PARSER_PATH, + useEslintrc: false, + }) + const report = cli.executeOnFiles(["svg-attrs-colon.vue"]) + const messages = report.results[0].messages + + assert(messages.length === 0) + }) + + it("parses camelCased attributes", () => { + const cli = new CLIEngine({ + cwd: FIXTURE_DIR, + envs: ["es6", "node"], + parser: PARSER_PATH, + useEslintrc: false, + }) + const report = cli.executeOnFiles(["svg-attrs-camel-case.vue"]) + const messages = report.results[0].messages + + assert(messages.length === 0) + }) + }) }) From 6fbd273dc90b958718eb307899f7fab90c015531 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Tue, 27 Jun 2017 18:03:12 +0900 Subject: [PATCH 002/345] Chore: add some tests --- .../svg-attrs-camel-case.ast.json | 1154 +++++++++++++++++ .../svg-attrs-camel-case.source.vue | 9 + .../svg-attrs-camel-case.tokens.json | 102 ++ .../svg-attrs-camel-case.traversal.json | 222 ++++ .../template-ast/svg-attrs-colon.ast.json | 1154 +++++++++++++++++ .../template-ast/svg-attrs-colon.source.vue | 9 + .../template-ast/svg-attrs-colon.tokens.json | 102 ++ .../svg-attrs-colon.traversal.json | 222 ++++ test/tools/update-template-ast.js | 2 +- 9 files changed, 2975 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/template-ast/svg-attrs-camel-case.ast.json create mode 100644 test/fixtures/template-ast/svg-attrs-camel-case.source.vue create mode 100644 test/fixtures/template-ast/svg-attrs-camel-case.tokens.json create mode 100644 test/fixtures/template-ast/svg-attrs-camel-case.traversal.json create mode 100644 test/fixtures/template-ast/svg-attrs-colon.ast.json create mode 100644 test/fixtures/template-ast/svg-attrs-colon.source.vue create mode 100644 test/fixtures/template-ast/svg-attrs-colon.tokens.json create mode 100644 test/fixtures/template-ast/svg-attrs-colon.traversal.json diff --git a/test/fixtures/template-ast/svg-attrs-camel-case.ast.json b/test/fixtures/template-ast/svg-attrs-camel-case.ast.json new file mode 100644 index 00000000..0f72f495 --- /dev/null +++ b/test/fixtures/template-ast/svg-attrs-camel-case.ast.json @@ -0,0 +1,1154 @@ +{ + "type": "Program", + "end": 114, + "loc": { + "start": { + "line": 8, + "column": 0 + }, + "end": { + "line": 8, + "column": 19 + } + }, + "range": [ + 94, + 113 + ], + "body": [ + { + "type": "ExpressionStatement", + "start": 94, + "end": 113, + "loc": { + "start": { + "line": 8, + "column": 0 + }, + "end": { + "line": 8, + "column": 19 + } + }, + "range": [ + 94, + 113 + ], + "expression": { + "type": "AssignmentExpression", + "start": 94, + "end": 113, + "loc": { + "start": { + "line": 8, + "column": 0 + }, + "end": { + "line": 8, + "column": 19 + } + }, + "range": [ + 94, + 113 + ], + "operator": "=", + "left": { + "type": "MemberExpression", + "start": 94, + "end": 108, + "loc": { + "start": { + "line": 8, + "column": 0 + }, + "end": { + "line": 8, + "column": 14 + } + }, + "range": [ + 94, + 108 + ], + "object": { + "type": "Identifier", + "start": 94, + "end": 100, + "loc": { + "start": { + "line": 8, + "column": 0 + }, + "end": { + "line": 8, + "column": 6 + } + }, + "range": [ + 94, + 100 + ], + "name": "module" + }, + "property": { + "type": "Identifier", + "start": 101, + "end": 108, + "loc": { + "start": { + "line": 8, + "column": 7 + }, + "end": { + "line": 8, + "column": 14 + } + }, + "range": [ + 101, + 108 + ], + "name": "exports" + }, + "computed": false + }, + "right": { + "type": "ObjectExpression", + "start": 111, + "end": 113, + "loc": { + "start": { + "line": 8, + "column": 17 + }, + "end": { + "line": 8, + "column": 19 + } + }, + "range": [ + 111, + 113 + ], + "properties": [] + } + } + } + ], + "sourceType": "script", + "comments": [], + "tokens": [ + { + "type": "Punctuator", + "range": [ + 85, + 93 + ], + "loc": { + "start": { + "line": 7, + "column": 0 + }, + "end": { + "line": 7, + "column": 8 + } + }, + "value": "" + } + ], + "templateBody": { + "range": [ + 0, + 83 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 5, + "column": 11 + } + }, + "type": "VElement", + "startTag": { + "type": "VStartTag", + "range": [ + 0, + 10 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 10 + } + }, + "id": { + "type": "VIdentifier", + "range": [ + 1, + 9 + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 9 + } + }, + "name": "template" + }, + "attributes": [], + "selfClosing": false + }, + "children": [ + { + "type": "VText", + "range": [ + 10, + 15 + ], + "loc": { + "start": { + "line": 1, + "column": 10 + }, + "end": { + "line": 2, + "column": 4 + } + }, + "value": "\n " + }, + { + "range": [ + 15, + 71 + ], + "loc": { + "start": { + "line": 2, + "column": 4 + }, + "end": { + "line": 4, + "column": 10 + } + }, + "type": "VElement", + "startTag": { + "type": "VStartTag", + "range": [ + 15, + 20 + ], + "loc": { + "start": { + "line": 2, + "column": 4 + }, + "end": { + "line": 2, + "column": 9 + } + }, + "id": { + "type": "VIdentifier", + "range": [ + 16, + 19 + ], + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 2, + "column": 8 + } + }, + "name": "div" + }, + "attributes": [], + "selfClosing": false + }, + "children": [ + { + "type": "VText", + "range": [ + 20, + 29 + ], + "loc": { + "start": { + "line": 2, + "column": 9 + }, + "end": { + "line": 3, + "column": 8 + } + }, + "value": "\n " + }, + { + "range": [ + 29, + 60 + ], + "loc": { + "start": { + "line": 3, + "column": 8 + }, + "end": { + "line": 3, + "column": 39 + } + }, + "type": "VElement", + "startTag": { + "type": "VStartTag", + "range": [ + 29, + 54 + ], + "loc": { + "start": { + "line": 3, + "column": 8 + }, + "end": { + "line": 3, + "column": 33 + } + }, + "id": { + "type": "VIdentifier", + "range": [ + 30, + 33 + ], + "loc": { + "start": { + "line": 3, + "column": 9 + }, + "end": { + "line": 3, + "column": 12 + } + }, + "name": "svg" + }, + "attributes": [ + { + "type": "VAttribute", + "range": [ + 34, + 53 + ], + "loc": { + "start": { + "line": 3, + "column": 13 + }, + "end": { + "line": 3, + "column": 32 + } + }, + "directive": false, + "key": { + "type": "VIdentifier", + "range": [ + 34, + 41 + ], + "loc": { + "start": { + "line": 3, + "column": 13 + }, + "end": { + "line": 3, + "column": 20 + } + }, + "name": "viewBox" + }, + "value": { + "type": "VAttributeValue", + "range": [ + 42, + 53 + ], + "loc": { + "start": { + "line": 3, + "column": 21 + }, + "end": { + "line": 3, + "column": 32 + } + }, + "value": "0 0 40 40" + } + } + ], + "selfClosing": false + }, + "children": [], + "endTag": { + "type": "VEndTag", + "range": [ + 54, + 60 + ], + "loc": { + "start": { + "line": 3, + "column": 33 + }, + "end": { + "line": 3, + "column": 39 + } + }, + "id": { + "type": "VIdentifier", + "range": [ + 56, + 59 + ], + "loc": { + "start": { + "line": 3, + "column": 35 + }, + "end": { + "line": 3, + "column": 38 + } + }, + "name": "svg" + } + }, + "variables": [] + }, + { + "type": "VText", + "range": [ + 60, + 65 + ], + "loc": { + "start": { + "line": 3, + "column": 39 + }, + "end": { + "line": 4, + "column": 4 + } + }, + "value": "\n " + } + ], + "endTag": { + "type": "VEndTag", + "range": [ + 65, + 71 + ], + "loc": { + "start": { + "line": 4, + "column": 4 + }, + "end": { + "line": 4, + "column": 10 + } + }, + "id": { + "type": "VIdentifier", + "range": [ + 67, + 70 + ], + "loc": { + "start": { + "line": 4, + "column": 6 + }, + "end": { + "line": 4, + "column": 9 + } + }, + "name": "div" + } + }, + "variables": [] + }, + { + "type": "VText", + "range": [ + 71, + 72 + ], + "loc": { + "start": { + "line": 4, + "column": 10 + }, + "end": { + "line": 5, + "column": 0 + } + }, + "value": "\n" + } + ], + "endTag": { + "type": "VEndTag", + "range": [ + 72, + 83 + ], + "loc": { + "start": { + "line": 5, + "column": 0 + }, + "end": { + "line": 5, + "column": 11 + } + }, + "id": { + "type": "VIdentifier", + "range": [ + 74, + 82 + ], + "loc": { + "start": { + "line": 5, + "column": 2 + }, + "end": { + "line": 5, + "column": 10 + } + }, + "name": "template" + } + }, + "variables": [], + "tokens": [ + { + "type": "Punctuator", + "range": [ + 0, + 1 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 1 + } + }, + "value": "<" + }, + { + "type": "VIdentifier", + "range": [ + 1, + 9 + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 9 + } + }, + "value": "template" + }, + { + "type": "Punctuator", + "range": [ + 9, + 10 + ], + "loc": { + "start": { + "line": 1, + "column": 9 + }, + "end": { + "line": 1, + "column": 10 + } + }, + "value": ">" + }, + { + "type": "VText", + "range": [ + 10, + 15 + ], + "loc": { + "start": { + "line": 1, + "column": 10 + }, + "end": { + "line": 2, + "column": 4 + } + }, + "value": "\n " + }, + { + "type": "Punctuator", + "range": [ + 15, + 16 + ], + "loc": { + "start": { + "line": 2, + "column": 4 + }, + "end": { + "line": 2, + "column": 5 + } + }, + "value": "<" + }, + { + "type": "VIdentifier", + "range": [ + 16, + 19 + ], + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 2, + "column": 8 + } + }, + "value": "div" + }, + { + "type": "Punctuator", + "range": [ + 19, + 20 + ], + "loc": { + "start": { + "line": 2, + "column": 8 + }, + "end": { + "line": 2, + "column": 9 + } + }, + "value": ">" + }, + { + "type": "VText", + "range": [ + 20, + 29 + ], + "loc": { + "start": { + "line": 2, + "column": 9 + }, + "end": { + "line": 3, + "column": 8 + } + }, + "value": "\n " + }, + { + "type": "Punctuator", + "range": [ + 29, + 30 + ], + "loc": { + "start": { + "line": 3, + "column": 8 + }, + "end": { + "line": 3, + "column": 9 + } + }, + "value": "<" + }, + { + "type": "VIdentifier", + "range": [ + 30, + 33 + ], + "loc": { + "start": { + "line": 3, + "column": 9 + }, + "end": { + "line": 3, + "column": 12 + } + }, + "value": "svg" + }, + { + "type": "VIdentifier", + "range": [ + 34, + 41 + ], + "loc": { + "start": { + "line": 3, + "column": 13 + }, + "end": { + "line": 3, + "column": 20 + } + }, + "value": "viewBox" + }, + { + "type": "Punctuator", + "range": [ + 41, + 42 + ], + "loc": { + "start": { + "line": 3, + "column": 20 + }, + "end": { + "line": 3, + "column": 21 + } + }, + "value": "=" + }, + { + "type": "VAttributeValue", + "range": [ + 42, + 53 + ], + "loc": { + "start": { + "line": 3, + "column": 21 + }, + "end": { + "line": 3, + "column": 32 + } + }, + "value": "\"0 0 40 40\"" + }, + { + "type": "Punctuator", + "range": [ + 53, + 54 + ], + "loc": { + "start": { + "line": 3, + "column": 32 + }, + "end": { + "line": 3, + "column": 33 + } + }, + "value": ">" + }, + { + "type": "Punctuator", + "range": [ + 54, + 56 + ], + "loc": { + "start": { + "line": 3, + "column": 33 + }, + "end": { + "line": 3, + "column": 35 + } + }, + "value": "" + }, + { + "type": "VText", + "range": [ + 60, + 65 + ], + "loc": { + "start": { + "line": 3, + "column": 39 + }, + "end": { + "line": 4, + "column": 4 + } + }, + "value": "\n " + }, + { + "type": "Punctuator", + "range": [ + 65, + 67 + ], + "loc": { + "start": { + "line": 4, + "column": 4 + }, + "end": { + "line": 4, + "column": 6 + } + }, + "value": "" + }, + { + "type": "VText", + "range": [ + 71, + 72 + ], + "loc": { + "start": { + "line": 4, + "column": 10 + }, + "end": { + "line": 5, + "column": 0 + } + }, + "value": "\n" + }, + { + "type": "Punctuator", + "range": [ + 72, + 74 + ], + "loc": { + "start": { + "line": 5, + "column": 0 + }, + "end": { + "line": 5, + "column": 2 + } + }, + "value": "" + } + ], + "comments": [] + } +} \ No newline at end of file diff --git a/test/fixtures/template-ast/svg-attrs-camel-case.source.vue b/test/fixtures/template-ast/svg-attrs-camel-case.source.vue new file mode 100644 index 00000000..175d69bc --- /dev/null +++ b/test/fixtures/template-ast/svg-attrs-camel-case.source.vue @@ -0,0 +1,9 @@ + + + diff --git a/test/fixtures/template-ast/svg-attrs-camel-case.tokens.json b/test/fixtures/template-ast/svg-attrs-camel-case.tokens.json new file mode 100644 index 00000000..a324e6a5 --- /dev/null +++ b/test/fixtures/template-ast/svg-attrs-camel-case.tokens.json @@ -0,0 +1,102 @@ +[ + [ + "Punctuator", + "<" + ], + [ + "VIdentifier", + "template" + ], + [ + "Punctuator", + ">" + ], + [ + "VText", + "\n " + ], + [ + "Punctuator", + "<" + ], + [ + "VIdentifier", + "div" + ], + [ + "Punctuator", + ">" + ], + [ + "VText", + "\n " + ], + [ + "Punctuator", + "<" + ], + [ + "VIdentifier", + "svg" + ], + [ + "VIdentifier", + "viewBox" + ], + [ + "Punctuator", + "=" + ], + [ + "VAttributeValue", + "\"0 0 40 40\"" + ], + [ + "Punctuator", + ">" + ], + [ + "Punctuator", + "" + ], + [ + "VText", + "\n " + ], + [ + "Punctuator", + "" + ], + [ + "VText", + "\n" + ], + [ + "Punctuator", + "" + ] +] \ No newline at end of file diff --git a/test/fixtures/template-ast/svg-attrs-camel-case.traversal.json b/test/fixtures/template-ast/svg-attrs-camel-case.traversal.json new file mode 100644 index 00000000..6b633381 --- /dev/null +++ b/test/fixtures/template-ast/svg-attrs-camel-case.traversal.json @@ -0,0 +1,222 @@ +[ + [ + "enter", + "VElement", + "" + ], + [ + "enter", + "VStartTag", + "" + ], + [ + "leave", + "VElement", + "" + ] +] \ No newline at end of file diff --git a/test/fixtures/template-ast/svg-attrs-colon.ast.json b/test/fixtures/template-ast/svg-attrs-colon.ast.json new file mode 100644 index 00000000..3d224c06 --- /dev/null +++ b/test/fixtures/template-ast/svg-attrs-colon.ast.json @@ -0,0 +1,1154 @@ +{ + "type": "Program", + "end": 113, + "loc": { + "start": { + "line": 8, + "column": 0 + }, + "end": { + "line": 8, + "column": 19 + } + }, + "range": [ + 93, + 112 + ], + "body": [ + { + "type": "ExpressionStatement", + "start": 93, + "end": 112, + "loc": { + "start": { + "line": 8, + "column": 0 + }, + "end": { + "line": 8, + "column": 19 + } + }, + "range": [ + 93, + 112 + ], + "expression": { + "type": "AssignmentExpression", + "start": 93, + "end": 112, + "loc": { + "start": { + "line": 8, + "column": 0 + }, + "end": { + "line": 8, + "column": 19 + } + }, + "range": [ + 93, + 112 + ], + "operator": "=", + "left": { + "type": "MemberExpression", + "start": 93, + "end": 107, + "loc": { + "start": { + "line": 8, + "column": 0 + }, + "end": { + "line": 8, + "column": 14 + } + }, + "range": [ + 93, + 107 + ], + "object": { + "type": "Identifier", + "start": 93, + "end": 99, + "loc": { + "start": { + "line": 8, + "column": 0 + }, + "end": { + "line": 8, + "column": 6 + } + }, + "range": [ + 93, + 99 + ], + "name": "module" + }, + "property": { + "type": "Identifier", + "start": 100, + "end": 107, + "loc": { + "start": { + "line": 8, + "column": 7 + }, + "end": { + "line": 8, + "column": 14 + } + }, + "range": [ + 100, + 107 + ], + "name": "exports" + }, + "computed": false + }, + "right": { + "type": "ObjectExpression", + "start": 110, + "end": 112, + "loc": { + "start": { + "line": 8, + "column": 17 + }, + "end": { + "line": 8, + "column": 19 + } + }, + "range": [ + 110, + 112 + ], + "properties": [] + } + } + } + ], + "sourceType": "script", + "comments": [], + "tokens": [ + { + "type": "Punctuator", + "range": [ + 84, + 92 + ], + "loc": { + "start": { + "line": 7, + "column": 0 + }, + "end": { + "line": 7, + "column": 8 + } + }, + "value": "" + } + ], + "templateBody": { + "range": [ + 0, + 82 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 5, + "column": 11 + } + }, + "type": "VElement", + "startTag": { + "type": "VStartTag", + "range": [ + 0, + 10 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 10 + } + }, + "id": { + "type": "VIdentifier", + "range": [ + 1, + 9 + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 9 + } + }, + "name": "template" + }, + "attributes": [], + "selfClosing": false + }, + "children": [ + { + "type": "VText", + "range": [ + 10, + 15 + ], + "loc": { + "start": { + "line": 1, + "column": 10 + }, + "end": { + "line": 2, + "column": 4 + } + }, + "value": "\n " + }, + { + "range": [ + 15, + 70 + ], + "loc": { + "start": { + "line": 2, + "column": 4 + }, + "end": { + "line": 4, + "column": 10 + } + }, + "type": "VElement", + "startTag": { + "type": "VStartTag", + "range": [ + 15, + 20 + ], + "loc": { + "start": { + "line": 2, + "column": 4 + }, + "end": { + "line": 2, + "column": 9 + } + }, + "id": { + "type": "VIdentifier", + "range": [ + 16, + 19 + ], + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 2, + "column": 8 + } + }, + "name": "svg" + }, + "attributes": [], + "selfClosing": false + }, + "children": [ + { + "type": "VText", + "range": [ + 20, + 29 + ], + "loc": { + "start": { + "line": 2, + "column": 9 + }, + "end": { + "line": 3, + "column": 8 + } + }, + "value": "\n " + }, + { + "range": [ + 29, + 59 + ], + "loc": { + "start": { + "line": 3, + "column": 8 + }, + "end": { + "line": 3, + "column": 38 + } + }, + "type": "VElement", + "startTag": { + "type": "VStartTag", + "range": [ + 29, + 53 + ], + "loc": { + "start": { + "line": 3, + "column": 8 + }, + "end": { + "line": 3, + "column": 32 + } + }, + "id": { + "type": "VIdentifier", + "range": [ + 30, + 33 + ], + "loc": { + "start": { + "line": 3, + "column": 9 + }, + "end": { + "line": 3, + "column": 12 + } + }, + "name": "use" + }, + "attributes": [ + { + "type": "VAttribute", + "range": [ + 34, + 52 + ], + "loc": { + "start": { + "line": 3, + "column": 13 + }, + "end": { + "line": 3, + "column": 31 + } + }, + "directive": false, + "key": { + "type": "VIdentifier", + "range": [ + 34, + 44 + ], + "loc": { + "start": { + "line": 3, + "column": 13 + }, + "end": { + "line": 3, + "column": 23 + } + }, + "name": "xlink:href" + }, + "value": { + "type": "VAttributeValue", + "range": [ + 45, + 52 + ], + "loc": { + "start": { + "line": 3, + "column": 24 + }, + "end": { + "line": 3, + "column": 31 + } + }, + "value": "#test" + } + } + ], + "selfClosing": false + }, + "children": [], + "endTag": { + "type": "VEndTag", + "range": [ + 53, + 59 + ], + "loc": { + "start": { + "line": 3, + "column": 32 + }, + "end": { + "line": 3, + "column": 38 + } + }, + "id": { + "type": "VIdentifier", + "range": [ + 55, + 58 + ], + "loc": { + "start": { + "line": 3, + "column": 34 + }, + "end": { + "line": 3, + "column": 37 + } + }, + "name": "use" + } + }, + "variables": [] + }, + { + "type": "VText", + "range": [ + 59, + 64 + ], + "loc": { + "start": { + "line": 3, + "column": 38 + }, + "end": { + "line": 4, + "column": 4 + } + }, + "value": "\n " + } + ], + "endTag": { + "type": "VEndTag", + "range": [ + 64, + 70 + ], + "loc": { + "start": { + "line": 4, + "column": 4 + }, + "end": { + "line": 4, + "column": 10 + } + }, + "id": { + "type": "VIdentifier", + "range": [ + 66, + 69 + ], + "loc": { + "start": { + "line": 4, + "column": 6 + }, + "end": { + "line": 4, + "column": 9 + } + }, + "name": "svg" + } + }, + "variables": [] + }, + { + "type": "VText", + "range": [ + 70, + 71 + ], + "loc": { + "start": { + "line": 4, + "column": 10 + }, + "end": { + "line": 5, + "column": 0 + } + }, + "value": "\n" + } + ], + "endTag": { + "type": "VEndTag", + "range": [ + 71, + 82 + ], + "loc": { + "start": { + "line": 5, + "column": 0 + }, + "end": { + "line": 5, + "column": 11 + } + }, + "id": { + "type": "VIdentifier", + "range": [ + 73, + 81 + ], + "loc": { + "start": { + "line": 5, + "column": 2 + }, + "end": { + "line": 5, + "column": 10 + } + }, + "name": "template" + } + }, + "variables": [], + "tokens": [ + { + "type": "Punctuator", + "range": [ + 0, + 1 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 1 + } + }, + "value": "<" + }, + { + "type": "VIdentifier", + "range": [ + 1, + 9 + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 9 + } + }, + "value": "template" + }, + { + "type": "Punctuator", + "range": [ + 9, + 10 + ], + "loc": { + "start": { + "line": 1, + "column": 9 + }, + "end": { + "line": 1, + "column": 10 + } + }, + "value": ">" + }, + { + "type": "VText", + "range": [ + 10, + 15 + ], + "loc": { + "start": { + "line": 1, + "column": 10 + }, + "end": { + "line": 2, + "column": 4 + } + }, + "value": "\n " + }, + { + "type": "Punctuator", + "range": [ + 15, + 16 + ], + "loc": { + "start": { + "line": 2, + "column": 4 + }, + "end": { + "line": 2, + "column": 5 + } + }, + "value": "<" + }, + { + "type": "VIdentifier", + "range": [ + 16, + 19 + ], + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 2, + "column": 8 + } + }, + "value": "svg" + }, + { + "type": "Punctuator", + "range": [ + 19, + 20 + ], + "loc": { + "start": { + "line": 2, + "column": 8 + }, + "end": { + "line": 2, + "column": 9 + } + }, + "value": ">" + }, + { + "type": "VText", + "range": [ + 20, + 29 + ], + "loc": { + "start": { + "line": 2, + "column": 9 + }, + "end": { + "line": 3, + "column": 8 + } + }, + "value": "\n " + }, + { + "type": "Punctuator", + "range": [ + 29, + 30 + ], + "loc": { + "start": { + "line": 3, + "column": 8 + }, + "end": { + "line": 3, + "column": 9 + } + }, + "value": "<" + }, + { + "type": "VIdentifier", + "range": [ + 30, + 33 + ], + "loc": { + "start": { + "line": 3, + "column": 9 + }, + "end": { + "line": 3, + "column": 12 + } + }, + "value": "use" + }, + { + "type": "VIdentifier", + "range": [ + 34, + 44 + ], + "loc": { + "start": { + "line": 3, + "column": 13 + }, + "end": { + "line": 3, + "column": 23 + } + }, + "value": "xlink:href" + }, + { + "type": "Punctuator", + "range": [ + 44, + 45 + ], + "loc": { + "start": { + "line": 3, + "column": 23 + }, + "end": { + "line": 3, + "column": 24 + } + }, + "value": "=" + }, + { + "type": "VAttributeValue", + "range": [ + 45, + 52 + ], + "loc": { + "start": { + "line": 3, + "column": 24 + }, + "end": { + "line": 3, + "column": 31 + } + }, + "value": "\"#test\"" + }, + { + "type": "Punctuator", + "range": [ + 52, + 53 + ], + "loc": { + "start": { + "line": 3, + "column": 31 + }, + "end": { + "line": 3, + "column": 32 + } + }, + "value": ">" + }, + { + "type": "Punctuator", + "range": [ + 53, + 55 + ], + "loc": { + "start": { + "line": 3, + "column": 32 + }, + "end": { + "line": 3, + "column": 34 + } + }, + "value": "" + }, + { + "type": "VText", + "range": [ + 59, + 64 + ], + "loc": { + "start": { + "line": 3, + "column": 38 + }, + "end": { + "line": 4, + "column": 4 + } + }, + "value": "\n " + }, + { + "type": "Punctuator", + "range": [ + 64, + 66 + ], + "loc": { + "start": { + "line": 4, + "column": 4 + }, + "end": { + "line": 4, + "column": 6 + } + }, + "value": "" + }, + { + "type": "VText", + "range": [ + 70, + 71 + ], + "loc": { + "start": { + "line": 4, + "column": 10 + }, + "end": { + "line": 5, + "column": 0 + } + }, + "value": "\n" + }, + { + "type": "Punctuator", + "range": [ + 71, + 73 + ], + "loc": { + "start": { + "line": 5, + "column": 0 + }, + "end": { + "line": 5, + "column": 2 + } + }, + "value": "" + } + ], + "comments": [] + } +} \ No newline at end of file diff --git a/test/fixtures/template-ast/svg-attrs-colon.source.vue b/test/fixtures/template-ast/svg-attrs-colon.source.vue new file mode 100644 index 00000000..73ca3332 --- /dev/null +++ b/test/fixtures/template-ast/svg-attrs-colon.source.vue @@ -0,0 +1,9 @@ + + + diff --git a/test/fixtures/template-ast/svg-attrs-colon.tokens.json b/test/fixtures/template-ast/svg-attrs-colon.tokens.json new file mode 100644 index 00000000..c893ffa6 --- /dev/null +++ b/test/fixtures/template-ast/svg-attrs-colon.tokens.json @@ -0,0 +1,102 @@ +[ + [ + "Punctuator", + "<" + ], + [ + "VIdentifier", + "template" + ], + [ + "Punctuator", + ">" + ], + [ + "VText", + "\n " + ], + [ + "Punctuator", + "<" + ], + [ + "VIdentifier", + "svg" + ], + [ + "Punctuator", + ">" + ], + [ + "VText", + "\n " + ], + [ + "Punctuator", + "<" + ], + [ + "VIdentifier", + "use" + ], + [ + "VIdentifier", + "xlink:href" + ], + [ + "Punctuator", + "=" + ], + [ + "VAttributeValue", + "\"#test\"" + ], + [ + "Punctuator", + ">" + ], + [ + "Punctuator", + "" + ], + [ + "VText", + "\n " + ], + [ + "Punctuator", + "" + ], + [ + "VText", + "\n" + ], + [ + "Punctuator", + "" + ] +] \ No newline at end of file diff --git a/test/fixtures/template-ast/svg-attrs-colon.traversal.json b/test/fixtures/template-ast/svg-attrs-colon.traversal.json new file mode 100644 index 00000000..c1e5e9b9 --- /dev/null +++ b/test/fixtures/template-ast/svg-attrs-colon.traversal.json @@ -0,0 +1,222 @@ +[ + [ + "enter", + "VElement", + "" + ], + [ + "enter", + "VStartTag", + "" + ], + [ + "leave", + "VElement", + "" + ] +] \ No newline at end of file diff --git a/test/tools/update-template-ast.js b/test/tools/update-template-ast.js index 33ece059..3c08eaa6 100644 --- a/test/tools/update-template-ast.js +++ b/test/tools/update-template-ast.js @@ -11,7 +11,7 @@ const fs = require("fs") const path = require("path") -const parse = require("../..").parse +const parse = require("../..").parseForESLint const traverseNodes = require("../../lib/traverse-nodes") //------------------------------------------------------------------------------ From 8168db16df3eaa327bacded0f244f0565b563a0f Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Tue, 27 Jun 2017 18:19:56 +0900 Subject: [PATCH 003/345] Fix: crash if was omitted --- lib/transform-html.js | 20 + test/fixtures/template-ast/table.ast.json | 830 ++++++++++++++++++ test/fixtures/template-ast/table.source.vue | 6 + test/fixtures/template-ast/table.tokens.json | 94 ++ .../template-ast/table.traversal.json | 212 +++++ 5 files changed, 1162 insertions(+) create mode 100644 test/fixtures/template-ast/table.ast.json create mode 100644 test/fixtures/template-ast/table.source.vue create mode 100644 test/fixtures/template-ast/table.tokens.json create mode 100644 test/fixtures/template-ast/table.traversal.json diff --git a/lib/transform-html.js b/lib/transform-html.js index 922eb2ef..9c34ae93 100644 --- a/lib/transform-html.js +++ b/lib/transform-html.js @@ -629,6 +629,26 @@ class HTMLTransformer { * @returns {ASTNode|ASTNode[]} The transformed node. */ visitElementNode(parent, node) { + // if __location does not exists, this is auto-inserted element for some reason. + // E.g. if a exists as a direct child of , element is inserted implicitly. + if (node.__location == null) { + const results = [] + for (const childNode of node.childNodes) { + const child = this.visit(parent, childNode) + + // Flatten. + if (Array.isArray(child)) { + for (const child1 of child) { + results.push(child1) + } + } + else if (child != null) { + results.push(child) + } + } + return results + } + const start = node.__location.startOffset const end = node.__location.endOffset const childNodes = (node.tagName === "template") diff --git a/test/fixtures/template-ast/table.ast.json b/test/fixtures/template-ast/table.ast.json new file mode 100644 index 00000000..a3b927e7 --- /dev/null +++ b/test/fixtures/template-ast/table.ast.json @@ -0,0 +1,830 @@ +{ + "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": { + "range": [ + 0, + 82 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 6, + "column": 11 + } + }, + "type": "VElement", + "startTag": { + "type": "VStartTag", + "range": [ + 0, + 10 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 10 + } + }, + "id": { + "type": "VIdentifier", + "range": [ + 1, + 9 + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 9 + } + }, + "name": "template" + }, + "attributes": [], + "selfClosing": false + }, + "children": [ + { + "type": "VText", + "range": [ + 10, + 15 + ], + "loc": { + "start": { + "line": 1, + "column": 10 + }, + "end": { + "line": 2, + "column": 4 + } + }, + "value": "\n " + }, + { + "range": [ + 15, + 70 + ], + "loc": { + "start": { + "line": 2, + "column": 4 + }, + "end": { + "line": 5, + "column": 12 + } + }, + "type": "VElement", + "startTag": { + "type": "VStartTag", + "range": [ + 15, + 22 + ], + "loc": { + "start": { + "line": 2, + "column": 4 + }, + "end": { + "line": 2, + "column": 11 + } + }, + "id": { + "type": "VIdentifier", + "range": [ + 16, + 21 + ], + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 2, + "column": 10 + } + }, + "name": "table" + }, + "attributes": [], + "selfClosing": false + }, + "children": [ + { + "type": "VText", + "range": [ + 22, + 31 + ], + "loc": { + "start": { + "line": 2, + "column": 11 + }, + "end": { + "line": 3, + "column": 8 + } + }, + "value": "\n " + }, + { + "range": [ + 31, + 62 + ], + "loc": { + "start": { + "line": 3, + "column": 8 + }, + "end": { + "line": 5, + "column": 4 + } + }, + "type": "VElement", + "startTag": { + "type": "VStartTag", + "range": [ + 31, + 35 + ], + "loc": { + "start": { + "line": 3, + "column": 8 + }, + "end": { + "line": 3, + "column": 12 + } + }, + "id": { + "type": "VIdentifier", + "range": [ + 32, + 34 + ], + "loc": { + "start": { + "line": 3, + "column": 9 + }, + "end": { + "line": 3, + "column": 11 + } + }, + "name": "tr" + }, + "attributes": [], + "selfClosing": false + }, + "children": [ + { + "type": "VText", + "range": [ + 35, + 48 + ], + "loc": { + "start": { + "line": 3, + "column": 12 + }, + "end": { + "line": 4, + "column": 12 + } + }, + "value": "\n " + }, + { + "range": [ + 48, + 62 + ], + "loc": { + "start": { + "line": 4, + "column": 12 + }, + "end": { + "line": 5, + "column": 4 + } + }, + "type": "VElement", + "startTag": { + "type": "VStartTag", + "range": [ + 48, + 52 + ], + "loc": { + "start": { + "line": 4, + "column": 12 + }, + "end": { + "line": 4, + "column": 16 + } + }, + "id": { + "type": "VIdentifier", + "range": [ + 49, + 51 + ], + "loc": { + "start": { + "line": 4, + "column": 13 + }, + "end": { + "line": 4, + "column": 15 + } + }, + "name": "td" + }, + "attributes": [], + "selfClosing": false + }, + "children": [ + { + "type": "VText", + "range": [ + 52, + 62 + ], + "loc": { + "start": { + "line": 4, + "column": 16 + }, + "end": { + "line": 5, + "column": 4 + } + }, + "value": "hello\n " + } + ], + "endTag": null, + "variables": [] + } + ], + "endTag": null, + "variables": [] + } + ], + "endTag": { + "type": "VEndTag", + "range": [ + 62, + 70 + ], + "loc": { + "start": { + "line": 5, + "column": 4 + }, + "end": { + "line": 5, + "column": 12 + } + }, + "id": { + "type": "VIdentifier", + "range": [ + 64, + 69 + ], + "loc": { + "start": { + "line": 5, + "column": 6 + }, + "end": { + "line": 5, + "column": 11 + } + }, + "name": "table" + } + }, + "variables": [] + }, + { + "type": "VText", + "range": [ + 70, + 71 + ], + "loc": { + "start": { + "line": 5, + "column": 12 + }, + "end": { + "line": 6, + "column": 0 + } + }, + "value": "\n" + } + ], + "endTag": { + "type": "VEndTag", + "range": [ + 71, + 82 + ], + "loc": { + "start": { + "line": 6, + "column": 0 + }, + "end": { + "line": 6, + "column": 11 + } + }, + "id": { + "type": "VIdentifier", + "range": [ + 73, + 81 + ], + "loc": { + "start": { + "line": 6, + "column": 2 + }, + "end": { + "line": 6, + "column": 10 + } + }, + "name": "template" + } + }, + "variables": [], + "tokens": [ + { + "type": "Punctuator", + "range": [ + 0, + 1 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 1 + } + }, + "value": "<" + }, + { + "type": "VIdentifier", + "range": [ + 1, + 9 + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 9 + } + }, + "value": "template" + }, + { + "type": "Punctuator", + "range": [ + 9, + 10 + ], + "loc": { + "start": { + "line": 1, + "column": 9 + }, + "end": { + "line": 1, + "column": 10 + } + }, + "value": ">" + }, + { + "type": "VText", + "range": [ + 10, + 15 + ], + "loc": { + "start": { + "line": 1, + "column": 10 + }, + "end": { + "line": 2, + "column": 4 + } + }, + "value": "\n " + }, + { + "type": "Punctuator", + "range": [ + 15, + 16 + ], + "loc": { + "start": { + "line": 2, + "column": 4 + }, + "end": { + "line": 2, + "column": 5 + } + }, + "value": "<" + }, + { + "type": "VIdentifier", + "range": [ + 16, + 21 + ], + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 2, + "column": 10 + } + }, + "value": "table" + }, + { + "type": "Punctuator", + "range": [ + 21, + 22 + ], + "loc": { + "start": { + "line": 2, + "column": 10 + }, + "end": { + "line": 2, + "column": 11 + } + }, + "value": ">" + }, + { + "type": "VText", + "range": [ + 22, + 31 + ], + "loc": { + "start": { + "line": 2, + "column": 11 + }, + "end": { + "line": 3, + "column": 8 + } + }, + "value": "\n " + }, + { + "type": "Punctuator", + "range": [ + 31, + 32 + ], + "loc": { + "start": { + "line": 3, + "column": 8 + }, + "end": { + "line": 3, + "column": 9 + } + }, + "value": "<" + }, + { + "type": "VIdentifier", + "range": [ + 32, + 34 + ], + "loc": { + "start": { + "line": 3, + "column": 9 + }, + "end": { + "line": 3, + "column": 11 + } + }, + "value": "tr" + }, + { + "type": "Punctuator", + "range": [ + 34, + 35 + ], + "loc": { + "start": { + "line": 3, + "column": 11 + }, + "end": { + "line": 3, + "column": 12 + } + }, + "value": ">" + }, + { + "type": "VText", + "range": [ + 35, + 48 + ], + "loc": { + "start": { + "line": 3, + "column": 12 + }, + "end": { + "line": 4, + "column": 12 + } + }, + "value": "\n " + }, + { + "type": "Punctuator", + "range": [ + 48, + 49 + ], + "loc": { + "start": { + "line": 4, + "column": 12 + }, + "end": { + "line": 4, + "column": 13 + } + }, + "value": "<" + }, + { + "type": "VIdentifier", + "range": [ + 49, + 51 + ], + "loc": { + "start": { + "line": 4, + "column": 13 + }, + "end": { + "line": 4, + "column": 15 + } + }, + "value": "td" + }, + { + "type": "Punctuator", + "range": [ + 51, + 52 + ], + "loc": { + "start": { + "line": 4, + "column": 15 + }, + "end": { + "line": 4, + "column": 16 + } + }, + "value": ">" + }, + { + "type": "VText", + "range": [ + 52, + 62 + ], + "loc": { + "start": { + "line": 4, + "column": 16 + }, + "end": { + "line": 5, + "column": 4 + } + }, + "value": "hello\n " + }, + { + "type": "Punctuator", + "range": [ + 62, + 64 + ], + "loc": { + "start": { + "line": 5, + "column": 4 + }, + "end": { + "line": 5, + "column": 6 + } + }, + "value": "" + }, + { + "type": "VText", + "range": [ + 70, + 71 + ], + "loc": { + "start": { + "line": 5, + "column": 12 + }, + "end": { + "line": 6, + "column": 0 + } + }, + "value": "\n" + }, + { + "type": "Punctuator", + "range": [ + 71, + 73 + ], + "loc": { + "start": { + "line": 6, + "column": 0 + }, + "end": { + "line": 6, + "column": 2 + } + }, + "value": "" + } + ], + "comments": [] + } +} \ No newline at end of file diff --git a/test/fixtures/template-ast/table.source.vue b/test/fixtures/template-ast/table.source.vue new file mode 100644 index 00000000..cdee40ec --- /dev/null +++ b/test/fixtures/template-ast/table.source.vue @@ -0,0 +1,6 @@ + diff --git a/test/fixtures/template-ast/table.tokens.json b/test/fixtures/template-ast/table.tokens.json new file mode 100644 index 00000000..a32af958 --- /dev/null +++ b/test/fixtures/template-ast/table.tokens.json @@ -0,0 +1,94 @@ +[ + [ + "Punctuator", + "<" + ], + [ + "VIdentifier", + "template" + ], + [ + "Punctuator", + ">" + ], + [ + "VText", + "\n " + ], + [ + "Punctuator", + "<" + ], + [ + "VIdentifier", + "table" + ], + [ + "Punctuator", + ">" + ], + [ + "VText", + "\n " + ], + [ + "Punctuator", + "<" + ], + [ + "VIdentifier", + "tr" + ], + [ + "Punctuator", + ">" + ], + [ + "VText", + "\n " + ], + [ + "Punctuator", + "<" + ], + [ + "VIdentifier", + "td" + ], + [ + "Punctuator", + ">" + ], + [ + "VText", + "hello\n " + ], + [ + "Punctuator", + "" + ], + [ + "VText", + "\n" + ], + [ + "Punctuator", + "" + ] +] \ No newline at end of file diff --git a/test/fixtures/template-ast/table.traversal.json b/test/fixtures/template-ast/table.traversal.json new file mode 100644 index 00000000..4fefad16 --- /dev/null +++ b/test/fixtures/template-ast/table.traversal.json @@ -0,0 +1,212 @@ +[ + [ + "enter", + "VElement", + "" + ], + [ + "enter", + "VStartTag", + "" + ], + [ + "leave", + "VElement", + "" + ] +] \ No newline at end of file From acdcf249914ad61dcd00fe5c028b06ad1f0a5b3e Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Tue, 27 Jun 2017 19:07:02 +0900 Subject: [PATCH 004/345] 1.1.0-7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3aaea84d..f91093aa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-eslint-parser", - "version": "1.1.0-6", + "version": "1.1.0-7", "description": "The ESLint custom parser for `.vue` files.", "engines": { "node": ">=4" From 40050770e7fff58b08130568d0bd9e1119403aba Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Thu, 29 Jun 2017 06:02:02 +0900 Subject: [PATCH 005/345] Chore: don't make package-lock.json --- .npmrc | 1 + 1 file changed, 1 insertion(+) create mode 100644 .npmrc diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..c1ca392f --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +package-lock = false From 2d22201f5ed981f7ad9631870b003aaf04a5d3f6 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Thu, 29 Jun 2017 06:41:24 +0900 Subject: [PATCH 006/345] Breaking: rewrite from scratch --- .babelrc | 5 - .eslintignore | 9 + .eslintrc.json | 36 +- .gitignore | 2 + .vscode/settings.json | 6 +- docs/ast.md | 28 +- index.js | 47 - lib/analyze-references.js | 44 - lib/decode-html-entities.js | 69 - lib/entities.json | 1 - lib/get-token-store.js | 42 - lib/parse-component.js | 195 -- lib/register-template-body-visitor.js | 56 - lib/script-parser.js | 326 ---- lib/token-generator.js | 96 - lib/transform-html.js | 734 ------- package.json | 70 +- rollup.config.js | 36 + src/ast/errors.ts | 151 ++ src/ast/index.ts | 11 + src/ast/locations.ts | 60 + src/ast/nodes.ts | 692 +++++++ src/ast/tokens.ts | 26 + lib/traverse-nodes.js => src/ast/traverse.ts | 75 +- src/common/debug.ts | 7 + src/common/location-calculator.ts | 174 ++ src/html/index.ts | 7 + src/html/parser.ts | 476 +++++ src/html/tokenizer.ts | 1735 +++++++++++++++++ src/html/util/alternative-cr.ts | 12 + src/html/util/entities.ts | 15 + src/html/util/tag-names.ts | 61 + src/html/util/unicode.ts | 166 ++ src/index.ts | 84 + src/parser-services.ts | 74 + src/script/index.ts | 285 +++ src/script/scope-analyzer.ts | 101 + src/template/index.ts | 580 ++++++ test/.eslintrc.json | 4 +- test/ast.js | 172 ++ .../attributes/ast.json} | 743 ++----- .../attributes/source.vue} | 0 .../attributes/traversal.json} | 132 +- .../comments/ast.json} | 395 ++-- .../comments/source.vue} | 0 .../comments/traversal.json} | 60 - .../ast.json} | 350 +--- .../source.vue} | 0 .../traversal.json} | 60 - .../directive-arguments/ast.json} | 504 ++--- .../directive-arguments/source.vue} | 0 .../directive-arguments/traversal.json} | 90 - .../directive-modifiers/ast.json} | 477 ++--- .../directive-modifiers/source.vue} | 0 .../directive-modifiers/traversal.json} | 80 - .../directive-shorthands/ast.json} | 350 +--- .../directive-shorthands/source.vue} | 0 .../directive-shorthands/traversal.json} | 60 - .../directives/ast.json} | 997 +++------- .../directives/source.vue} | 0 .../directives/traversal.json} | 180 +- .../elements/ast.json} | 418 +--- .../elements/source.vue} | 0 .../elements/traversal.json} | 80 - .../empty-script-only/ast.json} | 20 +- .../empty-script-only/source.vue} | 0 .../empty-script-only/traversal.json} | 0 .../empty-template-only/ast.json} | 112 +- .../empty-template-only/source.vue} | 0 .../empty-template-only/traversal.json} | 20 - .../empty.ast.json => ast/empty/ast.json} | 3 +- .../empty.source.vue => ast/empty/source.vue} | 0 .../empty/traversal.json} | 0 .../expression-container-only/ast.json} | 123 +- .../expression-container-only/source.vue} | 0 .../expression-container-only/traversal.json} | 20 - .../html-entities-in-static-places/ast.json} | 326 ++-- .../source.vue} | 0 .../traversal.json} | 44 +- .../ast.json} | 253 +-- .../source.vue} | 0 .../traversal.json} | 40 - .../html-entities/ast.json} | 282 +-- .../html-entities/source.vue} | 0 .../html-entities/traversal.json} | 40 - .../not-closing-elements/ast.json} | 318 +-- .../not-closing-elements/source.vue} | 0 .../not-closing-elements/traversal.json} | 50 - .../self-closing-elements/ast.json} | 325 +-- .../self-closing-elements/source.vue} | 0 .../self-closing-elements/traversal.json} | 50 - .../text-and-expression-container/ast.json} | 329 +++- .../text-and-expression-container/source.vue} | 0 .../traversal.json} | 20 - .../text.ast.json => ast/text/ast.json} | 184 +- .../text.source.vue => ast/text/source.vue} | 0 .../text/traversal.json} | 20 - .../ast.json} | 672 ++----- .../source.vue} | 0 .../traversal.json} | 100 - .../v-for-directives/ast.json} | 632 ++---- .../v-for-directives/source.vue} | 0 .../v-for-directives/traversal.json} | 100 - .../template-ast/attributes.tokens.json | 218 --- .../template-ast/comments.tokens.json | 106 - ...rective-argument-and-modifiers.tokens.json | 94 - .../directive-arguments.tokens.json | 138 -- .../directive-modifiers.tokens.json | 142 -- .../directive-shorthands.tokens.json | 94 - .../template-ast/directives.tokens.json | 298 --- .../template-ast/elements.tokens.json | 98 - .../empty-template-only.tokens.json | 26 - test/fixtures/template-ast/empty.tokens.json | 1 - .../template-ast/empty.traversal.json | 1 - .../expression-container-only.tokens.json | 38 - ...html-entities-in-static-places.tokens.json | 78 - ...entities-with-line-terminators.tokens.json | 78 - .../template-ast/html-entities.tokens.json | 106 - .../not-closing-elements.tokens.json | 78 - .../self-closing-elements.tokens.json | 78 - .../text-and-expression-container.tokens.json | 62 - test/fixtures/template-ast/text.tokens.json | 30 - ...-directives-with-destructuring.tokens.json | 362 ---- .../template-ast/v-for-directives.tokens.json | 302 --- test/fixtures/template-ast/xxxx.vue | 1 - test/index.js | 2 +- test/stub-rule-context.js | 2 +- test/template-ast.js | 139 -- test/tokens.js | 69 +- test/tools/.eslintrc.json | 3 + test/tools/update-template-ast.js | 52 +- test/variables-references.js | 9 +- tsconfig.json | 43 + typings/eslint-scope/index.d.ts | 77 + typings/eslint/lib/token-store.d.ts | 14 + .../eslint/lib/util/node-event-generator.d.ts | 14 + 136 files changed, 7664 insertions(+), 11018 deletions(-) delete mode 100644 .babelrc create mode 100644 .eslintignore delete mode 100644 index.js delete mode 100644 lib/analyze-references.js delete mode 100644 lib/decode-html-entities.js delete mode 100644 lib/entities.json delete mode 100644 lib/get-token-store.js delete mode 100644 lib/parse-component.js delete mode 100644 lib/register-template-body-visitor.js delete mode 100644 lib/script-parser.js delete mode 100644 lib/token-generator.js delete mode 100644 lib/transform-html.js create mode 100644 rollup.config.js create mode 100644 src/ast/errors.ts create mode 100644 src/ast/index.ts create mode 100644 src/ast/locations.ts create mode 100644 src/ast/nodes.ts create mode 100644 src/ast/tokens.ts rename lib/traverse-nodes.js => src/ast/traverse.ts (76%) create mode 100644 src/common/debug.ts create mode 100644 src/common/location-calculator.ts create mode 100644 src/html/index.ts create mode 100644 src/html/parser.ts create mode 100644 src/html/tokenizer.ts create mode 100644 src/html/util/alternative-cr.ts create mode 100644 src/html/util/entities.ts create mode 100644 src/html/util/tag-names.ts create mode 100644 src/html/util/unicode.ts create mode 100644 src/index.ts create mode 100644 src/parser-services.ts create mode 100644 src/script/index.ts create mode 100644 src/script/scope-analyzer.ts create mode 100644 src/template/index.ts create mode 100644 test/ast.js rename test/fixtures/{template-ast/attributes.ast.json => ast/attributes/ast.json} (70%) rename test/fixtures/{template-ast/attributes.source.vue => ast/attributes/source.vue} (100%) rename test/fixtures/{template-ast/attributes.traversal.json => ast/attributes/traversal.json} (74%) rename test/fixtures/{template-ast/comments.ast.json => ast/comments/ast.json} (69%) rename test/fixtures/{template-ast/comments.source.vue => ast/comments/source.vue} (100%) rename test/fixtures/{template-ast/comments.traversal.json => ast/comments/traversal.json} (75%) rename test/fixtures/{template-ast/directive-argument-and-modifiers.ast.json => ast/directive-argument-and-modifiers/ast.json} (68%) rename test/fixtures/{template-ast/directive-argument-and-modifiers.source.vue => ast/directive-argument-and-modifiers/source.vue} (100%) rename test/fixtures/{template-ast/directive-argument-and-modifiers.traversal.json => ast/directive-argument-and-modifiers/traversal.json} (74%) rename test/fixtures/{template-ast/directive-arguments.ast.json => ast/directive-arguments/ast.json} (70%) rename test/fixtures/{template-ast/directive-arguments.source.vue => ast/directive-arguments/source.vue} (100%) rename test/fixtures/{template-ast/directive-arguments.traversal.json => ast/directive-arguments/traversal.json} (74%) rename test/fixtures/{template-ast/directive-modifiers.ast.json => ast/directive-modifiers/ast.json} (74%) rename test/fixtures/{template-ast/directive-modifiers.source.vue => ast/directive-modifiers/source.vue} (100%) rename test/fixtures/{template-ast/directive-modifiers.traversal.json => ast/directive-modifiers/traversal.json} (77%) rename test/fixtures/{template-ast/directive-shorthands.ast.json => ast/directive-shorthands/ast.json} (68%) rename test/fixtures/{template-ast/directive-shorthands.source.vue => ast/directive-shorthands/source.vue} (100%) rename test/fixtures/{template-ast/directive-shorthands.traversal.json => ast/directive-shorthands/traversal.json} (73%) rename test/fixtures/{template-ast/directives.ast.json => ast/directives/ast.json} (71%) rename test/fixtures/{template-ast/directives.source.vue => ast/directives/source.vue} (100%) rename test/fixtures/{template-ast/directives.traversal.json => ast/directives/traversal.json} (74%) rename test/fixtures/{template-ast/elements.ast.json => ast/elements/ast.json} (57%) rename test/fixtures/{template-ast/elements.source.vue => ast/elements/source.vue} (100%) rename test/fixtures/{template-ast/elements.traversal.json => ast/elements/traversal.json} (61%) rename test/fixtures/{template-ast/empty-script-only.ast.json => ast/empty-script-only/ast.json} (81%) rename test/fixtures/{template-ast/empty-script-only.source.vue => ast/empty-script-only/source.vue} (100%) rename test/fixtures/{template-ast/empty-script-only.tokens.json => ast/empty-script-only/traversal.json} (100%) rename test/fixtures/{template-ast/empty-template-only.ast.json => ast/empty-template-only/ast.json} (62%) rename test/fixtures/{template-ast/empty-template-only.source.vue => ast/empty-template-only/source.vue} (100%) rename test/fixtures/{template-ast/empty-template-only.traversal.json => ast/empty-template-only/traversal.json} (61%) rename test/fixtures/{template-ast/empty.ast.json => ast/empty/ast.json} (88%) rename test/fixtures/{template-ast/empty.source.vue => ast/empty/source.vue} (100%) rename test/fixtures/{template-ast/empty-script-only.traversal.json => ast/empty/traversal.json} (100%) rename test/fixtures/{template-ast/expression-container-only.ast.json => ast/expression-container-only/ast.json} (75%) rename test/fixtures/{template-ast/expression-container-only.source.vue => ast/expression-container-only/source.vue} (100%) rename test/fixtures/{template-ast/expression-container-only.traversal.json => ast/expression-container-only/traversal.json} (73%) rename test/fixtures/{template-ast/html-entities-in-static-places.ast.json => ast/html-entities-in-static-places/ast.json} (77%) rename test/fixtures/{template-ast/html-entities-in-static-places.source.vue => ast/html-entities-in-static-places/source.vue} (100%) rename test/fixtures/{template-ast/html-entities-in-static-places.traversal.json => ast/html-entities-in-static-places/traversal.json} (77%) rename test/fixtures/{template-ast/html-entities-with-line-terminators.ast.json => ast/html-entities-with-line-terminators/ast.json} (75%) rename test/fixtures/{template-ast/html-entities-with-line-terminators.source.vue => ast/html-entities-with-line-terminators/source.vue} (100%) rename test/fixtures/{template-ast/html-entities-with-line-terminators.traversal.json => ast/html-entities-with-line-terminators/traversal.json} (78%) rename test/fixtures/{template-ast/html-entities.ast.json => ast/html-entities/ast.json} (82%) rename test/fixtures/{template-ast/html-entities.source.vue => ast/html-entities/source.vue} (100%) rename test/fixtures/{template-ast/html-entities.traversal.json => ast/html-entities/traversal.json} (83%) rename test/fixtures/{template-ast/not-closing-elements.ast.json => ast/not-closing-elements/ast.json} (68%) rename test/fixtures/{template-ast/not-closing-elements.source.vue => ast/not-closing-elements/source.vue} (100%) rename test/fixtures/{template-ast/not-closing-elements.traversal.json => ast/not-closing-elements/traversal.json} (74%) rename test/fixtures/{template-ast/self-closing-elements.ast.json => ast/self-closing-elements/ast.json} (66%) rename test/fixtures/{template-ast/self-closing-elements.source.vue => ast/self-closing-elements/source.vue} (100%) rename test/fixtures/{template-ast/self-closing-elements.traversal.json => ast/self-closing-elements/traversal.json} (72%) rename test/fixtures/{template-ast/text-and-expression-container.ast.json => ast/text-and-expression-container/ast.json} (69%) rename test/fixtures/{template-ast/text-and-expression-container.source.vue => ast/text-and-expression-container/source.vue} (100%) rename test/fixtures/{template-ast/text-and-expression-container.traversal.json => ast/text-and-expression-container/traversal.json} (84%) rename test/fixtures/{template-ast/text.ast.json => ast/text/ast.json} (68%) rename test/fixtures/{template-ast/text.source.vue => ast/text/source.vue} (100%) rename test/fixtures/{template-ast/text.traversal.json => ast/text/traversal.json} (69%) rename test/fixtures/{template-ast/v-for-directives-with-destructuring.ast.json => ast/v-for-directives-with-destructuring/ast.json} (88%) rename test/fixtures/{template-ast/v-for-directives-with-destructuring.source.vue => ast/v-for-directives-with-destructuring/source.vue} (100%) rename test/fixtures/{template-ast/v-for-directives-with-destructuring.traversal.json => ast/v-for-directives-with-destructuring/traversal.json} (87%) rename test/fixtures/{template-ast/v-for-directives.ast.json => ast/v-for-directives/ast.json} (85%) rename test/fixtures/{template-ast/v-for-directives.source.vue => ast/v-for-directives/source.vue} (100%) rename test/fixtures/{template-ast/v-for-directives.traversal.json => ast/v-for-directives/traversal.json} (84%) delete mode 100644 test/fixtures/template-ast/attributes.tokens.json delete mode 100644 test/fixtures/template-ast/comments.tokens.json delete mode 100644 test/fixtures/template-ast/directive-argument-and-modifiers.tokens.json delete mode 100644 test/fixtures/template-ast/directive-arguments.tokens.json delete mode 100644 test/fixtures/template-ast/directive-modifiers.tokens.json delete mode 100644 test/fixtures/template-ast/directive-shorthands.tokens.json delete mode 100644 test/fixtures/template-ast/directives.tokens.json delete mode 100644 test/fixtures/template-ast/elements.tokens.json delete mode 100644 test/fixtures/template-ast/empty-template-only.tokens.json delete mode 100644 test/fixtures/template-ast/empty.tokens.json delete mode 100644 test/fixtures/template-ast/empty.traversal.json delete mode 100644 test/fixtures/template-ast/expression-container-only.tokens.json delete mode 100644 test/fixtures/template-ast/html-entities-in-static-places.tokens.json delete mode 100644 test/fixtures/template-ast/html-entities-with-line-terminators.tokens.json delete mode 100644 test/fixtures/template-ast/html-entities.tokens.json delete mode 100644 test/fixtures/template-ast/not-closing-elements.tokens.json delete mode 100644 test/fixtures/template-ast/self-closing-elements.tokens.json delete mode 100644 test/fixtures/template-ast/text-and-expression-container.tokens.json delete mode 100644 test/fixtures/template-ast/text.tokens.json delete mode 100644 test/fixtures/template-ast/v-for-directives-with-destructuring.tokens.json delete mode 100644 test/fixtures/template-ast/v-for-directives.tokens.json delete mode 100644 test/fixtures/template-ast/xxxx.vue delete mode 100644 test/template-ast.js create mode 100644 test/tools/.eslintrc.json create mode 100644 tsconfig.json create mode 100644 typings/eslint-scope/index.d.ts create mode 100644 typings/eslint/lib/token-store.d.ts create mode 100644 typings/eslint/lib/util/node-event-generator.d.ts diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 852fc13c..00000000 --- a/.babelrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "presets": ["power-assert"], - "only": "test/*.js", - "sourceMaps": "inline" -} diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..3e9654a6 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,9 @@ +/.nyc_output +/.temp +/coverage +/node_modules +/src/html/util +/test/fixtures +/test/temp +/index.d.ts +/index.js diff --git a/.eslintrc.json b/.eslintrc.json index dbd8a3ab..9d1e855f 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,4 +1,34 @@ { - "extends": ["mysticatea", "mysticatea/node"], - "root": true -} \ No newline at end of file + "extends": [ + "mysticatea", + "mysticatea/node", + "mysticatea/modules" + ], + "parser": "typescript-eslint-parser", + "rules": { + "indent": "off", + "indent-legacy": ["error", 4, {"SwitchCase": 1}], + "init-declarations": "off", + "no-constant-condition": "off", + "no-param-reassign": "off", + "no-restricted-globals": ["error", "require"], + "no-undef": "off", + "no-unused-vars": "off", + "no-use-before-define": "off", + "valid-jsdoc": "off", + "node/no-unsupported-features": ["error", {"ignores": ["modules"]}] + }, + "settings": { + "node": { + "tryExtensions": [".ts", ".d.ts", ".js", ".json"] + } + }, + "overrides": [ + { + "files": "typings/**", + "rules": { + "node/no-missing-import": ["error", {"allowModules": ["estree"]}] + } + } + ] +} diff --git a/.gitignore b/.gitignore index 70648849..80b13514 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ /.nyc_output +/.temp /coverage /node_modules /test/temp +/index.* /npm-debug.log /test.js diff --git a/.vscode/settings.json b/.vscode/settings.json index af6bbfc5..0bcb47f3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,7 +2,7 @@ "eslint.validate": [ "javascript", {"language": "html", "autoFix": true}, - {"language": "vue", "autoFix": true} - ], - "typescript.check.workspaceVersion": false + {"language": "vue", "autoFix": true}, + {"language": "typescript", "autoFix": true} + ] } diff --git a/docs/ast.md b/docs/ast.md index 33bd0664..333a351d 100644 --- a/docs/ast.md +++ b/docs/ast.md @@ -37,7 +37,7 @@ interface VIdentifier <: Node { U+7FFFF, U+8FFFE, U+8FFFF, U+9FFFE, U+9FFFF, U+AFFFE, U+AFFFF, U+BFFFE, U+BFFFF, U+CFFFE, U+CFFFF, U+DFFFE, U+DFFFF, U+EFFFE, U+EFFFF, U+FFFFE, U+FFFFF, U+10FFFE and U+10FFFF. -- This is tag names or attribute names. +- This is attribute names. ## VText @@ -45,10 +45,12 @@ interface VIdentifier <: Node { interface VText <: Node { type: "VText" value: string + raw: string } ``` - Plain text of HTML. +- HTML entities in the `value` property are decoded. Those in the `raw` property are not. ## VExpressionContainer @@ -56,7 +58,6 @@ interface VText <: Node { interface VExpressionContainer <: Node { type: "VExpressionContainer" expression: Expression | null - syntaxError: Error | null references: [ Reference ] } @@ -73,7 +74,7 @@ interface VForExpression <: Expression { ``` - This is mustaches or directive values. -- If syntax errors exist, `expression` is `null` and `syntaxError` is an error object. Otherwise, `expression` is an [Expression] node and `syntaxError` is `null`. +- If syntax errors exist, `VExpressionContainer#expression` is `null`. - `Reference` is objects but not `Node`. Those are external references which are in the expression. - `VForExpression` is an expression node like [ForInStatement] but it has an array as `left` property and does not have `body` property. This is the value of `v-for` directives. @@ -96,10 +97,10 @@ interface VDirectiveKey <: Node { - In the shorthand of `v-on` cases, the `name` property is `"on"` and the `shorthand` property is `true`. - Otherwise, `shorthand` property is always `false`. -## VAttributeValue +## VLiteral ```js -interface VAttributeValue <: Node { +interface VLiteral <: Node { type: "VAttributeValue" value: string raw: string @@ -107,15 +108,23 @@ interface VAttributeValue <: Node { ``` - This is similar to [Literal] nodes but this is not always quoted. +- HTML entities in the `value` property are decoded. Those in the `raw` property are not. ## VAttribute ```js interface VAttribute <: Node { type: "VAttribute" - directive: boolean - key: VIdentifier | VDirectiveKey - value: VAttributeValue | VExpressionContainer | null + directive: false + key: VIdentifier + value: VLiteral | null +} + +interface VDirective <: Node { + type: "VAttribute" + directive: true + key: VDirectiveKey + value: VExpressionContainer | null } ``` @@ -129,7 +138,6 @@ interface VAttribute <: Node { ```js interface VStartTag <: Node { type: "VStartTag" - id: VIdentifier attributes: [ VAttribute ] selfClosing: boolean } @@ -142,7 +150,6 @@ If `selfClosing` is `true`, it means having `/`. E.g. `
`. ```js interface VEndTag <: Node { type: "VEndTag" - id: VIdentifier } ``` @@ -151,6 +158,7 @@ interface VEndTag <: Node { ```js interface VElement <: Node { type: "VElement" + name: string startTag: VStartTag children: [ VText | VExpressionContainer | VElement ] endTag: VEndTag | null diff --git a/index.js b/index.js deleted file mode 100644 index b6b4e9de..00000000 --- a/index.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * @author Toru Nagashima - * @copyright 2016 Toru Nagashima. All rights reserved. - * See LICENSE file in root directory for full license. - */ -"use strict" - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const parse = require("./lib/parse-component") - -//------------------------------------------------------------------------------ -// Exports -//------------------------------------------------------------------------------ - -/** - * Provides the `parse` method for `.vue` files. - * - * @module vue-eslint-parser - */ -module.exports = { - /** - * Parses the source code. - * - * If `options.filePath` is a `.vue` file, this extracts the first `", + raw: "", + }) + } + } + + return result +} + +/** + * Parse the source code of inline scripts. + * @param code The source code of inline scripts. + * @param locationCalculator The location calculator for the inline script. + * @param parserOptions The parser options. + * @returns The result of parsing. + */ +export function parseExpression(code: string, locationCalculator: LocationCalculator, parserOptions: any): ExpressionParseResult { + debug("[script] parse expression: \"(%s)\"", code) + + const ast = parseScriptFragment( + `(${code})`, + locationCalculator.getSubCalculatorAfter(-1), + parserOptions + ).ast + const references = analyzeExternalReferences(ast, parserOptions) + const expression = (ast.body[0] as ESLintExpressionStatement).expression + const tokens = ast.tokens || [] + const comments = ast.comments || [] + + // Remvoe parens. + tokens.shift() + tokens.pop() + + return {expression, tokens, comments, references, variables: []} +} + +/** + * Parse the source code of inline scripts. + * @param code The source code of inline scripts. + * @param locationCalculator The location calculator for the inline script. + * @param parserOptions The parser options. + * @returns The result of parsing. + */ +export function parseVForExpression(code: string, locationCalculator: LocationCalculator, parserOptions: any): ExpressionParseResult { + const processedCode = replaceAliasParens(code) + debug("[script] parse v-for expression: \"for(%s);\"", processedCode) + + const replaced = processedCode !== code + const ast = parseScriptFragment( + `for(let ${processedCode});`, + locationCalculator.getSubCalculatorAfter(-8), + parserOptions + ).ast + const tokens = ast.tokens || [] + const comments = ast.comments || [] + const scope = analyzeVariablesAndExternalReferences(ast, parserOptions) + const references = scope.references + const variables = scope.variables + const statement = ast.body[0] as (ESLintForInStatement | ESLintForOfStatement) + const left = normalizeLeft(statement.left, replaced) + const right = statement.right + const firstToken = tokens[3] || statement.left + const lastToken = tokens[tokens.length - 3] || statement.right + const expression: VForExpression = { + type: "VForExpression", + range: [firstToken.range[0], lastToken.range[1]], + loc: {start: firstToken.loc.start, end: lastToken.loc.end}, + parent: {} as VExpressionContainer, + left, + right, + } + + // Remvoe `for` `(` `let` `)` `;`. + tokens.shift() + tokens.shift() + tokens.shift() + tokens.pop() + tokens.pop() + + // Restore parentheses from array brackets. + if (replaced) { + const open = statement.left.range[0] + const close = statement.left.range[1] - 1 + + for (const token of tokens) { + if (token.range[0] === open) { + token.value = "(" + } + else if (token.range[0] === close) { + token.value = ")" + break + } + } + } + + return {expression, tokens, comments, references, variables} +} diff --git a/src/script/scope-analyzer.ts b/src/script/scope-analyzer.ts new file mode 100644 index 00000000..d023e6be --- /dev/null +++ b/src/script/scope-analyzer.ts @@ -0,0 +1,101 @@ +/** + * @author Toru Nagashima + * @copyright 2017 Toru Nagashima. All rights reserved. + * See LICENSE file in root directory for full license. + */ +import * as eslintScope from "eslint-scope" +import {ESLintIdentifier, ESLintProgram, Reference, Variable, getFallbackKeys} from "../ast" + +/** + * Check whether the given reference is unique in the belonging array. + * @param reference The current reference to check. + * @param index The index of the reference. + * @param references The belonging array of the reference. + */ +function isUnique(reference: eslintScope.Reference, index: number, references: eslintScope.Reference[]): boolean { + return (index === 0) || (reference.identifier !== references[index - 1].identifier) +} + +/** + * Transform the given reference object. + * @param reference The source reference object. + * @returns The transformed reference object. + */ +function transformReference(reference: eslintScope.Reference): Reference { + return { + id: reference.identifier as ESLintIdentifier, + mode: ( + reference.isReadOnly() ? "r" : + reference.isWriteOnly() ? "w" : + /* otherwise */ "rw" + ), + } +} + +/** + * Transform the given variable object. + * @param variable The source variable object. + * @returns The transformed variable object. + */ +function transformVariable(variable: eslintScope.Variable): Variable { + return { + id: variable.defs[0].name as ESLintIdentifier, + kind: "v-for", + } +} + +/** + * Get the `for` statement scope. + * @param scope The global scope. + * @returns The `for` statement scope. + */ +function getForScope(scope: eslintScope.Scope): eslintScope.Scope { + if (scope.childScopes[0].type === "module") { + scope = scope.childScopes[0] + } + return scope.childScopes[0] +} + +/** + * + * @param ast + * @param parserOptions + */ +function analyze(ast: ESLintProgram, parserOptions: any): eslintScope.Scope { + const ecmaVersion = parserOptions.ecmaVersion || 2017 + const ecmaFeatures = parserOptions.ecmaFeatures || {} + const sourceType = parserOptions.sourceType || "script" + const result = eslintScope.analyze(ast, { + ignoreEval: true, + nodejsScope: false, + impliedStrict: ecmaFeatures.impliedStrict, + ecmaVersion, + sourceType, + fallback: getFallbackKeys, + }) + + return result.globalScope +} + +/** + * Analyze the external references of the given AST. + * @param {ASTNode} ast The root node to analyze. + * @returns {Reference[]} The reference objects of external references. + */ +export function analyzeExternalReferences(ast: ESLintProgram, parserOptions: any): Reference[] { + const scope = analyze(ast, parserOptions) + return scope.through.filter(isUnique).map(transformReference) +} + +/** + * Analyze the external references of the given AST. + * @param {ASTNode} ast The root node to analyze. + * @returns {Reference[]} The reference objects of external references. + */ +export function analyzeVariablesAndExternalReferences(ast: ESLintProgram, parserOptions: any): {variables: Variable[], references: Reference[]} { + const scope = analyze(ast, parserOptions) + return { + variables: getForScope(scope).variables.map(transformVariable), + references: scope.through.filter(isUnique).map(transformReference), + } +} diff --git a/src/template/index.ts b/src/template/index.ts new file mode 100644 index 00000000..2c02b194 --- /dev/null +++ b/src/template/index.ts @@ -0,0 +1,580 @@ +/** + * @author Toru Nagashima + * @copyright 2017 Toru Nagashima. All rights reserved. + * See LICENSE file in root directory for full license. + */ +import lodash from "lodash" +import {HasConcreteInfo, HasLocation, ParseError, Reference, Token, Variable, VAttribute, VDirective, VDirectiveKey, VDocumentFragment, VElement, VExpressionContainer, VIdentifier, VLiteral, VText} from "../ast" +import {debug} from "../common/debug" +import {LocationCalculator} from "../common/location-calculator" +import {ExpressionParseResult, parseExpression, parseVForExpression} from "../script" + +const DIRECTIVE_NAME = /^(?:v-|[:@]).+[^.:@]$/ + +/** + * Extract the variable declarations of scope attributes. + * @param references The references which are variable declarations. + * @param outVariables The variable declarations. This is output. + */ +function extractScopeVariables(references: Reference[], outVariables: Variable[]): void { + let reference: Reference | undefined + while ((reference = references.shift()) != null) { + reference.id.parent = null + outVariables.push({id: reference.id, kind: "scope"}) + } +} + +/** + * Remove references by name. + * @param references The array of references to remove. + * @param name The name of target references. + */ +function removeByName(references: Reference[], name: string): void { + let i = 0 + while (i < references.length) { + const reference = references[i] + + if (reference.id.name === name) { + references.splice(i, 1) + } + else { + i += 1 + } + } +} + +/** + * Get the attribute which has the given name from the given element. + * @param node The element node to get. + * @param key The attribute name to get. + * @returns The found attribute or undefined. + */ +function getAttributeValue(node: VElement, key: string): string | undefined { + const attr = node.startTag.attributes.find(a => a.key.name === key) + return (attr && !attr.directive && attr.value) ? attr.value.value : undefined +} + +/** + * Get the belonging document of the given node. + * @param leafNode The node to get. + * @returns The belonging document. + */ +function getGlobalDocument(leafNode: VElement): VDocumentFragment | null { + let node: VElement | VDocumentFragment | null = leafNode + while (node != null && node.type !== "VDocumentFragment") { + node = node.parent + } + return node +} + +/** + * Parse the given attribute name as a directive key. + * @param node The identifier node to parse. + * @returns The directive key node. + */ +function createDirectiveKey(node: VIdentifier): VDirectiveKey { + let name = null + let argument = null + let modifiers = null + let shorthand = false + let remain = node.name + + if (remain.startsWith(":")) { + name = "bind" + shorthand = true + remain = remain.slice(1) + } + else if (remain.startsWith("@")) { + name = "on" + shorthand = true + remain = remain.slice(1) + } + else { + const colon = remain.indexOf(":") + if (colon !== -1) { + name = remain.slice(0, colon) + remain = remain.slice(colon + 1) + } + } + + const dotSplit = remain.split(".") + if (name == null) { + name = dotSplit[0] + } + else { + argument = dotSplit[0] + } + modifiers = dotSplit.slice(1) + + if (name.startsWith("v-")) { + name = name.slice(2) + } + + return { + type: "VDirectiveKey", + range: node.range, + loc: node.loc, + parent: node.parent, + name, + argument, + modifiers, + shorthand, + } +} + +/** + * Do splice. + * @param items The array to operate. + * @param start The start index. + * @param deleteCount The count of items to delete. + * @param newItems The array of items to insert. + */ +function splice(items: T[], start: number, deleteCount: number, newItems: T[]): void { + switch (newItems.length) { + case 0: + items.splice(start, deleteCount) + break + case 1: + items.splice(start, deleteCount, newItems[0]) + break + case 2: + items.splice(start, deleteCount, newItems[0], newItems[1]) + break + default: + Array.prototype.splice.apply( + items, + ([start, deleteCount] as any[]).concat(newItems) + ) + break + } +} + +/** + * Get `x.range[0]`. + * @param x The object to get. + * @returns `x.range[0]`. + */ +function byRange0(x: HasLocation): number { + return x.range[0] +} + +/** + * Get `x.range[1]`. + * @param x The object to get. + * @returns `x.range[1]`. + */ +function byRange1(x: HasLocation): number { + return x.range[1] +} + +/** + * Get `x.pos`. + * @param x The object to get. + * @returns `x.pos`. + */ +function byIndex(x: ParseError): number { + return x.index +} + +/** + * The template transformer to make expression containers. + */ +export class TemplateTransformer { + private code: string + private templateNode: VElement + private locationCalculator: LocationCalculator + private parserOptions: any + private tokens: Token[] + private comments: Token[] + private errors: ParseError[] + + /** + * Initialize this transformer. + * @param code The source code. + * @param node The `