@dword-design/eslint-plugin-import-alias 5.1.2 → 6.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -53
- package/dist/index.js +9 -13
- package/dist/rules/prefer-alias.js +80 -89
- package/package.json +14 -15
- package/dist/cjs-fallback.cjs +0 -4
package/README.md
CHANGED
|
@@ -3,49 +3,49 @@
|
|
|
3
3
|
<!-- /TITLE -->
|
|
4
4
|
|
|
5
5
|
<!-- BADGES/ -->
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
6
|
+
<p>
|
|
7
|
+
<a href="https://npmjs.org/package/@dword-design/eslint-plugin-import-alias">
|
|
8
|
+
<img
|
|
9
|
+
src="https://img.shields.io/npm/v/@dword-design/eslint-plugin-import-alias.svg"
|
|
10
|
+
alt="npm version"
|
|
11
|
+
>
|
|
12
|
+
</a><img src="https://img.shields.io/badge/os-linux%20%7C%C2%A0macos%20%7C%C2%A0windows-blue" alt="Linux macOS Windows compatible"><a href="https://github.com/dword-design/eslint-plugin-import-alias/actions">
|
|
13
|
+
<img
|
|
14
|
+
src="https://github.com/dword-design/eslint-plugin-import-alias/workflows/build/badge.svg"
|
|
15
|
+
alt="Build status"
|
|
16
|
+
>
|
|
17
|
+
</a><a href="https://codecov.io/gh/dword-design/eslint-plugin-import-alias">
|
|
18
|
+
<img
|
|
19
|
+
src="https://codecov.io/gh/dword-design/eslint-plugin-import-alias/branch/master/graph/badge.svg"
|
|
20
|
+
alt="Coverage status"
|
|
21
|
+
>
|
|
22
|
+
</a><a href="https://david-dm.org/dword-design/eslint-plugin-import-alias">
|
|
23
|
+
<img src="https://img.shields.io/david/dword-design/eslint-plugin-import-alias" alt="Dependency status">
|
|
24
|
+
</a><img src="https://img.shields.io/badge/renovate-enabled-brightgreen" alt="Renovate enabled"><br/><a href="https://gitpod.io/#https://github.com/dword-design/eslint-plugin-import-alias">
|
|
25
|
+
<img
|
|
26
|
+
src="https://gitpod.io/button/open-in-gitpod.svg"
|
|
27
|
+
alt="Open in Gitpod"
|
|
28
|
+
width="114"
|
|
29
|
+
>
|
|
30
|
+
</a><a href="https://www.buymeacoffee.com/dword">
|
|
31
|
+
<img
|
|
32
|
+
src="https://www.buymeacoffee.com/assets/img/guidelines/download-assets-sm-2.svg"
|
|
33
|
+
alt="Buy Me a Coffee"
|
|
34
|
+
width="114"
|
|
35
|
+
>
|
|
36
|
+
</a><a href="https://paypal.me/SebastianLandwehr">
|
|
37
|
+
<img
|
|
38
|
+
src="https://sebastianlandwehr.com/images/paypal.svg"
|
|
39
|
+
alt="PayPal"
|
|
40
|
+
width="163"
|
|
41
|
+
>
|
|
42
|
+
</a><a href="https://www.patreon.com/dworddesign">
|
|
43
|
+
<img
|
|
44
|
+
src="https://sebastianlandwehr.com/images/patreon.svg"
|
|
45
|
+
alt="Patreon"
|
|
46
|
+
width="163"
|
|
47
|
+
>
|
|
48
|
+
</a>
|
|
49
49
|
</p>
|
|
50
50
|
<!-- /BADGES -->
|
|
51
51
|
|
|
@@ -56,15 +56,15 @@ An ESLint plugin that enforces the use of import aliases. Also supports autofixi
|
|
|
56
56
|
Aliases are a great thing to make imports more readable and you do not have to change import paths that often when a file path is changed.
|
|
57
57
|
|
|
58
58
|
```js
|
|
59
|
-
import foo from '../../model/sub/foo'
|
|
60
|
-
import bar from '../other/bar'
|
|
59
|
+
import foo from '../../model/sub/foo';
|
|
60
|
+
import bar from '../other/bar';
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
changes to
|
|
64
64
|
|
|
65
65
|
```js
|
|
66
|
-
import foo from '@/model/sub/foo'
|
|
67
|
-
import bar from '@/sub/other/bar'
|
|
66
|
+
import foo from '@/model/sub/foo';
|
|
67
|
+
import bar from '@/sub/other/bar';
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
Now what if you are in a bigger team or you have a lot of projects to update. Or you just want to make sure that everything is consistent. This is where a linter comes into the play. This rule allows you to detect inconsistent imports and even autofix them. This works by matching alias paths agains the imports and replacing the import paths with the first matching aliased path.
|
|
@@ -85,12 +85,13 @@ $ yarn add @dword-design/eslint-plugin-import-alias
|
|
|
85
85
|
|
|
86
86
|
Add the plugin to your ESLint config:
|
|
87
87
|
|
|
88
|
-
```
|
|
89
|
-
{
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
88
|
+
```js
|
|
89
|
+
import { defineConfig } from 'eslint/config';
|
|
90
|
+
import importAlias from '@dword-design/import-alias';
|
|
91
|
+
|
|
92
|
+
export default defineConfig([
|
|
93
|
+
importAlias.configs.recommended,
|
|
94
|
+
]);
|
|
94
95
|
```
|
|
95
96
|
|
|
96
97
|
Alright, now you have to tell the plugin which aliases to use. In the simplest case, you are already using [babel-plugin-module-resolver](https://www.npmjs.com/package/babel-plugin-module-resolver) for your aliases. Your babel config would look something like this:
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
import preferAlias from
|
|
1
|
+
import preferAlias from './rules/prefer-alias.js';
|
|
2
|
+
const plugin = { rules: { 'prefer-alias': preferAlias } };
|
|
2
3
|
export default {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
},
|
|
11
|
-
rules: {
|
|
12
|
-
'prefer-alias': preferAlias
|
|
13
|
-
}
|
|
14
|
-
};
|
|
4
|
+
configs: {
|
|
5
|
+
recommended: {
|
|
6
|
+
plugins: { '@dword-design/import-alias': plugin },
|
|
7
|
+
rules: { '@dword-design/import-alias/prefer-alias': 'error' },
|
|
8
|
+
},
|
|
9
|
+
},
|
|
10
|
+
};
|
|
@@ -1,96 +1,87 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import pathLib from 'node:path';
|
|
2
|
+
import { loadOptions } from '@babel/core';
|
|
3
|
+
import defu from '@dword-design/defu';
|
|
3
4
|
import { resolvePath as defaultResolvePath } from 'babel-plugin-module-resolver';
|
|
4
|
-
import deepmerge from 'deepmerge';
|
|
5
|
-
import P from 'path';
|
|
6
5
|
const isParentImport = path => /^(\.\/)?\.\.\//.test(path);
|
|
7
6
|
const findMatchingAlias = (sourcePath, currentFile, options) => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
name: aliasName,
|
|
16
|
-
path
|
|
17
|
-
};
|
|
7
|
+
const resolvePath = options.resolvePath || defaultResolvePath;
|
|
8
|
+
const absoluteSourcePath = pathLib.resolve(pathLib.dirname(currentFile), sourcePath);
|
|
9
|
+
for (const aliasName of Object.keys(options.alias)) {
|
|
10
|
+
const path = pathLib.resolve(pathLib.dirname(currentFile), resolvePath(`${aliasName}/`, currentFile, options));
|
|
11
|
+
if (absoluteSourcePath.startsWith(path)) {
|
|
12
|
+
return { name: aliasName, path };
|
|
13
|
+
}
|
|
18
14
|
}
|
|
19
|
-
}
|
|
20
|
-
return undefined;
|
|
21
15
|
};
|
|
22
16
|
export default {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const resolvePath = options.resolvePath || defaultResolvePath;
|
|
43
|
-
return {
|
|
44
|
-
ImportDeclaration: node => {
|
|
45
|
-
var _ref, _options$alias2, _sourcePath2, _importWithoutAlias;
|
|
46
|
-
const sourcePath = node.source.value;
|
|
47
|
-
const hasAlias = (_ref = (_options$alias2 = options.alias, keys(_options$alias2)), some(alias => {
|
|
48
|
-
var _sourcePath;
|
|
49
|
-
return _sourcePath = sourcePath, startsWith(`${alias}/`)(_sourcePath);
|
|
50
|
-
})(_ref));
|
|
51
|
-
|
|
52
|
-
// relative parent
|
|
53
|
-
if (_sourcePath2 = sourcePath, isParentImport(_sourcePath2)) {
|
|
54
|
-
var _P$relative;
|
|
55
|
-
const matchingAlias = findMatchingAlias(sourcePath, currentFile, options);
|
|
56
|
-
if (!matchingAlias) {
|
|
57
|
-
return undefined;
|
|
58
|
-
}
|
|
59
|
-
const absoluteImportPath = P.resolve(folder, sourcePath);
|
|
60
|
-
const rewrittenImport = `${matchingAlias.name}/${_P$relative = P.relative(matchingAlias.path, absoluteImportPath), replace(/\\/g, '/')(_P$relative)}`;
|
|
61
|
-
return context.report({
|
|
62
|
-
fix: fixer => fixer.replaceTextRange([node.source.range[0] + 1, node.source.range[1] - 1], rewrittenImport),
|
|
63
|
-
message: `Unexpected parent import '${sourcePath}'. Use '${rewrittenImport}' instead`,
|
|
64
|
-
node
|
|
65
|
-
});
|
|
17
|
+
create: context => {
|
|
18
|
+
const currentFile = context.getFilename();
|
|
19
|
+
const folder = pathLib.dirname(currentFile);
|
|
20
|
+
// can't check a non-file
|
|
21
|
+
if (currentFile === '<text>')
|
|
22
|
+
return {};
|
|
23
|
+
const optionsFromRule = context.options[0] ?? {};
|
|
24
|
+
const babelConfig = (loadOptions({
|
|
25
|
+
filename: currentFile,
|
|
26
|
+
...optionsFromRule.babelOptions,
|
|
27
|
+
}) || {});
|
|
28
|
+
const optionsFromPlugin = babelConfig?.plugins?.find(_ => _.key === 'module-resolver')?.options ??
|
|
29
|
+
{};
|
|
30
|
+
const options = defu(optionsFromRule, optionsFromPlugin, {
|
|
31
|
+
alias: [],
|
|
32
|
+
cwd: context.cwd,
|
|
33
|
+
});
|
|
34
|
+
if (options.alias.length === 0) {
|
|
35
|
+
throw new Error('No alias configured. You have to define aliases by either passing them to the babel-plugin-module-resolver plugin in your Babel config, or directly to the prefer-alias rule.');
|
|
66
36
|
}
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
};
|
|
37
|
+
const resolvePath = options.resolvePath || defaultResolvePath;
|
|
38
|
+
return {
|
|
39
|
+
ImportDeclaration: node => {
|
|
40
|
+
const sourcePath = node.source.value;
|
|
41
|
+
const hasAlias = Object.keys(options.alias).some(alias => sourcePath.startsWith(`${alias}/`));
|
|
42
|
+
// relative parent
|
|
43
|
+
if (isParentImport(sourcePath)) {
|
|
44
|
+
const matchingAlias = findMatchingAlias(sourcePath, currentFile, options);
|
|
45
|
+
if (!matchingAlias) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const absoluteImportPath = pathLib.resolve(folder, sourcePath);
|
|
49
|
+
const rewrittenImport = `${matchingAlias.name}/${pathLib
|
|
50
|
+
.relative(matchingAlias.path, absoluteImportPath)
|
|
51
|
+
.replaceAll('\\', '/')}`;
|
|
52
|
+
return context.report({
|
|
53
|
+
fix: fixer => fixer.replaceTextRange([node.source.range[0] + 1, node.source.range[1] - 1], rewrittenImport),
|
|
54
|
+
message: `Unexpected parent import '${sourcePath}'. Use '${rewrittenImport}' instead`,
|
|
55
|
+
node,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
const importWithoutAlias = resolvePath(sourcePath, currentFile, options);
|
|
59
|
+
if (!isParentImport(importWithoutAlias) &&
|
|
60
|
+
hasAlias &&
|
|
61
|
+
!options.allowSubpathWithAlias) {
|
|
62
|
+
return context.report({
|
|
63
|
+
fix: fixer => fixer.replaceTextRange([node.source.range[0] + 1, node.source.range[1] - 1], importWithoutAlias),
|
|
64
|
+
message: `Unexpected subpath import via alias '${sourcePath}'. Use '${importWithoutAlias}' instead`,
|
|
65
|
+
node,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return;
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
meta: {
|
|
73
|
+
fixable: true,
|
|
74
|
+
schema: [
|
|
75
|
+
{
|
|
76
|
+
additionalProperties: false,
|
|
77
|
+
properties: {
|
|
78
|
+
alias: { type: 'object' },
|
|
79
|
+
aliasForSubpaths: { default: false, type: 'boolean' },
|
|
80
|
+
babelOptions: { type: 'object' },
|
|
81
|
+
},
|
|
82
|
+
type: 'object',
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
type: 'suggestion',
|
|
86
|
+
},
|
|
87
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dword-design/eslint-plugin-import-alias",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.2",
|
|
4
4
|
"description": "An ESLint plugin that enforces the use of import aliases. Also supports autofixing.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"alias",
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
"license": "MIT",
|
|
29
29
|
"author": "Sebastian Landwehr <info@sebastianlandwehr.com>",
|
|
30
30
|
"type": "module",
|
|
31
|
-
"
|
|
31
|
+
"exports": "./dist/index.js",
|
|
32
|
+
"main": "dist/index.js",
|
|
32
33
|
"files": [
|
|
33
34
|
"dist"
|
|
34
35
|
],
|
|
@@ -43,23 +44,21 @@
|
|
|
43
44
|
"test": "base test"
|
|
44
45
|
},
|
|
45
46
|
"dependencies": {
|
|
46
|
-
"@babel/core": "^7.
|
|
47
|
-
"@dword-design/
|
|
48
|
-
"babel-plugin-module-resolver": "^5.0.
|
|
49
|
-
"deepmerge": "^4.3.1",
|
|
50
|
-
"jiti": "^1.18.2"
|
|
47
|
+
"@babel/core": "^7.27.4",
|
|
48
|
+
"@dword-design/defu": "^1.0.0",
|
|
49
|
+
"babel-plugin-module-resolver": "^5.0.2"
|
|
51
50
|
},
|
|
52
51
|
"devDependencies": {
|
|
53
|
-
"@dword-design/base": "^
|
|
54
|
-
"@
|
|
55
|
-
"@dword-design/tester-plugin-tmp-dir": "^2.1.26",
|
|
52
|
+
"@dword-design/base": "^13.0.1",
|
|
53
|
+
"@playwright/test": "^1.52.0",
|
|
56
54
|
"depcheck-package-name": "^3.0.1",
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"output-files": "^2.0.
|
|
55
|
+
"endent": "npm:@dword-design/endent@^1.4.1",
|
|
56
|
+
"eslint": "^9.28.0",
|
|
57
|
+
"lodash-es": "^4.17.21",
|
|
58
|
+
"output-files": "^2.0.32",
|
|
59
|
+
"typescript-eslint": "^8.33.1"
|
|
61
60
|
},
|
|
62
|
-
"packageManager": "pnpm@
|
|
61
|
+
"packageManager": "pnpm@10.11.1+sha512.e519b9f7639869dc8d5c3c5dfef73b3f091094b0a006d7317353c72b124e80e1afd429732e28705ad6bfa1ee879c1fce46c128ccebd3192101f43dd67c667912",
|
|
63
62
|
"engines": {
|
|
64
63
|
"node": ">=18"
|
|
65
64
|
},
|
package/dist/cjs-fallback.cjs
DELETED