Skip to content

Commit fa23a33

Browse files
committed
Refactor isReferenced and hasStrictlyNsReferences to better express intent
1 parent ddc6693 commit fa23a33

File tree

2 files changed

+87
-84
lines changed

2 files changed

+87
-84
lines changed

packages/knip/src/graph-explorer/operations/has-strictly-ns-references.ts

Lines changed: 69 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -5,86 +5,90 @@ export const hasStrictlyNsReferences = (
55
graph: ModuleGraph,
66
filePath: string,
77
importsForExport: ImportMaps | undefined,
8-
identifier: string,
9-
seenFiles = new Set<string>()
8+
identifier: string
109
): [boolean, string?] => {
11-
if (!importsForExport) return [false];
10+
const seen = new Set<string>();
1211

13-
if (seenFiles.has(filePath)) return [false];
14-
seenFiles.add(filePath);
12+
const walkDown = (path: string, importMaps: ImportMaps | undefined, id: string): [boolean, string?] => {
13+
if (!importMaps) return [false];
1514

16-
let namespace: string | undefined;
15+
if (seen.has(path)) return [false];
16+
seen.add(path);
1717

18-
const followReExports = (sources: Set<string>, nextId: string): [boolean, string?] | undefined => {
19-
for (const filePath of sources) {
20-
const file = graph.get(filePath);
21-
if (!file?.importedBy) continue;
22-
const result = hasStrictlyNsReferences(graph, filePath, file.importedBy, nextId, seenFiles);
23-
if (result[0] === false && result[1]) return result;
24-
if (result[1] && !namespace) namespace = result[1];
25-
}
26-
return undefined;
27-
};
18+
let namespace: string | undefined;
19+
20+
const follow = (sources: Set<string>, nextId: string): [boolean, string?] | undefined => {
21+
for (const filePath of sources) {
22+
const file = graph.get(filePath);
23+
if (!file?.importedBy) continue;
24+
const result = walkDown(filePath, file.importedBy, nextId);
25+
if (result[0] === false && result[1]) return result;
26+
if (result[1] && !namespace) namespace = result[1];
27+
}
28+
return undefined;
29+
};
30+
31+
for (const ns of importMaps.importNs.keys()) {
32+
if (!importMaps.refs.has(ns)) return [false, ns];
2833

29-
for (const ns of importsForExport.importNs.keys()) {
30-
const hasNsRef = importsForExport.refs.has(ns);
31-
if (!hasNsRef) return [false, ns];
34+
for (const ref of importMaps.refs) {
35+
if (ref.startsWith(`${ns}.`)) return [false, ns];
36+
}
3237

33-
for (const ref of importsForExport.refs) {
34-
if (ref.startsWith(`${ns}.`)) return [false, ns];
38+
namespace = ns;
39+
40+
const nsAliases = getAliasReExportMap(importMaps, ns);
41+
if (nsAliases) {
42+
for (const [alias, sources] of nsAliases) {
43+
const result = follow(sources, alias);
44+
if (result) return result;
45+
}
46+
}
3547
}
3648

37-
namespace = ns;
49+
const directSources = getPassThroughReExportSources(importMaps, id);
50+
if (directSources) {
51+
const result = follow(directSources, id);
52+
if (result) return result;
53+
}
3854

39-
const nsAliases = getAliasReExportMap(importsForExport, ns);
40-
if (nsAliases) {
41-
for (const [alias, sources] of nsAliases) {
42-
const result = followReExports(sources, alias);
55+
const starSources = getStarReExportSources(importMaps);
56+
if (starSources) {
57+
const result = follow(starSources, id);
58+
if (result) return result;
59+
}
60+
61+
const [_id, ...rest] = id.split('.');
62+
const aliasEntries = getAliasReExportMap(importMaps, _id);
63+
if (aliasEntries) {
64+
for (const [alias, sources] of aliasEntries) {
65+
const result = follow(sources, [alias, ...rest].join('.'));
4366
if (result) return result;
4467
}
4568
}
46-
}
47-
48-
const directSources = getPassThroughReExportSources(importsForExport, identifier);
49-
if (directSources) {
50-
const result = followReExports(directSources, identifier);
51-
if (result) return result;
52-
}
53-
54-
const starSources = getStarReExportSources(importsForExport);
55-
if (starSources) {
56-
const result = followReExports(starSources, identifier);
57-
if (result) return result;
58-
}
59-
60-
const [id, ...rest] = identifier.split('.');
61-
const aliasEntries = getAliasReExportMap(importsForExport, id);
62-
if (aliasEntries) {
63-
for (const [alias, sources] of aliasEntries) {
64-
const result = followReExports(sources, [alias, ...rest].join('.'));
69+
70+
for (const [ns, sources] of importMaps.reExportNs) {
71+
const result = follow(sources, `${ns}.${id}`);
6572
if (result) return result;
6673
}
67-
}
68-
69-
for (const [ns, sources] of importsForExport.reExportNs) {
70-
const result = followReExports(sources, `${ns}.${identifier}`);
71-
if (result) return result;
72-
}
73-
74-
const importedSources = importsForExport.import.get(identifier);
75-
if (importedSources) {
76-
const result = followReExports(importedSources, identifier);
77-
if (result) return result;
78-
}
79-
80-
const importAsMap = importsForExport.importAs.get(identifier);
81-
if (importAsMap) {
82-
for (const [alias, sources] of importAsMap) {
83-
const result = followReExports(sources, alias);
74+
75+
const importedSources = importMaps.import.get(id);
76+
if (importedSources) {
77+
const result = follow(importedSources, id);
8478
if (result) return result;
8579
}
86-
}
8780

88-
if (namespace) return [true, namespace];
89-
return [false];
81+
const importAsMap = importMaps.importAs.get(id);
82+
if (importAsMap) {
83+
for (const [alias, sources] of importAsMap) {
84+
const result = follow(sources, alias);
85+
if (result) return result;
86+
}
87+
}
88+
89+
if (namespace) return [true, namespace];
90+
return [false];
91+
};
92+
93+
return walkDown(filePath, importsForExport, identifier);
9094
};

packages/knip/src/graph-explorer/operations/is-referenced.ts

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,25 @@ export const isReferenced = (
1616
) => {
1717
const seen = new Set<string>();
1818

19-
const check = (currentPath: string, currentId: string): [boolean, string | undefined] => {
20-
const isEntryFile = entryPaths.has(currentPath);
21-
let reExportingEntryFile: string | undefined = isEntryFile ? currentPath : undefined;
19+
const walkDown = (path: string, id: string): [boolean, string | undefined] => {
20+
const isEntryFile = entryPaths.has(path);
21+
let reExportingEntryFile: string | undefined = isEntryFile ? path : undefined;
2222

23-
if (seen.has(currentPath)) return [false, reExportingEntryFile];
24-
seen.add(currentPath);
23+
if (seen.has(path)) return [false, reExportingEntryFile];
24+
seen.add(path);
2525

26-
const restIds = currentId.split('.');
26+
const restIds = id.split('.');
2727
const identifier = restIds.shift();
28-
const file = graph.get(currentPath)?.importedBy;
28+
const file = graph.get(path)?.importedBy;
2929

3030
if (!identifier || !file) {
3131
return [false, reExportingEntryFile];
3232
}
3333

34-
const followSources = (sources: Set<string>, nextId: string): boolean => {
34+
const follow = (sources: Set<string>, nextId: string): boolean => {
3535
for (const byFilePath of sources) {
3636
if (seen.has(byFilePath)) continue;
37-
const result = check(byFilePath, nextId);
37+
const result = walkDown(byFilePath, nextId);
3838
if (result[1]) reExportingEntryFile = result[1];
3939
if (result[0]) return true;
4040
}
@@ -43,7 +43,7 @@ export const isReferenced = (
4343

4444
if (
4545
file.import.get(OPAQUE) ||
46-
((identifier === currentId || (identifier !== currentId && file.refs.has(currentId))) &&
46+
((identifier === id || (identifier !== id && file.refs.has(id))) &&
4747
(file.import.has(identifier) || file.importAs.has(identifier)))
4848
) {
4949
return [true, reExportingEntryFile];
@@ -61,20 +61,20 @@ export const isReferenced = (
6161
}
6262

6363
for (const namespace of file.importNs.keys()) {
64-
if (file.refs.has(`${namespace}.${currentId}`)) {
64+
if (file.refs.has(`${namespace}.${id}`)) {
6565
return [true, reExportingEntryFile];
6666
}
6767

6868
const nsAliasMap = getAliasReExportMap(file, namespace);
6969
if (nsAliasMap) {
7070
for (const [alias, sources] of nsAliasMap) {
71-
if (followSources(sources, `${alias}.${currentId}`)) return [true, reExportingEntryFile];
71+
if (follow(sources, `${alias}.${id}`)) return [true, reExportingEntryFile];
7272
}
7373
}
7474

7575
const nsReExportSources = getNamespaceReExportSources(file, namespace);
7676
if (nsReExportSources) {
77-
if (followSources(nsReExportSources, `${namespace}.${currentId}`)) return [true, reExportingEntryFile];
77+
if (follow(nsReExportSources, `${namespace}.${id}`)) return [true, reExportingEntryFile];
7878
}
7979
}
8080

@@ -83,28 +83,27 @@ export const isReferenced = (
8383
const aliasMap = getAliasReExportMap(file, identifier);
8484
if (aliasMap) {
8585
for (const [alias, sources] of aliasMap) {
86-
const ref = [alias, ...restIds].join('.');
87-
if (followSources(sources, ref)) return [true, reExportingEntryFile];
86+
if (follow(sources, [alias, ...restIds].join('.'))) return [true, reExportingEntryFile];
8887
}
8988
}
9089

9190
const directSources = getPassThroughReExportSources(file, identifier);
9291
const starSources = getStarReExportSources(file);
9392

9493
if (directSources) {
95-
if (followSources(directSources, currentId)) return [true, reExportingEntryFile];
94+
if (follow(directSources, id)) return [true, reExportingEntryFile];
9695
} else if (starSources) {
97-
if (followSources(starSources, currentId)) return [true, reExportingEntryFile];
96+
if (follow(starSources, id)) return [true, reExportingEntryFile];
9897
}
9998

10099
for (const [namespace, sources] of file.reExportNs) {
101-
if (followSources(sources, `${namespace}.${currentId}`)) {
100+
if (follow(sources, `${namespace}.${id}`)) {
102101
return [true, reExportingEntryFile];
103102
}
104103
}
105104

106105
return [false, reExportingEntryFile];
107106
};
108107

109-
return check(filePath, id);
108+
return walkDown(filePath, id);
110109
};

0 commit comments

Comments
 (0)