Skip to content

Commit

Permalink
Add internal lint rule no-relative-paths-to-internal-packages
Browse files Browse the repository at this point in the history
  • Loading branch information
kirkwaiblinger committed Mar 5, 2024
1 parent 6954a4a commit 088d69b
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 0 deletions.
2 changes: 2 additions & 0 deletions eslint.config.mjs
Expand Up @@ -177,6 +177,8 @@ export default tseslint.config(
//

'@typescript-eslint/internal/no-poorly-typed-ts-props': 'error',
'@typescript-eslint/internal/no-relative-paths-to-internal-packages':
'error',
'@typescript-eslint/internal/no-typescript-default-import': 'error',
'@typescript-eslint/internal/prefer-ast-types-enum': 'error',

Expand Down
2 changes: 2 additions & 0 deletions packages/eslint-plugin-internal/src/rules/index.ts
@@ -1,13 +1,15 @@
import type { Linter } from '@typescript-eslint/utils/ts-eslint';

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

export default {
'no-poorly-typed-ts-props': noPoorlyTypedTsProps,
'no-relative-paths-to-internal-packages': noRelativePathsToInternalPackages,
'no-typescript-default-import': noTypescriptDefaultImport,
'no-typescript-estree-import': noTypescriptEstreeImport,
'plugin-test-formatting': pluginTestFormatting,
Expand Down
@@ -0,0 +1,71 @@
import path from 'path';

import { createRule } from '../util';

export const REPO_ROOT = path.resolve(__dirname, '../../../..');
export const PACKAGES_DIR = path.join(REPO_ROOT, 'packages');

export default createRule({
name: __filename,
meta: {
type: 'problem',
docs: {
recommended: 'recommended',
description: 'Disallow relative paths to internal packages',
},
messages: {
noRelativePathsToInternalPackages:
'Use absolute paths instead of relative paths to import modules in other internal packages.',
},
schema: [],
fixable: 'code',
},

defaultOptions: [],

create(context) {
return {
ImportDeclaration(node): void {
const importSource = node.source;
if (
importSource.value.startsWith('@typescript-eslint') ||
!importSource.value.startsWith('.')
) {
return;
}

// The idea here is to check if the import source resolves to a different
// package than the package the file is currently in. This lets us not flag
// relative paths to modules inside a package called 'utils' but still flag
// if importing the '@typescript-eslint/utils' package with a relative path.

const pathOfFileFromPackagesDir = path.relative(
PACKAGES_DIR,
context.physicalFilename,
);
const packageOfFile = pathOfFileFromPackagesDir.split(path.sep)[0];
const absolutePathOfImport = path.resolve(
path.dirname(context.physicalFilename),
importSource.value,
);
const pathOfImportFromPackagesDir = path.relative(
PACKAGES_DIR,
absolutePathOfImport,
);
const packageOfImport = pathOfImportFromPackagesDir.split(path.sep)[0];

if (packageOfImport !== packageOfFile) {
context.report({
node: importSource,
messageId: 'noRelativePathsToInternalPackages',
fix: fixer =>
fixer.replaceText(
importSource,
`'@typescript-eslint/${path.relative(PACKAGES_DIR, absolutePathOfImport)}'`,
),
});
}
},
};
},
});
@@ -0,0 +1,113 @@
import { RuleTester } from '@typescript-eslint/rule-tester';
import path from 'path';

import rule, {
PACKAGES_DIR,
} from '../../src/rules/no-relative-paths-to-internal-packages';

const ruleTester = new RuleTester({
parser: '@typescript-eslint/parser',
});

ruleTester.run('no-relative-paths-to-internal-packages', rule, {
valid: [
"import { parse } from '@typescript-eslint/typescript-estree';",
"import { something } from 'not/a/relative/path';",
{
filename: path.resolve(
PACKAGES_DIR,
'eslint-plugin/src/rules/my-awesome-rule.ts',
),
code: "import { something } from './utils';",
},
{
code: "import type { ValueOf } from './utils';",
filename: path.resolve(
PACKAGES_DIR,
'ast-spec/src/expression/AssignmentExpression/spec.ts',
),
},
{
code: "import type { ValueOf } from '../../utils';",
filename: path.resolve(
PACKAGES_DIR,
'ast-spec/src/expression/AssignmentExpression/spec.ts',
),
},
{
code: "import type { ValueOf } from '../../../utils';",
filename: path.resolve(
PACKAGES_DIR,
'ast-spec/src/expression/AssignmentExpression/spec.ts',
),
},
],
invalid: [
{
code: "import { parse } from '../../../typescript-estree';",
filename: path.resolve(
PACKAGES_DIR,
'eslint-plugin/src/rules/my-awesome-rule.ts',
),
output: `import { parse } from '@typescript-eslint/typescript-estree';`,
errors: [
{
messageId: 'noRelativePathsToInternalPackages',
line: 1,
},
],
},
{
code: "import { parse } from '../../../typescript-estree/inner-module';",
filename: path.resolve(
PACKAGES_DIR,
'eslint-plugin/src/rules/my-awesome-rule.ts',
),
output: `import { parse } from '@typescript-eslint/typescript-estree/inner-module';`,
errors: [
{
messageId: 'noRelativePathsToInternalPackages',
line: 1,
},
],
},
{
code: "import type { ValueOf } from '../../../../utils';",
output: "import type { ValueOf } from '@typescript-eslint/utils';",
filename: path.resolve(
PACKAGES_DIR,
'ast-spec/src/expression/AssignmentExpression/spec.ts',
),
errors: [
{
messageId: 'noRelativePathsToInternalPackages',
line: 1,
},
],
},
{
code: `
import type {
MemberExpressionComputedName,
MemberExpressionNonComputedName,
} from '../../../types/src/generated/ast-spec';
`,
output: `
import type {
MemberExpressionComputedName,
MemberExpressionNonComputedName,
} from '@typescript-eslint/types/src/generated/ast-spec';
`,
filename: path.resolve(
PACKAGES_DIR,
'eslint-plugin/src/rules/prefer-find.ts',
),
errors: [
{
messageId: 'noRelativePathsToInternalPackages',
line: 5,
},
],
},
],
});

0 comments on commit 088d69b

Please sign in to comment.