8000 feat(eslint-plugin-internal): add internal lint rule no-relative-path… · naruaway/typescript-eslint@8b45691 · GitHub
[go: up one dir, main page]

Skip to content

Commit 8b45691

Browse files
feat(eslint-plugin-internal): add internal lint rule no-relative-paths-to-internal-packages (typescript-eslint#8596)
Add internal lint rule no-relative-paths-to-internal-packages
1 parent d830364 commit 8b45691

File tree

4 files changed

+193
-0
lines changed

4 files changed

+193
-0
lines changed

eslint.config.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,8 @@ export default tseslint.config(
175175
//
176176

177177
'@typescript-eslint/internal/no-poorly-typed-ts-props': 'error',
178+
'@typescript-eslint/internal/no-relative-paths-to-internal-packages':
179+
'error',
178180
'@typescript-eslint/internal/no-typescript-default-import': 'error',
179181
'@typescript-eslint/internal/prefer-ast-types-enum': 'error',
180182

packages/eslint-plugin-internal/src/rules/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import type { Linter } from '@typescript-eslint/utils/ts-eslint';
22

33
import noPoorlyTypedTsProps from './no-poorly-typed-ts-props';
4+
import noRelativePathsToInternalPackages from './no-relative-paths-to-internal-packages';
45
import noTypescriptDefaultImport from './no-typescript-default-import';
56
import noTypescriptEstreeImport from './no-typescript-estree-import';
67
import pluginTestFormatting from './plugin-test-formatting';
78
import preferASTTypesEnum from './prefer-ast-types-enum';
89

910
export default {
1011
'no-poorly-typed-ts-props': noPoorlyTypedTsProps,
12+
'no-relative-paths-to-internal-packages': noRelativePathsToInternalPackages,
1113
'no-typescript-default-import': noTypescriptDefaultImport,
1214
'no-typescript-estree-import': noTypescriptEstreeImport,
1315
'plugin-test-formatting': pluginTestFormatting,
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import path from 'path';
2+
3+
import { createRule } from '../util';
4+
5+
export const REPO_ROOT = path.resolve(__dirname, '../../../..');
6+
export const PACKAGES_DIR = path.join(REPO_ROOT, 'packages');
7+
8+
export default createRule({
9+
name: __filename,
10+
meta: {
11+
type: 'problem',
12+
docs: {
13+
recommended: 'recommended',
14+
description: 'Disallow relative paths to internal packages',
15+
},
16+
messages: {
17+
noRelativePathsToInternalPackages:
18+
'Use absolute paths instead of relative paths to import modules in other internal packages.',
19+
},
20+
schema: [],
21+
fixable: 'code',
22+
},
23+
24+
defaultOptions: [],
25+
26+
create(context) {
27+
return {
28+
ImportDeclaration(node): void {
29+
const importSource = node.source;
30+
if (
31+
importSource.value.startsWith('@typescript-eslint') ||
32+
!importSource.value.startsWith('.')
33+
) {
34+
return;
35+
}
36+
37+
// The idea here is to check if the import source resolves to a different
38+
// package than the package the file is currently in. This lets us not flag
39+
// relative paths to modules inside a package called 'utils' but still flag
40+
// if importing the '@typescript-eslint/utils' package with a relative path.
41+
42+
const pathOfFileFromPackagesDir = path.relative(
43+
PACKAGES_DIR,
44+
context.physicalFilename,
45+
);
46+
const packageOfFile = pathOfFileFromPackagesDir.split(path.sep)[0];
47+
const absolutePathOfImport = path.resolve(
48+
path.dirname(context.physicalFilename),
49+
importSource.value,
50+
);
51+
const pathOfImportFromPackagesDir = path.relative(
52+
PACKAGES_DIR,
53+
absolutePathOfImport,
54+
);
55+
const packageOfImport = pathOfImportFromPackagesDir.split(path.sep)[0];
56+
57+
if (packageOfImport !== packageOfFile) {
58+
context.report({
59+
node: importSource,
60+
messageId: 'noRelativePathsToInternalPackages',
61+
fix: fixer => {
62+
// Force the output path to be separated with '/' to get consistent
63+
// results on windows.
64+
const platformIndependentRelativePathOfImportFromPackagesDir =
65+
pathOfImportFromPackagesDir.split(path.sep).join('/');
66+
return fixer.replaceText(
67+
importSource,
68+
`'@typescript-eslint/${platformIndependentRelativePathOfImportFromPackagesDir}'`,
69+
);
70+
},
71+
});
72+
}
73+
},
74+
};
75+
},
76+
});
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { RuleTester } from '@typescript-eslint/rule-tester';
2+
import path from 'path';
3+
4+
import rule, {
5+
PACKAGES_DIR,
6+
} from '../../src/rules/no-relative-paths-to-internal-packages';
7+
8+
const ruleTester = new RuleTester({
9+
parser: '@typescript-eslint/parser',
10+
});
11+
12+
ruleTester.run('no-relative-paths-to-internal-packages', rule, {
13+
valid: [
14+
"import { parse } from '@typescript-eslint/typescript-estree';",
15+
"import { something } from 'not/a/relative/path';",
16+
{
17+
filename: path.resolve(
18+
PACKAGES_DIR,
19+
'eslint-plugin/src/rules/my-awesome-rule.ts',
20+
),
21+
code: "import { something } from './utils';",
22+
},
23+
{
24+
code: "import type { ValueOf } from './utils';",
25+
filename: path.resolve(
26+
PACKAGES_DIR,
27+
'ast-spec/src/expression/AssignmentExpression/spec.ts',
28+
),
29+
},
30+
{
31+
code: "import type { ValueOf } from '../../utils';",
32+
filename: path.resolve(
33+
PACKAGES_DIR,
34+
'ast-spec/src/expression/AssignmentExpression/spec.ts',
35+
),
36+
},
37+
{
38+
code: "import type { ValueOf } from '../../../utils';",
39+
filename: path.resolve(
40+
PACKAGES_DIR,
41+
'ast-spec/src/expression/AssignmentExpression/spec.ts',
42+
),
43+
},
44+
],
45+
invalid: [
46+
{
47+
code: "import { parse } from '../../../typescript-estree';",
48+
filename: path.resolve(
49+
PACKAGES_DIR,
50+
'eslint-plugin/src/rules/my-awesome-rule.ts',
51+
),
52+
output: `import { parse } from '@typescript-eslint/typescript-estree';`,
53+
errors: [
54+
{
55+
messageId: 'noRelativePathsToInternalPackages',
56+
line: 1,
57+
},
58+
],
59+
},
60+
{
61+
code: "import { parse } from '../../../typescript-estree/inner-module';",
62+
filename: path.resolve(
63+
PACKAGES_DIR,
64+
'eslint-plugin/src/rules/my-awesome-rule.ts',
65+
),
66+
output: `import { parse } from '@typescript-eslint/typescript-estree/inner-module';`,
67+
errors: [
68+
{
69+
messageId: 'noRelativePathsToInternalPackages',
70+
line: 1,
71+
},
72+
],
73+
},
74+
{
75+
code: "import type { ValueOf } from '../../../../utils';",
76+
output: "import type { ValueOf } from '@typescript-eslint/utils';",
77+
filename: path.resolve(
78+
PACKAGES_DIR,
79+
'ast-spec/src/expression/AssignmentExpression/spec.ts',
80+
),
81+
errors: [
82+
{
83+
messageId: 'noRelativePathsToInternalPackages',
84+
line: 1,
85+
},
86+
],
87+
},
88+
{
89+
code: `
90+
import type {
91+
MemberExpressionComputedName,
92+
MemberExpressionNonComputedName,
93+
} from '../../../types/src/generated/ast-spec';
94+
`,
95+
output: `
96+
import type {
97+
MemberExpressionComputedName,
98+
MemberExpressionNonComputedName,
99+
} from '@typescript-eslint/types/src/generated/ast-spec';
100+
`,
101+
filename: path.resolve(
102+
PACKAGES_DIR,
103+
'eslint-plugin/src/rules/prefer-find.ts',
104+
),
105+
errors: [
106+
{
107+
messageId: 'noRelativePathsToInternalPackages',
108+
line: 5,
109+
},
110+
],
111+
},
112+
],
113+
});

0 commit comments

Comments
 (0)
0