mirror of
https://github.com/foomo/eslint-plugin.git
synced 2025-10-16 12:25:34 +00:00
chore(feat): add no-package-imports rule
This commit is contained in:
parent
f237319379
commit
6cc821f540
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
node_modules
|
||||||
47
README.md
Normal file
47
README.md
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# @foomo/eslint-plugin
|
||||||
|
|
||||||
|
eslint utility rules to effectively handle monorepo setups
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
You'll first need to install [ESLint](http://eslint.org):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$npm i eslint --save-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, install `@foomo/eslint-plugin`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn add -D @foomo/eslint-plugin
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Add `@foomo/eslint-plugin` to the plugins section of your `.eslintrc` configuration file:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"plugins": ["@foomo/eslint-plugin"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then configure the rules you want to use under the rules section.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{****
|
||||||
|
"rules": {
|
||||||
|
"@foomo/no-package-imports": ["error", {
|
||||||
|
"options": [
|
||||||
|
{ "invalidPrefix": "packages", "invalidSuffix": "src", "monorepoRoot": "@organization" },
|
||||||
|
{ "invalidPrefix": "packages", "invalidSuffix": "src", "template": "@organization/$1/custom-prefix/$2" }
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Supported Rules
|
||||||
|
|
||||||
|
- no-package-imports:
|
||||||
|
fixes forbidden import paths
|
||||||
55
docs/rules/no-package-imports.md
Normal file
55
docs/rules/no-package-imports.md
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# Fix relative and absolute imports to invalid locations (no-package-imports)
|
||||||
|
|
||||||
|
Monorepo setups may introduce valid yet unwanted ES imports picked up by the IDE.
|
||||||
|
This rule aims at finding and fixing these problems.
|
||||||
|
|
||||||
|
## Rule Details
|
||||||
|
|
||||||
|
This rule detects invalid imports via the option configuration. For example you want to avoid imports from `../packages/*/src` and
|
||||||
|
replace these with `@organization/*`
|
||||||
|
|
||||||
|
Examples of **incorrect** code for this rule:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { Button } from "../../packages/components/src/Button";
|
||||||
|
```
|
||||||
|
|
||||||
|
Examples of **correct** code for this rule:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { Button } from "@organization/components/Button";
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
The rule accepts a list of **replacements**, below is an example of the configuration for the above example
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
"@foomo/no-package-imports": ["error", {
|
||||||
|
options: [{
|
||||||
|
invalidPrefix: "packages",
|
||||||
|
invalidSuffix: "src",
|
||||||
|
monorepoRoot: "@organization",
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Custom templates
|
||||||
|
|
||||||
|
One can also provide a custom replacement template **$1** is the package name **$2** is the import suffix e.g. everything after the **invalidSuffix**. Please note **$2** includes a slash so it can be omitted from the template.
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
"@foomo/no-package-imports": ["error", {
|
||||||
|
options: [{
|
||||||
|
invalidPrefix: "packages",
|
||||||
|
invalidSuffix: "src",
|
||||||
|
template: "@testprefix/$1/somepath$2",
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
22
lib/index.js
Normal file
22
lib/index.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview eslint plugin to effectively handle monorepo setups
|
||||||
|
* @author @gosticks
|
||||||
|
*/
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Requirements
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
var requireIndex = require("requireindex");
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Plugin Definition
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
// import all rules in lib/rules
|
||||||
|
module.exports.rules = requireIndex(__dirname + "/rules");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
101
lib/rules/no-package-imports.js
Normal file
101
lib/rules/no-package-imports.js
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview Fix relative and absolute imports to invalid locations
|
||||||
|
* @author @gosticks
|
||||||
|
*/
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Rule Definition
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
meta: {
|
||||||
|
docs: {
|
||||||
|
description: "Fix relative and absolute imports to invalid locations",
|
||||||
|
category: "Possible Errors",
|
||||||
|
recommended: false,
|
||||||
|
},
|
||||||
|
fixable: "code", // or "code" or "whitespace"
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
options: {
|
||||||
|
type: "array",
|
||||||
|
items: {
|
||||||
|
anyOf: [
|
||||||
|
{
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
invalidPrefix: {
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
invalidSuffix: {
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
monorepoRoot: {
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
template: {
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
additionalProperties: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
messages: {
|
||||||
|
incorrectImport: "Import is invalid within the current monorepo setup",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
create: function (context) {
|
||||||
|
// create a replacer regex and a target string
|
||||||
|
const replacers = context.options[0].options.map((option) => [
|
||||||
|
// match all string [../]*$invalidPrefix/anything/$invalidSuffix[/anything]
|
||||||
|
new RegExp(
|
||||||
|
`(?:(?:..\\/)*${option.invalidPrefix})+\\/([^\\/]+)\\/${option.invalidSuffix}(\\/(.+))?`
|
||||||
|
),
|
||||||
|
option.template ?? `${option.monorepoRoot}/$1$2`,
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* report an error.
|
||||||
|
* @param {ASTNode} node the node to report.
|
||||||
|
* @param {[RegExp, string]} replacer regex to match and template for substitution
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function report(node, [replacer, str]) {
|
||||||
|
context.report({
|
||||||
|
node,
|
||||||
|
messageId: "incorrectImport",
|
||||||
|
fix: function (fixer) {
|
||||||
|
return fixer.replaceTextRange(
|
||||||
|
[node.source.range[0] + 1, node.source.range[1] - 1],
|
||||||
|
`${node.source.value.replace(replacer, str)}`
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Public
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
return {
|
||||||
|
ImportDeclaration(node) {
|
||||||
|
// iterate over each replacer pair and substitute import source if needed
|
||||||
|
replacers.forEach(([replacer, str]) => {
|
||||||
|
const matchResults = node.source.value.match(replacer);
|
||||||
|
if (matchResults) {
|
||||||
|
report(node, [replacer, str]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
26
package.json
Normal file
26
package.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "@foomo/eslint-plugin",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "eslint utility rules for monorepo project setup",
|
||||||
|
"keywords": [
|
||||||
|
"eslint",
|
||||||
|
"eslintplugin",
|
||||||
|
"eslint-plugin"
|
||||||
|
],
|
||||||
|
"author": "@gosticks",
|
||||||
|
"main": "lib/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "mocha tests --recursive"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"requireindex": "~1.1.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"eslint": "^7.1.0",
|
||||||
|
"mocha": "^7.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
},
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
89
tests/lib/rules/no-package-imports.js
Normal file
89
tests/lib/rules/no-package-imports.js
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview Fix relative and absolute imports to invalid locations
|
||||||
|
* @author @gosticks
|
||||||
|
*/
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Requirements
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
var rule = require("../../../lib/rules/no-package-imports"),
|
||||||
|
RuleTester = require("eslint").RuleTester;
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Tests
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const incorrectImport = { messageId: "incorrectImport" };
|
||||||
|
const options = [
|
||||||
|
{
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
invalidPrefix: "packages",
|
||||||
|
invalidSuffix: "src",
|
||||||
|
monorepoRoot: "@monorepo",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
var ruleTester = new RuleTester();
|
||||||
|
ruleTester.run("no-package-imports", rule, {
|
||||||
|
valid: [
|
||||||
|
{
|
||||||
|
code: 'import Button from "@monorepo/components/Button"',
|
||||||
|
parserOptions: { ecmaVersion: 6, sourceType: "module" },
|
||||||
|
options,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
invalid: [
|
||||||
|
{
|
||||||
|
code: 'import { Button } from "../../packages/components/src/Button"',
|
||||||
|
output: 'import { Button } from "@monorepo/components/Button"',
|
||||||
|
parserOptions: { ecmaVersion: 6, sourceType: "module" },
|
||||||
|
options,
|
||||||
|
errors: [incorrectImport],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code:
|
||||||
|
'import { Button } from "../../packages/components-combined/src/Button"',
|
||||||
|
output: 'import { Button } from "@monorepo/components-combined/Button"',
|
||||||
|
parserOptions: { ecmaVersion: 6, sourceType: "module" },
|
||||||
|
options,
|
||||||
|
errors: [incorrectImport],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: 'import Button from "../../packages/components/src/Button"',
|
||||||
|
output: 'import Button from "@testprefix/components/somepath/Button"',
|
||||||
|
parserOptions: { ecmaVersion: 6, sourceType: "module" },
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
invalidPrefix: "packages",
|
||||||
|
invalidSuffix: "src",
|
||||||
|
template: "@testprefix/$1/somepath$2",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
errors: [incorrectImport],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: 'import Button from "../../../packages/components/src/Button"',
|
||||||
|
output: 'import Button from "@monorepo/components/Button"',
|
||||||
|
parserOptions: { ecmaVersion: 6, sourceType: "module" },
|
||||||
|
options,
|
||||||
|
errors: [incorrectImport],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: 'import Button from "packages/components/src/Button"',
|
||||||
|
output: 'import Button from "@monorepo/components/Button"',
|
||||||
|
parserOptions: { ecmaVersion: 6, sourceType: "module" },
|
||||||
|
options,
|
||||||
|
errors: [incorrectImport],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user