Skip to content

Commit

Permalink
Clean up @babel/eslint-parser (#10753)
Browse files Browse the repository at this point in the history
* Ensure compilation works for @babel/eslint-parser

* Update with review suggestions

* Incorporate feedback :)
  • Loading branch information
kaicataldo authored and nicolo-ribaudo committed Nov 25, 2019
1 parent 52f9641 commit e81bbd6
Show file tree
Hide file tree
Showing 18 changed files with 140 additions and 178 deletions.
44 changes: 20 additions & 24 deletions eslint/babel-eslint-parser/README.md
Original file line number Diff line number Diff line change
@@ -1,69 +1,65 @@
# babel-eslint [![npm](https://img.shields.io/npm/v/babel-eslint.svg)](https://www.npmjs.com/package/babel-eslint) [![travis](https://img.shields.io/travis/babel/babel-eslint/master.svg)](https://travis-ci.org/babel/babel-eslint) [![npm-downloads](https://img.shields.io/npm/dm/babel-eslint.svg)](https://www.npmjs.com/package/babel-eslint)
# @babel/eslint-parser [![npm](https://img.shields.io/npm/v/@babel/eslint-parser.svg)](https://www.npmjs.com/package/@babel/eslint-parser) [![travis](https://img.shields.io/travis/babel/@babel/eslint-parser/master.svg)](https://travis-ci.org/babel/@babel/eslint-parser) [![npm-downloads](https://img.shields.io/npm/dm/@babel/eslint-parser.svg)](https://www.npmjs.com/package/@babel/eslint-parser)

**babel-eslint** allows you to lint **ALL** valid Babel code with the fantastic
**@babel/eslint-parser** allows you to lint **ALL** valid Babel code with the fantastic
[ESLint](https://github.com/eslint/eslint).

## Breaking change in v11.x.x
## When should I use @babel/eslint-parser?

As of the v11.x.x release, babel-eslint now requires Babel as a peer dependency and expects a valid [Babel configuration file](https://babeljs.io/docs/en/configuration) to exist. This ensures that the same Babel configuration is used during both linting and compilation.
ESLint's default parser and core rules [only support the latest final ECMAScript standard](https://github.com/eslint/eslint/blob/a675c89573836adaf108a932696b061946abf1e6/README.md#what-about-experimental-features) and do not support experimental (such as new features) and non-standard (such as Flow or TypeScript types) syntax provided by Babel. @babel/eslint-parser is a parser that allows ESLint to run on source code that is transformed by Babel.

## When should I use babel-eslint?

ESLint's default parser and core rules [only support the latest final ECMAScript standard](https://github.com/eslint/eslint/blob/a675c89573836adaf108a932696b061946abf1e6/README.md#what-about-experimental-features) and do not support experimental (such as new features) and non-standard (such as Flow or TypeScript types) syntax provided by Babel. babel-eslint is a parser that allows ESLint to run on source code that is transformed by Babel.

**Note:** You only need to use babel-eslint if you are using Babel to transform your code. If this is not the case, please use the relevant parser for your chosen flavor of ECMAScript (note that the default parser supports all non-experimental syntax as well as JSX).
**Note:** You only need to use @babel/parser-eslint if you are using Babel to transform your code. If this is not the case, please use the relevant parser for your chosen flavor of ECMAScript (note that the default parser supports all non-experimental syntax as well as JSX).

## How does it work?

ESLint allows for the use of [custom parsers](https://eslint.org/docs/developer-guide/working-with-custom-parsers). When using this plugin, your code is parsed by Babel's parser (using the configuration specified in your [Babel configuration file](https://babeljs.io/docs/en/configuration)) and the resulting AST is
transformed into an [ESTree](https://github.com/estree/estree)-compliant structure that ESLint can understand. All location info such as line numbers,
columns is also retained so you can track down errors with ease.

**Note:** ESLint's core rules do not support experimental syntax and may therefore not work as expected when using babel-eslint. Please use the companion [`eslint-plugin-babel`](https://github.com/babel/eslint-plugin-babel) plugin for core rules that you have issues with.
**Note:** ESLint's core rules do not support experimental syntax and may therefore not work as expected when using `@babel/eslint-parser`. Please use the companion [`@babel/eslint-plugin`](https://github.com/babel/babel/tree/master/eslint/babel-eslint-plugin) plugin for core rules that you have issues with.

## Usage

### Installation

```sh
$ npm install eslint babel-eslint --save-dev
$ npm install eslint @babel/core @babel/eslint-parser --save-dev
# or
$ yarn add eslint babel-eslint -D
$ yarn add eslint @babel/core @babel/eslint-parser -D
```

**Note:** babel-eslint requires `babel/core@>=7.2.0` and a valid Babel configuration file to run. If you do not have this already set up, please see the [Babel Usage Guide](https://babeljs.io/docs/en/usage).
**Note:** @babel/eslint-parser requires `@babel/core@>=7.2.0` and a valid Babel configuration file to run. If you do not have this already set up, please see the [Babel Usage Guide](https://babeljs.io/docs/en/usage).

### Setup

To use babel-eslint, `"babel-eslint"` must be specified as the `parser` in your ESLint configuration file (see [here](https://eslint.org/docs/user-guide/configuring#specifying-parser) for more detailed information).
To use @babel/eslint-parser, `"@babel/eslint-parser"` must be specified as the `parser` in your ESLint configuration file (see [here](https://eslint.org/docs/user-guide/configuring#specifying-parser) for more detailed information).

**.eslintrc.js**

```js
module.exports = {
parser: "babel-eslint",
parser: "@babel/eslint-parser",
};
```

With the parser set, your configuration can be configured as described in the [Configuring ESLint](https://eslint.org/docs/user-guide/configuring) documentation.

**Note:** The `parserOptions` described in the [official documentation](https://eslint.org/docs/user-guide/configuring#specifying-parser-options) are for the default parser and are not necessarily supported by babel-eslint. Please see the section directly below for supported `parserOptions`.
**Note:** The `parserOptions` described in the [official documentation](https://eslint.org/docs/user-guide/configuring#specifying-parser-options) are for the default parser and are not necessarily supported by @babel/eslint-parser. Please see the section directly below for supported `parserOptions`.

### Additional parser configuration

Additional configuration options can be set in your ESLint configuration under the `parserOptions` key. Please note that the `ecmaFeatures` config property may still be required for ESLint to work properly with features not in ECMAScript 5 by default.

- `requireConfigFile` (default `true`) can be set to `false` to allow babel-eslint to run on files that do not have a Babel configuration associated with them. This can be useful for linting files that are not transformed by Babel (such as tooling configuration files), though we recommend using the default parser via [glob-based configuration](https://eslint.org/docs/user-guide/configuring#configuration-based-on-glob-patterns). Note: babel-eslint will not parse any experimental syntax when no configuration file is found.
- `requireConfigFile` (default `true`) can be set to `false` to allow @babel/eslint-parser to run on files that do not have a Babel configuration associated with them. This can be useful for linting files that are not transformed by Babel (such as tooling configuration files), though we recommend using the default parser via [glob-based configuration](https://eslint.org/docs/user-guide/configuring#configuration-based-on-glob-patterns). Note: @babel/eslint-parser will not parse any experimental syntax when no configuration file is found.
- `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.
- `ecmaFeatures.globalReturn` (default `false`) allow return statements in the global scope when used with `sourceType: "script"`.
- `babelOptions` passes through Babel's configuration [loading](https://babeljs.io/docs/en/options#config-loading-options) and [merging](https://babeljs.io/docs/en/options#config-merging-options) options (for instance, in case of a monorepo). When not defined, babel-eslint will use Babel's default configuration file resolution logic.
- `babelOptions` passes through Babel's configuration [loading](https://babeljs.io/docs/en/options#config-loading-options) and [merging](https://babeljs.io/docs/en/options#config-merging-options) options (for instance, in case of a monorepo). When not defined, @babel/eslint-parser will use Babel's default configuration file resolution logic.

**.eslintrc.js**

```js
module.exports = {
parser: "babel-eslint",
parser: "@babel/eslint-parser",
parserOptions: {
sourceType: "module",
allowImportExportEverywhere: false,
Expand All @@ -89,7 +85,7 @@ module.exports = {
overrides: [
{
files: ["files/transformed/by/babel/*.js"],
parser: "babel-eslint",
parser: "@babel/eslint-parser",
}
]
};
Expand All @@ -107,13 +103,13 @@ Flow:

> Check out [eslint-plugin-flowtype](https://github.com/gajus/eslint-plugin-flowtype): An `eslint` plugin that makes flow type annotations global variables and marks declarations as used. Solves the problem of false positives with `no-undef` and `no-unused-vars`.
- `no-undef` for global flow types: `ReactElement`, `ReactClass` [#130](https://github.com/babel/babel-eslint/issues/130#issuecomment-111215076)
- `no-undef` for global flow types: `ReactElement`, `ReactClass` [#130](https://github.com/babel/@babel/eslint-parser/issues/130#issuecomment-111215076)
- Workaround: define types as globals in `.eslintrc` or define types and import them `import type ReactElement from './types'`
- `no-unused-vars/no-undef` with Flow declarations (`declare module A {}`) [#132](https://github.com/babel/babel-eslint/issues/132#issuecomment-112815926)
- `no-unused-vars/no-undef` with Flow declarations (`declare module A {}`) [#132](https://github.com/babel/@babel/eslint-parser/issues/132#issuecomment-112815926)

Modules/strict mode

- `no-unused-vars: ["error", { vars: local }]` [#136](https://github.com/babel/babel-eslint/issues/136)
- `no-unused-vars: ["error", { vars: local }]` [#136](https://github.com/babel/@babel/eslint-parser/issues/136)

Please check out [eslint-plugin-react](https://github.com/yannickcr/eslint-plugin-react) for React/JSX issues.

Expand All @@ -123,6 +119,6 @@ Please check out [eslint-plugin-babel](https://github.com/babel/eslint-plugin-ba

## Questions and support

If you have an issue, please first check if it can be reproduced with the default parser and with the latest versions of `eslint` and `babel-eslint`. If it is not reproducible with the default parser, it is most likely an issue with babel-eslint.
If you have an issue, please first check if it can be reproduced with the default parser and with the latest versions of `eslint` and `@babel/eslint-parser`. If it is not reproducible with the default parser, it is most likely an issue with `@babel/eslint-parser`.

For questions and support please visit the [`#discussion`](https://babeljs.slack.com/messages/discussion/) Babel Slack channel (sign up [here](https://github.com/babel/notes/issues/38)) or the ESLint [Gitter](https://gitter.im/eslint/eslint).
24 changes: 11 additions & 13 deletions eslint/babel-eslint-parser/package.json
Original file line number Diff line number Diff line change
@@ -1,32 +1,30 @@
{
"name": "babel-eslint",
"version": "11.0.0-beta.1",
"description": "Custom parser for ESLint",
"name": "@babel/eslint-parser",
"version": "0.0.0",
"description": "ESLint parser that allows for linting of experimental syntax transformed by Babel",
"author": "Sebastian McKenzie <sebmck@gmail.com>",
"license": "MIT",
"private": true,
"repository": {
"type": "git",
"url": "https://github.com/babel/babel-eslint.git"
"url": "https://github.com/babel/babel.git",
"directory": "eslint/babel-eslint-parser"
},
"bugs": {
"url": "https://github.com/babel/babel-eslint/issues"
},
"homepage": "https://github.com/babel/babel-eslint",
"scripts": {
"changelog": "git log `git describe --tags --abbrev=0`..HEAD --pretty=format:' * %s (%an)' | grep -v 'Merge pull request'"
"url": "https://github.com/babel/babel/issues"
},
"homepage": "https://github.com/babel/babel/tree/master/eslint/babel-eslint-parser",
"engines": {
"node": ">=6"
"node": ">=10.9"
},
"main": "lib/index.js",
"peerDependencies": {
"@babel/core": ">=7.2.0",
"eslint": ">= 4.12.1"
"eslint": ">= 6.0.0"
},
"dependencies": {
"eslint-scope": "3.7.1",
"eslint-visitor-keys": "^1.0.0",
"eslint-scope": "5.0.0",
"eslint-visitor-keys": "^1.1.0",
"semver": "^6.3.0"
},
"devDependencies": {
Expand Down
35 changes: 18 additions & 17 deletions eslint/babel-eslint-parser/src/analyze-scope.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
"use strict";

const t = require("@babel/core").types;
const escope = require("eslint-scope");
const Definition = require("eslint-scope/lib/definition").Definition;
const OriginalPatternVisitor = require("eslint-scope/lib/pattern-visitor");
const OriginalReferencer = require("eslint-scope/lib/referencer");
const fallback = require("eslint-visitor-keys").getKeys;
const childVisitorKeys = require("./visitor-keys");
import { types as t } from "@babel/core";
import escope from "eslint-scope";
import { Definition } from "eslint-scope/lib/definition";
import OriginalPatternVisitor from "eslint-scope/lib/pattern-visitor";
import OriginalReferencer from "eslint-scope/lib/referencer";
import { getKeys as fallback } from "eslint-visitor-keys";
import childVisitorKeys from "./visitor-keys";

const flowFlippedAliasKeys = t.FLIPPED_ALIAS_KEYS.Flow.concat([
"ArrayPattern",
Expand All @@ -18,13 +16,16 @@ const flowFlippedAliasKeys = t.FLIPPED_ALIAS_KEYS.Flow.concat([
"ObjectPattern",
"RestElement",
]);
const visitorKeysMap = Object.keys(t.VISITOR_KEYS).reduce(function(acc, key) {
const value = t.VISITOR_KEYS[key];
const visitorKeysMap = Object.entries(t.VISITOR_KEYS).reduce(function(
acc,
[key, value],
) {
if (flowFlippedAliasKeys.indexOf(value) === -1) {
acc[key] = value;
}
return acc;
}, {});
},
{});

const propertyTypes = {
// loops
Expand Down Expand Up @@ -124,7 +125,7 @@ class Referencer extends OriginalReferencer {

// inherits.
visitProperty(node) {
if (node.value && node.value.type === "TypeCastExpression") {
if (node.value?.type === "TypeCastExpression") {
this._visitTypeAnnotation(node.value);
}
this._visitArray(node.decorators);
Expand Down Expand Up @@ -294,9 +295,9 @@ class Referencer extends OriginalReferencer {
}

_checkIdentifierOrVisit(node) {
if (node && node.typeAnnotation) {
if (node?.typeAnnotation) {
this._visitTypeAnnotation(node.typeAnnotation);
} else if (node && node.type === "Identifier") {
} else if (node?.type === "Identifier") {
this.visit(node);
} else {
this._visitTypeAnnotation(node);
Expand All @@ -312,7 +313,7 @@ class Referencer extends OriginalReferencer {
}
}

module.exports = function(ast, parserOptions) {
export default function(ast, parserOptions) {
const options = {
ignoreEval: true,
optimistic: false,
Expand All @@ -335,4 +336,4 @@ module.exports = function(ast, parserOptions) {
referencer.visit(ast);

return scopeManager;
};
}
10 changes: 4 additions & 6 deletions eslint/babel-eslint-parser/src/babylon-to-espree/convertAST.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
"use strict";

const t = require("@babel/core").types;
const convertProgramNode = require("./convertProgramNode");
import { types as t } from "@babel/core";
import convertProgramNode from "./convertProgramNode";

module.exports = function(ast, traverse, code) {
const state = { source: code };
Expand Down Expand Up @@ -79,8 +77,8 @@ const astTransformVisitor = {

// template string range fixes
if (path.isTemplateLiteral()) {
for (let j = 0; j < node.quasis.length; j++) {
const q = node.quasis[j];
for (let i = 0; i < node.quasis.length; i++) {
const q = node.quasis[i];
q.range[0] -= 1;
if (q.tail) {
q.range[1] += 1;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
"use strict";

module.exports = function(comments) {
export default function(comments) {
for (let i = 0; i < comments.length; i++) {
const comment = comments[i];
if (comment.type === "CommentBlock") {
Expand All @@ -14,4 +12,4 @@ module.exports = function(comments) {
comment.range = [comment.start, comment.end];
}
}
};
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
"use strict";

module.exports = function(ast) {
export default function(ast) {
ast.type = "Program";
ast.sourceType = ast.program.sourceType;
ast.directives = ast.program.directives;
Expand Down Expand Up @@ -37,4 +35,4 @@ module.exports = function(ast) {
ast.loc.start.line = ast.body[0].loc.start.line;
ast.range[0] = ast.body[0].start;
}
};
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
"use strict";

module.exports = function(tokens, tt) {
export default function(tokens, tt) {
let curlyBrace = null;
let templateTokens = [];
const result = [];
Expand Down Expand Up @@ -89,4 +87,4 @@ module.exports = function(tokens, tt) {
});

return result;
};
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
"use strict";

module.exports = function(token, tt, source) {
export default function(token, tt, source) {
const type = token.type;
token.range = [token.start, token.end];

Expand Down Expand Up @@ -82,4 +80,4 @@ module.exports = function(token, tt, source) {
}

return token;
};
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
"use strict";
import convertTemplateType from "./convertTemplateType";
import convertToken from "./convertToken";

const convertTemplateType = require("./convertTemplateType");
const convertToken = require("./convertToken");

module.exports = function(tokens, tt, code) {
export default function(tokens, tt, code) {
return convertTemplateType(tokens, tt)
.filter(t => t.type !== "CommentLine" && t.type !== "CommentBlock")
.map(t => convertToken(t, tt, code));
};
}
12 changes: 5 additions & 7 deletions eslint/babel-eslint-parser/src/babylon-to-espree/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
"use strict";
import convertTokens from "./convertTokens";
import convertComments from "./convertComments";
import convertAST from "./convertAST";

const convertTokens = require("./convertTokens");
const convertComments = require("./convertComments");
const convertAST = require("./convertAST");

module.exports = function(ast, traverse, tt, code) {
export default function(ast, traverse, tt, code) {
ast.tokens = convertTokens(ast.tokens, tt, code);
convertComments(ast.comments);
convertAST(ast, traverse, code);
};
}
20 changes: 9 additions & 11 deletions eslint/babel-eslint-parser/src/index.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
"use strict";
import semver from "semver";
import { version as CURRENT_BABEL_VERSION } from "@babel/core";
import parseWithScope from "./parse-with-scope";
import packageJson from "../package.json";

const semver = require("semver");
const babelCore = require("@babel/core");
const packageJson = require("../package.json");

const CURRENT_BABEL_VERSION = babelCore.version;
const SUPPORTED_BABEL_VERSION_RANGE =
packageJson.peerDependencies["@babel/core"];
const IS_RUNNING_SUPPORTED_VERSION = semver.satisfies(
CURRENT_BABEL_VERSION,
SUPPORTED_BABEL_VERSION_RANGE,
);

exports.parse = function(code, options) {
export function parse(code, options) {
return exports.parseForESLint(code, options).ast;
};
}

exports.parseForESLint = function(code, options = {}) {
export function parseForESLint(code, options = {}) {
if (!IS_RUNNING_SUPPORTED_VERSION) {
throw new Error(
`babel-eslint@${packageJson.version} does not support @babel/core@${CURRENT_BABEL_VERSION}. Please downgrade to babel-eslint@^10 or upgrade to @babel/core@${SUPPORTED_BABEL_VERSION_RANGE}`,
Expand All @@ -29,5 +27,5 @@ exports.parseForESLint = function(code, options = {}) {
options.allowImportExportEverywhere =
options.allowImportExportEverywhere || false;

return require("./parse-with-scope")(code, options);
};
return parseWithScope(code, options);
}

0 comments on commit e81bbd6

Please sign in to comment.