@taiga-ui/eslint-plugin-experience-next 0.502.0 → 0.504.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.
- package/README.md +1 -1
- package/index.d.ts +5 -1
- package/index.esm.js +526 -207
- package/package.json +1 -1
- package/rules/recommended/import-integrity.d.ts +5 -1
package/README.md
CHANGED
|
@@ -80,7 +80,7 @@ from third-party plugins. The exact severities and file globs live in
|
|
|
80
80
|
| [flat-exports](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/flat-exports.md) | Spread nested arrays when exporting Angular entity collections | | 🔧 | |
|
|
81
81
|
| [host-attributes-sort](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/host-attributes-sort.md) | Sort Angular host metadata attributes using configurable attribute groups | ✅ | 🔧 | |
|
|
82
82
|
| [html-logical-properties](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/html-logical-properties.md) | Enforce logical CSS properties over directional ones in Angular template style bindings | ✅ | 🔧 | |
|
|
83
|
-
| [import-integrity](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/import-integrity.md) | Fast default
|
|
83
|
+
| [import-integrity](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/import-integrity.md) | Fast import default, namespace, cycle, duplicate, named-as-default, self-import, and path checks | ✅ | 🔧 | |
|
|
84
84
|
| [injection-token-description](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/injection-token-description.md) | Require `InjectionToken` descriptions to include the token name | ✅ | 🔧 | |
|
|
85
85
|
| [no-commonjs-import-patterns](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-commonjs-import-patterns.md) | Disallow legacy CommonJS interop import patterns | ✅ | | |
|
|
86
86
|
| [no-deep-imports](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-deep-imports.md) | Disables deep imports of Taiga UI packages | ✅ | 🔧 | |
|
package/index.d.ts
CHANGED
|
@@ -38,11 +38,15 @@ declare const plugin: {
|
|
|
38
38
|
'html-logical-properties': import("eslint").Rule.RuleModule & {
|
|
39
39
|
name: string;
|
|
40
40
|
};
|
|
41
|
-
'import-integrity': import("@typescript-eslint/utils/ts-eslint").RuleModule<"importCycle" | "missingDefaultExport" | "namedAsDefault" | "unknownNamespaceMember", [({
|
|
41
|
+
'import-integrity': import("@typescript-eslint/utils/ts-eslint").RuleModule<"duplicateImport" | "importCycle" | "missingDefaultExport" | "namedAsDefault" | "namedAsDefaultMember" | "selfImport" | "unknownNamespaceMember" | "uselessPathSegments", [({
|
|
42
42
|
checkCycles?: boolean;
|
|
43
43
|
checkDefaultImports?: boolean;
|
|
44
|
+
checkDuplicateImports?: boolean;
|
|
44
45
|
checkNamedAsDefault?: boolean;
|
|
46
|
+
checkNamedAsDefaultMembers?: boolean;
|
|
45
47
|
checkNamespaceMembers?: boolean;
|
|
48
|
+
checkSelfImports?: boolean;
|
|
49
|
+
checkUselessPathSegments?: boolean;
|
|
46
50
|
ignoreExternalDefaultImports?: boolean;
|
|
47
51
|
} | undefined)?], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
48
52
|
name: string;
|
package/index.esm.js
CHANGED
|
@@ -867,7 +867,7 @@ var recommended = defineConfig([
|
|
|
867
867
|
'error',
|
|
868
868
|
{
|
|
869
869
|
methods: 'above',
|
|
870
|
-
printWidth:
|
|
870
|
+
printWidth: 90,
|
|
871
871
|
properties: 'above',
|
|
872
872
|
},
|
|
873
873
|
],
|
|
@@ -875,21 +875,22 @@ var recommended = defineConfig([
|
|
|
875
875
|
'func-style': ['error', 'declaration', { allowArrowFunctions: true }],
|
|
876
876
|
'guard-for-in': 'error',
|
|
877
877
|
'import/consistent-type-specifier-style': ['error', 'prefer-inline'],
|
|
878
|
-
'import/default': 'off',
|
|
878
|
+
'import/default': 'off', // Covered by @taiga-ui/experience-next/import-integrity.
|
|
879
879
|
'import/enforce-node-protocol-usage': ['error', 'always'],
|
|
880
880
|
'import/export': 'off',
|
|
881
881
|
'import/first': 'error',
|
|
882
|
-
'import/namespace': 'off',
|
|
882
|
+
'import/namespace': 'off', // Covered by @taiga-ui/experience-next/import-integrity.
|
|
883
883
|
'import/newline-after-import': ['error', { count: 1 }],
|
|
884
884
|
'import/no-absolute-path': 'error',
|
|
885
|
-
'import/no-cycle': 'off',
|
|
886
|
-
'import/no-duplicates':
|
|
885
|
+
'import/no-cycle': 'off', // Covered by @taiga-ui/experience-next/import-integrity.
|
|
886
|
+
'import/no-duplicates': 'off', // Covered by @taiga-ui/experience-next/import-integrity.
|
|
887
887
|
'import/no-extraneous-dependencies': 'off',
|
|
888
888
|
'import/no-mutable-exports': 'error',
|
|
889
|
-
'import/no-named-as-default': 'off',
|
|
890
|
-
'import/no-
|
|
889
|
+
'import/no-named-as-default': 'off', // Covered by @taiga-ui/experience-next/import-integrity.
|
|
890
|
+
'import/no-named-as-default-member': 'off', // Covered by @taiga-ui/experience-next/import-integrity.
|
|
891
|
+
'import/no-self-import': 'off', // Covered by @taiga-ui/experience-next/import-integrity.
|
|
891
892
|
'import/no-unresolved': 'off',
|
|
892
|
-
'import/no-useless-path-segments':
|
|
893
|
+
'import/no-useless-path-segments': 'off', // Covered by @taiga-ui/experience-next/import-integrity.
|
|
893
894
|
'import/no-webpack-loader-syntax': 'error',
|
|
894
895
|
'lines-around-comment': [
|
|
895
896
|
'error',
|
|
@@ -247753,6 +247754,84 @@ function resolveModuleFileName(program, containingFile, moduleSpecifier) {
|
|
|
247753
247754
|
}
|
|
247754
247755
|
return resolved.resolvedFileName;
|
|
247755
247756
|
}
|
|
247757
|
+
function splitModuleSpecifierQuery(moduleSpecifier) {
|
|
247758
|
+
const queryIndex = moduleSpecifier.indexOf('?');
|
|
247759
|
+
return queryIndex === -1
|
|
247760
|
+
? { path: moduleSpecifier, query: '' }
|
|
247761
|
+
: {
|
|
247762
|
+
path: moduleSpecifier.slice(0, queryIndex),
|
|
247763
|
+
query: moduleSpecifier.slice(queryIndex),
|
|
247764
|
+
};
|
|
247765
|
+
}
|
|
247766
|
+
function getModuleSpecifierPath(moduleSpecifier) {
|
|
247767
|
+
return splitModuleSpecifierQuery(moduleSpecifier).path;
|
|
247768
|
+
}
|
|
247769
|
+
function toRelativeImportPath(relativePath) {
|
|
247770
|
+
const stripped = relativePath.replaceAll(/\/$/g, '');
|
|
247771
|
+
return /^\.{1,2}$|^\.{1,2}\//.test(stripped) ? stripped : `./${stripped}`;
|
|
247772
|
+
}
|
|
247773
|
+
function normalizeImportPath(moduleSpecifierPath) {
|
|
247774
|
+
return toRelativeImportPath(path.posix.normalize(moduleSpecifierPath));
|
|
247775
|
+
}
|
|
247776
|
+
function countRelativeParents(pathSegments) {
|
|
247777
|
+
return pathSegments.filter((segment) => segment === '..').length;
|
|
247778
|
+
}
|
|
247779
|
+
function isIndexModulePath(moduleSpecifierPath) {
|
|
247780
|
+
return /(?:^|\/)index(?:\.[cm]?[jt]sx?)?$/.test(moduleSpecifierPath);
|
|
247781
|
+
}
|
|
247782
|
+
function quoteModuleSpecifier(source, moduleSpecifier) {
|
|
247783
|
+
const quote = source.raw.startsWith('"') ? '"' : "'";
|
|
247784
|
+
const escaped = moduleSpecifier
|
|
247785
|
+
.replaceAll('\\', '\\\\')
|
|
247786
|
+
.replaceAll(quote, `\\${quote}`);
|
|
247787
|
+
return `${quote}${escaped}${quote}`;
|
|
247788
|
+
}
|
|
247789
|
+
function resolveModuleKey(program, containingFile, moduleSpecifier, canonicalFileName) {
|
|
247790
|
+
const moduleSpecifierPath = getModuleSpecifierPath(moduleSpecifier);
|
|
247791
|
+
const resolved = resolveModule(program, containingFile, moduleSpecifierPath);
|
|
247792
|
+
if (!resolved) {
|
|
247793
|
+
return moduleSpecifier;
|
|
247794
|
+
}
|
|
247795
|
+
const queryIndex = moduleSpecifier.indexOf('?');
|
|
247796
|
+
const query = queryIndex === -1 ? '' : moduleSpecifier.slice(queryIndex);
|
|
247797
|
+
return `${canonicalFileName(resolved.resolvedFileName)}${query}`;
|
|
247798
|
+
}
|
|
247799
|
+
function getDefaultImportName(node) {
|
|
247800
|
+
const defaultImport = node.specifiers.find((specifier) => specifier.type === dist$3.AST_NODE_TYPES.ImportDefaultSpecifier);
|
|
247801
|
+
return defaultImport?.local.name ?? null;
|
|
247802
|
+
}
|
|
247803
|
+
function hasNamespaceImport(node) {
|
|
247804
|
+
return node.specifiers.some((specifier) => specifier.type === dist$3.AST_NODE_TYPES.ImportNamespaceSpecifier);
|
|
247805
|
+
}
|
|
247806
|
+
function hasTypeOnlyDefaultImport(node) {
|
|
247807
|
+
return node.importKind === 'type' && getDefaultImportName(node) !== null;
|
|
247808
|
+
}
|
|
247809
|
+
function hasImportAttributes(node) {
|
|
247810
|
+
const record = node;
|
|
247811
|
+
const attributes = record['attributes'];
|
|
247812
|
+
const assertions = record['assertions'];
|
|
247813
|
+
return ((Array.isArray(attributes) && attributes.length > 0) ||
|
|
247814
|
+
(Array.isArray(assertions) && assertions.length > 0));
|
|
247815
|
+
}
|
|
247816
|
+
function hasProblematicImportComments(node, sourceCode) {
|
|
247817
|
+
const text = sourceCode.getText(node);
|
|
247818
|
+
return (text.includes('/*') ||
|
|
247819
|
+
text.includes('//') ||
|
|
247820
|
+
sourceCode
|
|
247821
|
+
.getCommentsBefore(node)
|
|
247822
|
+
.some((comment) => comment.loc.end.line >= node.loc.start.line - 1) ||
|
|
247823
|
+
sourceCode
|
|
247824
|
+
.getCommentsAfter(node)
|
|
247825
|
+
.some((comment) => comment.loc.start.line === node.loc.end.line));
|
|
247826
|
+
}
|
|
247827
|
+
function getNamedSpecifierText(node, specifier, sourceCode) {
|
|
247828
|
+
const text = sourceCode.getText(specifier);
|
|
247829
|
+
const isTypeOnly = node.importKind === 'type' || specifier.importKind === 'type';
|
|
247830
|
+
return isTypeOnly && !text.trimStart().startsWith('type ') ? `type ${text}` : text;
|
|
247831
|
+
}
|
|
247832
|
+
function getNamedSpecifierKey(text) {
|
|
247833
|
+
return text.trim().replace(/^type\s+/, '');
|
|
247834
|
+
}
|
|
247756
247835
|
function importDeclarationHasRuntimeEdge(node) {
|
|
247757
247836
|
const importClause = node.importClause;
|
|
247758
247837
|
if (!importClause) {
|
|
@@ -247809,149 +247888,14 @@ function getRuntimeModuleSpecifier(statement) {
|
|
|
247809
247888
|
}
|
|
247810
247889
|
return null;
|
|
247811
247890
|
}
|
|
247812
|
-
function buildDependenciesByFileName(program, canonicalFileName) {
|
|
247813
|
-
const sourceFiles = program.getSourceFiles().filter(isProjectCodeFile);
|
|
247814
|
-
const projectFileNames = new Set(sourceFiles.map((sourceFile) => canonicalFileName(sourceFile.fileName)));
|
|
247815
|
-
const dependenciesByFileName = new Map();
|
|
247816
|
-
const displayFileNameByFileName = new Map();
|
|
247817
|
-
for (const sourceFile of sourceFiles) {
|
|
247818
|
-
const fileName = canonicalFileName(sourceFile.fileName);
|
|
247819
|
-
const edges = [];
|
|
247820
|
-
displayFileNameByFileName.set(fileName, sourceFile.fileName);
|
|
247821
|
-
for (const statement of sourceFile.statements) {
|
|
247822
|
-
const moduleSpecifier = getRuntimeModuleSpecifier(statement);
|
|
247823
|
-
if (!moduleSpecifier) {
|
|
247824
|
-
continue;
|
|
247825
|
-
}
|
|
247826
|
-
const resolvedFileName = resolveModuleFileName(program, sourceFile.fileName, moduleSpecifier);
|
|
247827
|
-
if (!resolvedFileName) {
|
|
247828
|
-
continue;
|
|
247829
|
-
}
|
|
247830
|
-
const targetFileName = canonicalFileName(resolvedFileName);
|
|
247831
|
-
if (!projectFileNames.has(targetFileName)) {
|
|
247832
|
-
continue;
|
|
247833
|
-
}
|
|
247834
|
-
edges.push({
|
|
247835
|
-
isImport: ts.isImportDeclaration(statement),
|
|
247836
|
-
moduleSpecifier,
|
|
247837
|
-
targetFileName,
|
|
247838
|
-
});
|
|
247839
|
-
}
|
|
247840
|
-
dependenciesByFileName.set(fileName, edges);
|
|
247841
|
-
}
|
|
247842
|
-
return { dependenciesByFileName, displayFileNameByFileName };
|
|
247843
|
-
}
|
|
247844
|
-
function findStronglyConnectedComponents(dependenciesByFileName) {
|
|
247845
|
-
let nextComponentId = 0;
|
|
247846
|
-
let nextIndex = 0;
|
|
247847
|
-
const componentIdByFileName = new Map();
|
|
247848
|
-
const componentSizeById = new Map();
|
|
247849
|
-
const nodeStateByFileName = new Map();
|
|
247850
|
-
const stack = [];
|
|
247851
|
-
function visit(fileName) {
|
|
247852
|
-
const state = {
|
|
247853
|
-
index: nextIndex,
|
|
247854
|
-
lowLink: nextIndex,
|
|
247855
|
-
onStack: true,
|
|
247856
|
-
};
|
|
247857
|
-
nextIndex += 1;
|
|
247858
|
-
nodeStateByFileName.set(fileName, state);
|
|
247859
|
-
stack.push(fileName);
|
|
247860
|
-
for (const edge of dependenciesByFileName.get(fileName) ?? []) {
|
|
247861
|
-
const targetState = nodeStateByFileName.get(edge.targetFileName);
|
|
247862
|
-
if (!targetState) {
|
|
247863
|
-
visit(edge.targetFileName);
|
|
247864
|
-
const visitedTargetState = nodeStateByFileName.get(edge.targetFileName);
|
|
247865
|
-
if (visitedTargetState) {
|
|
247866
|
-
state.lowLink = Math.min(state.lowLink, visitedTargetState.lowLink);
|
|
247867
|
-
}
|
|
247868
|
-
continue;
|
|
247869
|
-
}
|
|
247870
|
-
if (targetState.onStack) {
|
|
247871
|
-
state.lowLink = Math.min(state.lowLink, targetState.index);
|
|
247872
|
-
}
|
|
247873
|
-
}
|
|
247874
|
-
if (state.lowLink !== state.index) {
|
|
247875
|
-
return;
|
|
247876
|
-
}
|
|
247877
|
-
const componentId = nextComponentId;
|
|
247878
|
-
let componentSize = 0;
|
|
247879
|
-
let shouldPop = stack.length > 0;
|
|
247880
|
-
nextComponentId += 1;
|
|
247881
|
-
while (shouldPop) {
|
|
247882
|
-
const memberFileName = stack.pop();
|
|
247883
|
-
if (!memberFileName) {
|
|
247884
|
-
shouldPop = false;
|
|
247885
|
-
continue;
|
|
247886
|
-
}
|
|
247887
|
-
const memberState = nodeStateByFileName.get(memberFileName);
|
|
247888
|
-
if (memberState) {
|
|
247889
|
-
memberState.onStack = false;
|
|
247890
|
-
}
|
|
247891
|
-
componentSize += 1;
|
|
247892
|
-
componentIdByFileName.set(memberFileName, componentId);
|
|
247893
|
-
shouldPop = stack.length > 0 && memberFileName !== fileName;
|
|
247894
|
-
}
|
|
247895
|
-
componentSizeById.set(componentId, componentSize);
|
|
247896
|
-
}
|
|
247897
|
-
for (const fileName of dependenciesByFileName.keys()) {
|
|
247898
|
-
if (!nodeStateByFileName.has(fileName)) {
|
|
247899
|
-
visit(fileName);
|
|
247900
|
-
}
|
|
247901
|
-
}
|
|
247902
|
-
return { componentIdByFileName, componentSizeById };
|
|
247903
|
-
}
|
|
247904
|
-
function collectSelfCycles(dependenciesByFileName) {
|
|
247905
|
-
const selfCycleFileNames = new Set();
|
|
247906
|
-
for (const [fileName, edges] of dependenciesByFileName) {
|
|
247907
|
-
if (edges.some((edge) => edge.targetFileName === fileName)) {
|
|
247908
|
-
selfCycleFileNames.add(fileName);
|
|
247909
|
-
}
|
|
247910
|
-
}
|
|
247911
|
-
return selfCycleFileNames;
|
|
247912
|
-
}
|
|
247913
|
-
function buildSccHasImportEdge(dependenciesByFileName, componentIdByFileName) {
|
|
247914
|
-
const result = new Map();
|
|
247915
|
-
for (const [fileName, edges] of dependenciesByFileName) {
|
|
247916
|
-
const sourceComponentId = componentIdByFileName.get(fileName);
|
|
247917
|
-
if (sourceComponentId === undefined || result.get(sourceComponentId) === true) {
|
|
247918
|
-
continue;
|
|
247919
|
-
}
|
|
247920
|
-
for (const edge of edges) {
|
|
247921
|
-
if (edge.isImport &&
|
|
247922
|
-
componentIdByFileName.get(edge.targetFileName) === sourceComponentId) {
|
|
247923
|
-
result.set(sourceComponentId, true);
|
|
247924
|
-
break;
|
|
247925
|
-
}
|
|
247926
|
-
}
|
|
247927
|
-
}
|
|
247928
|
-
return result;
|
|
247929
|
-
}
|
|
247930
|
-
function buildBarrelFileNames(dependenciesByFileName) {
|
|
247931
|
-
const result = new Set();
|
|
247932
|
-
for (const [fileName, edges] of dependenciesByFileName) {
|
|
247933
|
-
if (edges.some((edge) => !edge.isImport)) {
|
|
247934
|
-
result.add(fileName);
|
|
247935
|
-
}
|
|
247936
|
-
}
|
|
247937
|
-
return result;
|
|
247938
|
-
}
|
|
247939
247891
|
function getImportGraph(program) {
|
|
247940
247892
|
const cached = importGraphCacheByProgram.get(program);
|
|
247941
247893
|
if (cached) {
|
|
247942
247894
|
return cached;
|
|
247943
247895
|
}
|
|
247944
|
-
const canonicalFileName = createCanonicalFileName();
|
|
247945
|
-
const { dependenciesByFileName, displayFileNameByFileName } = buildDependenciesByFileName(program, canonicalFileName);
|
|
247946
|
-
const { componentIdByFileName, componentSizeById } = findStronglyConnectedComponents(dependenciesByFileName);
|
|
247947
247896
|
const cache = {
|
|
247948
|
-
|
|
247949
|
-
|
|
247950
|
-
componentSizeById,
|
|
247951
|
-
dependenciesByFileName,
|
|
247952
|
-
displayFileNameByFileName,
|
|
247953
|
-
sccHasImportEdgeById: buildSccHasImportEdge(dependenciesByFileName, componentIdByFileName),
|
|
247954
|
-
selfCycleFileNames: collectSelfCycles(dependenciesByFileName),
|
|
247897
|
+
dependenciesByFileName: new Map(),
|
|
247898
|
+
displayFileNameByFileName: new Map(),
|
|
247955
247899
|
};
|
|
247956
247900
|
importGraphCacheByProgram.set(program, cache);
|
|
247957
247901
|
return cache;
|
|
@@ -248041,76 +247985,136 @@ function getImportOrReExportModuleSpecifier(node) {
|
|
|
248041
247985
|
}
|
|
248042
247986
|
return typeof node.source.value === 'string' ? node.source.value : null;
|
|
248043
247987
|
}
|
|
248044
|
-
function
|
|
248045
|
-
const
|
|
248046
|
-
|
|
248047
|
-
|
|
247988
|
+
function getProjectSourceFile(program, fileName) {
|
|
247989
|
+
const sourceFile = getSourceFileByFileName(program, fileName);
|
|
247990
|
+
return sourceFile && isProjectCodeFile(sourceFile) ? sourceFile : null;
|
|
247991
|
+
}
|
|
247992
|
+
function getDependenciesForFile(program, graph, fileName, canonicalFileName) {
|
|
247993
|
+
const cached = graph.dependenciesByFileName.get(fileName);
|
|
247994
|
+
if (cached) {
|
|
247995
|
+
return cached;
|
|
248048
247996
|
}
|
|
248049
|
-
const
|
|
248050
|
-
|
|
248051
|
-
|
|
247997
|
+
const sourceFile = getProjectSourceFile(program, fileName);
|
|
247998
|
+
const edges = [];
|
|
247999
|
+
graph.dependenciesByFileName.set(fileName, edges);
|
|
248000
|
+
if (!sourceFile) {
|
|
248001
|
+
return edges;
|
|
248002
|
+
}
|
|
248003
|
+
graph.displayFileNameByFileName.set(fileName, sourceFile.fileName);
|
|
248004
|
+
for (const statement of sourceFile.statements) {
|
|
248005
|
+
const moduleSpecifier = getRuntimeModuleSpecifier(statement);
|
|
248006
|
+
if (!moduleSpecifier) {
|
|
248007
|
+
continue;
|
|
248008
|
+
}
|
|
248009
|
+
const resolvedFileName = resolveModuleFileName(program, sourceFile.fileName, moduleSpecifier);
|
|
248010
|
+
if (!resolvedFileName) {
|
|
248052
248011
|
continue;
|
|
248053
248012
|
}
|
|
248054
|
-
const
|
|
248055
|
-
|
|
248056
|
-
|
|
248057
|
-
graph.componentIdByFileName.get(edge.targetFileName) === currentComponentId;
|
|
248058
|
-
if (isSelfCycle || isSameComponentCycle) {
|
|
248059
|
-
return edge.targetFileName;
|
|
248013
|
+
const targetSourceFile = getProjectSourceFile(program, resolvedFileName);
|
|
248014
|
+
if (!targetSourceFile) {
|
|
248015
|
+
continue;
|
|
248060
248016
|
}
|
|
248017
|
+
const targetFileName = canonicalFileName(targetSourceFile.fileName);
|
|
248018
|
+
graph.displayFileNameByFileName.set(targetFileName, targetSourceFile.fileName);
|
|
248019
|
+
edges.push({
|
|
248020
|
+
isImport: ts.isImportDeclaration(statement),
|
|
248021
|
+
moduleSpecifier,
|
|
248022
|
+
targetFileName,
|
|
248023
|
+
});
|
|
248061
248024
|
}
|
|
248062
|
-
return
|
|
248025
|
+
return edges;
|
|
248026
|
+
}
|
|
248027
|
+
function fileHasReExportEdges(program, graph, fileName, canonicalFileName) {
|
|
248028
|
+
return getDependenciesForFile(program, graph, fileName, canonicalFileName).some((edge) => !edge.isImport);
|
|
248029
|
+
}
|
|
248030
|
+
function getCycleStateKey(fileName, hasImportEdge) {
|
|
248031
|
+
return `${hasImportEdge ? '1' : '0'}:${fileName}`;
|
|
248032
|
+
}
|
|
248033
|
+
function reconstructSearchPath(stateKey, previousStateKeyByStateKey, fileNameByStateKey) {
|
|
248034
|
+
const pathSegments = [];
|
|
248035
|
+
let currentStateKey = stateKey;
|
|
248036
|
+
while (currentStateKey !== null) {
|
|
248037
|
+
const fileName = fileNameByStateKey.get(currentStateKey);
|
|
248038
|
+
if (!fileName) {
|
|
248039
|
+
return [];
|
|
248040
|
+
}
|
|
248041
|
+
pathSegments.push(fileName);
|
|
248042
|
+
const previousStateKey = previousStateKeyByStateKey.get(currentStateKey);
|
|
248043
|
+
if (previousStateKey === undefined) {
|
|
248044
|
+
return [];
|
|
248045
|
+
}
|
|
248046
|
+
currentStateKey = previousStateKey;
|
|
248047
|
+
}
|
|
248048
|
+
return pathSegments.reverse();
|
|
248063
248049
|
}
|
|
248064
|
-
function
|
|
248065
|
-
const queue = [
|
|
248066
|
-
const
|
|
248067
|
-
|
|
248068
|
-
|
|
248069
|
-
|
|
248050
|
+
function findCyclePathFromEdge(program, graph, currentFileName, edge, canonicalFileName) {
|
|
248051
|
+
const queue = [{ fileName: edge.targetFileName, hasImportEdge: edge.isImport }];
|
|
248052
|
+
const startStateKey = getCycleStateKey(edge.targetFileName, edge.isImport);
|
|
248053
|
+
const previousStateKeyByStateKey = new Map([
|
|
248054
|
+
[startStateKey, null],
|
|
248055
|
+
]);
|
|
248056
|
+
const fileNameByStateKey = new Map([
|
|
248057
|
+
[startStateKey, edge.targetFileName],
|
|
248058
|
+
]);
|
|
248059
|
+
let firstResult = null;
|
|
248060
|
+
for (const state of queue) {
|
|
248061
|
+
const stateKey = getCycleStateKey(state.fileName, state.hasImportEdge);
|
|
248062
|
+
if (state.fileName === currentFileName) {
|
|
248063
|
+
const path = reconstructSearchPath(stateKey, previousStateKeyByStateKey, fileNameByStateKey);
|
|
248064
|
+
if (path.length === 0) {
|
|
248065
|
+
continue;
|
|
248066
|
+
}
|
|
248067
|
+
const result = {
|
|
248068
|
+
cyclePath: [currentFileName, ...path],
|
|
248069
|
+
hasImportEdge: state.hasImportEdge,
|
|
248070
|
+
targetFileName: edge.targetFileName,
|
|
248071
|
+
};
|
|
248072
|
+
if (result.hasImportEdge) {
|
|
248073
|
+
return result;
|
|
248074
|
+
}
|
|
248075
|
+
firstResult ??= result;
|
|
248070
248076
|
continue;
|
|
248071
248077
|
}
|
|
248072
|
-
for (const
|
|
248073
|
-
|
|
248074
|
-
|
|
248078
|
+
for (const nextEdge of getDependenciesForFile(program, graph, state.fileName, canonicalFileName)) {
|
|
248079
|
+
const nextState = {
|
|
248080
|
+
fileName: nextEdge.targetFileName,
|
|
248081
|
+
hasImportEdge: state.hasImportEdge || nextEdge.isImport,
|
|
248082
|
+
};
|
|
248083
|
+
const nextStateKey = getCycleStateKey(nextState.fileName, nextState.hasImportEdge);
|
|
248084
|
+
if (previousStateKeyByStateKey.has(nextStateKey)) {
|
|
248075
248085
|
continue;
|
|
248076
248086
|
}
|
|
248077
|
-
|
|
248078
|
-
|
|
248087
|
+
previousStateKeyByStateKey.set(nextStateKey, stateKey);
|
|
248088
|
+
fileNameByStateKey.set(nextStateKey, nextState.fileName);
|
|
248089
|
+
queue.push(nextState);
|
|
248079
248090
|
}
|
|
248080
248091
|
}
|
|
248081
|
-
|
|
248082
|
-
|
|
248083
|
-
|
|
248084
|
-
|
|
248085
|
-
|
|
248086
|
-
|
|
248087
|
-
|
|
248088
|
-
|
|
248089
|
-
|
|
248090
|
-
|
|
248092
|
+
return firstResult;
|
|
248093
|
+
}
|
|
248094
|
+
function findCyclePathForSpecifier(program, graph, currentFileName, moduleSpecifier, canonicalFileName) {
|
|
248095
|
+
let firstResult = null;
|
|
248096
|
+
for (const edge of getDependenciesForFile(program, graph, currentFileName, canonicalFileName)) {
|
|
248097
|
+
if (edge.moduleSpecifier !== moduleSpecifier) {
|
|
248098
|
+
continue;
|
|
248099
|
+
}
|
|
248100
|
+
const result = findCyclePathFromEdge(program, graph, currentFileName, edge, canonicalFileName);
|
|
248101
|
+
if (!result) {
|
|
248102
|
+
continue;
|
|
248103
|
+
}
|
|
248104
|
+
if (result.hasImportEdge) {
|
|
248105
|
+
return result;
|
|
248091
248106
|
}
|
|
248092
|
-
|
|
248107
|
+
firstResult ??= result;
|
|
248093
248108
|
}
|
|
248094
|
-
return
|
|
248109
|
+
return firstResult;
|
|
248095
248110
|
}
|
|
248096
248111
|
function formatFileName(graph, fileName, cwd) {
|
|
248097
248112
|
const displayFileName = graph.displayFileNameByFileName.get(fileName) ?? fileName;
|
|
248098
248113
|
const relativeFileName = normalizeSlashes(path.relative(cwd, displayFileName));
|
|
248099
248114
|
return relativeFileName || normalizeSlashes(path.basename(displayFileName));
|
|
248100
248115
|
}
|
|
248101
|
-
function formatCyclePath(graph,
|
|
248102
|
-
|
|
248103
|
-
if (componentId === undefined || currentFileName === targetFileName) {
|
|
248104
|
-
return [currentFileName, targetFileName]
|
|
248105
|
-
.map((fileName) => formatFileName(graph, fileName, cwd))
|
|
248106
|
-
.join(' -> ');
|
|
248107
|
-
}
|
|
248108
|
-
return [
|
|
248109
|
-
currentFileName,
|
|
248110
|
-
...findPathWithinComponent(graph, targetFileName, currentFileName, componentId),
|
|
248111
|
-
]
|
|
248112
|
-
.map((fileName) => formatFileName(graph, fileName, cwd))
|
|
248113
|
-
.join(' -> ');
|
|
248116
|
+
function formatCyclePath(graph, cyclePath, cwd) {
|
|
248117
|
+
return cyclePath.map((fileName) => formatFileName(graph, fileName, cwd)).join(' -> ');
|
|
248114
248118
|
}
|
|
248115
248119
|
function getAliasedSymbolIfNeeded(checker, symbol) {
|
|
248116
248120
|
return (symbol.flags & ts.SymbolFlags.Alias) === 0
|
|
@@ -248250,12 +248254,240 @@ const rule$K = createRule({
|
|
|
248250
248254
|
const { checker, esTreeNodeToTSNodeMap, sourceCode, tsProgram } = getTypeAwareRuleContext(context);
|
|
248251
248255
|
const checkCycles = context.options[0]?.checkCycles ?? true;
|
|
248252
248256
|
const checkDefaultImports = context.options[0]?.checkDefaultImports ?? true;
|
|
248257
|
+
const checkDuplicateImports = context.options[0]?.checkDuplicateImports ?? true;
|
|
248253
248258
|
const checkNamedAsDefault = context.options[0]?.checkNamedAsDefault ?? true;
|
|
248259
|
+
const checkNamedAsDefaultMembers = context.options[0]?.checkNamedAsDefaultMembers ?? true;
|
|
248254
248260
|
const checkNamespaceMembers = context.options[0]?.checkNamespaceMembers ?? true;
|
|
248261
|
+
const checkSelfImports = context.options[0]?.checkSelfImports ?? true;
|
|
248262
|
+
const shouldCheckUselessPathSegments = context.options[0]?.checkUselessPathSegments ?? true;
|
|
248255
248263
|
const ignoreExternalDefaultImports = context.options[0]?.ignoreExternalDefaultImports ?? true;
|
|
248256
248264
|
const canonicalFileName = createCanonicalFileName();
|
|
248257
248265
|
const currentFileName = canonicalFileName(context.filename);
|
|
248266
|
+
const defaultImports = new Map();
|
|
248267
|
+
const duplicateImportMaps = {
|
|
248268
|
+
importsByModule: new Map(),
|
|
248269
|
+
namespaceImportsByModule: new Map(),
|
|
248270
|
+
};
|
|
248258
248271
|
const namespaceImports = new Map();
|
|
248272
|
+
function getDuplicateImportMap(node) {
|
|
248273
|
+
return hasNamespaceImport(node)
|
|
248274
|
+
? duplicateImportMaps.namespaceImportsByModule
|
|
248275
|
+
: duplicateImportMaps.importsByModule;
|
|
248276
|
+
}
|
|
248277
|
+
function collectDuplicateImport(node) {
|
|
248278
|
+
if (!checkDuplicateImports) {
|
|
248279
|
+
return;
|
|
248280
|
+
}
|
|
248281
|
+
const moduleKey = resolveModuleKey(tsProgram, context.filename, node.source.value, canonicalFileName);
|
|
248282
|
+
const importsByModule = getDuplicateImportMap(node);
|
|
248283
|
+
const imports = importsByModule.get(moduleKey) ?? [];
|
|
248284
|
+
imports.push(node);
|
|
248285
|
+
importsByModule.set(moduleKey, imports);
|
|
248286
|
+
}
|
|
248287
|
+
function buildImportRemovalFix(fixer, node) {
|
|
248288
|
+
const [start, end] = node.range;
|
|
248289
|
+
const lineStart = sourceCode.text.lastIndexOf('\n', start - 1) + 1;
|
|
248290
|
+
const removeStart = /^\s*$/.test(sourceCode.text.slice(lineStart, start))
|
|
248291
|
+
? lineStart
|
|
248292
|
+
: start;
|
|
248293
|
+
const removeEnd = sourceCode.text[end] === '\n' ? end + 1 : end;
|
|
248294
|
+
return [fixer.removeRange([removeStart, removeEnd])];
|
|
248295
|
+
}
|
|
248296
|
+
function buildDuplicateImportFix(fixer, first, rest) {
|
|
248297
|
+
const nodes = [first, ...rest];
|
|
248298
|
+
if (nodes.some((node) => hasNamespaceImport(node) ||
|
|
248299
|
+
hasTypeOnlyDefaultImport(node) ||
|
|
248300
|
+
hasImportAttributes(node) ||
|
|
248301
|
+
hasProblematicImportComments(node, sourceCode))) {
|
|
248302
|
+
return null;
|
|
248303
|
+
}
|
|
248304
|
+
const defaultNames = new Set(nodes.flatMap((node) => {
|
|
248305
|
+
const name = getDefaultImportName(node);
|
|
248306
|
+
return name ? [name] : [];
|
|
248307
|
+
}));
|
|
248308
|
+
if (defaultNames.size > 1) {
|
|
248309
|
+
return null;
|
|
248310
|
+
}
|
|
248311
|
+
const namedSpecifiersByKey = new Map();
|
|
248312
|
+
for (const node of nodes) {
|
|
248313
|
+
for (const specifier of node.specifiers) {
|
|
248314
|
+
if (specifier.type !== dist$3.AST_NODE_TYPES.ImportSpecifier) {
|
|
248315
|
+
continue;
|
|
248316
|
+
}
|
|
248317
|
+
const text = getNamedSpecifierText(node, specifier, sourceCode).trim();
|
|
248318
|
+
const key = getNamedSpecifierKey(text);
|
|
248319
|
+
const existing = namedSpecifiersByKey.get(key);
|
|
248320
|
+
const isTypeOnly = text.startsWith('type ');
|
|
248321
|
+
if (!existing || (existing.startsWith('type ') && !isTypeOnly)) {
|
|
248322
|
+
namedSpecifiersByKey.set(key, text);
|
|
248323
|
+
}
|
|
248324
|
+
}
|
|
248325
|
+
}
|
|
248326
|
+
const namedSpecifiers = [...namedSpecifiersByKey.values()];
|
|
248327
|
+
const [defaultName = null] = defaultNames;
|
|
248328
|
+
const bindings = [
|
|
248329
|
+
...(defaultName ? [defaultName] : []),
|
|
248330
|
+
...(namedSpecifiers.length > 0
|
|
248331
|
+
? [`{${namedSpecifiers.join(', ')}}`]
|
|
248332
|
+
: []),
|
|
248333
|
+
];
|
|
248334
|
+
const sourceText = sourceCode.getText(first.source);
|
|
248335
|
+
const semi = sourceCode.getText(first).endsWith(';') ? ';' : '';
|
|
248336
|
+
const replacement = bindings.length === 0
|
|
248337
|
+
? `import ${sourceText}${semi}`
|
|
248338
|
+
: `import ${bindings.join(', ')} from ${sourceText}${semi}`;
|
|
248339
|
+
return [
|
|
248340
|
+
fixer.replaceText(first, replacement),
|
|
248341
|
+
...rest.flatMap((node) => buildImportRemovalFix(fixer, node)),
|
|
248342
|
+
];
|
|
248343
|
+
}
|
|
248344
|
+
function reportDuplicateImports(importsByModule) {
|
|
248345
|
+
for (const nodes of importsByModule.values()) {
|
|
248346
|
+
if (nodes.length < 2) {
|
|
248347
|
+
continue;
|
|
248348
|
+
}
|
|
248349
|
+
const [first, ...rest] = nodes;
|
|
248350
|
+
if (!first) {
|
|
248351
|
+
continue;
|
|
248352
|
+
}
|
|
248353
|
+
const moduleSpecifier = first.source.value;
|
|
248354
|
+
context.report({
|
|
248355
|
+
data: { moduleSpecifier },
|
|
248356
|
+
fix: (fixer) => buildDuplicateImportFix(fixer, first, rest),
|
|
248357
|
+
messageId: 'duplicateImport',
|
|
248358
|
+
node: first.source,
|
|
248359
|
+
});
|
|
248360
|
+
for (const node of rest) {
|
|
248361
|
+
context.report({
|
|
248362
|
+
data: { moduleSpecifier },
|
|
248363
|
+
messageId: 'duplicateImport',
|
|
248364
|
+
node: node.source,
|
|
248365
|
+
});
|
|
248366
|
+
}
|
|
248367
|
+
}
|
|
248368
|
+
}
|
|
248369
|
+
function reportAllDuplicateImports() {
|
|
248370
|
+
if (!checkDuplicateImports) {
|
|
248371
|
+
return;
|
|
248372
|
+
}
|
|
248373
|
+
reportDuplicateImports(duplicateImportMaps.importsByModule);
|
|
248374
|
+
reportDuplicateImports(duplicateImportMaps.namespaceImportsByModule);
|
|
248375
|
+
}
|
|
248376
|
+
function checkSelfImport(moduleSpecifier, node) {
|
|
248377
|
+
if (!checkSelfImports ||
|
|
248378
|
+
context.filename === '<text>' ||
|
|
248379
|
+
moduleSpecifier.includes('?')) {
|
|
248380
|
+
return;
|
|
248381
|
+
}
|
|
248382
|
+
const resolved = resolveModule(tsProgram, context.filename, getModuleSpecifierPath(moduleSpecifier));
|
|
248383
|
+
if (!resolved ||
|
|
248384
|
+
canonicalFileName(resolved.resolvedFileName) !== currentFileName) {
|
|
248385
|
+
return;
|
|
248386
|
+
}
|
|
248387
|
+
context.report({
|
|
248388
|
+
messageId: 'selfImport',
|
|
248389
|
+
node,
|
|
248390
|
+
});
|
|
248391
|
+
}
|
|
248392
|
+
function getNormalizedPathReplacement(moduleSpecifierPath, canonicalResolvedFileName) {
|
|
248393
|
+
const normalizedPath = normalizeImportPath(moduleSpecifierPath);
|
|
248394
|
+
if (normalizedPath === moduleSpecifierPath) {
|
|
248395
|
+
return null;
|
|
248396
|
+
}
|
|
248397
|
+
const normalizedResolved = resolveModule(tsProgram, context.filename, normalizedPath);
|
|
248398
|
+
return normalizedResolved &&
|
|
248399
|
+
canonicalFileName(normalizedResolved.resolvedFileName) ===
|
|
248400
|
+
canonicalResolvedFileName
|
|
248401
|
+
? normalizedPath
|
|
248402
|
+
: null;
|
|
248403
|
+
}
|
|
248404
|
+
function getIndexPathReplacement(moduleSpecifierPath, canonicalResolvedFileName) {
|
|
248405
|
+
if (!isIndexModulePath(moduleSpecifierPath)) {
|
|
248406
|
+
return null;
|
|
248407
|
+
}
|
|
248408
|
+
const parentDirectory = path.posix.dirname(moduleSpecifierPath);
|
|
248409
|
+
if (parentDirectory === '.' || parentDirectory === '..') {
|
|
248410
|
+
return parentDirectory;
|
|
248411
|
+
}
|
|
248412
|
+
const parentResolved = resolveModule(tsProgram, context.filename, parentDirectory);
|
|
248413
|
+
return parentResolved &&
|
|
248414
|
+
canonicalFileName(parentResolved.resolvedFileName) !==
|
|
248415
|
+
canonicalResolvedFileName
|
|
248416
|
+
? `${parentDirectory}/`
|
|
248417
|
+
: parentDirectory;
|
|
248418
|
+
}
|
|
248419
|
+
function getExcessParentPathReplacement(moduleSpecifierPath, resolvedFileName) {
|
|
248420
|
+
if (moduleSpecifierPath.startsWith('./')) {
|
|
248421
|
+
return null;
|
|
248422
|
+
}
|
|
248423
|
+
const expectedPath = computeRelativeImportPath(context.filename, resolvedFileName);
|
|
248424
|
+
const importSegments = moduleSpecifierPath.replace(/^\.\//, '').split('/');
|
|
248425
|
+
const importParentCount = countRelativeParents(importSegments);
|
|
248426
|
+
const expectedParentCount = countRelativeParents(expectedPath.split('/'));
|
|
248427
|
+
if (importParentCount <= expectedParentCount ||
|
|
248428
|
+
expectedPath === moduleSpecifierPath) {
|
|
248429
|
+
return null;
|
|
248430
|
+
}
|
|
248431
|
+
return expectedPath;
|
|
248432
|
+
}
|
|
248433
|
+
function getUselessPathSegmentsReplacement(moduleSpecifierPath) {
|
|
248434
|
+
const resolved = resolveModule(tsProgram, context.filename, moduleSpecifierPath);
|
|
248435
|
+
if (!resolved) {
|
|
248436
|
+
return null;
|
|
248437
|
+
}
|
|
248438
|
+
const canonicalResolvedFileName = canonicalFileName(resolved.resolvedFileName);
|
|
248439
|
+
return (getNormalizedPathReplacement(moduleSpecifierPath, canonicalResolvedFileName) ??
|
|
248440
|
+
getIndexPathReplacement(moduleSpecifierPath, canonicalResolvedFileName) ??
|
|
248441
|
+
getExcessParentPathReplacement(moduleSpecifierPath, resolved.resolvedFileName));
|
|
248442
|
+
}
|
|
248443
|
+
function checkUselessPathSegments(source) {
|
|
248444
|
+
if (!shouldCheckUselessPathSegments || !source.value.startsWith('.')) {
|
|
248445
|
+
return;
|
|
248446
|
+
}
|
|
248447
|
+
const parts = splitModuleSpecifierQuery(source.value);
|
|
248448
|
+
const proposedPath = getUselessPathSegmentsReplacement(parts.path);
|
|
248449
|
+
if (!proposedPath) {
|
|
248450
|
+
return;
|
|
248451
|
+
}
|
|
248452
|
+
const proposedModuleSpecifier = `${proposedPath}${parts.query}`;
|
|
248453
|
+
context.report({
|
|
248454
|
+
data: {
|
|
248455
|
+
moduleSpecifier: source.value,
|
|
248456
|
+
proposedPath: proposedModuleSpecifier,
|
|
248457
|
+
},
|
|
248458
|
+
fix: (fixer) => fixer.replaceText(source, quoteModuleSpecifier(source, proposedModuleSpecifier)),
|
|
248459
|
+
messageId: 'uselessPathSegments',
|
|
248460
|
+
node: source,
|
|
248461
|
+
});
|
|
248462
|
+
}
|
|
248463
|
+
function checkDeclarationModuleSpecifier(node) {
|
|
248464
|
+
if (!isStringLiteral(node.source)) {
|
|
248465
|
+
return;
|
|
248466
|
+
}
|
|
248467
|
+
checkUselessPathSegments(node.source);
|
|
248468
|
+
checkSelfImport(node.source.value, node.source);
|
|
248469
|
+
}
|
|
248470
|
+
function checkRequireSelfImport(node) {
|
|
248471
|
+
if (node.callee.type !== dist$3.AST_NODE_TYPES.Identifier ||
|
|
248472
|
+
node.callee.name !== 'require' ||
|
|
248473
|
+
node.arguments.length !== 1) {
|
|
248474
|
+
return;
|
|
248475
|
+
}
|
|
248476
|
+
const [moduleSpecifier] = node.arguments;
|
|
248477
|
+
if (moduleSpecifier?.type !== dist$3.AST_NODE_TYPES.Literal ||
|
|
248478
|
+
typeof moduleSpecifier.value !== 'string') {
|
|
248479
|
+
return;
|
|
248480
|
+
}
|
|
248481
|
+
checkSelfImport(moduleSpecifier.value, moduleSpecifier);
|
|
248482
|
+
}
|
|
248483
|
+
function checkDynamicImport(node) {
|
|
248484
|
+
const { source } = node;
|
|
248485
|
+
if (!isStringLiteral(source)) {
|
|
248486
|
+
return;
|
|
248487
|
+
}
|
|
248488
|
+
checkUselessPathSegments(source);
|
|
248489
|
+
checkSelfImport(source.value, source);
|
|
248490
|
+
}
|
|
248259
248491
|
function checkImportCycle(node) {
|
|
248260
248492
|
if (!checkCycles) {
|
|
248261
248493
|
return;
|
|
@@ -248269,11 +248501,11 @@ const rule$K = createRule({
|
|
|
248269
248501
|
return;
|
|
248270
248502
|
}
|
|
248271
248503
|
const graph = getImportGraph(tsProgram);
|
|
248272
|
-
const
|
|
248273
|
-
if (!
|
|
248504
|
+
const cycle = findCyclePathForSpecifier(tsProgram, graph, currentFileName, moduleSpecifier, canonicalFileName);
|
|
248505
|
+
if (!cycle) {
|
|
248274
248506
|
return;
|
|
248275
248507
|
}
|
|
248276
|
-
const cyclePath = formatCyclePath(graph,
|
|
248508
|
+
const cyclePath = formatCyclePath(graph, cycle.cyclePath, context.cwd);
|
|
248277
248509
|
if (node.type === dist$3.AST_NODE_TYPES.ImportDeclaration) {
|
|
248278
248510
|
// Compute the redirect replacement eagerly so we can decide whether
|
|
248279
248511
|
// to suppress. If a redirect to the direct source file is possible,
|
|
@@ -248295,12 +248527,10 @@ const rule$K = createRule({
|
|
|
248295
248527
|
}
|
|
248296
248528
|
else {
|
|
248297
248529
|
// For re-export nodes (export * from / export {x} from), suppress the
|
|
248298
|
-
// error when the
|
|
248530
|
+
// error when the reachable cycle contains an ImportDeclaration edge.
|
|
248299
248531
|
// That ImportDeclaration will be reported (and fixed) directly, so
|
|
248300
248532
|
// reporting the barrel re-export would create duplicate, unfixable noise.
|
|
248301
|
-
|
|
248302
|
-
if (componentId !== undefined &&
|
|
248303
|
-
graph.sccHasImportEdgeById.get(componentId) === true) {
|
|
248533
|
+
if (cycle.hasImportEdge) {
|
|
248304
248534
|
return;
|
|
248305
248535
|
}
|
|
248306
248536
|
context.report({
|
|
@@ -248322,7 +248552,7 @@ const rule$K = createRule({
|
|
|
248322
248552
|
// Not a barrel (has no re-export edges) — symbols are defined locally,
|
|
248323
248553
|
// so there is no shorter direct-source path to redirect to.
|
|
248324
248554
|
if (!canonicalBarrelFileName ||
|
|
248325
|
-
!getImportGraph(tsProgram)
|
|
248555
|
+
!fileHasReExportEdges(tsProgram, getImportGraph(tsProgram), canonicalBarrelFileName, canonicalFileName)) {
|
|
248326
248556
|
return null;
|
|
248327
248557
|
}
|
|
248328
248558
|
const specifiersBySourceFile = new Map();
|
|
@@ -248377,7 +248607,9 @@ const rule$K = createRule({
|
|
|
248377
248607
|
return newImports.join('\n');
|
|
248378
248608
|
}
|
|
248379
248609
|
function checkDefaultImport(node) {
|
|
248380
|
-
if ((!checkDefaultImports &&
|
|
248610
|
+
if ((!checkDefaultImports &&
|
|
248611
|
+
!checkNamedAsDefault &&
|
|
248612
|
+
!checkNamedAsDefaultMembers) ||
|
|
248381
248613
|
node.importKind === 'type') {
|
|
248382
248614
|
return;
|
|
248383
248615
|
}
|
|
@@ -248404,6 +248636,17 @@ const rule$K = createRule({
|
|
|
248404
248636
|
return;
|
|
248405
248637
|
}
|
|
248406
248638
|
const exportNames = getNamespaceImportExportNames(checker, esTreeNodeToTSNodeMap, node);
|
|
248639
|
+
if (checkNamedAsDefaultMembers && exportNames) {
|
|
248640
|
+
const [variable] = sourceCode.getDeclaredVariables(defaultImport);
|
|
248641
|
+
if (variable) {
|
|
248642
|
+
defaultImports.set(defaultImport.local.name, {
|
|
248643
|
+
exportNames,
|
|
248644
|
+
moduleSpecifier,
|
|
248645
|
+
node: defaultImport,
|
|
248646
|
+
variable,
|
|
248647
|
+
});
|
|
248648
|
+
}
|
|
248649
|
+
}
|
|
248407
248650
|
if (!checkNamedAsDefault ||
|
|
248408
248651
|
!hasNamedValueExport(exportNames, defaultImport.local.name)) {
|
|
248409
248652
|
return;
|
|
@@ -248414,6 +248657,49 @@ const rule$K = createRule({
|
|
|
248414
248657
|
node: defaultImport,
|
|
248415
248658
|
});
|
|
248416
248659
|
}
|
|
248660
|
+
function reportNamedAsDefaultMember(defaultImportIdentifier, memberName, reportNode) {
|
|
248661
|
+
if (!checkNamedAsDefaultMembers || !memberName || memberName === 'default') {
|
|
248662
|
+
return;
|
|
248663
|
+
}
|
|
248664
|
+
const usage = defaultImports.get(defaultImportIdentifier.name);
|
|
248665
|
+
if (!usage ||
|
|
248666
|
+
!hasNamedValueExport(usage.exportNames, memberName) ||
|
|
248667
|
+
getResolvedVariable(sourceCode, defaultImportIdentifier) !==
|
|
248668
|
+
usage.variable) {
|
|
248669
|
+
return;
|
|
248670
|
+
}
|
|
248671
|
+
context.report({
|
|
248672
|
+
data: {
|
|
248673
|
+
defaultName: usage.node.local.name,
|
|
248674
|
+
memberName,
|
|
248675
|
+
moduleSpecifier: usage.moduleSpecifier,
|
|
248676
|
+
},
|
|
248677
|
+
messageId: 'namedAsDefaultMember',
|
|
248678
|
+
node: reportNode,
|
|
248679
|
+
});
|
|
248680
|
+
}
|
|
248681
|
+
function checkNamedAsDefaultMemberExpression(node) {
|
|
248682
|
+
if (!checkNamedAsDefaultMembers ||
|
|
248683
|
+
defaultImports.size === 0 ||
|
|
248684
|
+
node.object.type !== dist$3.AST_NODE_TYPES.Identifier) {
|
|
248685
|
+
return;
|
|
248686
|
+
}
|
|
248687
|
+
reportNamedAsDefaultMember(node.object, getMemberExpressionPropertyName(node), node.property);
|
|
248688
|
+
}
|
|
248689
|
+
function checkNamedAsDefaultMemberDestructuring(node) {
|
|
248690
|
+
if (!checkNamedAsDefaultMembers ||
|
|
248691
|
+
defaultImports.size === 0 ||
|
|
248692
|
+
node.id.type !== dist$3.AST_NODE_TYPES.ObjectPattern ||
|
|
248693
|
+
node.init?.type !== dist$3.AST_NODE_TYPES.Identifier) {
|
|
248694
|
+
return;
|
|
248695
|
+
}
|
|
248696
|
+
for (const property of node.id.properties) {
|
|
248697
|
+
if (property.type !== dist$3.AST_NODE_TYPES.Property) {
|
|
248698
|
+
continue;
|
|
248699
|
+
}
|
|
248700
|
+
reportNamedAsDefaultMember(node.init, getObjectPropertyName(property), property.key);
|
|
248701
|
+
}
|
|
248702
|
+
}
|
|
248417
248703
|
function checkNamedAsDefaultExport(node) {
|
|
248418
248704
|
if (!checkNamedAsDefault ||
|
|
248419
248705
|
node.exportKind === 'type' ||
|
|
@@ -248529,29 +248815,46 @@ const rule$K = createRule({
|
|
|
248529
248815
|
});
|
|
248530
248816
|
}
|
|
248531
248817
|
return {
|
|
248532
|
-
|
|
248818
|
+
CallExpression: checkRequireSelfImport,
|
|
248819
|
+
ExportAllDeclaration(node) {
|
|
248820
|
+
checkImportCycle(node);
|
|
248821
|
+
checkDeclarationModuleSpecifier(node);
|
|
248822
|
+
},
|
|
248533
248823
|
ExportNamedDeclaration(node) {
|
|
248534
248824
|
checkImportCycle(node);
|
|
248825
|
+
checkDeclarationModuleSpecifier(node);
|
|
248535
248826
|
checkNamedAsDefaultExport(node);
|
|
248536
248827
|
},
|
|
248537
248828
|
ImportDeclaration(node) {
|
|
248538
248829
|
checkImportCycle(node);
|
|
248830
|
+
checkDeclarationModuleSpecifier(node);
|
|
248831
|
+
collectDuplicateImport(node);
|
|
248539
248832
|
checkDefaultImport(node);
|
|
248540
248833
|
collectNamespaceImport(node);
|
|
248541
248834
|
},
|
|
248542
|
-
|
|
248835
|
+
ImportExpression: checkDynamicImport,
|
|
248836
|
+
MemberExpression(node) {
|
|
248837
|
+
checkNamedAsDefaultMemberExpression(node);
|
|
248838
|
+
checkNamespaceMember(node);
|
|
248839
|
+
},
|
|
248840
|
+
'Program:exit': reportAllDuplicateImports,
|
|
248841
|
+
VariableDeclarator: checkNamedAsDefaultMemberDestructuring,
|
|
248543
248842
|
};
|
|
248544
248843
|
},
|
|
248545
248844
|
meta: {
|
|
248546
248845
|
docs: {
|
|
248547
|
-
description: 'Fast replacement for import/default, import/namespace, import/no-cycle,
|
|
248846
|
+
description: 'Fast replacement for import/default, import/namespace, import/no-cycle, import/no-duplicates, import/no-named-as-default, import/no-named-as-default-member, import/no-self-import, and import/no-useless-path-segments checks',
|
|
248548
248847
|
},
|
|
248549
248848
|
fixable: 'code',
|
|
248550
248849
|
messages: {
|
|
248850
|
+
duplicateImport: '"{{moduleSpecifier}}" imported multiple times.',
|
|
248551
248851
|
importCycle: 'Import cycle detected: {{cyclePath}}.',
|
|
248552
248852
|
missingDefaultExport: 'No default export found in "{{moduleSpecifier}}".',
|
|
248553
248853
|
namedAsDefault: 'Using exported name "{{name}}" as identifier for default export.',
|
|
248854
|
+
namedAsDefaultMember: 'Default import "{{defaultName}}" from "{{moduleSpecifier}}" also has a named export "{{memberName}}". Use a named import instead.',
|
|
248855
|
+
selfImport: 'Module imports itself.',
|
|
248554
248856
|
unknownNamespaceMember: 'Namespace import "{{namespaceName}}" from "{{moduleSpecifier}}" has no exported member "{{memberName}}".',
|
|
248857
|
+
uselessPathSegments: 'Useless path segments for "{{moduleSpecifier}}", should be "{{proposedPath}}".',
|
|
248555
248858
|
},
|
|
248556
248859
|
schema: [
|
|
248557
248860
|
{
|
|
@@ -248565,14 +248868,30 @@ const rule$K = createRule({
|
|
|
248565
248868
|
description: 'Report default imports from modules without a default export. Defaults to true.',
|
|
248566
248869
|
type: 'boolean',
|
|
248567
248870
|
},
|
|
248871
|
+
checkDuplicateImports: {
|
|
248872
|
+
description: 'Report repeated import declarations for the same resolved module. Defaults to true.',
|
|
248873
|
+
type: 'boolean',
|
|
248874
|
+
},
|
|
248568
248875
|
checkNamedAsDefault: {
|
|
248569
248876
|
description: 'Report default imports and default re-exports named after a named export from the same module. Defaults to true.',
|
|
248570
248877
|
type: 'boolean',
|
|
248571
248878
|
},
|
|
248879
|
+
checkNamedAsDefaultMembers: {
|
|
248880
|
+
description: 'Report property access or destructuring of default imports when the property name is a named export from the same module. Defaults to true.',
|
|
248881
|
+
type: 'boolean',
|
|
248882
|
+
},
|
|
248572
248883
|
checkNamespaceMembers: {
|
|
248573
248884
|
description: 'Report static namespace import member accesses that are not exported by the imported module. Defaults to true.',
|
|
248574
248885
|
type: 'boolean',
|
|
248575
248886
|
},
|
|
248887
|
+
checkSelfImports: {
|
|
248888
|
+
description: 'Report imports, re-exports, dynamic imports, and require() calls that resolve to the current file. Defaults to true.',
|
|
248889
|
+
type: 'boolean',
|
|
248890
|
+
},
|
|
248891
|
+
checkUselessPathSegments: {
|
|
248892
|
+
description: 'Report relative imports, re-exports, and dynamic imports with unnecessary path segments or /index suffixes. Defaults to true.',
|
|
248893
|
+
type: 'boolean',
|
|
248894
|
+
},
|
|
248576
248895
|
ignoreExternalDefaultImports: {
|
|
248577
248896
|
description: 'Skip default import checks for modules resolved from external libraries. Defaults to true.',
|
|
248578
248897
|
type: 'boolean',
|
package/package.json
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { type TSESLint } from '@typescript-eslint/utils';
|
|
2
|
-
type MessageId = 'importCycle' | 'missingDefaultExport' | 'namedAsDefault' | 'unknownNamespaceMember';
|
|
2
|
+
type MessageId = 'duplicateImport' | 'importCycle' | 'missingDefaultExport' | 'namedAsDefault' | 'namedAsDefaultMember' | 'selfImport' | 'unknownNamespaceMember' | 'uselessPathSegments';
|
|
3
3
|
type Options = [
|
|
4
4
|
{
|
|
5
5
|
checkCycles?: boolean;
|
|
6
6
|
checkDefaultImports?: boolean;
|
|
7
|
+
checkDuplicateImports?: boolean;
|
|
7
8
|
checkNamedAsDefault?: boolean;
|
|
9
|
+
checkNamedAsDefaultMembers?: boolean;
|
|
8
10
|
checkNamespaceMembers?: boolean;
|
|
11
|
+
checkSelfImports?: boolean;
|
|
12
|
+
checkUselessPathSegments?: boolean;
|
|
9
13
|
ignoreExternalDefaultImports?: boolean;
|
|
10
14
|
}?
|
|
11
15
|
];
|