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