Skip to content
This repository has been archived by the owner on Aug 18, 2021. It is now read-only.

Add support for customizing parser plugins #692

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

### Why Use babel-eslint

You only need to use babel-eslint if you are using types (Flow) or experimental features not supported in ESLint itself yet. Otherwise try the default parser (you don't have to use it just because you are using Babel).
You only need to use babel-eslint if you are using types (Flow or TypeScript) or experimental features not supported in ESLint itself yet. Otherwise try the default parser (you don't have to use it just because you are using Babel).

---

Expand All @@ -31,6 +31,11 @@ Please check out [eslint-plugin-react](https://github.com/yannickcr/eslint-plugi

Please check out [eslint-plugin-babel](https://github.com/babel/eslint-plugin-babel) for other issues

TypeScript:
> These issues are related to [eslint-plugin-typescript](https://github.com/nzakas/eslint-plugin-typescript).
- `typescript/no-namespace`: Babel doesn't support TypeScript's namesapce.
- `typescript/prefer-namespace-keyword`: Babel doesn't support TypeScript's namesapce.

## How does it work?

ESLint allows custom parsers. This is great but some of the syntax nodes that Babel supports
Expand Down Expand Up @@ -82,6 +87,8 @@ Check out the [ESLint docs](http://eslint.org/docs/rules/) for all possible rule
- `sourceType` can be set to `'module'`(default) or `'script'` if your code isn't using ECMAScript modules.
- `allowImportExportEverywhere` (default `false`) can be set to `true` to allow import and export declarations to appear anywhere a statement is allowed if your build environment supports that. Otherwise import and export declarations can only appear at a program's top level.
- `codeFrame` (default `true`) can be set to `false` to disable the code frame in the reporter. This is useful since some eslint formatters don't play well with it.
- `plugins` is an array which let you add more babel parser syntax plugins. Note that most of plugins are enabled by default, so you don't need to add something explicitly. You can enable `typescript` plugin via this option, and the `flow` plugin will be disabled automatically.
- `excludePlugins` is an array which let you disable some plugins. One possible use case is that you don't want to use one experimental ECMAScript feature accidentally. Note that you don't need to disable `flow` plugin if you have enabled `typescript` plugin.

**.eslintrc**

Expand All @@ -91,7 +98,13 @@ Check out the [ESLint docs](http://eslint.org/docs/rules/) for all possible rule
"parserOptions": {
"sourceType": "module",
"allowImportExportEverywhere": false,
"codeFrame": true
"codeFrame": true,
"plugins": [
"typescript"
],
"exclude": [
"bigInt"
]
}
}
```
Expand Down
12 changes: 12 additions & 0 deletions lib/babylon-to-espree/toAST.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,5 +123,17 @@ var astTransformVisitor = {
}
}
}

// TypeScript
if (path.isTSInterfaceDeclaration()) {
node.heritage = node.extends || [];
}

if (path.isTSTypeAssertion()) {
node.type = "TSTypeAssertionExpression";
node.loc.start.column--;
const typeAnnotation = Object.assign({}, node.typeAnnotation);
node.typeAnnotation.typeAnnotation = typeAnnotation;
}
},
};
6 changes: 2 additions & 4 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@ exports.parse = function(code, options) {
return exports.parseForESLint(code, options).ast;
};

exports.parseForESLint = function(code, options) {
options = options || {};
exports.parseForESLint = function(code, options = {}) {
options.ecmaVersion = options.ecmaVersion || 2018;
options.sourceType = options.sourceType || "module";
options.allowImportExportEverywhere =
options.allowImportExportEverywhere || false;
options.allowImportExportEverywhere = !!options.allowImportExportEverywhere;

return require("./parse-with-scope")(code, options);
};
Expand Down
22 changes: 18 additions & 4 deletions lib/parse.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use strict";

var babylonToEspree = require("./babylon-to-espree");
var parse = require("@babel/parser").parse;
var parser = require("@babel/parser");
var tt = require("@babel/parser").tokTypes;
var traverse = require("@babel/traverse").default;
var codeFrameColumns = require("@babel/code-frame").codeFrameColumns;
Expand All @@ -10,6 +10,9 @@ module.exports = function(code, options) {
const legacyDecorators =
options.ecmaFeatures && options.ecmaFeatures.legacyDecorators;

options.plugins = options.plugins || [];
const isTS = options.plugins.includes("typescript");

var opts = {
codeFrame: options.hasOwnProperty("codeFrame") ? options.codeFrame : true,
sourceType: options.sourceType,
Expand All @@ -19,9 +22,9 @@ module.exports = function(code, options) {
ranges: true,
tokens: true,
plugins: [
["flow", { all: true }],
"jsx",
"estree",
isTS ? "typescript" : ["flow", { all: true }],
"jsx",
"asyncFunctions",
"asyncGenerators",
"classConstructorCall",
Expand Down Expand Up @@ -51,9 +54,20 @@ module.exports = function(code, options) {
],
};

options.excludePlugins = options.excludePlugins || [];
options.excludePlugins.forEach(plugin => {
opts.plugins = opts.plugins.filter(p => {
if (typeof p === "string") {
return p !== plugin;
} else {
return p[0] !== plugin;
}
});
});

var ast;
try {
ast = parse(code, opts);
ast = parser.parse(code, opts);
} catch (err) {
if (err instanceof SyntaxError) {
err.lineNumber = err.loc.line;
Expand Down
41 changes: 41 additions & 0 deletions test/customize-plugins.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const assert = require("assert");
const CLIEngine = require("eslint").CLIEngine;

describe("support customizing plugins via parser options", () => {
it("remove flow if typescript is existed", () => {
const code = "function fn(arg: ?number) {}";
const cli = new CLIEngine({
parser: require.resolve("../lib"),
parserOptions: {
plugins: ["typescript"],
},
useEslintrc: false,
});

const report = cli.executeOnText(code);
assert.strictEqual(report.errorCount, 1);
assert.ok(
report.results[0].messages[0].message.includes("Unexpected token")
);
});

it("support excluding specific plugins", () => {
const cli = new CLIEngine({
parser: require.resolve("../lib"),
parserOptions: {
excludePlugins: ["flow", "dynamicImport"],
},
useEslintrc: false,
});

let report = cli.executeOnText("function fn(arg: ?number) {}");
assert.strictEqual(report.errorCount, 1);
assert.ok(
report.results[0].messages[0].message.includes("Unexpected token")
);

report = cli.executeOnText('import(".")');
assert.strictEqual(report.errorCount, 1);
assert.ok(report.results[0].messages[0].message.includes("experimental"));
});
});