@dword-design/eslint-plugin-import-alias 6.0.3 → 7.0.0

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.
@@ -0,0 +1,18 @@
1
+ declare module 'babel-plugin-module-resolver' {
2
+ export function resolvePath(
3
+ sourcePath: string,
4
+ currentFile: string,
5
+ options: { alias?: Record<string, string>; cwd?: string },
6
+ ): string;
7
+ }
8
+ declare module '@babel/core' {
9
+ export type BabelPlugin = { key: string; options?: Record<string, unknown> };
10
+
11
+ export interface BabelConfig {
12
+ plugins?: BabelPlugin[];
13
+ }
14
+
15
+ export function loadOptions(
16
+ options?: Record<string, unknown>,
17
+ ): BabelConfig | null;
18
+ }
@@ -0,0 +1,19 @@
1
+ declare const _default: {
2
+ configs: {
3
+ recommended: {
4
+ plugins: {
5
+ '@dword-design/import-alias': {
6
+ rules: {
7
+ 'prefer-alias': import("@typescript-eslint/utils/dist/ts-eslint").RuleModule<"parentImport" | "subpathImport", [Partial<import("./rules/prefer-alias").Options> & {
8
+ babelOptions?: Record<string, unknown>;
9
+ }], unknown, import("@typescript-eslint/utils/dist/ts-eslint").RuleListener>;
10
+ };
11
+ };
12
+ };
13
+ rules: {
14
+ '@dword-design/import-alias/prefer-alias': string;
15
+ };
16
+ };
17
+ };
18
+ };
19
+ export default _default;
package/dist/index.js CHANGED
@@ -1,10 +1,18 @@
1
- import preferAlias from './rules/prefer-alias.js';
2
- const plugin = { rules: { 'prefer-alias': preferAlias } };
3
- export default {
4
- configs: {
5
- recommended: {
6
- plugins: { '@dword-design/import-alias': plugin },
7
- rules: { '@dword-design/import-alias/prefer-alias': 'error' },
8
- },
9
- },
1
+ import preferAlias from "./rules/prefer-alias.js";
2
+ const plugin = {
3
+ rules: {
4
+ "prefer-alias": preferAlias
5
+ }
10
6
  };
7
+ export default {
8
+ configs: {
9
+ recommended: {
10
+ plugins: {
11
+ "@dword-design/import-alias": plugin
12
+ },
13
+ rules: {
14
+ "@dword-design/import-alias/prefer-alias": "error"
15
+ }
16
+ }
17
+ }
18
+ };
@@ -0,0 +1,17 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ export interface BabelPluginModuleResolverOptions {
3
+ alias?: Record<string, string>;
4
+ cwd?: string;
5
+ resolvePath?: (sourcePath: string, currentFile: string, options: Pick<BabelPluginModuleResolverOptions, 'alias' | 'cwd'>) => string;
6
+ }
7
+ export interface Options {
8
+ alias: Record<string, string>;
9
+ aliasForSubpaths: boolean;
10
+ resolvePath: (sourcePath: string, currentFile: string, options: Pick<BabelPluginModuleResolverOptions, 'alias' | 'cwd'>) => string;
11
+ cwd: string;
12
+ }
13
+ type OptionsInput = Partial<Options> & {
14
+ babelOptions?: Record<string, unknown>;
15
+ };
16
+ declare const _default: ESLintUtils.RuleModule<"parentImport" | "subpathImport", [OptionsInput], unknown, ESLintUtils.RuleListener>;
17
+ export default _default;
@@ -1,87 +1,111 @@
1
- import pathLib from 'node:path';
2
- import { loadOptions } from '@babel/core';
3
- import defu from '@dword-design/defu';
4
- import { resolvePath as defaultResolvePath } from 'babel-plugin-module-resolver';
1
+ import pathLib from "node:path";
2
+ import { loadOptions } from "@babel/core";
3
+ import defaults from "@dword-design/defaults";
4
+ import { ESLintUtils } from "@typescript-eslint/utils";
5
+ import { resolvePath as defaultResolvePath } from "babel-plugin-module-resolver";
6
+ import { omit, pick } from "lodash-es";
7
+ const createRule = ESLintUtils.RuleCreator(() => "");
5
8
  const isParentImport = path => /^(\.\/)?\.\.\//.test(path);
6
9
  const findMatchingAlias = (sourcePath, currentFile, options) => {
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
- }
10
+ const absoluteSourcePath = pathLib.resolve(pathLib.dirname(currentFile), sourcePath);
11
+ for (const aliasName of Object.keys(options.alias)) {
12
+ const path = pathLib.resolve(pathLib.dirname(currentFile), options.resolvePath(`${aliasName}/`, currentFile, pick(options, ["alias", "cwd"])));
13
+ if (absoluteSourcePath.startsWith(path)) {
14
+ return {
15
+ name: aliasName,
16
+ path
17
+ };
14
18
  }
19
+ }
15
20
  };
16
- export default {
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.');
21
+ export default createRule({
22
+ create: context => {
23
+ const currentFile = context.getFilename();
24
+ const folder = pathLib.dirname(currentFile);
25
+ if (currentFile === "<text>") return {};
26
+ const optionsFromRule = defaults(context.options[0] ?? {}, {
27
+ babelOptions: {}
28
+ });
29
+ const babelConfig = loadOptions({
30
+ filename: currentFile,
31
+ ...optionsFromRule.babelOptions
32
+ });
33
+ const babelPlugin = babelConfig?.plugins?.find?.(iteratedPlugin => iteratedPlugin.key === "module-resolver") ?? null;
34
+ const babelPluginOptions = babelPlugin?.options ?? {};
35
+ const optionsFromPlugin = pick(babelPluginOptions, ["alias", "resolvePath"]);
36
+ const options = defaults(omit(optionsFromRule, ["babelOptions"]), optionsFromPlugin, {
37
+ alias: {},
38
+ aliasForSubpaths: false,
39
+ cwd: context.cwd,
40
+ resolvePath: defaultResolvePath
41
+ });
42
+ if (Object.keys(options.alias).length === 0) {
43
+ 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.");
44
+ }
45
+ return {
46
+ ImportDeclaration: node => {
47
+ const sourcePath = node.source.value;
48
+ const hasAlias = Object.keys(options.alias).some(alias => sourcePath.startsWith(`${alias}/`));
49
+ if (isParentImport(sourcePath)) {
50
+ const matchingAlias = findMatchingAlias(sourcePath, currentFile, options);
51
+ if (!matchingAlias) {
52
+ return;
53
+ }
54
+ const absoluteImportPath = pathLib.resolve(folder, sourcePath);
55
+ const rewrittenImport = `${matchingAlias.name}/${pathLib.relative(matchingAlias.path, absoluteImportPath).replaceAll("\\", "/")}`;
56
+ return context.report({
57
+ data: {
58
+ rewrittenImport,
59
+ sourcePath
60
+ },
61
+ fix: fixer => fixer.replaceTextRange([node.source.range[0] + 1, node.source.range[1] - 1], rewrittenImport),
62
+ messageId: "parentImport",
63
+ node
64
+ });
36
65
  }
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.aliasForSubpaths) {
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;
66
+ const importWithoutAlias = options.resolvePath(sourcePath, currentFile, options);
67
+ if (!isParentImport(importWithoutAlias) && hasAlias && !options.aliasForSubpaths) {
68
+ return context.report({
69
+ data: {
70
+ rewrittenImport: importWithoutAlias,
71
+ sourcePath
69
72
  },
70
- };
73
+ fix: fixer => fixer.replaceTextRange([node.source.range[0] + 1, node.source.range[1] - 1], importWithoutAlias),
74
+ messageId: "subpathImport",
75
+ node
76
+ });
77
+ }
78
+ return;
79
+ }
80
+ };
81
+ },
82
+ defaultOptions: [{}],
83
+ meta: {
84
+ docs: {
85
+ description: "Enforce usage of import aliases over relative parent imports"
71
86
  },
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',
87
+ fixable: "code",
88
+ messages: {
89
+ parentImport: "Unexpected parent import '{{sourcePath}}'. Use '{{rewrittenImport}}' instead",
90
+ subpathImport: "Unexpected subpath import via alias '{{sourcePath}}'. Use '{{rewrittenImport}}' instead"
86
91
  },
87
- };
92
+ schema: [{
93
+ additionalProperties: false,
94
+ properties: {
95
+ alias: {
96
+ type: "object"
97
+ },
98
+ aliasForSubpaths: {
99
+ default: false,
100
+ type: "boolean"
101
+ },
102
+ babelOptions: {
103
+ type: "object"
104
+ }
105
+ },
106
+ type: "object"
107
+ }],
108
+ type: "suggestion"
109
+ },
110
+ name: "prefer-alias"
111
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dword-design/eslint-plugin-import-alias",
3
- "version": "6.0.3",
3
+ "version": "7.0.0",
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,12 @@
28
28
  "license": "MIT",
29
29
  "author": "Sebastian Landwehr <info@sebastianlandwehr.com>",
30
30
  "type": "module",
31
- "exports": "./dist/index.js",
31
+ "exports": {
32
+ ".": {
33
+ "default": "./dist/index.js",
34
+ "types": "./dist/index.d.ts"
35
+ }
36
+ },
32
37
  "main": "dist/index.js",
33
38
  "files": [
34
39
  "dist"
@@ -41,26 +46,31 @@
41
46
  "lint": "base lint",
42
47
  "prepare": "base prepare",
43
48
  "prepublishOnly": "base prepublishOnly",
44
- "test": "base test"
49
+ "test": "base test",
50
+ "typecheck": "base typecheck",
51
+ "verify": "base verify"
45
52
  },
46
53
  "dependencies": {
47
- "@babel/core": "^7.27.4",
48
- "@dword-design/defu": "^1.0.0",
54
+ "@babel/core": "^7.28.5",
55
+ "@dword-design/defaults": "^1.1.1",
56
+ "@types/babel__core": "^7.20.5",
57
+ "@types/lodash-es": "^4.17.12",
58
+ "@typescript-eslint/utils": "^8.50.1",
49
59
  "babel-plugin-module-resolver": "^5.0.2"
50
60
  },
51
61
  "devDependencies": {
52
- "@dword-design/base": "^13.0.1",
53
- "@playwright/test": "^1.52.0",
54
- "depcheck-package-name": "^3.0.1",
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"
62
+ "@dword-design/base": "^16.1.5",
63
+ "@playwright/test": "^1.57.0",
64
+ "depcheck-package-name": "^5.0.0",
65
+ "endent": "npm:@dword-design/endent@^1.4.7",
66
+ "eslint": "^9.39.2",
67
+ "lodash-es": "^4.17.22",
68
+ "output-files": "^3.0.0",
69
+ "typescript-eslint": "^8.50.0"
60
70
  },
61
71
  "packageManager": "pnpm@10.11.1+sha512.e519b9f7639869dc8d5c3c5dfef73b3f091094b0a006d7317353c72b124e80e1afd429732e28705ad6bfa1ee879c1fce46c128ccebd3192101f43dd67c667912",
62
72
  "engines": {
63
- "node": ">=18"
73
+ "node": ">=22"
64
74
  },
65
75
  "publishConfig": {
66
76
  "access": "public"