eslint-plugin-fast-import 1.0.0-beta2
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/.prettierrc.json +6 -0
- package/.vscode/launch.json +39 -0
- package/LICENSE +21 -0
- package/README.md +3 -0
- package/dist/module/computeAnalyzedInfo.d.ts +3 -0
- package/dist/module/computeAnalyzedInfo.js +297 -0
- package/dist/module/computeAnalyzedInfo.js.map +1 -0
- package/dist/module/computeBaseInfo.d.ts +24 -0
- package/dist/module/computeBaseInfo.js +564 -0
- package/dist/module/computeBaseInfo.js.map +1 -0
- package/dist/module/computeResolvedInfo.d.ts +7 -0
- package/dist/module/computeResolvedInfo.js +361 -0
- package/dist/module/computeResolvedInfo.js.map +1 -0
- package/dist/module/module.d.ts +20 -0
- package/dist/module/module.js +224 -0
- package/dist/module/module.js.map +1 -0
- package/dist/module/util.d.ts +44 -0
- package/dist/module/util.js +67 -0
- package/dist/module/util.js.map +1 -0
- package/dist/plugin.d.ts +19 -0
- package/dist/plugin.js +57 -0
- package/dist/plugin.js.map +1 -0
- package/dist/rules/circular/circular.d.ts +2 -0
- package/dist/rules/circular/circular.js +107 -0
- package/dist/rules/circular/circular.js.map +1 -0
- package/dist/rules/entryPoint/entryPoint.d.ts +1 -0
- package/dist/rules/entryPoint/entryPoint.js +38 -0
- package/dist/rules/entryPoint/entryPoint.js.map +1 -0
- package/dist/rules/externalBarrelReexports/externalBarrelReexports.d.ts +1 -0
- package/dist/rules/externalBarrelReexports/externalBarrelReexports.js +41 -0
- package/dist/rules/externalBarrelReexports/externalBarrelReexports.js.map +1 -0
- package/dist/rules/missing/missing.d.ts +1 -0
- package/dist/rules/missing/missing.js +59 -0
- package/dist/rules/missing/missing.js.map +1 -0
- package/dist/rules/testInProd/testInProd.d.ts +1 -0
- package/dist/rules/testInProd/testInProd.js +42 -0
- package/dist/rules/testInProd/testInProd.js.map +1 -0
- package/dist/rules/unused/unused.d.ts +3 -0
- package/dist/rules/unused/unused.js +77 -0
- package/dist/rules/unused/unused.js.map +1 -0
- package/dist/rules/util.d.ts +11 -0
- package/dist/rules/util.js +107 -0
- package/dist/rules/util.js.map +1 -0
- package/dist/settings/settings.d.ts +12 -0
- package/dist/settings/settings.js +123 -0
- package/dist/settings/settings.js.map +1 -0
- package/dist/settings/typescript.d.ts +3 -0
- package/dist/settings/typescript.js +39 -0
- package/dist/settings/typescript.js.map +1 -0
- package/dist/settings/user.d.ts +45 -0
- package/dist/settings/user.js +52 -0
- package/dist/settings/user.js.map +1 -0
- package/dist/settings/util.d.ts +2 -0
- package/dist/settings/util.js +33 -0
- package/dist/settings/util.js.map +1 -0
- package/dist/types/analyzed.d.ts +120 -0
- package/dist/types/analyzed.js +2 -0
- package/dist/types/analyzed.js.map +1 -0
- package/dist/types/base.d.ts +230 -0
- package/dist/types/base.js +2 -0
- package/dist/types/base.js.map +1 -0
- package/dist/types/context.d.ts +2 -0
- package/dist/types/context.js +2 -0
- package/dist/types/context.js.map +1 -0
- package/dist/types/resolved.d.ts +60 -0
- package/dist/types/resolved.js +2 -0
- package/dist/types/resolved.js.map +1 -0
- package/dist/util/code.d.ts +1 -0
- package/dist/util/code.js +6 -0
- package/dist/util/code.js.map +1 -0
- package/dist/util/error.d.ts +14 -0
- package/dist/util/error.js +19 -0
- package/dist/util/error.js.map +1 -0
- package/dist/util/files.d.ts +10 -0
- package/dist/util/files.js +122 -0
- package/dist/util/files.js.map +1 -0
- package/dist/util/logging.d.ts +5 -0
- package/dist/util/logging.js +19 -0
- package/dist/util/logging.js.map +1 -0
- package/eslint.config.mjs +85 -0
- package/jest.config.ts +10 -0
- package/package.json +49 -0
- package/tsconfig.json +31 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { TSESTree } from '@typescript-eslint/typescript-estree';
|
|
2
|
+
type ImportDeclaration = TSESTree.ImportDeclaration | TSESTree.ImportExpression;
|
|
3
|
+
export type ExportDeclaration = TSESTree.ExportDefaultDeclaration | TSESTree.ExportNamedDeclaration;
|
|
4
|
+
type ReexportDeclaration = TSESTree.ExportNamedDeclarationWithSource | TSESTree.ExportAllDeclaration;
|
|
5
|
+
/**
|
|
6
|
+
* Reads in a file specified by `filePath` and returns the raw string contents
|
|
7
|
+
* and the parsed AST.
|
|
8
|
+
*/
|
|
9
|
+
export declare function parseFile(filePath: string): {
|
|
10
|
+
filePath: string;
|
|
11
|
+
fileContents: string;
|
|
12
|
+
ast: import("@typescript-eslint/typescript-estree").AST<{
|
|
13
|
+
loc: true;
|
|
14
|
+
range: true;
|
|
15
|
+
tokens: true;
|
|
16
|
+
jsx: boolean;
|
|
17
|
+
}>;
|
|
18
|
+
};
|
|
19
|
+
type WalkOptions = {
|
|
20
|
+
filePath: string;
|
|
21
|
+
fileContents: string;
|
|
22
|
+
ast: TSESTree.Program;
|
|
23
|
+
/**
|
|
24
|
+
* A callback to be called for all of the various import statement nodes,
|
|
25
|
+
* including dynamic import statements
|
|
26
|
+
*/
|
|
27
|
+
importDeclaration: (node: ImportDeclaration) => void;
|
|
28
|
+
/**
|
|
29
|
+
* A callback to be called for all of the various export statement nodes.
|
|
30
|
+
*
|
|
31
|
+
* Note: this does _not_ include reexport nodes, even though sometimes the
|
|
32
|
+
* actual node is the same type as a reexport node.
|
|
33
|
+
*/
|
|
34
|
+
exportDeclaration: (node: ExportDeclaration) => void;
|
|
35
|
+
/**
|
|
36
|
+
* A callback to be called for all of the various reexport statement nodes
|
|
37
|
+
*/
|
|
38
|
+
reexportDeclaration: (node: ReexportDeclaration) => void;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* This helper function makes traversing the AST of a file easier.
|
|
42
|
+
*/
|
|
43
|
+
export declare function traverse({ ast, filePath, fileContents, importDeclaration, exportDeclaration, reexportDeclaration, }: WalkOptions): void;
|
|
44
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { InternalError } from '../util/error.js';
|
|
2
|
+
import { TSESTree } from '@typescript-eslint/typescript-estree';
|
|
3
|
+
import { parse, simpleTraverse } from '@typescript-eslint/typescript-estree';
|
|
4
|
+
import { readFileSync } from 'fs';
|
|
5
|
+
/**
|
|
6
|
+
* Reads in a file specified by `filePath` and returns the raw string contents
|
|
7
|
+
* and the parsed AST.
|
|
8
|
+
*/
|
|
9
|
+
export function parseFile(filePath) {
|
|
10
|
+
const fileContents = readFileSync(filePath, 'utf-8');
|
|
11
|
+
const ast = parse(fileContents, {
|
|
12
|
+
loc: true,
|
|
13
|
+
range: true,
|
|
14
|
+
tokens: true,
|
|
15
|
+
// JSX is a proper superset of JavaScript, meaning JSX can appear in both .js and .jsx files. TSX is *not* a
|
|
16
|
+
// proper superset of TypeScript, however, and so JSX can only appear in .tsx files, not .ts files
|
|
17
|
+
jsx: !filePath.endsWith('.ts'),
|
|
18
|
+
});
|
|
19
|
+
return { filePath, fileContents, ast };
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* This helper function makes traversing the AST of a file easier.
|
|
23
|
+
*/
|
|
24
|
+
export function traverse({ ast, filePath, fileContents, importDeclaration, exportDeclaration, reexportDeclaration, }) {
|
|
25
|
+
// For some reason, `simpleTraverse` only types `node` as `TSESTree.Node`, not
|
|
26
|
+
// the type of node specified by the function name. This helper function
|
|
27
|
+
// asserts the type and does a run-time check, just in case, to make
|
|
28
|
+
// TypeScript happy.
|
|
29
|
+
//
|
|
30
|
+
// This type parameter is actually necessary, since it's used in the assert
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
|
|
32
|
+
function validateNodeType(node, nodeType) {
|
|
33
|
+
if (node.type !== nodeType) {
|
|
34
|
+
throw new InternalError(`simpleTraverse returned unexpected type ${node.type}`, { filePath, fileContents, node });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
simpleTraverse(ast, {
|
|
38
|
+
visitors: {
|
|
39
|
+
ImportDeclaration(node) {
|
|
40
|
+
validateNodeType(node, TSESTree.AST_NODE_TYPES.ImportDeclaration);
|
|
41
|
+
importDeclaration(node);
|
|
42
|
+
},
|
|
43
|
+
ImportExpression(node) {
|
|
44
|
+
validateNodeType(node, TSESTree.AST_NODE_TYPES.ImportExpression);
|
|
45
|
+
importDeclaration(node);
|
|
46
|
+
},
|
|
47
|
+
ExportDefaultDeclaration(node) {
|
|
48
|
+
validateNodeType(node, TSESTree.AST_NODE_TYPES.ExportDefaultDeclaration);
|
|
49
|
+
exportDeclaration(node);
|
|
50
|
+
},
|
|
51
|
+
ExportAllDeclaration(node) {
|
|
52
|
+
validateNodeType(node, TSESTree.AST_NODE_TYPES.ExportAllDeclaration);
|
|
53
|
+
reexportDeclaration(node);
|
|
54
|
+
},
|
|
55
|
+
ExportNamedDeclaration(node) {
|
|
56
|
+
validateNodeType(node, TSESTree.AST_NODE_TYPES.ExportNamedDeclaration);
|
|
57
|
+
if (node.source) {
|
|
58
|
+
reexportDeclaration(node);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
exportDeclaration(node);
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=util.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/module/util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AAChE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAelC;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,QAAgB;IACxC,MAAM,YAAY,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACrD,MAAM,GAAG,GAAG,KAAK,CAAC,YAAY,EAAE;QAC9B,GAAG,EAAE,IAAI;QACT,KAAK,EAAE,IAAI;QACX,MAAM,EAAE,IAAI;QAEZ,4GAA4G;QAC5G,kGAAkG;QAClG,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC;KAC/B,CAAC,CAAC;IACH,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC;AACzC,CAAC;AAyBD;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,EACvB,GAAG,EACH,QAAQ,EACR,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,GACP;IACZ,8EAA8E;IAC9E,wEAAwE;IACxE,oEAAoE;IACpE,oBAAoB;IACpB,EAAE;IACF,2EAA2E;IAC3E,6EAA6E;IAC7E,SAAS,gBAAgB,CACvB,IAAmB,EACnB,QAAiC;QAEjC,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC3B,MAAM,IAAI,aAAa,CACrB,2CAA2C,IAAI,CAAC,IAAI,EAAE,EACtD,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,CACjC,CAAC;QACJ,CAAC;IACH,CAAC;IAED,cAAc,CAAC,GAAG,EAAE;QAClB,QAAQ,EAAE;YACR,iBAAiB,CAAC,IAAI;gBACpB,gBAAgB,CACd,IAAI,EACJ,QAAQ,CAAC,cAAc,CAAC,iBAAiB,CAC1C,CAAC;gBACF,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAC1B,CAAC;YACD,gBAAgB,CAAC,IAAI;gBACnB,gBAAgB,CACd,IAAI,EACJ,QAAQ,CAAC,cAAc,CAAC,gBAAgB,CACzC,CAAC;gBACF,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAC1B,CAAC;YACD,wBAAwB,CAAC,IAAI;gBAC3B,gBAAgB,CACd,IAAI,EACJ,QAAQ,CAAC,cAAc,CAAC,wBAAwB,CACjD,CAAC;gBACF,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAC1B,CAAC;YACD,oBAAoB,CAAC,IAAI;gBACvB,gBAAgB,CACd,IAAI,EACJ,QAAQ,CAAC,cAAc,CAAC,oBAAoB,CAC7C,CAAC;gBACF,mBAAmB,CAAC,IAAI,CAAC,CAAC;YAC5B,CAAC;YACD,sBAAsB,CAAC,IAAI;gBACzB,gBAAgB,CACd,IAAI,EACJ,QAAQ,CAAC,cAAc,CAAC,sBAAsB,CAC/C,CAAC;gBACF,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;oBAChB,mBAAmB,CAAC,IAAI,CAAC,CAAC;gBAC5B,CAAC;qBAAM,CAAC;oBACN,iBAAiB,CAAC,IAAI,CAAC,CAAC;gBAC1B,CAAC;YACH,CAAC;SACF;KACF,CAAC,CAAC;AACL,CAAC"}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
declare const plugin: {
|
|
2
|
+
meta: {
|
|
3
|
+
name: string;
|
|
4
|
+
version: string;
|
|
5
|
+
};
|
|
6
|
+
configs: {};
|
|
7
|
+
rules: {
|
|
8
|
+
'no-unused-exports': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noUnusedExports" | "noTestOnlyImports", [{
|
|
9
|
+
allowNonTestTypeExports: boolean;
|
|
10
|
+
} | undefined], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
11
|
+
'no-circular-imports': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noCircularImports", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
12
|
+
'no-entry-point-imports': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noEntryPointImports", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
13
|
+
'no-missing-imports': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noMissingImports", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
14
|
+
'no-external-barrel-reexports': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noExternalBarrelReexports", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
15
|
+
'no-test-imports-in-prod': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noTestImports", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
16
|
+
};
|
|
17
|
+
processors: {};
|
|
18
|
+
};
|
|
19
|
+
export default plugin;
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { getDirname } from 'cross-dirname';
|
|
4
|
+
import { noUnusedExports } from './rules/unused/unused.js';
|
|
5
|
+
import { noCircularImports } from './rules/circular/circular.js';
|
|
6
|
+
import { noEntryPointImports } from './rules/entryPoint/entryPoint.js';
|
|
7
|
+
import { noMissingImports } from './rules/missing/missing.js';
|
|
8
|
+
import { noExternalBarrelReexports } from './rules/externalBarrelReexports/externalBarrelReexports.js';
|
|
9
|
+
import { noTestImportsInProd } from './rules/testInProd/testInProd.js';
|
|
10
|
+
const { name, version } = JSON.parse(readFileSync(join(getDirname(), '..', 'package.json'), 'utf8'));
|
|
11
|
+
const plugin = {
|
|
12
|
+
meta: {
|
|
13
|
+
name,
|
|
14
|
+
version,
|
|
15
|
+
},
|
|
16
|
+
configs: {},
|
|
17
|
+
rules: {
|
|
18
|
+
'no-unused-exports': noUnusedExports,
|
|
19
|
+
'no-circular-imports': noCircularImports,
|
|
20
|
+
'no-entry-point-imports': noEntryPointImports,
|
|
21
|
+
'no-missing-imports': noMissingImports,
|
|
22
|
+
'no-external-barrel-reexports': noExternalBarrelReexports,
|
|
23
|
+
'no-test-imports-in-prod': noTestImportsInProd,
|
|
24
|
+
},
|
|
25
|
+
processors: {},
|
|
26
|
+
};
|
|
27
|
+
// assign configs here so we can reference `plugin`
|
|
28
|
+
Object.assign(plugin.configs, {
|
|
29
|
+
recommended: {
|
|
30
|
+
plugins: {
|
|
31
|
+
'fast-import': plugin,
|
|
32
|
+
},
|
|
33
|
+
rules: {
|
|
34
|
+
'fast-import/no-unused-exports': 'error',
|
|
35
|
+
'fast-import/no-circular-imports': 'error',
|
|
36
|
+
'fast-import/no-entry-point-imports': 'error',
|
|
37
|
+
'fast-import/no-missing-imports': 'error',
|
|
38
|
+
'fast-import/no-external-barrel-reexports': 'error',
|
|
39
|
+
'fast-import/no-test-imports-in-prod': 'error',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
off: {
|
|
43
|
+
plugins: {
|
|
44
|
+
'fast-import': plugin,
|
|
45
|
+
},
|
|
46
|
+
rules: {
|
|
47
|
+
'fast-import/no-unused-exports': 'off',
|
|
48
|
+
'fast-import/no-circular-imports': 'off',
|
|
49
|
+
'fast-import/no-entry-point-imports': 'off',
|
|
50
|
+
'fast-import/no-missing-imports': 'off',
|
|
51
|
+
'fast-import/no-external-barrel-reexports': 'off',
|
|
52
|
+
'fast-import/no-test-imports-in-prod': 'off',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
export default plugin;
|
|
57
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,MAAM,4DAA4D,CAAC;AACvG,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAEvE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,KAAK,CAClC,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAC1B,CAAC;AAEvC,MAAM,MAAM,GAAG;IACb,IAAI,EAAE;QACJ,IAAI;QACJ,OAAO;KACR;IACD,OAAO,EAAE,EAAE;IACX,KAAK,EAAE;QACL,mBAAmB,EAAE,eAAe;QACpC,qBAAqB,EAAE,iBAAiB;QACxC,wBAAwB,EAAE,mBAAmB;QAC7C,oBAAoB,EAAE,gBAAgB;QACtC,8BAA8B,EAAE,yBAAyB;QACzD,yBAAyB,EAAE,mBAAmB;KAC/C;IACD,UAAU,EAAE,EAAE;CACf,CAAC;AAEF,mDAAmD;AACnD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE;IAC5B,WAAW,EAAE;QACX,OAAO,EAAE;YACP,aAAa,EAAE,MAAM;SACtB;QACD,KAAK,EAAE;YACL,+BAA+B,EAAE,OAAO;YACxC,iCAAiC,EAAE,OAAO;YAC1C,oCAAoC,EAAE,OAAO;YAC7C,gCAAgC,EAAE,OAAO;YACzC,0CAA0C,EAAE,OAAO;YACnD,qCAAqC,EAAE,OAAO;SAC/C;KACF;IACD,GAAG,EAAE;QACH,OAAO,EAAE;YACP,aAAa,EAAE,MAAM;SACtB;QACD,KAAK,EAAE;YACL,+BAA+B,EAAE,KAAK;YACtC,iCAAiC,EAAE,KAAK;YACxC,oCAAoC,EAAE,KAAK;YAC3C,gCAAgC,EAAE,KAAK;YACvC,0CAA0C,EAAE,KAAK;YACjD,qCAAqC,EAAE,KAAK;SAC7C;KACF;CACF,CAAC,CAAC;AAEH,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { createRule, getESMInfo, registerUpdateListener } from '../util.js';
|
|
2
|
+
import { InternalError } from '../../util/error.js';
|
|
3
|
+
function checkFile(originalFilePath, currentFilePath, projectInfo, importStack, visitedFiles) {
|
|
4
|
+
const fileDetails = projectInfo.files.get(currentFilePath);
|
|
5
|
+
if (!fileDetails) {
|
|
6
|
+
throw new InternalError(`Could not get file info for "${currentFilePath}"`);
|
|
7
|
+
}
|
|
8
|
+
// Non-JS files by definition can't be circilar, since they can't import JS
|
|
9
|
+
if (fileDetails.fileType !== 'code') {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
// Mark this file as visited
|
|
13
|
+
visitedFiles.push(currentFilePath);
|
|
14
|
+
// Now check if this file is part of a cycle
|
|
15
|
+
const firstInstanceIndex = importStack.indexOf(currentFilePath);
|
|
16
|
+
if (firstInstanceIndex !== -1) {
|
|
17
|
+
const filesInCycle = importStack.slice(firstInstanceIndex);
|
|
18
|
+
return filesInCycle.includes(originalFilePath);
|
|
19
|
+
}
|
|
20
|
+
// If this wasn't a cycle, then keep exploring
|
|
21
|
+
for (const importEntry of [
|
|
22
|
+
...fileDetails.imports,
|
|
23
|
+
...fileDetails.reexports,
|
|
24
|
+
]) {
|
|
25
|
+
if (('isTypeImport' in importEntry && importEntry.isTypeImport) ||
|
|
26
|
+
('isTypeReexport' in importEntry && importEntry.isTypeReexport) ||
|
|
27
|
+
importEntry.moduleType !== 'firstPartyCode' ||
|
|
28
|
+
visitedFiles.includes(importEntry.resolvedModulePath)) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (checkFile(originalFilePath, importEntry.resolvedModulePath, projectInfo, [...importStack, currentFilePath], visitedFiles)) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
// Map of filepaths to imports/reexports with circular dependencies
|
|
38
|
+
const circularImportMap = new Map();
|
|
39
|
+
registerUpdateListener(() => {
|
|
40
|
+
circularImportMap.clear();
|
|
41
|
+
});
|
|
42
|
+
// This is only used in tests, since update listeners aren't guaranteed to
|
|
43
|
+
// be called on each run
|
|
44
|
+
// eslint-disable-next-line fast-import/no-unused-exports
|
|
45
|
+
export function _resetCircularMap() {
|
|
46
|
+
circularImportMap.clear();
|
|
47
|
+
}
|
|
48
|
+
export const noCircularImports = createRule({
|
|
49
|
+
name: 'no-circular-imports',
|
|
50
|
+
meta: {
|
|
51
|
+
docs: {
|
|
52
|
+
description: 'Ensures that there are no circular imports',
|
|
53
|
+
},
|
|
54
|
+
schema: [],
|
|
55
|
+
fixable: undefined,
|
|
56
|
+
type: 'problem',
|
|
57
|
+
messages: {
|
|
58
|
+
noCircularImports: 'Imports cannot be circular',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
defaultOptions: [],
|
|
62
|
+
create(context) {
|
|
63
|
+
const esmInfo = getESMInfo(context);
|
|
64
|
+
if (!esmInfo) {
|
|
65
|
+
return {};
|
|
66
|
+
}
|
|
67
|
+
const { fileInfo, projectInfo } = esmInfo;
|
|
68
|
+
if (fileInfo.fileType !== 'code') {
|
|
69
|
+
return {};
|
|
70
|
+
}
|
|
71
|
+
// If we recomputed on this run, then we need to recompute cycles
|
|
72
|
+
if (!circularImportMap.has(context.filename)) {
|
|
73
|
+
const importedFilesSearched = new Set();
|
|
74
|
+
const circularImportNodes = [];
|
|
75
|
+
for (const importEntry of [...fileInfo.imports, ...fileInfo.reexports]) {
|
|
76
|
+
if (!('resolvedModulePath' in importEntry) ||
|
|
77
|
+
!importEntry.resolvedModulePath ||
|
|
78
|
+
importedFilesSearched.has(importEntry.resolvedModulePath)) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
importedFilesSearched.add(importEntry.resolvedModulePath);
|
|
82
|
+
if (checkFile(context.filename, importEntry.resolvedModulePath, projectInfo, [context.filename], [])) {
|
|
83
|
+
circularImportNodes.push(importEntry.resolvedModulePath);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
circularImportMap.set(context.filename, circularImportNodes);
|
|
87
|
+
}
|
|
88
|
+
const circularImports = circularImportMap.get(context.filename);
|
|
89
|
+
if (!circularImports) {
|
|
90
|
+
throw new InternalError(`Circular imports are undefined for ${context.filename}`);
|
|
91
|
+
}
|
|
92
|
+
for (const circularImport of circularImports) {
|
|
93
|
+
for (const importEntry of [...fileInfo.imports, ...fileInfo.reexports]) {
|
|
94
|
+
if (importEntry.moduleType === 'firstPartyCode' &&
|
|
95
|
+
importEntry.resolvedModulePath === circularImport) {
|
|
96
|
+
context.report({
|
|
97
|
+
messageId: 'noCircularImports',
|
|
98
|
+
node: importEntry.statementNode,
|
|
99
|
+
});
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return {};
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
//# sourceMappingURL=circular.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circular.js","sourceRoot":"","sources":["../../../src/rules/circular/circular.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAE5E,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAKpD,SAAS,SAAS,CAChB,gBAAwB,EACxB,eAAuB,EACvB,WAAgC,EAChC,WAAqB,EACrB,YAAsB;IAEtB,MAAM,WAAW,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAC3D,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,aAAa,CAAC,gCAAgC,eAAe,GAAG,CAAC,CAAC;IAC9E,CAAC;IAED,2EAA2E;IAC3E,IAAI,WAAW,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;QACpC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,4BAA4B;IAC5B,YAAY,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAEnC,4CAA4C;IAC5C,MAAM,kBAAkB,GAAG,WAAW,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAChE,IAAI,kBAAkB,KAAK,CAAC,CAAC,EAAE,CAAC;QAC9B,MAAM,YAAY,GAAG,WAAW,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;QAC3D,OAAO,YAAY,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IACjD,CAAC;IAED,8CAA8C;IAC9C,KAAK,MAAM,WAAW,IAAI;QACxB,GAAG,WAAW,CAAC,OAAO;QACtB,GAAG,WAAW,CAAC,SAAS;KACzB,EAAE,CAAC;QACF,IACE,CAAC,cAAc,IAAI,WAAW,IAAI,WAAW,CAAC,YAAY,CAAC;YAC3D,CAAC,gBAAgB,IAAI,WAAW,IAAI,WAAW,CAAC,cAAc,CAAC;YAC/D,WAAW,CAAC,UAAU,KAAK,gBAAgB;YAC3C,YAAY,CAAC,QAAQ,CAAC,WAAW,CAAC,kBAAkB,CAAC,EACrD,CAAC;YACD,SAAS;QACX,CAAC;QACD,IACE,SAAS,CACP,gBAAgB,EAChB,WAAW,CAAC,kBAAkB,EAC9B,WAAW,EACX,CAAC,GAAG,WAAW,EAAE,eAAe,CAAC,EACjC,YAAY,CACb,EACD,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,mEAAmE;AACnE,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAoB,CAAC;AAEtD,sBAAsB,CAAC,GAAG,EAAE;IAC1B,iBAAiB,CAAC,KAAK,EAAE,CAAC;AAC5B,CAAC,CAAC,CAAC;AAEH,0EAA0E;AAC1E,wBAAwB;AACxB,yDAAyD;AACzD,MAAM,UAAU,iBAAiB;IAC/B,iBAAiB,CAAC,KAAK,EAAE,CAAC;AAC5B,CAAC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG,UAAU,CAAsB;IAC/D,IAAI,EAAE,qBAAqB;IAC3B,IAAI,EAAE;QACJ,IAAI,EAAE;YACJ,WAAW,EAAE,4CAA4C;SAC1D;QACD,MAAM,EAAE,EAAE;QACV,OAAO,EAAE,SAAS;QAClB,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE;YACR,iBAAiB,EAAE,4BAA4B;SAChD;KACF;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QACpC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;QAC1C,IAAI,QAAQ,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;YACjC,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,iEAAiE;QACjE,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7C,MAAM,qBAAqB,GAAG,IAAI,GAAG,EAAU,CAAC;YAChD,MAAM,mBAAmB,GAAa,EAAE,CAAC;YACzC,KAAK,MAAM,WAAW,IAAI,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,GAAG,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;gBACvE,IACE,CAAC,CAAC,oBAAoB,IAAI,WAAW,CAAC;oBACtC,CAAC,WAAW,CAAC,kBAAkB;oBAC/B,qBAAqB,CAAC,GAAG,CAAC,WAAW,CAAC,kBAAkB,CAAC,EACzD,CAAC;oBACD,SAAS;gBACX,CAAC;gBACD,qBAAqB,CAAC,GAAG,CAAC,WAAW,CAAC,kBAAkB,CAAC,CAAC;gBAC1D,IACE,SAAS,CACP,OAAO,CAAC,QAAQ,EAChB,WAAW,CAAC,kBAAkB,EAC9B,WAAW,EACX,CAAC,OAAO,CAAC,QAAQ,CAAC,EAClB,EAAE,CACH,EACD,CAAC;oBACD,mBAAmB,CAAC,IAAI,CAAC,WAAW,CAAC,kBAAkB,CAAC,CAAC;gBAC3D,CAAC;YACH,CAAC;YACD,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;QAC/D,CAAC;QAED,MAAM,eAAe,GAAG,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAChE,IAAI,CAAC,eAAe,EAAE,CAAC;YACrB,MAAM,IAAI,aAAa,CACrB,sCAAsC,OAAO,CAAC,QAAQ,EAAE,CACzD,CAAC;QACJ,CAAC;QAED,KAAK,MAAM,cAAc,IAAI,eAAe,EAAE,CAAC;YAC7C,KAAK,MAAM,WAAW,IAAI,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,GAAG,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;gBACvE,IACE,WAAW,CAAC,UAAU,KAAK,gBAAgB;oBAC3C,WAAW,CAAC,kBAAkB,KAAK,cAAc,EACjD,CAAC;oBACD,OAAO,CAAC,MAAM,CAAC;wBACb,SAAS,EAAE,mBAAmB;wBAC9B,IAAI,EAAE,WAAW,CAAC,aAAa;qBAChC,CAAC,CAAC;oBACH,SAAS;gBACX,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,EAAE,CAAC;IACZ,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const noEntryPointImports: import("@typescript-eslint/utils/ts-eslint").RuleModule<"noEntryPointImports", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createRule, getESMInfo } from '../util.js';
|
|
2
|
+
export const noEntryPointImports = createRule({
|
|
3
|
+
name: 'no-entry-point-imports',
|
|
4
|
+
meta: {
|
|
5
|
+
docs: {
|
|
6
|
+
description: 'Ensures that exports in entry point files are not imported',
|
|
7
|
+
},
|
|
8
|
+
schema: [],
|
|
9
|
+
fixable: undefined,
|
|
10
|
+
type: 'problem',
|
|
11
|
+
messages: {
|
|
12
|
+
noEntryPointImports: 'Entry point exports should be not imported',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
defaultOptions: [],
|
|
16
|
+
create(context) {
|
|
17
|
+
const esmInfo = getESMInfo(context);
|
|
18
|
+
// No project info means this file wasn't found as part of the project, e.g.
|
|
19
|
+
// because it's ignored
|
|
20
|
+
if (!esmInfo) {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
const { fileInfo } = esmInfo;
|
|
24
|
+
if (fileInfo.fileType !== 'code') {
|
|
25
|
+
return {};
|
|
26
|
+
}
|
|
27
|
+
for (const exportEntry of [...fileInfo.exports, ...fileInfo.reexports]) {
|
|
28
|
+
if (exportEntry.isEntryPoint && exportEntry.importedByFiles.length) {
|
|
29
|
+
context.report({
|
|
30
|
+
messageId: 'noEntryPointImports',
|
|
31
|
+
node: exportEntry.reportNode,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return {};
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
//# sourceMappingURL=entryPoint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entryPoint.js","sourceRoot":"","sources":["../../../src/rules/entryPoint/entryPoint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEpD,MAAM,CAAC,MAAM,mBAAmB,GAAG,UAAU,CAAC;IAC5C,IAAI,EAAE,wBAAwB;IAC9B,IAAI,EAAE;QACJ,IAAI,EAAE;YACJ,WAAW,EAAE,4DAA4D;SAC1E;QACD,MAAM,EAAE,EAAE;QACV,OAAO,EAAE,SAAS;QAClB,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE;YACR,mBAAmB,EAAE,4CAA4C;SAClE;KACF;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QAEpC,4EAA4E;QAC5E,uBAAuB;QACvB,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;QAC7B,IAAI,QAAQ,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;YACjC,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,KAAK,MAAM,WAAW,IAAI,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,GAAG,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YACvE,IAAI,WAAW,CAAC,YAAY,IAAI,WAAW,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC;gBACnE,OAAO,CAAC,MAAM,CAAC;oBACb,SAAS,EAAE,qBAAqB;oBAChC,IAAI,EAAE,WAAW,CAAC,UAAU;iBAC7B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,EAAE,CAAC;IACZ,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const noExternalBarrelReexports: import("@typescript-eslint/utils/ts-eslint").RuleModule<"noExternalBarrelReexports", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { createRule, getESMInfo } from '../util.js';
|
|
2
|
+
export const noExternalBarrelReexports = createRule({
|
|
3
|
+
name: 'no-external-barrel-reexports',
|
|
4
|
+
meta: {
|
|
5
|
+
docs: {
|
|
6
|
+
description: 'Ensures that code does not barrel reexport builtin or third party modules. Doing so is not supported by fast-import for performance reasons',
|
|
7
|
+
},
|
|
8
|
+
schema: [],
|
|
9
|
+
fixable: undefined,
|
|
10
|
+
type: 'problem',
|
|
11
|
+
messages: {
|
|
12
|
+
noExternalBarrelReexports: 'Barrel reexporting builtin or third party modules is not supported',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
defaultOptions: [],
|
|
16
|
+
create(context) {
|
|
17
|
+
const esmInfo = getESMInfo(context);
|
|
18
|
+
if (!esmInfo) {
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
const { fileInfo } = esmInfo;
|
|
22
|
+
if (fileInfo.fileType !== 'code') {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
// Now check reexports
|
|
26
|
+
for (const reexportEntry of fileInfo.reexports) {
|
|
27
|
+
if (reexportEntry.reexportType === 'single') {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (reexportEntry.moduleType === 'builtin' ||
|
|
31
|
+
reexportEntry.moduleType === 'thirdParty') {
|
|
32
|
+
context.report({
|
|
33
|
+
node: reexportEntry.reportNode,
|
|
34
|
+
messageId: 'noExternalBarrelReexports',
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return {};
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
//# sourceMappingURL=externalBarrelReexports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"externalBarrelReexports.js","sourceRoot":"","sources":["../../../src/rules/externalBarrelReexports/externalBarrelReexports.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEpD,MAAM,CAAC,MAAM,yBAAyB,GAAG,UAAU,CAAC;IAClD,IAAI,EAAE,8BAA8B;IACpC,IAAI,EAAE;QACJ,IAAI,EAAE;YACJ,WAAW,EACT,6IAA6I;SAChJ;QACD,MAAM,EAAE,EAAE;QACV,OAAO,EAAE,SAAS;QAClB,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE;YACR,yBAAyB,EACvB,oEAAoE;SACvE;KACF;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QACpC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;QAC7B,IAAI,QAAQ,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;YACjC,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,sBAAsB;QACtB,KAAK,MAAM,aAAa,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;YAC/C,IAAI,aAAa,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;gBAC5C,SAAS;YACX,CAAC;YACD,IACE,aAAa,CAAC,UAAU,KAAK,SAAS;gBACtC,aAAa,CAAC,UAAU,KAAK,YAAY,EACzC,CAAC;gBACD,OAAO,CAAC,MAAM,CAAC;oBACb,IAAI,EAAE,aAAa,CAAC,UAAU;oBAC9B,SAAS,EAAE,2BAA2B;iBACvC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,EAAE,CAAC;IACZ,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const noMissingImports: import("@typescript-eslint/utils/ts-eslint").RuleModule<"noMissingImports", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { createRule, getESMInfo } from '../util.js';
|
|
2
|
+
export const noMissingImports = createRule({
|
|
3
|
+
name: 'no-missing-imports',
|
|
4
|
+
meta: {
|
|
5
|
+
docs: {
|
|
6
|
+
description: 'Ensures that imports point to valid exports',
|
|
7
|
+
},
|
|
8
|
+
schema: [],
|
|
9
|
+
fixable: undefined,
|
|
10
|
+
type: 'problem',
|
|
11
|
+
messages: {
|
|
12
|
+
noMissingImports: 'Import does not point to a valid export',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
defaultOptions: [],
|
|
16
|
+
create(context) {
|
|
17
|
+
const esmInfo = getESMInfo(context);
|
|
18
|
+
if (!esmInfo) {
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
const { fileInfo } = esmInfo;
|
|
22
|
+
if (fileInfo.fileType !== 'code') {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
// First check imports
|
|
26
|
+
for (const importEntry of fileInfo.imports) {
|
|
27
|
+
// Barrel/dynamic imports don't resolve to a single export, and can be
|
|
28
|
+
// spread across multiple files even. The specific items being imported
|
|
29
|
+
// isn't specified either, meaning the only way to know if an import is
|
|
30
|
+
// valid is to introspect how it's used at runtime. This isn't tractable,
|
|
31
|
+
// so we just don't validate them instead
|
|
32
|
+
if (importEntry.moduleType !== 'firstPartyCode' ||
|
|
33
|
+
importEntry.importType !== 'single') {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (importEntry.rootModuleType === undefined) {
|
|
37
|
+
context.report({
|
|
38
|
+
node: importEntry.reportNode,
|
|
39
|
+
messageId: 'noMissingImports',
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Now check reexports
|
|
44
|
+
for (const reexportEntry of fileInfo.reexports) {
|
|
45
|
+
if (reexportEntry.moduleType !== 'firstPartyCode' ||
|
|
46
|
+
reexportEntry.reexportType !== 'single') {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (reexportEntry.rootModuleType === undefined) {
|
|
50
|
+
context.report({
|
|
51
|
+
node: reexportEntry.reportNode,
|
|
52
|
+
messageId: 'noMissingImports',
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return {};
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
//# sourceMappingURL=missing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"missing.js","sourceRoot":"","sources":["../../../src/rules/missing/missing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEpD,MAAM,CAAC,MAAM,gBAAgB,GAAG,UAAU,CAAC;IACzC,IAAI,EAAE,oBAAoB;IAC1B,IAAI,EAAE;QACJ,IAAI,EAAE;YACJ,WAAW,EAAE,6CAA6C;SAC3D;QACD,MAAM,EAAE,EAAE;QACV,OAAO,EAAE,SAAS;QAClB,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE;YACR,gBAAgB,EAAE,yCAAyC;SAC5D;KACF;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QACpC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;QAC7B,IAAI,QAAQ,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;YACjC,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,sBAAsB;QACtB,KAAK,MAAM,WAAW,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;YAC3C,sEAAsE;YACtE,uEAAuE;YACvE,uEAAuE;YACvE,yEAAyE;YACzE,yCAAyC;YACzC,IACE,WAAW,CAAC,UAAU,KAAK,gBAAgB;gBAC3C,WAAW,CAAC,UAAU,KAAK,QAAQ,EACnC,CAAC;gBACD,SAAS;YACX,CAAC;YACD,IAAI,WAAW,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;gBAC7C,OAAO,CAAC,MAAM,CAAC;oBACb,IAAI,EAAE,WAAW,CAAC,UAAU;oBAC5B,SAAS,EAAE,kBAAkB;iBAC9B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,sBAAsB;QACtB,KAAK,MAAM,aAAa,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;YAC/C,IACE,aAAa,CAAC,UAAU,KAAK,gBAAgB;gBAC7C,aAAa,CAAC,YAAY,KAAK,QAAQ,EACvC,CAAC;gBACD,SAAS;YACX,CAAC;YACD,IAAI,aAAa,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;gBAC/C,OAAO,CAAC,MAAM,CAAC;oBACb,IAAI,EAAE,aAAa,CAAC,UAAU;oBAC9B,SAAS,EAAE,kBAAkB;iBAC9B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,EAAE,CAAC;IACZ,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const noTestImportsInProd: import("@typescript-eslint/utils/ts-eslint").RuleModule<"noTestImports", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createRule, getESMInfo, isNonTestFile } from '../util.js';
|
|
2
|
+
export const noTestImportsInProd = createRule({
|
|
3
|
+
name: 'no-test-imports-in-prod',
|
|
4
|
+
meta: {
|
|
5
|
+
docs: {
|
|
6
|
+
description: 'Ensures that production code does not import test code',
|
|
7
|
+
},
|
|
8
|
+
schema: [],
|
|
9
|
+
fixable: undefined,
|
|
10
|
+
type: 'problem',
|
|
11
|
+
messages: {
|
|
12
|
+
noTestImports: 'Test code should not be imported in production code',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
defaultOptions: [],
|
|
16
|
+
create(context) {
|
|
17
|
+
const esmInfo = getESMInfo(context);
|
|
18
|
+
if (!esmInfo) {
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
const { fileInfo } = esmInfo;
|
|
22
|
+
if (fileInfo.fileType !== 'code' ||
|
|
23
|
+
!isNonTestFile(context.filename, esmInfo.projectInfo.rootDir)) {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
for (const importEntry of [...fileInfo.imports, ...fileInfo.reexports]) {
|
|
27
|
+
if (importEntry.moduleType !== 'firstPartyCode' &&
|
|
28
|
+
importEntry.moduleType !== 'firstPartyOther') {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (importEntry.resolvedModulePath &&
|
|
32
|
+
!isNonTestFile(importEntry.resolvedModulePath, esmInfo.projectInfo.rootDir)) {
|
|
33
|
+
context.report({
|
|
34
|
+
node: importEntry.reportNode,
|
|
35
|
+
messageId: 'noTestImports',
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return {};
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
//# sourceMappingURL=testInProd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testInProd.js","sourceRoot":"","sources":["../../../src/rules/testInProd/testInProd.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEnE,MAAM,CAAC,MAAM,mBAAmB,GAAG,UAAU,CAAC;IAC5C,IAAI,EAAE,yBAAyB;IAC/B,IAAI,EAAE;QACJ,IAAI,EAAE;YACJ,WAAW,EAAE,wDAAwD;SACtE;QACD,MAAM,EAAE,EAAE;QACV,OAAO,EAAE,SAAS;QAClB,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE;YACR,aAAa,EAAE,qDAAqD;SACrE;KACF;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QACpC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;QAC7B,IACE,QAAQ,CAAC,QAAQ,KAAK,MAAM;YAC5B,CAAC,aAAa,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,EAC7D,CAAC;YACD,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,KAAK,MAAM,WAAW,IAAI,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,GAAG,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YACvE,IACE,WAAW,CAAC,UAAU,KAAK,gBAAgB;gBAC3C,WAAW,CAAC,UAAU,KAAK,iBAAiB,EAC5C,CAAC;gBACD,SAAS;YACX,CAAC;YACD,IACE,WAAW,CAAC,kBAAkB;gBAC9B,CAAC,aAAa,CACZ,WAAW,CAAC,kBAAkB,EAC9B,OAAO,CAAC,WAAW,CAAC,OAAO,CAC5B,EACD,CAAC;gBACD,OAAO,CAAC,MAAM,CAAC;oBACb,IAAI,EAAE,WAAW,CAAC,UAAU;oBAC5B,SAAS,EAAE,eAAe;iBAC3B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,EAAE,CAAC;IACZ,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
3
|
+
import { createRule, getESMInfo, isNonTestFile } from '../util.js';
|
|
4
|
+
const schema = z
|
|
5
|
+
.strictObject({
|
|
6
|
+
allowNonTestTypeExports: z.boolean(),
|
|
7
|
+
})
|
|
8
|
+
.optional();
|
|
9
|
+
export const noUnusedExports = createRule({
|
|
10
|
+
name: 'no-unused-exports',
|
|
11
|
+
meta: {
|
|
12
|
+
docs: {
|
|
13
|
+
description: 'Ensures that all exports are imported in another file',
|
|
14
|
+
},
|
|
15
|
+
schema: [zodToJsonSchema(schema)],
|
|
16
|
+
fixable: undefined,
|
|
17
|
+
type: 'problem',
|
|
18
|
+
messages: {
|
|
19
|
+
noUnusedExports: 'Exports must be imported in another file',
|
|
20
|
+
noTestOnlyImports: 'Exports in non-test files must be imported by other non-test files',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
defaultOptions: [
|
|
24
|
+
{
|
|
25
|
+
allowNonTestTypeExports: true,
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
create(context) {
|
|
29
|
+
// .d.ts files are not typically referenced directly, and instead are used
|
|
30
|
+
// to type ambient modules. Sometimes they are used directly though when
|
|
31
|
+
// paired with a neighboring vanilla JS file. Either way, we're making the
|
|
32
|
+
// tradeoff of potentially missing an unused type export for better DX.
|
|
33
|
+
if (context.filename.endsWith('.d.ts')) {
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
// ESLint isn't applying defaults, so options is length 0 when consumers
|
|
37
|
+
// don't specify options, even though ESLint is supposed to do that. I'm not
|
|
38
|
+
// sure what's going on
|
|
39
|
+
const { allowNonTestTypeExports = true } = context.options[0] ?? {};
|
|
40
|
+
const esmInfo = getESMInfo(context);
|
|
41
|
+
if (!esmInfo) {
|
|
42
|
+
return {};
|
|
43
|
+
}
|
|
44
|
+
const { fileInfo, projectInfo: { rootDir }, } = esmInfo;
|
|
45
|
+
if (fileInfo.fileType !== 'code') {
|
|
46
|
+
return {};
|
|
47
|
+
}
|
|
48
|
+
// Check each export and reexport to make sure it's being used
|
|
49
|
+
for (const exportEntry of [...fileInfo.exports, ...fileInfo.reexports]) {
|
|
50
|
+
// If this is an entry point, then it's being imported externally
|
|
51
|
+
if (exportEntry.isEntryPoint) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
// If imported by is empty, then this isn't used anywhere
|
|
55
|
+
if (exportEntry.importedByFiles.length === 0) {
|
|
56
|
+
context.report({
|
|
57
|
+
messageId: 'noUnusedExports',
|
|
58
|
+
node: exportEntry.reportNode,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
// Otherwise, check to see if all of its imports are only in tests
|
|
62
|
+
else if (isNonTestFile(context.filename, rootDir) &&
|
|
63
|
+
!exportEntry.importedByFiles.some((i) => isNonTestFile(i, rootDir))) {
|
|
64
|
+
if (!(`isTypeExport` in exportEntry) ||
|
|
65
|
+
!exportEntry.isTypeExport ||
|
|
66
|
+
!allowNonTestTypeExports) {
|
|
67
|
+
context.report({
|
|
68
|
+
messageId: 'noTestOnlyImports',
|
|
69
|
+
node: exportEntry.reportNode,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return {};
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
//# sourceMappingURL=unused.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unused.js","sourceRoot":"","sources":["../../../src/rules/unused/unused.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEnE,MAAM,MAAM,GAAG,CAAC;KACb,YAAY,CAAC;IACZ,uBAAuB,EAAE,CAAC,CAAC,OAAO,EAAE;CACrC,CAAC;KACD,QAAQ,EAAE,CAAC;AAId,MAAM,CAAC,MAAM,eAAe,GAAG,UAAU,CAGvC;IACA,IAAI,EAAE,mBAAmB;IACzB,IAAI,EAAE;QACJ,IAAI,EAAE;YACJ,WAAW,EAAE,uDAAuD;SACrE;QACD,MAAM,EAAE,CAAC,eAAe,CAAC,MAAM,CAAgB,CAAC;QAChD,OAAO,EAAE,SAAS;QAClB,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE;YACR,eAAe,EAAE,0CAA0C;YAC3D,iBAAiB,EACf,oEAAoE;SACvE;KACF;IACD,cAAc,EAAE;QACd;YACE,uBAAuB,EAAE,IAAI;SAC9B;KACF;IACD,MAAM,CAAC,OAAO;QACZ,0EAA0E;QAC1E,wEAAwE;QACxE,0EAA0E;QAC1E,uEAAuE;QACvE,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YACvC,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,wEAAwE;QACxE,4EAA4E;QAC5E,uBAAuB;QACvB,MAAM,EAAE,uBAAuB,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAEpE,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QACpC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,EACJ,QAAQ,EACR,WAAW,EAAE,EAAE,OAAO,EAAE,GACzB,GAAG,OAAO,CAAC;QACZ,IAAI,QAAQ,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;YACjC,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,8DAA8D;QAC9D,KAAK,MAAM,WAAW,IAAI,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,GAAG,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YACvE,iEAAiE;YACjE,IAAI,WAAW,CAAC,YAAY,EAAE,CAAC;gBAC7B,SAAS;YACX,CAAC;YAED,yDAAyD;YACzD,IAAI,WAAW,CAAC,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC7C,OAAO,CAAC,MAAM,CAAC;oBACb,SAAS,EAAE,iBAAiB;oBAC5B,IAAI,EAAE,WAAW,CAAC,UAAU;iBAC7B,CAAC,CAAC;YACL,CAAC;YAED,kEAAkE;iBAC7D,IACH,aAAa,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC;gBACxC,CAAC,WAAW,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,EACnE,CAAC;gBACD,IACE,CAAC,CAAC,cAAc,IAAI,WAAW,CAAC;oBAChC,CAAC,WAAW,CAAC,YAAY;oBACzB,CAAC,uBAAuB,EACxB,CAAC;oBACD,OAAO,CAAC,MAAM,CAAC;wBACb,SAAS,EAAE,mBAAmB;wBAC9B,IAAI,EAAE,WAAW,CAAC,UAAU;qBAC7B,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,EAAE,CAAC;IACZ,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
import type { GenericContext } from '../types/context.js';
|
|
3
|
+
import type { ParsedSettings } from '../settings/settings.js';
|
|
4
|
+
export declare const createRule: <Options extends readonly unknown[], MessageIds extends string>({ meta, name, ...rule }: Readonly<ESLintUtils.RuleWithMetaAndName<Options, MessageIds, unknown>>) => ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener>;
|
|
5
|
+
export declare function registerUpdateListener(cb: () => void): void;
|
|
6
|
+
export declare function getESMInfo(context: GenericContext): {
|
|
7
|
+
fileInfo: import("../types/base.js").BaseOtherFileDetails | import("../types/analyzed.js").AnalyzedCodeFileDetails;
|
|
8
|
+
projectInfo: import("../types/analyzed.js").AnalyzedProjectInfo;
|
|
9
|
+
settings: ParsedSettings;
|
|
10
|
+
} | undefined;
|
|
11
|
+
export declare function isNonTestFile(filePath: string, rootDir: string): boolean;
|