Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
- Loading branch information
1 parent
beca7e2
commit 75c2300
Showing
49 changed files
with
1,088 additions
and
74 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
src | ||
test | ||
.* | ||
*.log |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" | ||
) | ||
} | ||
] | ||
} | ||
} | ||
``` |
36 changes: 36 additions & 0 deletions
36
eslint/babel-eslint-plugin-development-internal/package.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import dryErrorMessages from "./rules/dry-error-messages"; | ||
|
||
module.exports = { | ||
rules: { | ||
"dry-error-messages": dryErrorMessages, | ||
}, | ||
}; |
148 changes: 148 additions & 0 deletions
148
eslint/babel-eslint-plugin-development-internal/src/rules/dry-error-messages.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 }, | ||
}); | ||
}, | ||
}; | ||
}, | ||
}; |
Oops, something went wrong.