diff --git a/src/index.js b/src/index.js index ee2c587e80..c2911f455f 100755 --- a/src/index.js +++ b/src/index.js @@ -11,8 +11,10 @@ import { types as tokTypes } from "./tokenizer/types"; import "./tokenizer"; import "./tokenizer/context"; +import estreePlugin from "./plugins/estree"; import flowPlugin from "./plugins/flow"; import jsxPlugin from "./plugins/jsx"; +plugins.estree = estreePlugin; plugins.flow = flowPlugin; plugins.jsx = jsxPlugin; diff --git a/src/parser/expression.js b/src/parser/expression.js index 2919c6f50d..dae8408552 100644 --- a/src/parser/expression.js +++ b/src/parser/expression.js @@ -776,6 +776,7 @@ pp.parseObj = function (isPattern, refShorthandDefaultPos) { const asyncId = this.parseIdentifier(); if (this.match(tt.colon) || this.match(tt.parenL) || this.match(tt.braceR) || this.match(tt.eq) || this.match(tt.comma)) { prop.key = asyncId; + prop.computed = false; } else { isAsync = true; if (this.hasPlugin("asyncGenerators")) isGenerator = this.eat(tt.star); diff --git a/src/parser/index.js b/src/parser/index.js index fc6a98bd4e..c3b6c742ac 100644 --- a/src/parser/index.js +++ b/src/parser/index.js @@ -55,8 +55,8 @@ export default class Parser extends Tokenizer { } loadAllPlugins() { - // ensure flow plugin loads last - const pluginNames = Object.keys(plugins).filter((name) => name !== "flow"); + // ensure flow plugin loads last, also ensure estree is not loaded with * + const pluginNames = Object.keys(plugins).filter((name) => name !== "flow" && name !== "estree"); pluginNames.push("flow"); pluginNames.forEach((name) => { @@ -81,6 +81,12 @@ export default class Parser extends Tokenizer { pluginList.push("flow"); } + if (pluginList.indexOf("estree") >= 0) { + // ensure estree plugin loads first + pluginList = pluginList.filter((plugin) => plugin !== "estree"); + pluginList.unshift("estree"); + } + for (const name of pluginList) { if (!pluginMap[name]) { pluginMap[name] = true; diff --git a/src/plugins/estree.js b/src/plugins/estree.js new file mode 100644 index 0000000000..5a78438108 --- /dev/null +++ b/src/plugins/estree.js @@ -0,0 +1,242 @@ +import { types as tt } from "../tokenizer/types"; +import Parser from "../parser"; + +const pp = Parser.prototype; + +pp.estreeParseRegExpLiteral = function ({ pattern, flags }) { + let regex = null; + try { + regex = new RegExp(pattern, flags); + } catch (e) { + // In environments that don't support these flags value will + // be null as the regex can't be represented natively. + } + const node = this.estreeParseLiteral(regex); + node.regex = { pattern, flags }; + + return node; +}; + +pp.estreeParseLiteral = function (value) { + const node = this.parseLiteral(value, "Literal"); + node.raw = node.extra.raw; + delete node.extra; + + return node; +}; + +pp.directiveToStmt = function (directive) { + const directiveLiteral = directive.value; + + const stmt = this.startNodeAt(directive.start, directive.loc.start); + const expression = this.startNodeAt(directiveLiteral.start, directiveLiteral.loc.start); + + expression.value = directiveLiteral.value; + expression.raw = directiveLiteral.extra.raw; + + stmt.expression = this.finishNodeAt(expression, "Literal", directiveLiteral.end, directiveLiteral.loc.end); + stmt.directive = directiveLiteral.extra.raw.slice(1, -1); + + return this.finishNodeAt(stmt, "ExpressionStatement", directive.end, directive.loc.end); +}; + +function isSimpleProperty(node) { + return node && + node.type === "Property" && + node.kind === "init" && + node.method === false; +} + +export default function (instance) { + instance.extend("checkDeclaration", function(inner) { + return function (node) { + if (isSimpleProperty(node)) { + this.checkDeclaration(node.value); + } else { + inner.call(this, node); + } + }; + }); + + instance.extend("checkGetterSetterParamCount", function() { + return function (prop) { + const paramCount = prop.kind === "get" ? 0 : 1; + if (prop.value.params.length !== paramCount) { + const start = prop.start; + if (prop.kind === "get") { + this.raise(start, "getter should have no params"); + } else { + this.raise(start, "setter should have exactly one param"); + } + } + }; + }); + + instance.extend("checkLVal", function(inner) { + return function (expr, isBinding, checkClashes, ...args) { + switch (expr.type) { + case "ObjectPattern": + expr.properties.forEach((prop) => { + this.checkLVal( + prop.type === "Property" ? prop.value : prop, + isBinding, + checkClashes, + "object destructuring pattern" + ); + }); + break; + default: + inner.call(this, expr, isBinding, checkClashes, ...args); + } + }; + }); + + instance.extend("checkPropClash", function () { + return function (prop, propHash) { + if (prop.computed || !isSimpleProperty(prop)) return; + + const key = prop.key; + // It is either an Identifier or a String/NumericLiteral + const name = key.type === "Identifier" ? key.name : String(key.value); + + if (name === "__proto__") { + if (propHash.proto) this.raise(key.start, "Redefinition of __proto__ property"); + propHash.proto = true; + } + }; + }); + + instance.extend("isStrictBody", function () { + return function (node, isExpression) { + if (!isExpression && node.body.body.length > 0) { + for (const directive of (node.body.body: Array)) { + if (directive.type === "ExpressionStatement" && directive.expression.type === "Literal") { + if (directive.expression.value === "use strict") return true; + } else { + // Break for the first non literal expression + break; + } + } + } + + return false; + }; + }); + + instance.extend("isValidDirective", function () { + return function (stmt) { + return stmt.type === "ExpressionStatement" && + stmt.expression.type === "Literal" && + typeof stmt.expression.value === "string" && + (!stmt.expression.extra || !stmt.expression.extra.parenthesized); + }; + }); + + instance.extend("parseBlockBody", function (inner) { + return function (node, ...args) { + inner.call(this, node, ...args); + + node.directives.reverse().forEach((directive) => { + node.body.unshift(this.directiveToStmt(directive)); + }); + delete node.directives; + }; + }); + + instance.extend("parseClassMethod", function (inner) { + return function (classBody, ...args) { + inner.call(this, classBody, ...args); + + const body = classBody.body; + body[body.length - 1].type = "MethodDefinition"; + }; + }); + + instance.extend("parseExprAtom", function(inner) { + return function (...args) { + switch (this.state.type) { + case tt.regexp: + return this.estreeParseRegExpLiteral(this.state.value); + + case tt.num: + case tt.string: + return this.estreeParseLiteral(this.state.value); + + case tt._null: + return this.estreeParseLiteral(null); + + case tt._true: + return this.estreeParseLiteral(true); + + case tt._false: + return this.estreeParseLiteral(false); + + default: + return inner.call(this, ...args); + } + }; + }); + + instance.extend("parseMethod", function(inner) { + return function (node, ...args) { + let funcNode = this.startNode(); + funcNode.kind = node.kind; // provide kind, so inner method correctly sets state + funcNode = inner.call(this, funcNode, ...args); + delete funcNode.kind; + node.value = this.finishNode(funcNode, "FunctionExpression"); + + return node; + }; + }); + + instance.extend("parseObjectMethod", function(inner) { + return function (...args) { + const node = inner.call(this, ...args); + + if (node) { + if (node.kind === "method") node.kind = "init"; + node.type = "Property"; + } + + return node; + }; + }); + + instance.extend("parseObjectProperty", function(inner) { + return function (...args) { + const node = inner.call(this, ...args); + + if (node) { + node.kind = "init"; + node.type = "Property"; + } + + return node; + }; + }); + + instance.extend("toAssignable", function(inner) { + return function (node, isBinding, ...args) { + if (isSimpleProperty(node)) { + this.toAssignable(node.value, isBinding, ...args); + + return node; + } else if (node.type === "ObjectExpression") { + node.type = "ObjectPattern"; + for (const prop of (node.properties: Array)) { + if (prop.kind === "get" || prop.kind === "set") { + this.raise(prop.key.start, "Object pattern can't contain getter or setter"); + } else if (prop.method) { + this.raise(prop.key.start, "Object pattern can't contain methods"); + } else { + this.toAssignable(prop, isBinding, "object destructuring pattern"); + } + } + + return node; + } + + return inner.call(this, node, isBinding, ...args); + }; + }); +} diff --git a/test/estree-throws.js b/test/estree-throws.js new file mode 100644 index 0000000000..eba937611a --- /dev/null +++ b/test/estree-throws.js @@ -0,0 +1,5 @@ +import path from "path"; +import { runThrowTestsWithEstree } from "./utils/runFixtureTests"; +import { parse } from "../lib"; + +runThrowTestsWithEstree(path.join(__dirname, "fixtures"), parse); diff --git a/test/fixtures/estree/class-method/basic/actual.js b/test/fixtures/estree/class-method/basic/actual.js new file mode 100644 index 0000000000..8f68621150 --- /dev/null +++ b/test/fixtures/estree/class-method/basic/actual.js @@ -0,0 +1,3 @@ +class A { + foo() {} +} diff --git a/test/fixtures/estree/class-method/basic/expected.json b/test/fixtures/estree/class-method/basic/expected.json new file mode 100644 index 0000000000..87805524b4 --- /dev/null +++ b/test/fixtures/estree/class-method/basic/expected.json @@ -0,0 +1,154 @@ +{ + "type": "File", + "start": 0, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 1 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 1 + } + }, + "sourceType": "script", + "body": [ + { + "type": "ClassDeclaration", + "start": 0, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 1 + } + }, + "id": { + "type": "Identifier", + "start": 6, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 6 + }, + "end": { + "line": 1, + "column": 7 + }, + "identifierName": "A" + }, + "name": "A" + }, + "superClass": null, + "body": { + "type": "ClassBody", + "start": 8, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 8 + }, + "end": { + "line": 3, + "column": 1 + } + }, + "body": [ + { + "type": "MethodDefinition", + "start": 12, + "end": 20, + "loc": { + "start": { + "line": 2, + "column": 2 + }, + "end": { + "line": 2, + "column": 10 + } + }, + "computed": false, + "key": { + "type": "Identifier", + "start": 12, + "end": 15, + "loc": { + "start": { + "line": 2, + "column": 2 + }, + "end": { + "line": 2, + "column": 5 + }, + "identifierName": "foo" + }, + "name": "foo" + }, + "static": false, + "kind": "method", + "value": { + "type": "FunctionExpression", + "start": 15, + "end": 20, + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 2, + "column": 10 + } + }, + "id": null, + "generator": false, + "async": false, + "params": [], + "body": { + "type": "BlockStatement", + "start": 18, + "end": 20, + "loc": { + "start": { + "line": 2, + "column": 8 + }, + "end": { + "line": 2, + "column": 10 + } + }, + "body": [] + }, + "expression": false + } + } + ] + } + } + ] + } +} diff --git a/test/fixtures/estree/directives/block/actual.js b/test/fixtures/estree/directives/block/actual.js new file mode 100644 index 0000000000..8d54ee0f82 --- /dev/null +++ b/test/fixtures/estree/directives/block/actual.js @@ -0,0 +1,5 @@ +function foo() { + "use strict"; + var a = 1; + "use strict"; +} diff --git a/test/fixtures/estree/directives/block/expected.json b/test/fixtures/estree/directives/block/expected.json new file mode 100644 index 0000000000..1370348bc2 --- /dev/null +++ b/test/fixtures/estree/directives/block/expected.json @@ -0,0 +1,218 @@ +{ + "type": "File", + "start": 0, + "end": 63, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 5, + "column": 1 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 63, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 5, + "column": 1 + } + }, + "sourceType": "script", + "body": [ + { + "type": "FunctionDeclaration", + "start": 0, + "end": 63, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 5, + "column": 1 + } + }, + "id": { + "type": "Identifier", + "start": 9, + "end": 12, + "loc": { + "start": { + "line": 1, + "column": 9 + }, + "end": { + "line": 1, + "column": 12 + }, + "identifierName": "foo" + }, + "name": "foo" + }, + "generator": false, + "expression": false, + "async": false, + "params": [], + "body": { + "type": "BlockStatement", + "start": 15, + "end": 63, + "loc": { + "start": { + "line": 1, + "column": 15 + }, + "end": { + "line": 5, + "column": 1 + } + }, + "body": [ + { + "type": "ExpressionStatement", + "start": 19, + "end": 32, + "loc": { + "start": { + "line": 2, + "column": 2 + }, + "end": { + "line": 2, + "column": 15 + } + }, + "expression": { + "type": "Literal", + "start": 19, + "end": 31, + "loc": { + "start": { + "line": 2, + "column": 2 + }, + "end": { + "line": 2, + "column": 14 + } + }, + "value": "use strict", + "raw": "\"use strict\"" + }, + "directive": "use strict" + }, + { + "type": "VariableDeclaration", + "start": 35, + "end": 45, + "loc": { + "start": { + "line": 3, + "column": 2 + }, + "end": { + "line": 3, + "column": 12 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 39, + "end": 44, + "loc": { + "start": { + "line": 3, + "column": 6 + }, + "end": { + "line": 3, + "column": 11 + } + }, + "id": { + "type": "Identifier", + "start": 39, + "end": 40, + "loc": { + "start": { + "line": 3, + "column": 6 + }, + "end": { + "line": 3, + "column": 7 + }, + "identifierName": "a" + }, + "name": "a" + }, + "init": { + "type": "Literal", + "start": 43, + "end": 44, + "loc": { + "start": { + "line": 3, + "column": 10 + }, + "end": { + "line": 3, + "column": 11 + } + }, + "value": 1, + "raw": "1" + } + } + ], + "kind": "var" + }, + { + "type": "ExpressionStatement", + "start": 48, + "end": 61, + "loc": { + "start": { + "line": 4, + "column": 2 + }, + "end": { + "line": 4, + "column": 15 + } + }, + "expression": { + "type": "Literal", + "start": 48, + "end": 60, + "loc": { + "start": { + "line": 4, + "column": 2 + }, + "end": { + "line": 4, + "column": 14 + } + }, + "value": "use strict", + "raw": "\"use strict\"" + } + } + ] + } + } + ] + } +} \ No newline at end of file diff --git a/test/fixtures/estree/directives/function-non-strict/actual.js b/test/fixtures/estree/directives/function-non-strict/actual.js new file mode 100644 index 0000000000..783e003dd3 --- /dev/null +++ b/test/fixtures/estree/directives/function-non-strict/actual.js @@ -0,0 +1,4 @@ +function foo () { + "use smth" + 1+1; +} diff --git a/test/fixtures/estree/directives/function-non-strict/expected.json b/test/fixtures/estree/directives/function-non-strict/expected.json new file mode 100644 index 0000000000..9e22c6306c --- /dev/null +++ b/test/fixtures/estree/directives/function-non-strict/expected.json @@ -0,0 +1,184 @@ +{ + "type": "File", + "start": 0, + "end": 39, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 4, + "column": 1 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 39, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 4, + "column": 1 + } + }, + "sourceType": "script", + "body": [ + { + "type": "FunctionDeclaration", + "start": 0, + "end": 39, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 4, + "column": 1 + } + }, + "id": { + "type": "Identifier", + "start": 9, + "end": 12, + "loc": { + "start": { + "line": 1, + "column": 9 + }, + "end": { + "line": 1, + "column": 12 + }, + "identifierName": "foo" + }, + "name": "foo" + }, + "generator": false, + "expression": false, + "async": false, + "params": [], + "body": { + "type": "BlockStatement", + "start": 16, + "end": 39, + "loc": { + "start": { + "line": 1, + "column": 16 + }, + "end": { + "line": 4, + "column": 1 + } + }, + "body": [ + { + "type": "ExpressionStatement", + "start": 20, + "end": 30, + "loc": { + "start": { + "line": 2, + "column": 2 + }, + "end": { + "line": 2, + "column": 12 + } + }, + "expression": { + "type": "Literal", + "start": 20, + "end": 30, + "loc": { + "start": { + "line": 2, + "column": 2 + }, + "end": { + "line": 2, + "column": 12 + } + }, + "value": "use smth", + "raw": "\"use smth\"" + }, + "directive": "use smth" + }, + { + "type": "ExpressionStatement", + "start": 33, + "end": 37, + "loc": { + "start": { + "line": 3, + "column": 2 + }, + "end": { + "line": 3, + "column": 6 + } + }, + "expression": { + "type": "BinaryExpression", + "start": 33, + "end": 36, + "loc": { + "start": { + "line": 3, + "column": 2 + }, + "end": { + "line": 3, + "column": 5 + } + }, + "left": { + "type": "Literal", + "start": 33, + "end": 34, + "loc": { + "start": { + "line": 3, + "column": 2 + }, + "end": { + "line": 3, + "column": 3 + } + }, + "value": 1, + "raw": "1" + }, + "operator": "+", + "right": { + "type": "Literal", + "start": 35, + "end": 36, + "loc": { + "start": { + "line": 3, + "column": 4 + }, + "end": { + "line": 3, + "column": 5 + } + }, + "value": 1, + "raw": "1" + } + } + } + ] + } + } + ] + } +} \ No newline at end of file diff --git a/test/fixtures/estree/directives/program/actual.js b/test/fixtures/estree/directives/program/actual.js new file mode 100644 index 0000000000..6603b9c5c2 --- /dev/null +++ b/test/fixtures/estree/directives/program/actual.js @@ -0,0 +1,3 @@ +"use strict"; +var a = 1; +"use strict"; diff --git a/test/fixtures/estree/directives/program/expected.json b/test/fixtures/estree/directives/program/expected.json new file mode 100644 index 0000000000..d9fc1c0ad6 --- /dev/null +++ b/test/fixtures/estree/directives/program/expected.json @@ -0,0 +1,165 @@ +{ + "type": "File", + "start": 0, + "end": 38, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 13 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 38, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 13 + } + }, + "sourceType": "script", + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 13, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 13 + } + }, + "expression": { + "type": "Literal", + "start": 0, + "end": 12, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 12 + } + }, + "value": "use strict", + "raw": "\"use strict\"" + }, + "directive": "use strict" + }, + { + "type": "VariableDeclaration", + "start": 14, + "end": 24, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 10 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 18, + "end": 23, + "loc": { + "start": { + "line": 2, + "column": 4 + }, + "end": { + "line": 2, + "column": 9 + } + }, + "id": { + "type": "Identifier", + "start": 18, + "end": 19, + "loc": { + "start": { + "line": 2, + "column": 4 + }, + "end": { + "line": 2, + "column": 5 + }, + "identifierName": "a" + }, + "name": "a" + }, + "init": { + "type": "Literal", + "start": 22, + "end": 23, + "loc": { + "start": { + "line": 2, + "column": 8 + }, + "end": { + "line": 2, + "column": 9 + } + }, + "value": 1, + "raw": "1" + } + } + ], + "kind": "var" + }, + { + "type": "ExpressionStatement", + "start": 25, + "end": 38, + "loc": { + "start": { + "line": 3, + "column": 0 + }, + "end": { + "line": 3, + "column": 13 + } + }, + "expression": { + "type": "Literal", + "start": 25, + "end": 37, + "loc": { + "start": { + "line": 3, + "column": 0 + }, + "end": { + "line": 3, + "column": 12 + } + }, + "value": "use strict", + "raw": "\"use strict\"" + } + } + ] + } +} \ No newline at end of file diff --git a/test/fixtures/estree/directives/raw/actual.js b/test/fixtures/estree/directives/raw/actual.js new file mode 100644 index 0000000000..10ecb3d5fa --- /dev/null +++ b/test/fixtures/estree/directives/raw/actual.js @@ -0,0 +1 @@ +"use\x20strict"; diff --git a/test/fixtures/estree/directives/raw/expected.json b/test/fixtures/estree/directives/raw/expected.json new file mode 100644 index 0000000000..7467c2bffc --- /dev/null +++ b/test/fixtures/estree/directives/raw/expected.json @@ -0,0 +1,66 @@ +{ + "type": "File", + "start": 0, + "end": 16, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 16 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 16, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 16 + } + }, + "sourceType": "script", + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 16, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 16 + } + }, + "expression": { + "type": "Literal", + "start": 0, + "end": 15, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 15 + } + }, + "value": "use\\x20strict", + "raw": "\"use\\x20strict\"" + }, + "directive": "use\\x20strict" + } + ] + } +} \ No newline at end of file diff --git a/test/fixtures/estree/literal/boolean/actual.js b/test/fixtures/estree/literal/boolean/actual.js new file mode 100644 index 0000000000..5328fe8a56 --- /dev/null +++ b/test/fixtures/estree/literal/boolean/actual.js @@ -0,0 +1,2 @@ +var a = true; +var b = false; diff --git a/test/fixtures/estree/literal/boolean/expected.json b/test/fixtures/estree/literal/boolean/expected.json new file mode 100644 index 0000000000..9cb28a78b5 --- /dev/null +++ b/test/fixtures/estree/literal/boolean/expected.json @@ -0,0 +1,167 @@ +{ + "type": "File", + "start": 0, + "end": 28, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 14 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 28, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 14 + } + }, + "sourceType": "script", + "body": [ + { + "type": "VariableDeclaration", + "start": 0, + "end": 13, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 13 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 4, + "end": 12, + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 12 + } + }, + "id": { + "type": "Identifier", + "start": 4, + "end": 5, + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 5 + }, + "identifierName": "a" + }, + "name": "a" + }, + "init": { + "type": "Literal", + "start": 8, + "end": 12, + "loc": { + "start": { + "line": 1, + "column": 8 + }, + "end": { + "line": 1, + "column": 12 + } + }, + "value": true, + "raw": "true" + } + } + ], + "kind": "var" + }, + { + "type": "VariableDeclaration", + "start": 14, + "end": 28, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 14 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 18, + "end": 27, + "loc": { + "start": { + "line": 2, + "column": 4 + }, + "end": { + "line": 2, + "column": 13 + } + }, + "id": { + "type": "Identifier", + "start": 18, + "end": 19, + "loc": { + "start": { + "line": 2, + "column": 4 + }, + "end": { + "line": 2, + "column": 5 + }, + "identifierName": "b" + }, + "name": "b" + }, + "init": { + "type": "Literal", + "start": 22, + "end": 27, + "loc": { + "start": { + "line": 2, + "column": 8 + }, + "end": { + "line": 2, + "column": 13 + } + }, + "value": false, + "raw": "false" + } + } + ], + "kind": "var" + } + ] + } +} diff --git a/test/fixtures/estree/literal/null/actual.js b/test/fixtures/estree/literal/null/actual.js new file mode 100644 index 0000000000..cb9da3d57c --- /dev/null +++ b/test/fixtures/estree/literal/null/actual.js @@ -0,0 +1 @@ +var a = null; diff --git a/test/fixtures/estree/literal/null/expected.json b/test/fixtures/estree/literal/null/expected.json new file mode 100644 index 0000000000..3d8f657e41 --- /dev/null +++ b/test/fixtures/estree/literal/null/expected.json @@ -0,0 +1,100 @@ +{ + "type": "File", + "start": 0, + "end": 13, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 13 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 13, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 13 + } + }, + "sourceType": "script", + "body": [ + { + "type": "VariableDeclaration", + "start": 0, + "end": 13, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 13 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 4, + "end": 12, + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 12 + } + }, + "id": { + "type": "Identifier", + "start": 4, + "end": 5, + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 5 + }, + "identifierName": "a" + }, + "name": "a" + }, + "init": { + "type": "Literal", + "start": 8, + "end": 12, + "loc": { + "start": { + "line": 1, + "column": 8 + }, + "end": { + "line": 1, + "column": 12 + } + }, + "value": null, + "raw": "null" + } + } + ], + "kind": "var" + } + ] + } +} diff --git a/test/fixtures/estree/literal/number/actual.js b/test/fixtures/estree/literal/number/actual.js new file mode 100644 index 0000000000..f688910ec0 --- /dev/null +++ b/test/fixtures/estree/literal/number/actual.js @@ -0,0 +1 @@ +var a = 1; diff --git a/test/fixtures/estree/literal/number/expected.json b/test/fixtures/estree/literal/number/expected.json new file mode 100644 index 0000000000..4a0f809cc1 --- /dev/null +++ b/test/fixtures/estree/literal/number/expected.json @@ -0,0 +1,100 @@ +{ + "type": "File", + "start": 0, + "end": 10, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 10 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 10, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 10 + } + }, + "sourceType": "script", + "body": [ + { + "type": "VariableDeclaration", + "start": 0, + "end": 10, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 10 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 4, + "end": 9, + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 9 + } + }, + "id": { + "type": "Identifier", + "start": 4, + "end": 5, + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 5 + }, + "identifierName": "a" + }, + "name": "a" + }, + "init": { + "type": "Literal", + "start": 8, + "end": 9, + "loc": { + "start": { + "line": 1, + "column": 8 + }, + "end": { + "line": 1, + "column": 9 + } + }, + "value": 1, + "raw": "1" + } + } + ], + "kind": "var" + } + ] + } +} diff --git a/test/fixtures/estree/literal/regexp/actual.js b/test/fixtures/estree/literal/regexp/actual.js new file mode 100644 index 0000000000..96e83810cc --- /dev/null +++ b/test/fixtures/estree/literal/regexp/actual.js @@ -0,0 +1 @@ +var a = /.*/i; diff --git a/test/fixtures/estree/literal/regexp/expected.json b/test/fixtures/estree/literal/regexp/expected.json new file mode 100644 index 0000000000..1cc7f34829 --- /dev/null +++ b/test/fixtures/estree/literal/regexp/expected.json @@ -0,0 +1,104 @@ +{ + "type": "File", + "start": 0, + "end": 14, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 14 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 14, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 14 + } + }, + "sourceType": "script", + "body": [ + { + "type": "VariableDeclaration", + "start": 0, + "end": 14, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 14 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 4, + "end": 13, + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 13 + } + }, + "id": { + "type": "Identifier", + "start": 4, + "end": 5, + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 5 + }, + "identifierName": "a" + }, + "name": "a" + }, + "init": { + "type": "Literal", + "start": 8, + "end": 13, + "loc": { + "start": { + "line": 1, + "column": 8 + }, + "end": { + "line": 1, + "column": 13 + } + }, + "value": "/.*/i", + "raw": "/.*/i", + "regex": { + "pattern": ".*", + "flags": "i" + } + } + } + ], + "kind": "var" + } + ] + } +} diff --git a/test/fixtures/estree/literal/string/actual.js b/test/fixtures/estree/literal/string/actual.js new file mode 100644 index 0000000000..f85f0903c4 --- /dev/null +++ b/test/fixtures/estree/literal/string/actual.js @@ -0,0 +1 @@ +var a = "string"; diff --git a/test/fixtures/estree/literal/string/expected.json b/test/fixtures/estree/literal/string/expected.json new file mode 100644 index 0000000000..eb0a80831e --- /dev/null +++ b/test/fixtures/estree/literal/string/expected.json @@ -0,0 +1,100 @@ +{ + "type": "File", + "start": 0, + "end": 17, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 17 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 17, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 17 + } + }, + "sourceType": "script", + "body": [ + { + "type": "VariableDeclaration", + "start": 0, + "end": 17, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 17 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 4, + "end": 16, + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 16 + } + }, + "id": { + "type": "Identifier", + "start": 4, + "end": 5, + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 5 + }, + "identifierName": "a" + }, + "name": "a" + }, + "init": { + "type": "Literal", + "start": 8, + "end": 16, + "loc": { + "start": { + "line": 1, + "column": 8 + }, + "end": { + "line": 1, + "column": 16 + } + }, + "value": "string", + "raw": "\"string\"" + } + } + ], + "kind": "var" + } + ] + } +} diff --git a/test/fixtures/estree/object-method/basic/actual.js b/test/fixtures/estree/object-method/basic/actual.js new file mode 100644 index 0000000000..644adbe09e --- /dev/null +++ b/test/fixtures/estree/object-method/basic/actual.js @@ -0,0 +1,3 @@ +var bar = { + foo() {} +}; diff --git a/test/fixtures/estree/object-method/basic/expected.json b/test/fixtures/estree/object-method/basic/expected.json new file mode 100644 index 0000000000..03f4b2182c --- /dev/null +++ b/test/fixtures/estree/object-method/basic/expected.json @@ -0,0 +1,172 @@ +{ + "type": "File", + "start": 0, + "end": 25, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 2 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 25, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 2 + } + }, + "sourceType": "script", + "body": [ + { + "type": "VariableDeclaration", + "start": 0, + "end": 25, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 2 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 4, + "end": 24, + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 3, + "column": 1 + } + }, + "id": { + "type": "Identifier", + "start": 4, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 7 + }, + "identifierName": "bar" + }, + "name": "bar" + }, + "init": { + "type": "ObjectExpression", + "start": 10, + "end": 24, + "loc": { + "start": { + "line": 1, + "column": 10 + }, + "end": { + "line": 3, + "column": 1 + } + }, + "properties": [ + { + "type": "Property", + "start": 14, + "end": 22, + "loc": { + "start": { + "line": 2, + "column": 2 + }, + "end": { + "line": 2, + "column": 10 + } + }, + "method": true, + "shorthand": false, + "computed": false, + "key": { + "type": "Identifier", + "start": 14, + "end": 17, + "loc": { + "start": { + "line": 2, + "column": 2 + }, + "end": { + "line": 2, + "column": 5 + }, + "identifierName": "foo" + }, + "name": "foo" + }, + "kind": "init", + "value": { + "type": "FunctionExpression", + "start": 17, + "end": 22, + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 2, + "column": 10 + } + }, + "id": null, + "generator": false, + "expression": false, + "async": false, + "params": [], + "body": { + "type": "BlockStatement", + "start": 20, + "end": 22, + "loc": { + "start": { + "line": 2, + "column": 8 + }, + "end": { + "line": 2, + "column": 10 + } + }, + "body": [] + } + } + } + ] + } + } + ], + "kind": "var" + } + ] + } +} \ No newline at end of file diff --git a/test/fixtures/estree/object-property/basic/actual.js b/test/fixtures/estree/object-property/basic/actual.js new file mode 100644 index 0000000000..b5322b9476 --- /dev/null +++ b/test/fixtures/estree/object-property/basic/actual.js @@ -0,0 +1 @@ +const a = { foo: 1 }; diff --git a/test/fixtures/estree/object-property/basic/expected.json b/test/fixtures/estree/object-property/basic/expected.json new file mode 100644 index 0000000000..61a0aaeb62 --- /dev/null +++ b/test/fixtures/estree/object-property/basic/expected.json @@ -0,0 +1,153 @@ +{ + "type": "File", + "start": 0, + "end": 21, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 21 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 21, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 21 + } + }, + "sourceType": "script", + "body": [ + { + "type": "VariableDeclaration", + "start": 0, + "end": 21, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 21 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 6, + "end": 20, + "loc": { + "start": { + "line": 1, + "column": 6 + }, + "end": { + "line": 1, + "column": 20 + } + }, + "id": { + "type": "Identifier", + "start": 6, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 6 + }, + "end": { + "line": 1, + "column": 7 + }, + "identifierName": "a" + }, + "name": "a" + }, + "init": { + "type": "ObjectExpression", + "start": 10, + "end": 20, + "loc": { + "start": { + "line": 1, + "column": 10 + }, + "end": { + "line": 1, + "column": 20 + } + }, + "properties": [ + { + "type": "Property", + "start": 12, + "end": 18, + "loc": { + "start": { + "line": 1, + "column": 12 + }, + "end": { + "line": 1, + "column": 18 + } + }, + "method": false, + "shorthand": false, + "computed": false, + "key": { + "type": "Identifier", + "start": 12, + "end": 15, + "loc": { + "start": { + "line": 1, + "column": 12 + }, + "end": { + "line": 1, + "column": 15 + }, + "identifierName": "foo" + }, + "name": "foo" + }, + "value": { + "type": "Literal", + "start": 17, + "end": 18, + "loc": { + "start": { + "line": 1, + "column": 17 + }, + "end": { + "line": 1, + "column": 18 + } + }, + "value": 1, + "raw": "1" + }, + "kind": "init" + } + ] + } + } + ], + "kind": "const" + } + ] + } +} diff --git a/test/fixtures/estree/options.json b/test/fixtures/estree/options.json new file mode 100644 index 0000000000..acae373c99 --- /dev/null +++ b/test/fixtures/estree/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["flow", "jsx", "estree"] +} diff --git a/test/index.js b/test/index.js index 93e5e2fb39..9e4b5d513a 100644 --- a/test/index.js +++ b/test/index.js @@ -1,5 +1,5 @@ import path from "path"; -import runFixtureTests from "./utils/runFixtureTests"; +import { runFixtureTests } from "./utils/runFixtureTests"; import { parse, parseExpression } from "../lib"; runFixtureTests(path.join(__dirname, "fixtures"), parse); diff --git a/test/utils/runFixtureTests.js b/test/utils/runFixtureTests.js index e3ea9499b7..c391632f84 100644 --- a/test/utils/runFixtureTests.js +++ b/test/utils/runFixtureTests.js @@ -1,13 +1,13 @@ var test = require("ava"); var getFixtures = require("babel-helper-fixtures").multiple; -module.exports = function runFixtureTests(fixturesPath, parseFunction) { +exports.runFixtureTests = function runFixtureTests(fixturesPath, parseFunction) { var fixtures = getFixtures(fixturesPath); Object.keys(fixtures).forEach(function (name) { fixtures[name].forEach(function (testSuite) { testSuite.tests.forEach(function (task) { - var testFn = task.disabled ? test.skip : test; + var testFn = task.disabled ? test.skip : task.options.only ? test.only : test; testFn(name + "/" + testSuite.title + "/" + task.title, function () { try { @@ -22,10 +22,41 @@ module.exports = function runFixtureTests(fixturesPath, parseFunction) { }); }; +exports.runThrowTestsWithEstree = function runThrowTestsWithEstree(fixturesPath, parseFunction) { + var fixtures = getFixtures(fixturesPath); + + Object.keys(fixtures).forEach(function (name) { + fixtures[name].forEach(function (testSuite) { + testSuite.tests.forEach(function (task) { + if (!task.options.throws) return; + + task.options.plugins = task.options.plugins || []; + task.options.plugins.push("estree"); + + var testFn = task.disabled ? test.skip : task.options.only ? test.only : test; + + testFn(name + "/" + testSuite.title + "/" + task.title, function () { + try { + return runTest(task, parseFunction); + } catch (err) { + err.message = task.actual.loc + ": " + err.message; + throw err; + } + }); + }); + }); + }); +}; + function save(test, ast) { delete ast.tokens; if (ast.comments && !ast.comments.length) delete ast.comments; + + // Ensure that RegExp are serialized as strings + const toJSON = RegExp.prototype.toJSON; + RegExp.prototype.toJSON = RegExp.prototype.toString; require("fs").writeFileSync(test.expect.loc, JSON.stringify(ast, null, " ")); + RegExp.prototype.toJSON = toJSON; } function runTest(test, parseFunction) { @@ -69,7 +100,8 @@ function runTest(test, parseFunction) { } function ppJSON(v) { - return v instanceof RegExp ? v.toString() : JSON.stringify(v, null, 2); + v = v instanceof RegExp ? v.toString() : v; + return JSON.stringify(v, null, 2); } function addPath(str, pt) { @@ -81,20 +113,20 @@ function addPath(str, pt) { } function misMatch(exp, act) { - if (!exp || !act || (typeof exp != "object") || (typeof act != "object")) { - if (exp !== act && typeof exp != "function") - return ppJSON(exp) + " !== " + ppJSON(act); - } else if (exp instanceof RegExp || act instanceof RegExp) { + if (exp instanceof RegExp || act instanceof RegExp) { var left = ppJSON(exp), right = ppJSON(act); if (left !== right) return left + " !== " + right; - } else if (exp.splice) { - if (!act.slice) return ppJSON(exp) + " != " + ppJSON(act); + } else if (Array.isArray(exp)) { + if (!Array.isArray(act)) return ppJSON(exp) + " != " + ppJSON(act); if (act.length != exp.length) return "array length mismatch " + exp.length + " != " + act.length; for (var i = 0; i < act.length; ++i) { var mis = misMatch(exp[i], act[i]); if (mis) return addPath(mis, i); } - } else { + } else if (!exp || !act || (typeof exp != "object") || (typeof act != "object")) { + if (exp !== act && typeof exp != "function") + return ppJSON(exp) + " !== " + ppJSON(act); + } else { for (var prop in exp) { var mis = misMatch(exp[prop], act[prop]); if (mis) return addPath(mis, prop);