Merge branch 'master' of github.com:babel/babel-eslint

This commit is contained in:
Sebastian McKenzie 2015-03-09 02:45:56 +11:00
parent a31835836d
commit 6556b06f23
5 changed files with 279 additions and 21 deletions

View File

@ -7,10 +7,25 @@ exports.toToken = function (token) {
if (type === tokTypes.name) { if (type === tokTypes.name) {
token.type = "Identifier"; 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.type = "Punctuator";
if (!token.value) {
token.value = type.type; 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; return token;
}; };
@ -51,6 +66,11 @@ var astTransformVisitor = {
// classes // classes
if (t.isClassDeclaration(node) || t.isClassExpression(node)) {
node.name = node.id;
delete node.id;
}
if (t.isReferencedIdentifier(node, parent, { name: "super" })) { if (t.isReferencedIdentifier(node, parent, { name: "super" })) {
return t.inherits(t.thisExpression(), node); return t.inherits(t.thisExpression(), node);
} }
@ -60,22 +80,6 @@ var astTransformVisitor = {
this.remove(); 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 <div> or an attribute name
}
}
if (t.isJSXMemberExpression(node)) {
node.type = "MemberExpression";
}
// functions // functions
if (t.isFunction(node)) { if (t.isFunction(node)) {

View File

@ -21,7 +21,7 @@ function monkeypatch() {
var eslintLoc; var eslintLoc;
try { try {
eslintLoc = require.resolve("eslint"); eslintLoc = Module._resolveFilename("eslint", module.parent);
} catch (err) { } catch (err) {
throw new ReferenceError("couldn't resolve eslint"); throw new ReferenceError("couldn't resolve eslint");
} }
@ -43,6 +43,17 @@ function monkeypatch() {
opts.sourceType = "module"; opts.sourceType = "module";
return analyze.call(this, ast, opts) 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) { exports.parse = function (code) {

View File

@ -16,5 +16,13 @@
"bugs": { "bugs": {
"url": "https://github.com/babel/babel-eslint/issues" "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"
}
} }

View File

@ -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("<App />");
});
it("jsx expression with 'this' as identifier", function () {
parseAndAssertSame("<this />");
});
it("jsx expression with a dynamic attribute", function () {
parseAndAssertSame("<App foo={bar} />");
});
it("jsx expression with a member expression as identifier", function () {
parseAndAssertSame("<foo.bar />");
});
});

View File

@ -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 = <App />;",
{ "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 = <div className=\"foo\" />",
{ "no-undef": 1 },
[]
);
});
it("Variables in JSX should be used (issues #15, #17, #21, #29)", function () {
verifyAndAssertMessages(
"import App from './App'; export default (<App />);",
{ "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 },
[]
);
});
});