@macrulez/devtoolz 0.1.0 → 0.2.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 +164 -3
- package/dist/cli.js +233 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/circular-imports/core.d.ts +15 -0
- package/dist/commands/circular-imports/core.js +95 -0
- package/dist/commands/circular-imports/core.js.map +1 -0
- package/dist/commands/circular-imports/report.d.ts +3 -0
- package/dist/commands/circular-imports/report.js +41 -0
- package/dist/commands/circular-imports/report.js.map +1 -0
- package/dist/commands/circular-imports/run.d.ts +19 -0
- package/dist/commands/circular-imports/run.js +29 -0
- package/dist/commands/circular-imports/run.js.map +1 -0
- package/dist/commands/dead-exports/core.d.ts +1 -1
- package/dist/commands/dead-exports/core.js +2 -2
- package/dist/commands/dead-exports/core.js.map +1 -1
- package/dist/commands/dead-exports/entry.d.ts +0 -1
- package/dist/commands/dead-exports/entry.js +1 -14
- package/dist/commands/dead-exports/entry.js.map +1 -1
- package/dist/commands/dead-exports/run.js +3 -2
- package/dist/commands/dead-exports/run.js.map +1 -1
- package/dist/commands/empty-catch/core.d.ts +8 -0
- package/dist/commands/empty-catch/core.js +61 -0
- package/dist/commands/empty-catch/core.js.map +1 -0
- package/dist/commands/empty-catch/report.d.ts +3 -0
- package/dist/commands/empty-catch/report.js +27 -0
- package/dist/commands/empty-catch/report.js.map +1 -0
- package/dist/commands/empty-catch/run.d.ts +17 -0
- package/dist/commands/empty-catch/run.js +34 -0
- package/dist/commands/empty-catch/run.js.map +1 -0
- package/dist/commands/empty-catch/vue.d.ts +2 -0
- package/dist/commands/empty-catch/vue.js +16 -0
- package/dist/commands/empty-catch/vue.js.map +1 -0
- package/dist/commands/orphan-tests/core.d.ts +9 -0
- package/dist/commands/orphan-tests/core.js +42 -0
- package/dist/commands/orphan-tests/core.js.map +1 -0
- package/dist/commands/orphan-tests/report.d.ts +3 -0
- package/dist/commands/orphan-tests/report.js +30 -0
- package/dist/commands/orphan-tests/report.js.map +1 -0
- package/dist/commands/orphan-tests/run.d.ts +25 -0
- package/dist/commands/orphan-tests/run.js +55 -0
- package/dist/commands/orphan-tests/run.js.map +1 -0
- package/dist/commands/readme-check/run.js +8 -3
- package/dist/commands/readme-check/run.js.map +1 -1
- package/dist/commands/scripts-check/core.d.ts +18 -0
- package/dist/commands/scripts-check/core.js +121 -0
- package/dist/commands/scripts-check/core.js.map +1 -0
- package/dist/commands/scripts-check/report.d.ts +3 -0
- package/dist/commands/scripts-check/report.js +34 -0
- package/dist/commands/scripts-check/report.js.map +1 -0
- package/dist/commands/scripts-check/run.d.ts +15 -0
- package/dist/commands/scripts-check/run.js +81 -0
- package/dist/commands/scripts-check/run.js.map +1 -0
- package/dist/commands/stale-ts-ignore/check.d.ts +23 -0
- package/dist/commands/stale-ts-ignore/check.js +104 -0
- package/dist/commands/stale-ts-ignore/check.js.map +1 -0
- package/dist/commands/stale-ts-ignore/core.d.ts +11 -0
- package/dist/commands/stale-ts-ignore/core.js +42 -0
- package/dist/commands/stale-ts-ignore/core.js.map +1 -0
- package/dist/commands/stale-ts-ignore/report.d.ts +3 -0
- package/dist/commands/stale-ts-ignore/report.js +30 -0
- package/dist/commands/stale-ts-ignore/report.js.map +1 -0
- package/dist/commands/stale-ts-ignore/run.d.ts +23 -0
- package/dist/commands/stale-ts-ignore/run.js +101 -0
- package/dist/commands/stale-ts-ignore/run.js.map +1 -0
- package/dist/commands/stale-ts-ignore/vue.d.ts +9 -0
- package/dist/commands/stale-ts-ignore/vue.js +16 -0
- package/dist/commands/stale-ts-ignore/vue.js.map +1 -0
- package/dist/commands/strip-comments/core.js +2 -56
- package/dist/commands/strip-comments/core.js.map +1 -1
- package/dist/commands/todo-report/core.d.ts +16 -0
- package/dist/commands/todo-report/core.js +112 -0
- package/dist/commands/todo-report/core.js.map +1 -0
- package/dist/commands/todo-report/report.d.ts +3 -0
- package/dist/commands/todo-report/report.js +31 -0
- package/dist/commands/todo-report/report.js.map +1 -0
- package/dist/commands/todo-report/run.d.ts +22 -0
- package/dist/commands/todo-report/run.js +41 -0
- package/dist/commands/todo-report/run.js.map +1 -0
- package/dist/commands/todo-report/vue.d.ts +2 -0
- package/dist/commands/todo-report/vue.js +71 -0
- package/dist/commands/todo-report/vue.js.map +1 -0
- package/dist/commands/unused-deps/config-files.d.ts +9 -0
- package/dist/commands/unused-deps/config-files.js +64 -0
- package/dist/commands/unused-deps/config-files.js.map +1 -0
- package/dist/commands/unused-deps/core.d.ts +25 -0
- package/dist/commands/unused-deps/core.js +121 -0
- package/dist/commands/unused-deps/core.js.map +1 -0
- package/dist/commands/unused-deps/disk-resolve.d.ts +11 -0
- package/dist/commands/unused-deps/disk-resolve.js +40 -0
- package/dist/commands/unused-deps/disk-resolve.js.map +1 -0
- package/dist/commands/unused-deps/package-name.d.ts +8 -0
- package/dist/commands/unused-deps/package-name.js +20 -0
- package/dist/commands/unused-deps/package-name.js.map +1 -0
- package/dist/commands/unused-deps/report.d.ts +3 -0
- package/dist/commands/unused-deps/report.js +32 -0
- package/dist/commands/unused-deps/report.js.map +1 -0
- package/dist/commands/unused-deps/run.d.ts +18 -0
- package/dist/commands/unused-deps/run.js +68 -0
- package/dist/commands/unused-deps/run.js.map +1 -0
- package/dist/index.d.ts +51 -5
- package/dist/index.js +33 -3
- package/dist/index.js.map +1 -1
- package/dist/utils/escape-regexp.d.ts +1 -0
- package/dist/utils/escape-regexp.js +4 -0
- package/dist/utils/escape-regexp.js.map +1 -0
- package/dist/utils/find-comments.d.ts +10 -0
- package/dist/utils/find-comments.js +62 -0
- package/dist/utils/find-comments.js.map +1 -0
- package/dist/utils/find-package-dir.d.ts +2 -0
- package/dist/utils/find-package-dir.js +15 -0
- package/dist/utils/find-package-dir.js.map +1 -0
- package/dist/utils/line-of.d.ts +2 -0
- package/dist/utils/line-of.js +12 -0
- package/dist/utils/line-of.js.map +1 -0
- package/dist/utils/load-tsconfig.d.ts +20 -0
- package/dist/utils/load-tsconfig.js +33 -0
- package/dist/utils/load-tsconfig.js.map +1 -0
- package/dist/utils/parse-module.d.ts +32 -0
- package/dist/utils/parse-module.js +178 -0
- package/dist/utils/parse-module.js.map +1 -0
- package/dist/utils/resolve-specifier.d.ts +15 -0
- package/dist/utils/resolve-specifier.js +63 -0
- package/dist/utils/resolve-specifier.js.map +1 -0
- package/dist/utils/walk.js +20 -1
- package/dist/utils/walk.js.map +1 -1
- package/dist/utils/workspace.d.ts +14 -0
- package/dist/utils/workspace.js +101 -0
- package/dist/utils/workspace.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { analyzeFile } from '../../utils/parse-module.js';
|
|
3
|
+
import { resolveRelativeSpecifier } from '../../utils/resolve-specifier.js';
|
|
4
|
+
/**
|
|
5
|
+
* One graph edge per (file, targetFile) pair, not per import statement —
|
|
6
|
+
* multiple imports between the same two files collapse into one edge.
|
|
7
|
+
* The edge is type-only only if EVERY import between them is; a single
|
|
8
|
+
* value-level import between the pair makes the whole edge value-level.
|
|
9
|
+
*/
|
|
10
|
+
function buildGraph(files) {
|
|
11
|
+
const graph = new Map();
|
|
12
|
+
for (const file of files) {
|
|
13
|
+
const text = readFileSync(file, 'utf8');
|
|
14
|
+
const analysis = analyzeFile(text, file);
|
|
15
|
+
const edgesByTarget = new Map(); // target -> typeOnly so far
|
|
16
|
+
for (const imp of analysis.imports) {
|
|
17
|
+
if (!imp.from.startsWith('.'))
|
|
18
|
+
continue; // bare specifiers (third-party packages) aren't part of this graph
|
|
19
|
+
const target = resolveRelativeSpecifier(file, imp.from);
|
|
20
|
+
if (!target || !files.includes(target))
|
|
21
|
+
continue; // outside the scanned set — nothing to walk into
|
|
22
|
+
const existing = edgesByTarget.get(target);
|
|
23
|
+
edgesByTarget.set(target, existing === undefined ? imp.isType : existing && imp.isType);
|
|
24
|
+
}
|
|
25
|
+
graph.set(file, [...edgesByTarget].map(([to, typeOnly]) => ({ to, typeOnly })));
|
|
26
|
+
}
|
|
27
|
+
return graph;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Every simple cycle is found via a DFS with an explicit recursion stack
|
|
31
|
+
* (a "back edge" to a node still on the stack is a cycle) — not full
|
|
32
|
+
* enumeration of every simple cycle in the graph (exponential in the
|
|
33
|
+
* worst case, and overkill here): one representative cycle is reported
|
|
34
|
+
* per strongly-connected cluster reached this way, matching how tools
|
|
35
|
+
* like madge/circular-dependency-plugin report this in practice.
|
|
36
|
+
*/
|
|
37
|
+
function findCycles(graph) {
|
|
38
|
+
const WHITE = 0, GRAY = 1, BLACK = 2;
|
|
39
|
+
const color = new Map();
|
|
40
|
+
const stack = [];
|
|
41
|
+
const found = [];
|
|
42
|
+
const seenKeys = new Set();
|
|
43
|
+
function canonicalKey(cycleFiles) {
|
|
44
|
+
let minIdx = 0;
|
|
45
|
+
for (let i = 1; i < cycleFiles.length; i++) {
|
|
46
|
+
if (cycleFiles[i] < cycleFiles[minIdx])
|
|
47
|
+
minIdx = i;
|
|
48
|
+
}
|
|
49
|
+
return [...cycleFiles.slice(minIdx), ...cycleFiles.slice(0, minIdx)].join('\u0000');
|
|
50
|
+
}
|
|
51
|
+
function dfs(node) {
|
|
52
|
+
color.set(node, GRAY);
|
|
53
|
+
stack.push(node);
|
|
54
|
+
for (const edge of graph.get(node) ?? []) {
|
|
55
|
+
const targetColor = color.get(edge.to) ?? WHITE;
|
|
56
|
+
if (targetColor === WHITE) {
|
|
57
|
+
dfs(edge.to);
|
|
58
|
+
}
|
|
59
|
+
else if (targetColor === GRAY) {
|
|
60
|
+
const startIdx = stack.indexOf(edge.to);
|
|
61
|
+
const cycleFiles = stack.slice(startIdx);
|
|
62
|
+
const key = canonicalKey(cycleFiles);
|
|
63
|
+
if (!seenKeys.has(key)) {
|
|
64
|
+
seenKeys.add(key);
|
|
65
|
+
const typeOnly = isCycleTypeOnly(graph, cycleFiles);
|
|
66
|
+
found.push({ files: cycleFiles, typeOnly });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
stack.pop();
|
|
71
|
+
color.set(node, BLACK);
|
|
72
|
+
}
|
|
73
|
+
for (const node of graph.keys()) {
|
|
74
|
+
if ((color.get(node) ?? WHITE) === WHITE)
|
|
75
|
+
dfs(node);
|
|
76
|
+
}
|
|
77
|
+
return found;
|
|
78
|
+
}
|
|
79
|
+
function isCycleTypeOnly(graph, cycleFiles) {
|
|
80
|
+
for (let i = 0; i < cycleFiles.length; i++) {
|
|
81
|
+
const from = cycleFiles[i];
|
|
82
|
+
const to = cycleFiles[(i + 1) % cycleFiles.length];
|
|
83
|
+
const edge = (graph.get(from) ?? []).find((e) => e.to === to);
|
|
84
|
+
if (!edge?.typeOnly)
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
export function analyzeCircularImports(files, options) {
|
|
90
|
+
const graph = buildGraph(files);
|
|
91
|
+
const allCycles = findCycles(graph);
|
|
92
|
+
const findings = options.includeTypes ? allCycles : allCycles.filter((c) => !c.typeOnly);
|
|
93
|
+
return { findings, filesAnalyzed: files.length };
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../src/commands/circular-imports/core.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { banner, cleanCelebration, isFancyOutputEnabled, } from '../../format/vibes.js';
|
|
2
|
+
import { colorize, DIM, WHITE } from '../../format/colors.js';
|
|
3
|
+
/**
|
|
4
|
+
* A cycle doesn't fit the flat file:line/name/tag shape formatFindingsRows
|
|
5
|
+
* expects — it's a chain of files closing back on itself — so it gets its
|
|
6
|
+
* own small block renderer instead, one cycle per block, files in order.
|
|
7
|
+
*/
|
|
8
|
+
function formatCycle(files, typeOnly, options) {
|
|
9
|
+
const fancy = isFancyOutputEnabled(options);
|
|
10
|
+
const chain = [...files, files[0]];
|
|
11
|
+
const lines = chain.map((file, i) => {
|
|
12
|
+
const prefix = i === 0 ? ' ' : ' → ';
|
|
13
|
+
return `${prefix}${fancy ? colorize(WHITE, file) : file}`;
|
|
14
|
+
});
|
|
15
|
+
if (typeOnly) {
|
|
16
|
+
const note = '(type-only — harmless at runtime, types are erased)';
|
|
17
|
+
lines.push(` ${fancy ? colorize(DIM, note) : note}`);
|
|
18
|
+
}
|
|
19
|
+
return lines;
|
|
20
|
+
}
|
|
21
|
+
export function renderCircularImportsReport(report, options = {}) {
|
|
22
|
+
const isClean = report.findings.length === 0;
|
|
23
|
+
if (options.quiet && isClean)
|
|
24
|
+
return '';
|
|
25
|
+
const lines = [];
|
|
26
|
+
if (!options.quiet && !options.plain)
|
|
27
|
+
lines.push(banner(), '');
|
|
28
|
+
lines.push(`Scanned ${report.filesScanned} file${report.filesScanned === 1 ? '' : 's'}.`, '');
|
|
29
|
+
if (isClean) {
|
|
30
|
+
lines.push(cleanCelebration(options));
|
|
31
|
+
return lines.join('\n');
|
|
32
|
+
}
|
|
33
|
+
lines.push(`${report.findings.length} circular import${report.findings.length === 1 ? '' : 's'} found:`, '');
|
|
34
|
+
report.findings.forEach((finding, i) => {
|
|
35
|
+
lines.push(...formatCycle(finding.files, finding.typeOnly, options));
|
|
36
|
+
if (i < report.findings.length - 1)
|
|
37
|
+
lines.push('');
|
|
38
|
+
});
|
|
39
|
+
return lines.join('\n');
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../../src/commands/circular-imports/report.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,gBAAgB,EAChB,oBAAoB,GAErB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AAG7D;;;;GAIG;AACH,SAAS,WAAW,CAAC,KAAe,EAAE,QAAiB,EAAE,OAAqB;IAC5E,MAAM,KAAK,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAA;IAC3C,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;IAClC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;QAClC,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAA;QACtC,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAc,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IACrE,CAAC,CAAC,CAAA;IACF,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,IAAI,GAAG,qDAAqD,CAAA;QAClE,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;IACvD,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,UAAU,2BAA2B,CACzC,MAA6B,EAC7B,UAAwB,EAAE;IAE1B,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAA;IAC5C,IAAI,OAAO,CAAC,KAAK,IAAI,OAAO;QAAE,OAAO,EAAE,CAAA;IAEvC,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,CAAC,CAAA;IAE9D,KAAK,CAAC,IAAI,CAAC,WAAW,MAAM,CAAC,YAAY,QAAQ,MAAM,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,EAAE,EAAE,CAAC,CAAA;IAE7F,IAAI,OAAO,EAAE,CAAC;QACZ,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAA;QACrC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC;IAED,KAAK,CAAC,IAAI,CACR,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,mBAAmB,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,SAAS,EAC5F,EAAE,CACH,CAAA;IACD,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE;QACrC,KAAK,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAA;QACpE,IAAI,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACpD,CAAC,CAAC,CAAA;IAEF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface CircularImportsRunOptions {
|
|
2
|
+
paths: string[];
|
|
3
|
+
cwd: string;
|
|
4
|
+
extensions?: string[];
|
|
5
|
+
ignoreGlobs?: string[];
|
|
6
|
+
respectGitignore?: boolean;
|
|
7
|
+
/** also report cycles made entirely of `import type` edges. default false */
|
|
8
|
+
includeTypes?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface CircularImportsFileFinding {
|
|
11
|
+
files: string[];
|
|
12
|
+
typeOnly: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface CircularImportsReport {
|
|
15
|
+
filesScanned: number;
|
|
16
|
+
findings: CircularImportsFileFinding[];
|
|
17
|
+
exitCode: number;
|
|
18
|
+
}
|
|
19
|
+
export declare function runCircularImports(options: CircularImportsRunOptions): CircularImportsReport;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { relative } from 'node:path';
|
|
2
|
+
import { walk } from '../../utils/walk.js';
|
|
3
|
+
import { analyzeCircularImports } from './core.js';
|
|
4
|
+
const DEFAULT_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'];
|
|
5
|
+
export function runCircularImports(options) {
|
|
6
|
+
const extensions = options.extensions ?? DEFAULT_EXTENSIONS;
|
|
7
|
+
const files = walk(options.paths, {
|
|
8
|
+
cwd: options.cwd,
|
|
9
|
+
extensions,
|
|
10
|
+
ignoreGlobs: options.ignoreGlobs ?? [],
|
|
11
|
+
...(options.respectGitignore !== undefined
|
|
12
|
+
? { respectGitignore: options.respectGitignore }
|
|
13
|
+
: {}),
|
|
14
|
+
});
|
|
15
|
+
const analysis = analyzeCircularImports(files, { includeTypes: options.includeTypes ?? false });
|
|
16
|
+
const toRelative = (f) => relative(options.cwd, f).split('\\').join('/');
|
|
17
|
+
const findings = analysis.findings.map((f) => ({
|
|
18
|
+
files: f.files.map(toRelative),
|
|
19
|
+
typeOnly: f.typeOnly,
|
|
20
|
+
}));
|
|
21
|
+
// Stable, readable order — by the cycle's first (alphabetically smallest, already canonical from core.ts) file.
|
|
22
|
+
findings.sort((a, b) => a.files[0].localeCompare(b.files[0]));
|
|
23
|
+
return {
|
|
24
|
+
filesScanned: analysis.filesAnalyzed,
|
|
25
|
+
findings,
|
|
26
|
+
exitCode: findings.length > 0 ? 1 : 0,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/commands/circular-imports/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAC1C,OAAO,EAAE,sBAAsB,EAA8B,MAAM,WAAW,CAAA;AAE9E,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAuBzE,MAAM,UAAU,kBAAkB,CAAC,OAAkC;IACnE,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,kBAAkB,CAAA;IAC3D,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE;QAChC,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,UAAU;QACV,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,EAAE;QACtC,GAAG,CAAC,OAAO,CAAC,gBAAgB,KAAK,SAAS;YACxC,CAAC,CAAC,EAAE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,EAAE;YAChD,CAAC,CAAC,EAAE,CAAC;KACR,CAAC,CAAA;IAEF,MAAM,QAAQ,GAAG,sBAAsB,CAAC,KAAK,EAAE,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,KAAK,EAAE,CAAC,CAAA;IAE/F,MAAM,UAAU,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACxF,MAAM,QAAQ,GAA4B,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACtE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC;QAC9B,QAAQ,EAAE,CAAC,CAAC,QAAQ;KACrB,CAAC,CAAC,CAAA;IACH,gHAAgH;IAChH,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAY,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAW,CAAC,CAAC,CAAA;IAEnF,OAAO;QACL,YAAY,EAAE,QAAQ,CAAC,aAAa;QACpC,QAAQ;QACR,QAAQ,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KACtC,CAAA;AACH,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
|
-
import { analyzeFile } from '
|
|
3
|
-
import { resolveRelativeSpecifier } from '
|
|
2
|
+
import { analyzeFile } from '../../utils/parse-module.js';
|
|
3
|
+
import { resolveRelativeSpecifier } from '../../utils/resolve-specifier.js';
|
|
4
4
|
import { resolvePublicEntries } from './entry.js';
|
|
5
5
|
function specifierKey(file, name) {
|
|
6
6
|
return `${file}::${name}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../src/commands/dead-exports/core.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,WAAW,EAAqB,MAAM,
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../src/commands/dead-exports/core.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// packages use. `--entry` exists specifically to override this when it
|
|
13
13
|
// guesses wrong.
|
|
14
14
|
import { existsSync, readFileSync } from 'node:fs';
|
|
15
|
-
import {
|
|
15
|
+
import { join, resolve } from 'node:path';
|
|
16
16
|
const BUILD_DIR_NAMES = new Set(['dist', 'build', 'lib', 'out']);
|
|
17
17
|
const SOURCE_EXTENSIONS = ['.ts', '.tsx'];
|
|
18
18
|
function readPackageJson(dir) {
|
|
@@ -77,17 +77,4 @@ export function resolvePublicEntries(packageDir, extraEntries = []) {
|
|
|
77
77
|
}
|
|
78
78
|
return [...entries];
|
|
79
79
|
}
|
|
80
|
-
// Exposed only so entry.ts doesn't need a second "does this dir have a
|
|
81
|
-
// package.json" check duplicated in core.ts.
|
|
82
|
-
export function findNearestPackageDir(startDir) {
|
|
83
|
-
let dir = resolve(startDir);
|
|
84
|
-
for (;;) {
|
|
85
|
-
if (existsSync(join(dir, 'package.json')))
|
|
86
|
-
return dir;
|
|
87
|
-
const parent = dirname(dir);
|
|
88
|
-
if (parent === dir)
|
|
89
|
-
return null;
|
|
90
|
-
dir = parent;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
80
|
//# sourceMappingURL=entry.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entry.js","sourceRoot":"","sources":["../../../src/commands/dead-exports/entry.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,8DAA8D;AAC9D,mEAAmE;AACnE,yCAAyC;AACzC,EAAE;AACF,qEAAqE;AACrE,yEAAyE;AACzE,0EAA0E;AAC1E,uEAAuE;AACvE,yEAAyE;AACzE,kEAAkE;AAClE,uEAAuE;AACvE,iBAAiB;AAEjB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"entry.js","sourceRoot":"","sources":["../../../src/commands/dead-exports/entry.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { relative } from 'node:path';
|
|
2
2
|
import { walk } from '../../utils/walk.js';
|
|
3
3
|
import { analyzeDeadExports } from './core.js';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { resolvePublicEntries } from './entry.js';
|
|
5
|
+
import { findNearestPackageDir } from '../../utils/find-package-dir.js';
|
|
6
|
+
import { detectWorkspace } from '../../utils/workspace.js';
|
|
6
7
|
const DEFAULT_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'];
|
|
7
8
|
// Build-tool config files (tsup.config.ts, vitest.config.ts, ...) are
|
|
8
9
|
// loaded by that tool's own CLI via filename convention, never through a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/commands/dead-exports/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAC1C,OAAO,EAAE,kBAAkB,EAA0B,MAAM,WAAW,CAAA;AACtE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/commands/dead-exports/run.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Finds `catch` blocks that do nothing with the error, or do so little
|
|
2
|
+
// it amounts to swallowing it. A fully empty `catch (e) {}` is already
|
|
3
|
+
// caught by ESLint's own `no-empty` rule — the real value here is the
|
|
4
|
+
// case no syntax rule can see: a catch body that's nothing but
|
|
5
|
+
// console.* calls, which reads as error handling but silently lets
|
|
6
|
+
// execution continue exactly as an empty catch would.
|
|
7
|
+
import ts from 'typescript';
|
|
8
|
+
function scriptKindFor(file) {
|
|
9
|
+
return file.endsWith('.tsx')
|
|
10
|
+
? ts.ScriptKind.TSX
|
|
11
|
+
: file.endsWith('.jsx')
|
|
12
|
+
? ts.ScriptKind.JSX
|
|
13
|
+
: ts.ScriptKind.TS;
|
|
14
|
+
}
|
|
15
|
+
/** Whether a real comment (not text inside a string) appears anywhere in the given source span — runs the actual scanner, not a text search. */
|
|
16
|
+
function blockHasComment(sourceText, start, end) {
|
|
17
|
+
const scanner = ts.createScanner(ts.ScriptTarget.Latest, false, ts.LanguageVariant.Standard);
|
|
18
|
+
scanner.setText(sourceText, start, end - start);
|
|
19
|
+
let tok = scanner.scan();
|
|
20
|
+
while (tok !== ts.SyntaxKind.EndOfFileToken) {
|
|
21
|
+
if (tok === ts.SyntaxKind.SingleLineCommentTrivia ||
|
|
22
|
+
tok === ts.SyntaxKind.MultiLineCommentTrivia) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
tok = scanner.scan();
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
function isConsoleCall(expr) {
|
|
30
|
+
return (ts.isCallExpression(expr) &&
|
|
31
|
+
ts.isPropertyAccessExpression(expr.expression) &&
|
|
32
|
+
ts.isIdentifier(expr.expression.expression) &&
|
|
33
|
+
expr.expression.expression.text === 'console');
|
|
34
|
+
}
|
|
35
|
+
function isConsoleOnlyStatement(stmt) {
|
|
36
|
+
return ts.isExpressionStatement(stmt) && isConsoleCall(stmt.expression);
|
|
37
|
+
}
|
|
38
|
+
export function analyzeEmptyCatch(text, file) {
|
|
39
|
+
const sf = ts.createSourceFile(file, text, ts.ScriptTarget.Latest, true, scriptKindFor(file));
|
|
40
|
+
const findings = [];
|
|
41
|
+
function visit(node) {
|
|
42
|
+
if (ts.isCatchClause(node)) {
|
|
43
|
+
const block = node.block;
|
|
44
|
+
if (!blockHasComment(text, block.getStart(sf), block.getEnd())) {
|
|
45
|
+
const statements = block.statements;
|
|
46
|
+
if (statements.length === 0) {
|
|
47
|
+
const pos = sf.getLineAndCharacterOfPosition(node.getStart(sf));
|
|
48
|
+
findings.push({ line: pos.line + 1, column: pos.character + 1, kind: 'empty' });
|
|
49
|
+
}
|
|
50
|
+
else if (statements.every(isConsoleOnlyStatement)) {
|
|
51
|
+
const pos = sf.getLineAndCharacterOfPosition(node.getStart(sf));
|
|
52
|
+
findings.push({ line: pos.line + 1, column: pos.character + 1, kind: 'console-only' });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
ts.forEachChild(node, visit);
|
|
57
|
+
}
|
|
58
|
+
visit(sf);
|
|
59
|
+
return findings;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../src/commands/empty-catch/core.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,uEAAuE;AACvE,sEAAsE;AACtE,+DAA+D;AAC/D,mEAAmE;AACnE,sDAAsD;AAEtD,OAAO,EAAE,MAAM,YAAY,CAAA;AAU3B,SAAS,aAAa,CAAC,IAAY;IACjC,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QAC1B,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG;QACnB,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;YACrB,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG;YACnB,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,CAAA;AACxB,CAAC;AAED,gJAAgJ;AAChJ,SAAS,eAAe,CAAC,UAAkB,EAAE,KAAa,EAAE,GAAW;IACrE,MAAM,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAA;IAC5F,OAAO,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,EAAE,GAAG,GAAG,KAAK,CAAC,CAAA;IAC/C,IAAI,GAAG,GAAG,OAAO,CAAC,IAAI,EAAE,CAAA;IACxB,OAAO,GAAG,KAAK,EAAE,CAAC,UAAU,CAAC,cAAc,EAAE,CAAC;QAC5C,IACE,GAAG,KAAK,EAAE,CAAC,UAAU,CAAC,uBAAuB;YAC7C,GAAG,KAAK,EAAE,CAAC,UAAU,CAAC,sBAAsB,EAC5C,CAAC;YACD,OAAO,IAAI,CAAA;QACb,CAAC;QACD,GAAG,GAAG,OAAO,CAAC,IAAI,EAAE,CAAA;IACtB,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,aAAa,CAAC,IAAmB;IACxC,OAAO,CACL,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC;QACzB,EAAE,CAAC,0BAA0B,CAAC,IAAI,CAAC,UAAU,CAAC;QAC9C,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;QAC3C,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,KAAK,SAAS,CAC9C,CAAA;AACH,CAAC;AAED,SAAS,sBAAsB,CAAC,IAAkB;IAChD,OAAO,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;AACzE,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAY,EAAE,IAAY;IAC1D,MAAM,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC,CAAA;IAC7F,MAAM,QAAQ,GAAwB,EAAE,CAAA;IAExC,SAAS,KAAK,CAAC,IAAa;QAC1B,IAAI,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAA;YACxB,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC;gBAC/D,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAA;gBACnC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBAC5B,MAAM,GAAG,GAAG,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAA;oBAC/D,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,SAAS,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAA;gBACjF,CAAC;qBAAM,IAAI,UAAU,CAAC,KAAK,CAAC,sBAAsB,CAAC,EAAE,CAAC;oBACpD,MAAM,GAAG,GAAG,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAA;oBAC/D,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,SAAS,GAAG,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAA;gBACxF,CAAC;YACH,CAAC;QACH,CAAC;QACD,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAC9B,CAAC;IACD,KAAK,CAAC,EAAE,CAAC,CAAA;IAET,OAAO,QAAQ,CAAA;AACjB,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { banner, cleanCelebration } from '../../format/vibes.js';
|
|
2
|
+
import { formatFindingsRows } from '../../format/findings-list.js';
|
|
3
|
+
const KIND_LABEL = {
|
|
4
|
+
empty: "nothing done with the error — it's silently swallowed",
|
|
5
|
+
'console-only': 'only logged, never handled — silently swallowed either way',
|
|
6
|
+
};
|
|
7
|
+
export function renderEmptyCatchReport(report, options = {}) {
|
|
8
|
+
const isClean = report.findings.length === 0;
|
|
9
|
+
if (options.quiet && isClean)
|
|
10
|
+
return '';
|
|
11
|
+
const lines = [];
|
|
12
|
+
if (!options.quiet && !options.plain)
|
|
13
|
+
lines.push(banner(), '');
|
|
14
|
+
lines.push(`Scanned ${report.filesScanned} file${report.filesScanned === 1 ? '' : 's'}.`, '');
|
|
15
|
+
if (isClean) {
|
|
16
|
+
lines.push(cleanCelebration(options));
|
|
17
|
+
return lines.join('\n');
|
|
18
|
+
}
|
|
19
|
+
lines.push(`${report.findings.length} problem${report.findings.length === 1 ? '' : 's'} found:`);
|
|
20
|
+
lines.push(...formatFindingsRows(report.findings.map((f) => ({
|
|
21
|
+
location: `${f.file}:${f.line}:${f.column}`,
|
|
22
|
+
name: f.kind,
|
|
23
|
+
tag: KIND_LABEL[f.kind] ?? f.kind,
|
|
24
|
+
})), options));
|
|
25
|
+
return lines.join('\n');
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../../src/commands/empty-catch/report.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAqB,MAAM,uBAAuB,CAAA;AACnF,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAA;AAGlE,MAAM,UAAU,GAA2B;IACzC,KAAK,EAAE,uDAAuD;IAC9D,cAAc,EAAE,4DAA4D;CAC7E,CAAA;AAED,MAAM,UAAU,sBAAsB,CACpC,MAAwB,EACxB,UAAwB,EAAE;IAE1B,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAA;IAC5C,IAAI,OAAO,CAAC,KAAK,IAAI,OAAO;QAAE,OAAO,EAAE,CAAA;IAEvC,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,CAAC,CAAA;IAE9D,KAAK,CAAC,IAAI,CAAC,WAAW,MAAM,CAAC,YAAY,QAAQ,MAAM,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,EAAE,EAAE,CAAC,CAAA;IAE7F,IAAI,OAAO,EAAE,CAAC;QACZ,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAA;QACrC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,WAAW,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAA;IAChG,KAAK,CAAC,IAAI,CACR,GAAG,kBAAkB,CACnB,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC1B,QAAQ,EAAE,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,EAAE;QAC3C,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI;KAClC,CAAC,CAAC,EACH,OAAO,CACR,CACF,CAAA;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type EmptyCatchFinding } from './core.js';
|
|
2
|
+
export interface EmptyCatchRunOptions {
|
|
3
|
+
paths: string[];
|
|
4
|
+
cwd: string;
|
|
5
|
+
extensions?: string[];
|
|
6
|
+
ignoreGlobs?: string[];
|
|
7
|
+
respectGitignore?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface EmptyCatchFileFinding extends EmptyCatchFinding {
|
|
10
|
+
file: string;
|
|
11
|
+
}
|
|
12
|
+
export interface EmptyCatchReport {
|
|
13
|
+
filesScanned: number;
|
|
14
|
+
findings: EmptyCatchFileFinding[];
|
|
15
|
+
exitCode: number;
|
|
16
|
+
}
|
|
17
|
+
export declare function runEmptyCatch(options: EmptyCatchRunOptions): EmptyCatchReport;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { relative } from 'node:path';
|
|
3
|
+
import { walk } from '../../utils/walk.js';
|
|
4
|
+
import { analyzeEmptyCatch } from './core.js';
|
|
5
|
+
import { analyzeEmptyCatchVue } from './vue.js';
|
|
6
|
+
const DEFAULT_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.cjs', '.mjs', '.vue'];
|
|
7
|
+
export function runEmptyCatch(options) {
|
|
8
|
+
const extensions = options.extensions ?? DEFAULT_EXTENSIONS;
|
|
9
|
+
const files = walk(options.paths, {
|
|
10
|
+
cwd: options.cwd,
|
|
11
|
+
extensions,
|
|
12
|
+
ignoreGlobs: options.ignoreGlobs ?? [],
|
|
13
|
+
...(options.respectGitignore !== undefined
|
|
14
|
+
? { respectGitignore: options.respectGitignore }
|
|
15
|
+
: {}),
|
|
16
|
+
});
|
|
17
|
+
const findings = [];
|
|
18
|
+
for (const file of files) {
|
|
19
|
+
const text = readFileSync(file, 'utf8');
|
|
20
|
+
const fileFindings = file.endsWith('.vue')
|
|
21
|
+
? analyzeEmptyCatchVue(text, file)
|
|
22
|
+
: analyzeEmptyCatch(text, file);
|
|
23
|
+
const relFile = relative(options.cwd, file).split('\\').join('/');
|
|
24
|
+
for (const f of fileFindings)
|
|
25
|
+
findings.push({ ...f, file: relFile });
|
|
26
|
+
}
|
|
27
|
+
findings.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line);
|
|
28
|
+
return {
|
|
29
|
+
filesScanned: files.length,
|
|
30
|
+
findings,
|
|
31
|
+
exitCode: findings.length > 0 ? 1 : 0,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/commands/empty-catch/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAC1C,OAAO,EAAE,iBAAiB,EAA0B,MAAM,WAAW,CAAA;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;AAE/C,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAoBjF,MAAM,UAAU,aAAa,CAAC,OAA6B;IACzD,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,kBAAkB,CAAA;IAC3D,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE;QAChC,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,UAAU;QACV,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,EAAE;QACtC,GAAG,CAAC,OAAO,CAAC,gBAAgB,KAAK,SAAS;YACxC,CAAC,CAAC,EAAE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,EAAE;YAChD,CAAC,CAAC,EAAE,CAAC;KACR,CAAC,CAAA;IAEF,MAAM,QAAQ,GAA4B,EAAE,CAAA;IAC5C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QACvC,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;YACxC,CAAC,CAAC,oBAAoB,CAAC,IAAI,EAAE,IAAI,CAAC;YAClC,CAAC,CAAC,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QACjC,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACjE,KAAK,MAAM,CAAC,IAAI,YAAY;YAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAA;IACtE,CAAC;IAED,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAA;IAExE,OAAO;QACL,YAAY,EAAE,KAAK,CAAC,MAAM;QAC1B,QAAQ;QACR,QAAQ,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KACtC,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { analyzeEmptyCatch } from './core.js';
|
|
2
|
+
const SCRIPT_RE = /(<script[^>]*>)([\s\S]*?)(<\/script>)/;
|
|
3
|
+
export function analyzeEmptyCatchVue(text, file) {
|
|
4
|
+
const scriptMatch = text.match(SCRIPT_RE);
|
|
5
|
+
if (!scriptMatch)
|
|
6
|
+
return [];
|
|
7
|
+
const [, , body] = scriptMatch;
|
|
8
|
+
const findings = analyzeEmptyCatch(body, file.replace(/\.vue$/, '.ts'));
|
|
9
|
+
// Finding line numbers are relative to the <script> body alone (that's
|
|
10
|
+
// all core.ts ever sees) — offset by however many lines come before it
|
|
11
|
+
// in the real file, so a report against the whole .vue file points at
|
|
12
|
+
// the right line.
|
|
13
|
+
const lineOffset = (text.slice(0, scriptMatch.index).match(/\n/g) ?? []).length;
|
|
14
|
+
return findings.map((f) => ({ ...f, line: f.line + lineOffset }));
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vue.js","sourceRoot":"","sources":["../../../src/commands/empty-catch/vue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAA0B,MAAM,WAAW,CAAA;AAErE,MAAM,SAAS,GAAG,uCAAuC,CAAA;AAEzD,MAAM,UAAU,oBAAoB,CAAC,IAAY,EAAE,IAAY;IAC7D,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;IACzC,IAAI,CAAC,WAAW;QAAE,OAAO,EAAE,CAAA;IAE3B,MAAM,CAAC,EAAE,AAAD,EAAG,IAAI,CAAC,GAAG,WAA+C,CAAA;IAClE,MAAM,QAAQ,GAAG,iBAAiB,CAAC,IAAc,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAA;IAEjF,uEAAuE;IACvE,uEAAuE;IACvE,sEAAsE;IACtE,kBAAkB;IAClB,MAAM,UAAU,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAA;IAC/E,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,GAAG,UAAU,EAAE,CAAC,CAAC,CAAA;AACnE,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface OrphanTestFinding {
|
|
2
|
+
file: string;
|
|
3
|
+
triedExtensions: string[];
|
|
4
|
+
}
|
|
5
|
+
export declare function parseTestFile(fileName: string, testSuffixes: string[]): {
|
|
6
|
+
base: string;
|
|
7
|
+
} | null;
|
|
8
|
+
export declare function hasMatchingSource(sourceBaseAbsPath: string, sourceExtensions: string[]): boolean;
|
|
9
|
+
export declare function candidateSourceDirs(testFileDir: string): string[];
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { basename, dirname, extname } from 'node:path';
|
|
3
|
+
// A test file's own name, stripped of its test suffix AND extension —
|
|
4
|
+
// `Foo.test.ts` with suffixes `['.test', '.spec']` becomes `Foo`. A file
|
|
5
|
+
// whose name doesn't end in any configured suffix isn't a test file this
|
|
6
|
+
// command recognizes at all (a plain `.ts` file that happened to get
|
|
7
|
+
// walked alongside real test files) and is silently skipped, not flagged.
|
|
8
|
+
export function parseTestFile(fileName, testSuffixes) {
|
|
9
|
+
const ext = extname(fileName);
|
|
10
|
+
if (!ext)
|
|
11
|
+
return null;
|
|
12
|
+
const withoutExt = fileName.slice(0, fileName.length - ext.length);
|
|
13
|
+
for (const suffix of testSuffixes) {
|
|
14
|
+
if (withoutExt.endsWith(suffix)) {
|
|
15
|
+
return { base: withoutExt.slice(0, withoutExt.length - suffix.length) };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
export function hasMatchingSource(sourceBaseAbsPath, sourceExtensions) {
|
|
21
|
+
return sourceExtensions.some((ext) => existsSync(`${sourceBaseAbsPath}${ext}`));
|
|
22
|
+
}
|
|
23
|
+
// Real dogfooding against this very workspace's own packages found that
|
|
24
|
+
// strict same-directory co-location (`Foo.test.ts` next to `Foo.ts`) is
|
|
25
|
+
// the MINORITY case — most instead put the test one level down, in a
|
|
26
|
+
// `__tests__`/`tests` sibling of the real source directory
|
|
27
|
+
// (`src/adapters/__tests__/http.test.ts` next to `src/adapters/http.ts`).
|
|
28
|
+
// Treating that as a second co-located candidate — not a `--source-dir`/
|
|
29
|
+
// `--test-dir` mirror, since it's still ONE tree, just with tests one
|
|
30
|
+
// level deeper — turns the default mode from "barely applicable to real
|
|
31
|
+
// projects" into the common case it should be, without a new flag: this
|
|
32
|
+
// container-name list is stable convention, not a per-project setting.
|
|
33
|
+
const TEST_CONTAINER_DIR_NAMES = new Set(['__tests__', 'tests', 'test', 'spec', 'specs']);
|
|
34
|
+
export function candidateSourceDirs(testFileDir) {
|
|
35
|
+
const dirs = [testFileDir];
|
|
36
|
+
const parent = dirname(testFileDir);
|
|
37
|
+
if (TEST_CONTAINER_DIR_NAMES.has(basename(testFileDir)) && parent !== testFileDir) {
|
|
38
|
+
dirs.push(parent);
|
|
39
|
+
}
|
|
40
|
+
return dirs;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../src/commands/orphan-tests/core.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAOtD,sEAAsE;AACtE,yEAAyE;AACzE,yEAAyE;AACzE,qEAAqE;AACrE,0EAA0E;AAC1E,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,YAAsB;IACpE,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC7B,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAA;IACrB,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAAA;IAClE,KAAK,MAAM,MAAM,IAAI,YAAY,EAAE,CAAC;QAClC,IAAI,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAChC,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAA;QACzE,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,iBAAyB,EAAE,gBAA0B;IACrF,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,iBAAiB,GAAG,GAAG,EAAE,CAAC,CAAC,CAAA;AACjF,CAAC;AAED,wEAAwE;AACxE,wEAAwE;AACxE,qEAAqE;AACrE,2DAA2D;AAC3D,0EAA0E;AAC1E,yEAAyE;AACzE,sEAAsE;AACtE,wEAAwE;AACxE,wEAAwE;AACxE,uEAAuE;AACvE,MAAM,wBAAwB,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;AAEzF,MAAM,UAAU,mBAAmB,CAAC,WAAmB;IACrD,MAAM,IAAI,GAAG,CAAC,WAAW,CAAC,CAAA;IAC1B,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;IACnC,IAAI,wBAAwB,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;QAClF,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IACnB,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC"}
|