diff --git a/eslint/babel-eslint-parser/acorn-to-esprima.js b/eslint/babel-eslint-parser/acorn-to-esprima.js
index ad39a897a4..66e1993c86 100644
--- a/eslint/babel-eslint-parser/acorn-to-esprima.js
+++ b/eslint/babel-eslint-parser/acorn-to-esprima.js
@@ -7,9 +7,24 @@ exports.toToken = function (token) {
if (type === tokTypes.name) {
token.type = "Identifier";
- } else if (type === tokTypes.semi || type === tokTypes.comma || type === tokTypes.parenL || type === tokTypes.parenR || type === tokTypes.braceL || type === tokTypes.braceR) {
+ } else if (type === tokTypes.semi || type === tokTypes.comma || type === tokTypes.parenL || type === tokTypes.parenR || type === tokTypes.braceL || type === tokTypes.braceR || type === tokTypes.slash || type === tokTypes.dot || type.isAssign) {
token.type = "Punctuator";
- token.value = type.type;
+ if (!token.value) {
+ token.value = type.type;
+ }
+ } else if (type === tokTypes.jsxTagStart) {
+ token.type = "Punctuator";
+ token.value = "<";
+ } else if (type === tokTypes.jsxTagEnd) {
+ token.type = "Punctuator";
+ token.value = ">";
+ } else if (type === tokTypes.jsxName) {
+ token.type = "JSXIdentifier";
+ } else if (type.keyword) {
+ token.type = "Keyword";
+ } else if (type === tokTypes.num) {
+ token.type = "Numeric";
+ token.value = String(token.value);
}
return token;
@@ -50,7 +65,12 @@ var astTransformVisitor = {
}
// classes
-
+
+ if (t.isClassDeclaration(node) || t.isClassExpression(node)) {
+ node.name = node.id;
+ delete node.id;
+ }
+
if (t.isReferencedIdentifier(node, parent, { name: "super" })) {
return t.inherits(t.thisExpression(), node);
}
@@ -60,22 +80,6 @@ var astTransformVisitor = {
this.remove();
}
- // JSX
-
- if (t.isJSXIdentifier(node)) {
- if (node.name === "this" && t.isReferenced(node, parent)) {
- return t.inherits(t.thisExpression(), node);
- } else if (!t.isJSXAttribute(parent) && !isCompatTag(node.name)) {
- node.type = "Identifier";
- } else {
- // just ignore this node as it'd be something like
or an attribute name
- }
- }
-
- if (t.isJSXMemberExpression(node)) {
- node.type = "MemberExpression";
- }
-
// functions
if (t.isFunction(node)) {
diff --git a/eslint/babel-eslint-parser/index.js b/eslint/babel-eslint-parser/index.js
index ca364bad9c..3def57ec38 100644
--- a/eslint/babel-eslint-parser/index.js
+++ b/eslint/babel-eslint-parser/index.js
@@ -21,7 +21,7 @@ function monkeypatch() {
var eslintLoc;
try {
- eslintLoc = require.resolve("eslint");
+ eslintLoc = Module._resolveFilename("eslint", module.parent);
} catch (err) {
throw new ReferenceError("couldn't resolve eslint");
}
@@ -43,6 +43,17 @@ function monkeypatch() {
opts.sourceType = "module";
return analyze.call(this, ast, opts)
};
+
+ var eslint = require(eslintLoc);
+ var getScope = eslint.linter.getScope;
+ eslint.linter.getScope = function () {
+ var scope = getScope.apply(this, arguments);
+ if (scope.type === "global" && !scope.__patchedWithModuleVariables) {
+ scope.__patchedWithModuleVariables = true;
+ scope.variables.push.apply(scope.variables, scope.childScopes[0].variables);
+ }
+ return scope;
+ };
}
exports.parse = function (code) {
diff --git a/eslint/babel-eslint-parser/package.json b/eslint/babel-eslint-parser/package.json
index e0a103db37..c064dadd48 100644
--- a/eslint/babel-eslint-parser/package.json
+++ b/eslint/babel-eslint-parser/package.json
@@ -16,5 +16,13 @@
"bugs": {
"url": "https://github.com/babel/babel-eslint/issues"
},
- "homepage": "https://github.com/babel/babel-eslint"
+ "homepage": "https://github.com/babel/babel-eslint",
+ "devDependencies": {
+ "eslint": "^0.15.1",
+ "espree": "^1.10.0",
+ "mocha": "^2.1.0"
+ },
+ "scripts": {
+ "test": "mocha"
+ }
}
diff --git a/eslint/babel-eslint-parser/test/babel-eslint.js b/eslint/babel-eslint-parser/test/babel-eslint.js
new file mode 100644
index 0000000000..f8365c1844
--- /dev/null
+++ b/eslint/babel-eslint-parser/test/babel-eslint.js
@@ -0,0 +1,90 @@
+var util = require("util");
+var espree = require("espree");
+var babelEslint = require("..");
+
+
+function assertSameAST(a, b, path) {
+ if (!path) {
+ path = [];
+ }
+
+ function error(text) {
+ throw new Error("At " + path.join(".") + ": " + text + ":\n" + util.inspect(a) + "\n" + util.inspect(b));
+ }
+
+ var typeA = a === null ? "null" : typeof a;
+ var typeB = b === null ? "null" : typeof b;
+ if (typeA !== typeB) {
+ error("have not the same type (" + typeA + " !== " + typeB + ")");
+ } else if (typeA === "object") {
+ var keysA = Object.keys(a);
+ var keysB = Object.keys(b);
+ keysA.sort();
+ keysB.sort();
+ while (true) {
+ var keyA = keysA.shift();
+
+ // Exception: ignore "end" and "start" outside "loc" properties
+ if ((keyA === "end" || keyA === "start") && path[path.length - 1] !== "loc") continue;
+ // Exception: ignore root "comments" property
+ if (keyA === "comments" && path.length === 0) continue;
+
+ var keyB = keysB.shift();
+
+ if (keyA === undefined && keyB === undefined) break;
+ if (keyA === undefined || keyA > keyB) error("first does not have key \"" + keyB + "\"");
+ if (keyB === undefined || keyA < keyB) error("second does not have key \"" + keyA + "\"");
+ path.push(keyA);
+ assertSameAST(a[keyA], b[keyB], path);
+ path.pop();
+ }
+ } else if (a !== b) {
+ error("are different (" + JSON.stringify(a) + " !== " + JSON.stringify(b) + ")");
+ }
+}
+
+function parseAndAssertSame(code) {
+ var esAST = espree.parse(code, {
+ ecmaFeatures: {
+ classes: true,
+ jsx: true
+ },
+ tokens: true,
+ loc: true,
+ range: true
+ });
+ var acornAST = babelEslint.parse(code);
+ assertSameAST(acornAST, esAST);
+}
+
+describe("acorn-to-esprima", function () {
+
+ it("simple expression", function () {
+ parseAndAssertSame("a = 1");
+ });
+
+ it("class declaration", function () {
+ parseAndAssertSame("class Foo {}");
+ });
+
+ it("class expression", function () {
+ parseAndAssertSame("var a = class Foo {}");
+ });
+
+ it("jsx expression", function () {
+ parseAndAssertSame("
");
+ });
+
+ it("jsx expression with 'this' as identifier", function () {
+ parseAndAssertSame("
");
+ });
+
+ it("jsx expression with a dynamic attribute", function () {
+ parseAndAssertSame("
");
+ });
+
+ it("jsx expression with a member expression as identifier", function () {
+ parseAndAssertSame("
");
+ });
+
+});
diff --git a/eslint/babel-eslint-parser/test/non-regression.js b/eslint/babel-eslint-parser/test/non-regression.js
new file mode 100644
index 0000000000..68f5113109
--- /dev/null
+++ b/eslint/babel-eslint-parser/test/non-regression.js
@@ -0,0 +1,145 @@
+/*eslint-env mocha*/
+"use strict";
+var eslint = require("eslint");
+
+function verifyAndAssertMessages(code, rules, expectedMessages) {
+ var messages = eslint.linter.verify(
+ code,
+ {
+ parser: require.resolve(".."),
+ rules: rules,
+ env: {
+ node: true
+ }
+ }
+ );
+
+ if (messages.length !== expectedMessages.length) {
+ throw new Error("Expected " + expectedMessages.length + " message(s), got " + messages.length);
+ }
+
+ messages.forEach(function (message, i) {
+ var formatedMessage = message.line + ":" + message.column + " " + message.message + (message.ruleId ? " " + message.ruleId : "");
+ if (formatedMessage !== expectedMessages[i]) {
+ throw new Error("Message " + i + " does not match:\nExpected: " + expectedMessages[i] + "\nActual: " + formatedMessage);
+ }
+ });
+}
+
+describe("verify", function () {
+
+ it("arrow function support (issue #1)", function () {
+ verifyAndAssertMessages(
+ "describe('stuff', () => {});",
+ {},
+ []
+ );
+ });
+
+ it("EOL validation (issue #2)", function () {
+ verifyAndAssertMessages(
+ "module.exports = \"something\";",
+ { "eol-last": 1, "semi": 1 },
+ [ "1:1 Newline required at end of file but not found. eol-last" ]
+ );
+ });
+
+ it("Readable error messages (issue #3)", function () {
+ verifyAndAssertMessages(
+ "{ , res }",
+ {},
+ [ "1:2 Unexpected token" ]
+ );
+ });
+
+ it("Unused vars in JSX (issue #5)", function () {
+ verifyAndAssertMessages(
+ "var App = require('./App');\n" +
+ "module.exports =
;",
+ { "no-unused-vars": 1 },
+ []
+ );
+ });
+
+ it("Modules support (issue #5)", function () {
+ verifyAndAssertMessages(
+ "import Foo from 'foo';\n" +
+ "export default Foo;",
+ { },
+ []
+ );
+ });
+
+ it("Rest parameters (issue #7)", function () {
+ verifyAndAssertMessages(
+ "function foo(...args) { return args; }",
+ { "no-undef": 1 },
+ []
+ );
+ });
+
+ it("Exported classes should be used (issue #8)", function () {
+ verifyAndAssertMessages(
+ "class Foo {} module.exports = Foo;",
+ { "no-unused-vars": 1 },
+ []
+ );
+ });
+
+ it("super keyword in class (issue #10)", function () {
+ verifyAndAssertMessages(
+ "class Foo { constructor() { super() } }",
+ { "no-undef": 1 },
+ []
+ );
+ });
+
+ it("Rest parameter in destructuring assignment (issue #11)", function () {
+ verifyAndAssertMessages(
+ "const [a, ...rest] = ['1', '2', '3']; module.exports = rest;",
+ { "no-undef": 1 },
+ []
+ );
+ });
+
+ it("JSX attribute names marked as variables (issue #12)", function () {
+ verifyAndAssertMessages(
+ "module.exports =
",
+ { "no-undef": 1 },
+ []
+ );
+ });
+
+ it("Variables in JSX should be used (issues #15, #17, #21, #29)", function () {
+ verifyAndAssertMessages(
+ "import App from './App'; export default (
);",
+ { "no-unused-vars": 1, "no-undef": 1 },
+ []
+ );
+ });
+
+ it("Multiple destructured assignment with compound properties (issue #16)", function () {
+ verifyAndAssertMessages(
+ "module.exports = { ...a.a, ...a.b };",
+ { "no-dupe-keys": 1 },
+ []
+ );
+ });
+
+ it("Arrow function with non-block bodies (issue #20)", function () {
+ verifyAndAssertMessages(
+ "\"use strict\"; () => 1",
+ { "strict": 1 },
+ []
+ );
+ });
+
+ it("await keyword (issue #22)", function () {
+ verifyAndAssertMessages(
+ "async function foo() { await bar(); }",
+ { "no-unused-expressions": 1 },
+ []
+ );
+ });
+
+});