Add @babel/eslint-plugin-development-internal (#11376)

* Add @babel/eslint-plugin-internal

* Add dry-error-messages rule

* Address feedback

* Enable new rule

* fix author field

* Fix errors

* Add readme

* Add example configuration

* Handle directories

* run make bootstrap

* More updates!

* Fix errors

* Update tests

* Fix CI race condition
This commit is contained in:
Kai Cataldo 2020-06-22 19:43:29 -04:00 committed by GitHub
parent beca7e2d8e
commit 75c2300c28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 1088 additions and 74 deletions

View File

@ -1,10 +1,17 @@
"use strict";
const path = require("path");
module.exports = {
root: true,
plugins: ["prettier", "@babel/development", "import", "jest"],
// replace it by `@babel/internal` when `@babel/eslint-config-internal` is published
extends: path.resolve(__dirname, "eslint/babel-eslint-config-internal"),
plugins: [
"import",
"jest",
"prettier",
"@babel/development",
"@babel/development-internal",
],
extends: "@babel/internal",
rules: {
"prettier/prettier": "error",
// TODO: remove after babel-eslint-config-internal is fully integrated into this repository.
@ -44,8 +51,8 @@ module.exports = {
"jest/no-identical-title": "off",
"jest/no-standalone-expect": "off",
"jest/no-test-callback": "off",
"jest/valid-describe": "off"
}
"jest/valid-describe": "off",
},
},
{
files: ["packages/babel-plugin-*/src/index.js"],
@ -55,5 +62,19 @@ module.exports = {
eqeqeq: ["error", "always", { null: "ignore" }],
},
},
{
files: ["packages/babel-parser/src/**/*.js"],
rules: {
"@babel/development-internal/dry-error-messages": [
"error",
{
errorModule: path.resolve(
__dirname,
"packages/babel-parser/src/parser/error.js"
),
},
],
},
},
],
};

View File

@ -84,9 +84,11 @@ build-no-bundle-ci: bootstrap-only
watch: build-no-bundle
BABEL_ENV=development $(YARN) gulp watch
code-quality-ci: flowcheck-ci lint-ci
code-quality-ci: build-no-bundle-ci
$(MAKE) flowcheck-ci & $(MAKE) lint-ci
flowcheck-ci: bootstrap-flowcheck
flowcheck-ci:
$(MAKE) flow
code-quality: flow lint
@ -94,17 +96,15 @@ code-quality: flow lint
flow:
$(YARN) flow check --strip-root
bootstrap-flowcheck: build-no-bundle-ci
lint-ci: lint-js-ci lint-ts-ci check-compat-data-ci
lint-js-ci: bootstrap-only
lint-js-ci:
$(MAKE) lint-js
lint-ts-ci: bootstrap-flowcheck
lint-ts-ci:
$(MAKE) lint-ts
check-compat-data-ci: build-no-bundle-ci
check-compat-data-ci:
$(MAKE) check-compat-data
lint: lint-js lint-ts

View File

@ -1,7 +1,7 @@
"use strict";
module.exports = {
parser: "babel-eslint",
parser: "@babel/eslint-parser",
extends: "eslint:recommended",
plugins: ["flowtype"],
parserOptions: {

View File

@ -13,7 +13,7 @@
},
"main": "index.js",
"peerDependencies": {
"babel-eslint": "^10.0.0 || ^11.0.0-0",
"@babel/eslint-parser": "*",
"eslint-plugin-flowtype": "^3.0.0"
}
}

View File

@ -0,0 +1,4 @@
src
test
.*
*.log

View File

@ -0,0 +1,70 @@
# @babel/eslint-plugin-development-internal
The Babel team's custom ESLint rules for the babel/babel monorepo.
## Installation
```sh
$ npm install --save-dev @babel/eslint-plugin-development-internal
```
or
```sh
$ yarn add --save-dev @babel/eslint-plugin-development-internal
```
## Usage
The plugin can be loaded in your `.eslintrc.*` configuration file as follows: (note that you can omit the `eslint-plugin-` prefix):
```json
{
"plugins": ["@babel/development-internal"]
}
```
## Rules
### `@babel/development-internal/dry-error-messages`
Intended for use in `packages/babel-parser/src/**/*`. When enabled, this rule warns when `this.raise()` invocations raise errors that are not imported from a designated error module.
Accepts an object configuration option:
```ts
{
errorModule: string
}
```
`errorModule` (required): The rule expects either an absolute path or a module name (for a module in `node_modules`). Please note that the rule will not check anything if` errorModule` is not given.
Example configuration:
```js
{
rules: {
"@babel/development-internal/dry-error-messages": [
"error",
{
errorModule: "@babel/shared-error-messages"
}
]
}
}
```
and
```js
{
rules: {
"@babel/development-internal/dry-error-messages": [
"error",
{
errorModule: path.resolve(
__dirname,
"packages/shared-error-messages/lib/index.js"
)
}
]
}
}
```

View File

@ -0,0 +1,36 @@
{
"name": "@babel/eslint-plugin-development-internal",
"version": "0.0.0",
"description": "The Babel Team's ESLint custom rules plugin. Since it's internal, it might not respect semver.",
"main": "lib/index.js",
"repository": {
"type": "git",
"url": "git+https://github.com/babel/babel.git",
"directory": "eslint/babel-eslint-plugin-development-internal"
},
"keywords": [
"babel",
"eslint",
"eslintplugin",
"eslint-plugin",
"babel-eslint"
],
"author": "Kai Cataldo <kai@kaicataldo.com>",
"license": "MIT",
"private": true,
"engines": {
"node": ">=10.9"
},
"bugs": {
"url": "https://github.com/babel/babel/issues"
},
"homepage": "https://github.com/babel/babel/tree/master/eslint/babel-eslint-plugin-development-internal",
"peerDependencies": {
"@babel/eslint-parser": "0.0.0",
"eslint": ">=6.0.0"
},
"devDependencies": {
"@babel/eslint-shared-fixtures": "*",
"eslint": "^6.0.0"
}
}

View File

@ -0,0 +1,7 @@
import dryErrorMessages from "./rules/dry-error-messages";
module.exports = {
rules: {
"dry-error-messages": dryErrorMessages,
},
};

View File

@ -0,0 +1,148 @@
import path from "path";
const REL_PATH_REGEX = /^\.{1,2}/;
function isRelativePath(filePath) {
return REL_PATH_REGEX.test(filePath);
}
function resolveAbsolutePath(currentFilePath, moduleToResolve) {
return isRelativePath(moduleToResolve)
? path.resolve(path.dirname(currentFilePath), moduleToResolve)
: moduleToResolve;
}
function isSourceErrorModule(currentFilePath, targetModulePath, src) {
for (const srcPath of [src, `${src}.js`, `${src}/index`, `${src}/index.js`]) {
if (
path.normalize(resolveAbsolutePath(currentFilePath, targetModulePath)) ===
path.normalize(resolveAbsolutePath(currentFilePath, srcPath))
) {
return true;
}
}
return false;
}
function isCurrentFileErrorModule(currentFilePath, errorModule) {
return currentFilePath === errorModule;
}
function findIdNode(node) {
if (node.type === "Identifier") {
return node;
}
if (node.type === "MemberExpression" && node.object.type === "Identifier") {
return node.object;
}
return null;
}
function findReference(node, scope) {
let currentScope = scope;
while (currentScope) {
const ref = currentScope.set.get(node.name);
if (ref) {
return ref;
}
currentScope = currentScope.upper;
}
return null;
}
function referencesImportedBinding(node, scope, bindings) {
const ref = findReference(node, scope);
if (ref) {
const topLevelDef = ref.defs[0];
if (topLevelDef.type === "ImportBinding") {
const defNode = topLevelDef.node;
for (const spec of bindings) {
if (
spec.loc.start === defNode.loc.start &&
spec.loc.end === defNode.loc.end
) {
return true;
}
}
}
}
return false;
}
export default {
meta: {
type: "suggestion",
docs: {
description:
"enforce @babel/parser's error messages to be consolidated in one module",
},
schema: [
{
type: "object",
properties: {
errorModule: { type: "string" },
},
additionalProperties: false,
required: ["errorModule"],
},
],
messages: {
mustBeImported: 'Error messages must be imported from "{{errorModule}}".',
},
},
create({ options, report, getFilename, getScope }) {
const [{ errorModule = "" } = {}] = options;
const filename = getFilename();
const importedBindings = new Set();
if (
// Do not run check if errorModule config option is not given.
!errorModule.length ||
// Do not check the target error module file.
isCurrentFileErrorModule(filename, errorModule)
) {
return {};
}
return {
// Check imports up front so that we don't have to check them for every ThrowStatement.
ImportDeclaration(node) {
if (isSourceErrorModule(filename, errorModule, node.source.value)) {
for (const spec of node.specifiers) {
importedBindings.add(spec);
}
}
},
"CallExpression[callee.type='MemberExpression'][callee.object.type='ThisExpression'][callee.property.name='raise'][arguments.length>=2]"(
node,
) {
const [, errorMsgNode] = node.arguments;
const nodeToCheck = findIdNode(errorMsgNode);
if (
nodeToCheck &&
referencesImportedBinding(nodeToCheck, getScope(), importedBindings)
) {
return;
}
report({
node: errorMsgNode,
messageId: "mustBeImported",
data: { errorModule },
});
},
};
},
};

View File

@ -0,0 +1,695 @@
import path from "path";
import rule from "../../src/rules/dry-error-messages";
import RuleTester from "@babel/eslint-shared-fixtures/utils/RuleTester";
const FILENAME = path.resolve(__dirname, "test/lib/index.js");
const ERRORS_MODULE = "errorsModule";
const MODULE_SAME_DIR = path.resolve(__dirname, "test/lib/errorsModule.js");
const MODULE_PARENT_DIR = path.resolve(__dirname, "test/errorsModule.js");
const ruleTester = new RuleTester();
ruleTester.run("dry-error-messages", rule, {
valid: [
// Ignores malformed `this.raise` invocations.
{
filename: FILENAME,
code: "this.raise(loc);",
options: [{ errorModule: ERRORS_MODULE }],
},
{
filename: FILENAME,
code: "this.notRaise(loc, 'Uh oh');",
options: [{ errorModule: ERRORS_MODULE }],
},
{
filename: FILENAME,
code: "throw new Error(this.raise('Uh oh'));",
options: [{ errorModule: ERRORS_MODULE }],
},
{
filename: FILENAME,
code: "this.raise(() => { throw new Error('Uh oh') });",
options: [{ errorModule: ERRORS_MODULE }],
},
{
filename: FILENAME,
code: "throw new Error('Uh oh')",
options: [{ errorModule: ERRORS_MODULE }],
},
{
filename: FILENAME,
code: "throw this.createError('Uh oh')",
options: [{ errorModule: ERRORS_MODULE }],
},
{
filename: FILENAME,
code: "throw this.error",
options: [{ errorModule: ERRORS_MODULE }],
},
{
filename: FILENAME,
code: "this.raise",
options: [{ errorModule: ERRORS_MODULE }],
},
{
filename: FILENAME,
code: "throw obj.error",
options: [{ errorModule: ERRORS_MODULE }],
},
{
filename: FILENAME,
code: "throw obj.raise",
options: [{ errorModule: ERRORS_MODULE }],
},
{
filename: FILENAME,
code:
"import { Errors } from 'errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: ERRORS_MODULE }],
},
{
filename: FILENAME,
code:
"import { Errors } from './errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: MODULE_SAME_DIR }],
},
{
filename: FILENAME,
code:
"import { Errors } from '../errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: MODULE_PARENT_DIR }],
},
{
filename: FILENAME,
code:
"import { NotErrors, Errors } from 'errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: ERRORS_MODULE }],
},
{
filename: FILENAME,
code:
"import { NotErrors, Errors } from './errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: MODULE_SAME_DIR }],
},
{
filename: FILENAME,
code:
"import { NotErrors, Errors } from '../errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: MODULE_PARENT_DIR }],
},
{
filename: FILENAME,
code:
"import { Errors } from 'errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: ERRORS_MODULE }],
},
{
filename: FILENAME,
code:
"import { Errors } from './errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: MODULE_SAME_DIR }],
},
{
filename: FILENAME,
code:
"import { Errors } from '../errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: MODULE_PARENT_DIR }],
},
{
filename: FILENAME,
code:
"import { NotErrors, Errors } from 'errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: ERRORS_MODULE }],
},
{
filename: FILENAME,
code:
"import { NotErrors, Errors } from './errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: MODULE_SAME_DIR }],
},
{
filename: FILENAME,
code:
"import { NotErrors, Errors } from '../errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: MODULE_PARENT_DIR }],
},
{
filename: FILENAME,
code:
"import Errors from 'errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: ERRORS_MODULE }],
},
{
filename: FILENAME,
code:
"import Errors from './errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: MODULE_SAME_DIR }],
},
{
filename: FILENAME,
code:
"import Errors from '../errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: MODULE_PARENT_DIR }],
},
{
filename: FILENAME,
code:
"import Errors, { NotErrors } from 'errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: ERRORS_MODULE }],
},
{
filename: FILENAME,
code:
"import Errors, { NotErrors } from './errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: MODULE_SAME_DIR }],
},
{
filename: FILENAME,
code:
"import Errors, { NotErrors } from '../errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: MODULE_PARENT_DIR }],
},
{
filename: FILENAME,
code:
"import NotErrors, { Errors } from 'errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: ERRORS_MODULE }],
},
{
filename: FILENAME,
code:
"import NotErrors, { Errors } from './errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: MODULE_SAME_DIR }],
},
{
filename: FILENAME,
code:
"import NotErrors, { Errors } from '../errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: MODULE_PARENT_DIR }],
},
{
filename: FILENAME,
code:
"import Errors from 'errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: ERRORS_MODULE }],
},
{
filename: FILENAME,
code:
"import Errors from './errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: MODULE_SAME_DIR }],
},
{
filename: FILENAME,
code:
"import Errors from '../errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: MODULE_PARENT_DIR }],
},
{
filename: FILENAME,
code:
"import Errors, { NotErrors } from 'errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: ERRORS_MODULE }],
},
{
filename: FILENAME,
code:
"import Errors, { NotErrors } from './errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: MODULE_SAME_DIR }],
},
{
filename: FILENAME,
code:
"import Errors, { NotErrors } from '../errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: MODULE_PARENT_DIR }],
},
{
filename: FILENAME,
code:
"import NotErrors, { Errors } from 'errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: ERRORS_MODULE }],
},
{
filename: FILENAME,
code:
"import NotErrors, { Errors } from './errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: MODULE_SAME_DIR }],
},
{
filename: FILENAME,
code:
"import NotErrors, { Errors } from '../errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: MODULE_PARENT_DIR }],
},
// Do not warn when file linted is error module.
{
filename: FILENAME,
code: "this.raise(loc, 'Oh no!');",
options: [{ errorModule: FILENAME }],
},
{
filename: MODULE_SAME_DIR,
code: "this.raise(loc, 'Oh no!');",
options: [{ errorModule: MODULE_SAME_DIR }],
},
// Do not warn if second argument is missing
{
filename: FILENAME,
code: "this.raise(loc);",
options: [{ errorModule: ERRORS_MODULE }],
},
],
invalid: [
{
filename: FILENAME,
code: "this.raise(loc, new Error('Uh oh'));",
options: [{ errorModule: ERRORS_MODULE }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: ERRORS_MODULE },
},
],
},
{
filename: FILENAME,
code: "throw this.raise(loc, new Error('Uh oh'));",
options: [{ errorModule: ERRORS_MODULE }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: ERRORS_MODULE },
},
],
},
{
filename: FILENAME,
code: "this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: ERRORS_MODULE }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: ERRORS_MODULE },
},
],
},
{
filename: FILENAME,
code:
"const Errors = { someErrorMessage: 'Uh oh!' }; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: ERRORS_MODULE }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: ERRORS_MODULE },
},
],
},
{
filename: FILENAME,
code: "import { Errors } from 'errorsModule'; this.raise(loc, 'Uh oh!');",
options: [{ errorModule: ERRORS_MODULE }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: ERRORS_MODULE },
},
],
},
{
filename: FILENAME,
code:
"import { Errors } from 'errorsModule'; const msg = 'Uh oh!'; this.raise(loc, msg);",
options: [{ errorModule: ERRORS_MODULE }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: ERRORS_MODULE },
},
],
},
{
filename: FILENAME,
code:
"import { Errors } from 'not-errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: ERRORS_MODULE }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: ERRORS_MODULE },
},
],
},
{
filename: FILENAME,
code:
"import { Errors } from './not-errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: MODULE_SAME_DIR }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: MODULE_SAME_DIR },
},
],
},
{
filename: FILENAME,
code:
"import { Errors } from '../not-errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: MODULE_PARENT_DIR }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: MODULE_PARENT_DIR },
},
],
},
{
filename: FILENAME,
code:
"import { NotErrors, Errors } from 'not-errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: ERRORS_MODULE }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: ERRORS_MODULE },
},
],
},
{
filename: FILENAME,
code:
"import { NotErrors, Errors } from './not-errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: MODULE_SAME_DIR }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: MODULE_SAME_DIR },
},
],
},
{
filename: FILENAME,
code:
"import { NotErrors, Errors } from '../not-errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: MODULE_PARENT_DIR }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: MODULE_PARENT_DIR },
},
],
},
{
filename: FILENAME,
code:
"import { Errors } from 'not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: ERRORS_MODULE }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: ERRORS_MODULE },
},
],
},
{
filename: FILENAME,
code:
"import { Errors } from './not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: MODULE_SAME_DIR }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: MODULE_SAME_DIR },
},
],
},
{
filename: FILENAME,
code:
"import { Errors } from '../not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: MODULE_PARENT_DIR }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: MODULE_PARENT_DIR },
},
],
},
{
filename: FILENAME,
code:
"import { NotErrors, Errors } from 'not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: ERRORS_MODULE }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: ERRORS_MODULE },
},
],
},
{
filename: FILENAME,
code:
"import { NotErrors, Errors } from './not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: MODULE_SAME_DIR }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: MODULE_SAME_DIR },
},
],
},
{
filename: FILENAME,
code:
"import { NotErrors, Errors } from '../not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: MODULE_PARENT_DIR }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: MODULE_PARENT_DIR },
},
],
},
{
filename: FILENAME,
code:
"import Errors from 'not-errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: ERRORS_MODULE }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: ERRORS_MODULE },
},
],
},
{
filename: FILENAME,
code:
"import Errors from './not-errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: MODULE_SAME_DIR }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: MODULE_SAME_DIR },
},
],
},
{
filename: FILENAME,
code:
"import Errors from '../not-errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: MODULE_PARENT_DIR }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: MODULE_PARENT_DIR },
},
],
},
{
filename: FILENAME,
code:
"import Errors, { NotErrors } from 'not-errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: ERRORS_MODULE }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: ERRORS_MODULE },
},
],
},
{
filename: FILENAME,
code:
"import Errors, { NotErrors } from './not-errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: MODULE_SAME_DIR }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: MODULE_SAME_DIR },
},
],
},
{
filename: FILENAME,
code:
"import Errors, { NotErrors } from '../not-errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: MODULE_PARENT_DIR }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: MODULE_PARENT_DIR },
},
],
},
{
filename: FILENAME,
code:
"import NotErrors, { Errors } from 'not-errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: ERRORS_MODULE }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: ERRORS_MODULE },
},
],
},
{
filename: FILENAME,
code:
"import NotErrors, { Errors } from './not-errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: MODULE_SAME_DIR }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: MODULE_SAME_DIR },
},
],
},
{
filename: FILENAME,
code:
"import NotErrors, { Errors } from '../not-errorsModule'; this.raise(loc, Errors.someErrorMessage);",
options: [{ errorModule: MODULE_PARENT_DIR }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: MODULE_PARENT_DIR },
},
],
},
{
filename: FILENAME,
code:
"import Errors from 'not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: ERRORS_MODULE }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: ERRORS_MODULE },
},
],
},
{
filename: FILENAME,
code:
"import Errors from './not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: MODULE_SAME_DIR }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: MODULE_SAME_DIR },
},
],
},
{
filename: FILENAME,
code:
"import Errors from '../not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: MODULE_PARENT_DIR }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: MODULE_PARENT_DIR },
},
],
},
{
filename: FILENAME,
code:
"import Errors, { NotErrors } from 'not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: ERRORS_MODULE }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: ERRORS_MODULE },
},
],
},
{
filename: FILENAME,
code:
"import Errors, { NotErrors } from './not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: MODULE_SAME_DIR }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: MODULE_SAME_DIR },
},
],
},
{
filename: FILENAME,
code:
"import Errors, { NotErrors } from '../not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: MODULE_PARENT_DIR }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: MODULE_PARENT_DIR },
},
],
},
{
filename: FILENAME,
code:
"import NotErrors, { Errors } from 'not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: ERRORS_MODULE }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: ERRORS_MODULE },
},
],
},
{
filename: FILENAME,
code:
"import NotErrors, { Errors } from './not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: MODULE_SAME_DIR }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: MODULE_SAME_DIR },
},
],
},
{
filename: FILENAME,
code:
"import NotErrors, { Errors } from '../not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }",
options: [{ errorModule: MODULE_PARENT_DIR }],
errors: [
{
messageId: "mustBeImported",
data: { errorModule: MODULE_PARENT_DIR },
},
],
},
],
});

View File

@ -2,7 +2,7 @@ import noDeprecatedClone from "./rules/no-deprecated-clone";
import noUndefinedIdentifier from "./rules/no-undefined-identifier";
import pluginName from "./rules/plugin-name";
export default {
module.exports = {
rules: {
"no-deprecated-clone": noDeprecatedClone,
"no-undefined-identifier": noUndefinedIdentifier,

View File

@ -34,7 +34,6 @@
},
"devDependencies": {
"@babel/eslint-shared-fixtures": "*",
"@babel/eslint-parser": "*",
"eslint": "^6.0.0",
"lodash.clonedeep": "^4.5.0"
}

View File

@ -1,14 +0,0 @@
import { RuleTester } from "eslint";
RuleTester.setDefaultConfig({
parser: require.resolve("@babel/eslint-parser"),
parserOptions: {
babelOptions: {
configFile: require.resolve(
"@babel/eslint-shared-fixtures/config/babel.config.js",
),
},
},
});
export default RuleTester;

View File

@ -1,5 +1,5 @@
import rule from "../../src/rules/new-cap";
import RuleTester from "../helpers/RuleTester";
import RuleTester from "@babel/eslint-shared-fixtures/utils/RuleTester";
const ruleTester = new RuleTester();
ruleTester.run("@babel/new-cap", rule, {

View File

@ -1,6 +1,6 @@
import cloneDeep from "lodash.clonedeep";
import rule from "../../src/rules/no-invalid-this";
import RuleTester from "../helpers/RuleTester";
import RuleTester from "@babel/eslint-shared-fixtures/utils/RuleTester";
/**
* A constant value for non strict mode environment.

View File

@ -1,5 +1,5 @@
import rule from "../../src/rules/no-unused-expressions";
import RuleTester from "../helpers/RuleTester";
import RuleTester from "@babel/eslint-shared-fixtures/utils/RuleTester";
const ruleTester = new RuleTester();
ruleTester.run("@babel/no-unused-expressions", rule, {

View File

@ -1,13 +1,8 @@
import rule from "../../src/rules/object-curly-spacing";
import RuleTester from "../helpers/RuleTester";
import RuleTester from "@babel/eslint-shared-fixtures/utils/RuleTester";
const ruleTester = new RuleTester();
ruleTester.run("@babel/object-curly-spacing", rule, {
valid: [
{
code: 'export x from "mod";',
},
],
valid: ['export x from "mod";'],
invalid: [],
});

View File

@ -1,5 +1,5 @@
import rule from "../../src/rules/semi";
import RuleTester from "../helpers/RuleTester";
import RuleTester from "@babel/eslint-shared-fixtures/utils/RuleTester";
const ruleTester = new RuleTester();

View File

@ -5,6 +5,7 @@
"license": "MIT",
"private": true,
"dependencies": {
"@babel/eslint-parser": "*",
"@babel/plugin-proposal-class-properties": "^7.1.0",
"@babel/plugin-proposal-decorators": "^7.1.2",
"@babel/plugin-proposal-do-expressions": "^7.7.4",

View File

@ -0,0 +1,17 @@
const path = require("path");
const { RuleTester } = require("eslint");
RuleTester.setDefaultConfig({
parser: require.resolve("@babel/eslint-parser"),
parserOptions: {
sourceType: "module",
ecmaVersion: 11,
babelOptions: {
configFile: require.resolve(
path.join(__dirname, "../config/babel.config.js")
),
},
},
});
module.exports = RuleTester;

View File

@ -13,7 +13,10 @@
"devDependencies": {
"@babel/cli": "^7.10.1",
"@babel/core": "^7.10.2",
"@babel/eslint-plugin-development": "^1.0.1",
"@babel/eslint-config-internal": "link:./eslint/babel-eslint-config-internal",
"@babel/eslint-parser": "link:./eslint/babel-eslint-parser",
"@babel/eslint-plugin-development": "link:./eslint/babel-eslint-plugin-development",
"@babel/eslint-plugin-development-internal": "link:./eslint/babel-eslint-plugin-development-internal",
"@babel/plugin-proposal-class-properties": "^7.10.1",
"@babel/plugin-proposal-dynamic-import": "^7.10.1",
"@babel/plugin-proposal-export-namespace-from": "^7.10.1",

View File

@ -59,6 +59,7 @@ export const ErrorMessages = Object.freeze({
ImportCallSpreadArgument: "... is not allowed in import()",
ImportMetaOutsideModule: `import.meta may appear only with 'sourceType: "module"'`,
ImportOutsideModule: `'import' and 'export' may appear only with 'sourceType: "module"'`,
InvalidBigIntLiteral: "Invalid BigIntLiteral",
InvalidCodePoint: "Code point out of bounds",
InvalidDigit: "Expected number in radix %0",
InvalidEscapeSequence: "Bad character escape sequence",

View File

@ -1467,7 +1467,9 @@ export default class ExpressionParser extends LValParser {
error += " or class properties";
}
/* eslint-disable @babel/development-internal/dry-error-messages */
this.raise(metaProp.start, error);
/* eslint-enable @babel/development-internal/dry-error-messages */
}
return metaProp;

View File

@ -143,7 +143,9 @@ export default class LValParser extends NodeUtils {
? Errors.PatternHasAccessor
: Errors.PatternHasMethod;
/* eslint-disable @babel/development-internal/dry-error-messages */
this.raise(prop.key.start, error);
/* eslint-enable @babel/development-internal/dry-error-messages */
} else if (prop.type === "SpreadElement" && !isLast) {
this.raiseRestNotLast(prop.start);
} else {
@ -356,6 +358,7 @@ export default class LValParser extends NodeUtils {
? isStrictBindReservedWord(expr.name, this.inModule)
: isStrictBindOnlyReservedWord(expr.name))
) {
/* eslint-disable @babel/development-internal/dry-error-messages */
this.raise(
expr.start,
bindingType === BIND_NONE
@ -363,6 +366,7 @@ export default class LValParser extends NodeUtils {
: Errors.StrictEvalArgumentsBinding,
expr.name,
);
/* eslint-enable @babel/development-internal/dry-error-messages */
}
if (checkClashes) {
@ -459,6 +463,7 @@ export default class LValParser extends NodeUtils {
break;
default: {
/* eslint-disable @babel/development-internal/dry-error-messages */
this.raise(
expr.start,
bindingType === BIND_NONE
@ -466,6 +471,7 @@ export default class LValParser extends NodeUtils {
: Errors.InvalidLhsBinding,
contextDescription,
);
/* eslint-enable @babel/development-internal/dry-error-messages */
}
}
}

View File

@ -2019,6 +2019,7 @@ export default class StatementParser extends ExpressionParser {
name: string,
): void {
if (this.state.exportedIdentifiers.indexOf(name) > -1) {
/* eslint-disable @babel/development-internal/dry-error-messages */
this.raise(
node.start,
name === "default"
@ -2026,6 +2027,7 @@ export default class StatementParser extends ExpressionParser {
: Errors.DuplicateExport,
name,
);
/* eslint-enable @babel/development-internal/dry-error-messages */
}
this.state.exportedIdentifiers.push(name);
}

View File

@ -132,7 +132,9 @@ export default class UtilParser extends Tokenizer {
// Throws if the current token and the prev one are separated by a space.
assertNoSpace(message: string = "Unexpected space."): void {
if (this.state.start > this.state.lastTokEnd) {
/* eslint-disable @babel/development-internal/dry-error-messages */
this.raise(this.state.lastTokEnd, message);
/* eslint-enable @babel/development-internal/dry-error-messages */
}
}
@ -146,7 +148,9 @@ export default class UtilParser extends Tokenizer {
if (typeof messageOrType !== "string") {
messageOrType = `Unexpected token, expected "${messageOrType.label}"`;
}
/* eslint-disable @babel/development-internal/dry-error-messages */
throw this.raise(pos != null ? pos : this.state.start, messageOrType);
/* eslint-enable @babel/development-internal/dry-error-messages */
}
expectPlugin(name: string, pos?: ?number): true {
@ -178,16 +182,10 @@ export default class UtilParser extends Tokenizer {
this.state.yieldPos !== -1 &&
(this.state.awaitPos === -1 || this.state.yieldPos < this.state.awaitPos)
) {
this.raise(
this.state.yieldPos,
"Yield cannot be used as name inside a generator function",
);
this.raise(this.state.yieldPos, Errors.YieldBindingIdentifier);
}
if (this.state.awaitPos !== -1) {
this.raise(
this.state.awaitPos,
"Await cannot be used as name inside an async function",
);
this.raise(this.state.awaitPos, Errors.AwaitBindingIdentifier);
}
}

View File

@ -2,6 +2,9 @@
/*:: declare var invariant; */
// Error messages are colocated with the plugin.
/* eslint-disable @babel/development-internal/dry-error-messages */
import type Parser from "../parser";
import { types as tt, type TokenType } from "../tokenizer/types";
import * as N from "../types";
@ -473,6 +476,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
) {
const label = this.state.value;
const suggestion = exportSuggestions[label];
throw this.raise(
this.state.start,
FlowErrors.UnsupportedDeclareExportKind,

View File

@ -1,5 +1,8 @@
// @flow
// Error messages are colocated with the plugin.
/* eslint-disable @babel/development-internal/dry-error-messages */
import * as charCodes from "charcodes";
import XHTMLEntities from "./xhtml";

View File

@ -2,6 +2,9 @@
/*:: declare var invariant; */
// Error messages are colocated with the plugin.
/* eslint-disable @babel/development-internal/dry-error-messages */
import type { TokenType } from "../../tokenizer/types";
import type State from "../../tokenizer/state";
import { types as tt } from "../../tokenizer/types";

View File

@ -420,12 +420,14 @@ export default class Tokenizer extends ParserErrors {
// misleading
this.expectPlugin("recordAndTuple");
if (this.getPluginOption("recordAndTuple", "syntaxType") !== "hash") {
/* eslint-disable @babel/development-internal/dry-error-messages */
throw this.raise(
this.state.pos,
next === charCodes.leftCurlyBrace
? Errors.RecordExpressionHashIncorrectStartSyntaxType
: Errors.TupleExpressionHashIncorrectStartSyntaxType,
);
/* eslint-enable @babel/development-internal/dry-error-messages */
}
if (next === charCodes.leftCurlyBrace) {
@ -1148,7 +1150,7 @@ export default class Tokenizer extends ParserErrors {
if (next === charCodes.plusSign || next === charCodes.dash) {
++this.state.pos;
}
if (this.readInt(10) === null) this.raise(start, "Invalid number");
if (this.readInt(10) === null) this.raise(start, Errors.InvalidNumber);
isFloat = true;
next = this.input.charCodeAt(this.state.pos);
}
@ -1171,7 +1173,7 @@ export default class Tokenizer extends ParserErrors {
// disallow floats, legacy octal syntax and non octal decimals
// new style octal ("0o") is handled in this.readRadixNumber
if (isFloat || octal || isNonOctalDecimalInt) {
this.raise(start, "Invalid BigIntLiteral");
this.raise(start, Errors.InvalidBigIntLiteral);
}
++this.state.pos;
isBigInt = true;

View File

@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":39,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"errors": [
"SyntaxError: Yield cannot be used as name inside a generator function (2:7)"
"SyntaxError: Can not use 'yield' as identifier inside a generator (2:7)"
],
"program": {
"type": "Program",

View File

@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":53,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"errors": [
"SyntaxError: Yield cannot be used as name inside a generator function (2:15)"
"SyntaxError: Can not use 'yield' as identifier inside a generator (2:15)"
],
"program": {
"type": "Program",

View File

@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":42,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"errors": [
"SyntaxError: Yield cannot be used as name inside a generator function (2:7)"
"SyntaxError: Can not use 'yield' as identifier inside a generator (2:7)"
],
"program": {
"type": "Program",

View File

@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":49,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"errors": [
"SyntaxError: Yield cannot be used as name inside a generator function (2:17)"
"SyntaxError: Can not use 'yield' as identifier inside a generator (2:17)"
],
"program": {
"type": "Program",

View File

@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":47,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"errors": [
"SyntaxError: Yield cannot be used as name inside a generator function (2:8)",
"SyntaxError: Can not use 'yield' as identifier inside a generator (2:8)",
"SyntaxError: Binding invalid left-hand side in function parameter list (2:8)"
],
"program": {

View File

@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":41,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"errors": [
"SyntaxError: Yield cannot be used as name inside a generator function (2:8)"
"SyntaxError: Can not use 'yield' as identifier inside a generator (2:8)"
],
"program": {
"type": "Program",

View File

@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":35,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"errors": [
"SyntaxError: Yield cannot be used as name inside a generator function (2:3)",
"SyntaxError: Can not use 'yield' as identifier inside a generator (2:3)",
"SyntaxError: Binding invalid left-hand side in function parameter list (2:3)"
],
"program": {

View File

@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":38,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"errors": [
"SyntaxError: Yield cannot be used as name inside a generator function (2:3)",
"SyntaxError: Can not use 'yield' as identifier inside a generator (2:3)",
"SyntaxError: Binding invalid left-hand side in function parameter list (2:3)"
],
"program": {

View File

@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":41,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"errors": [
"SyntaxError: Yield cannot be used as name inside a generator function (2:9)",
"SyntaxError: Can not use 'yield' as identifier inside a generator (2:9)",
"SyntaxError: Binding invalid left-hand side in function parameter list (2:9)"
],
"program": {

View File

@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":17,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":17}},
"errors": [
"SyntaxError: Await cannot be used as name inside an async function (1:6)"
"SyntaxError: Can not use 'await' as identifier inside an async function (1:6)"
],
"program": {
"type": "Program",

View File

@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":36}},
"errors": [
"SyntaxError: Await cannot be used as name inside an async function (1:20)"
"SyntaxError: Can not use 'await' as identifier inside an async function (1:20)"
],
"program": {
"type": "Program",

View File

@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":63,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"errors": [
"SyntaxError: Await cannot be used as name inside an async function (2:23)"
"SyntaxError: Can not use 'await' as identifier inside an async function (2:23)"
],
"program": {
"type": "Program",

View File

@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":46,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"errors": [
"SyntaxError: Await cannot be used as name inside an async function (2:7)"
"SyntaxError: Can not use 'await' as identifier inside an async function (2:7)"
],
"program": {
"type": "Program",

View File

@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":52,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"errors": [
"SyntaxError: Await cannot be used as name inside an async function (2:13)"
"SyntaxError: Can not use 'await' as identifier inside an async function (2:13)"
],
"program": {
"type": "Program",

View File

@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":38,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":38}},
"errors": [
"SyntaxError: Yield cannot be used as name inside a generator function (1:21)"
"SyntaxError: Can not use 'yield' as identifier inside a generator (1:21)"
],
"program": {
"type": "Program",

View File

@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":30,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":30}},
"errors": [
"SyntaxError: Yield cannot be used as name inside a generator function (1:16)",
"SyntaxError: Can not use 'yield' as identifier inside a generator (1:16)",
"SyntaxError: Binding invalid left-hand side in function parameter list (1:16)"
],
"program": {

View File

@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":39,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":39}},
"errors": [
"SyntaxError: Yield cannot be used as name inside a generator function (1:25)",
"SyntaxError: Can not use 'yield' as identifier inside a generator (1:25)",
"SyntaxError: Binding invalid left-hand side in function parameter list (1:25)"
],
"program": {

View File

@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":35,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":35}},
"errors": [
"SyntaxError: Yield cannot be used as name inside a generator function (1:21)"
"SyntaxError: Can not use 'yield' as identifier inside a generator (1:21)"
],
"program": {
"type": "Program",

View File

@ -56,10 +56,21 @@
semver "^5.4.1"
source-map "^0.5.0"
"@babel/eslint-plugin-development@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@babel/eslint-plugin-development/-/eslint-plugin-development-1.0.1.tgz#1f5206ae95795db09b65e7dbc2b158d66497a44c"
integrity sha512-ioEhN8HgKr4Yx8ef+XryNpKN4FimSFceb0qVVxvoUzpFn3xyq17MlY5AquEqtXObE7Nu7WKq7QL9INzjCrugyw==
"@babel/eslint-config-internal@link:./eslint/babel-eslint-config-internal":
version "0.0.0"
uid ""
"@babel/eslint-parser@link:./eslint/babel-eslint-parser":
version "0.0.0"
uid ""
"@babel/eslint-plugin-development-internal@link:./eslint/babel-eslint-plugin-development-internal":
version "0.0.0"
uid ""
"@babel/eslint-plugin-development@link:./eslint/babel-eslint-plugin-development":
version "0.0.0"
uid ""
"@babel/generator@^7.10.1", "@babel/generator@^7.10.2", "@babel/generator@^7.4.0":
version "7.10.2"