@wolfcola/dead-export-finder 0.0.0-beta-20260513233134 → 1.1.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/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +150 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -150
- package/package.json +2 -2
- package/dist/test-setup.d.ts +0 -2
- package/dist/test-setup.d.ts.map +0 -1
- package/dist/test-setup.js +0 -2
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command, Options } from '@effect/cli';
|
|
3
|
+
import { NodeContext, NodeRuntime } from '@effect/platform-node';
|
|
4
|
+
import { FileSystem } from '@effect/platform';
|
|
5
|
+
import { Console, Data, Effect, Layer, Array as Arr, Option, pipe } from 'effect';
|
|
6
|
+
import { WorkspaceDetector, WorkspaceDetectorLive } from './lib/workspace-detector.js';
|
|
7
|
+
import { FileScanner, FileScannerLive } from './lib/file-scanner.js';
|
|
8
|
+
import { ExportParser, ExportParserLive } from './lib/export-parser.js';
|
|
9
|
+
import { ImportParser, ImportParserLive } from './lib/import-parser.js';
|
|
10
|
+
import { ExportGraph, ExportGraphLive } from './lib/export-graph.js';
|
|
11
|
+
import { Reporter, ReporterLive } from './lib/reporter.js';
|
|
12
|
+
// ─── Exit code error ──────────────────────────────────────────────────────────
|
|
13
|
+
class ExitWithCode extends Data.TaggedError('ExitWithCode') {
|
|
14
|
+
}
|
|
15
|
+
// ─── Options ──────────────────────────────────────────────────────────────────
|
|
16
|
+
const packages = Options.text('packages').pipe(Options.withAlias('p'), Options.withDescription('Scope analysis to specific package names (repeat for multiple).'), Options.repeated, Options.optional);
|
|
17
|
+
const ignore = Options.text('ignore').pipe(Options.withAlias('i'), Options.withDescription('Glob patterns to exclude from scanning (repeat for multiple).'), Options.repeated, Options.optional);
|
|
18
|
+
const verbose = Options.boolean('verbose').pipe(Options.withAlias('v'), Options.withDescription('Print verbose output including timing and parse warnings.'), Options.withDefault(false));
|
|
19
|
+
// ─── Layer ────────────────────────────────────────────────────────────────────
|
|
20
|
+
const AppLayer = Layer.mergeAll(ExportParserLive, ImportParserLive, ExportGraphLive, ReporterLive, WorkspaceDetectorLive, FileScannerLive).pipe(Layer.provideMerge(NodeContext.layer));
|
|
21
|
+
const scanWorkspace = (workspace, ignoreGlobs, isVerbose) => Effect.gen(function* () {
|
|
22
|
+
const scanner = yield* FileScanner;
|
|
23
|
+
const results = yield* Effect.all(pipe(workspace.packages, Arr.map((pkg) => pipe(scanner.scan(pkg.root, ignoreGlobs), Effect.catchTag('GlobError', (e) => Effect.gen(function* () {
|
|
24
|
+
const msg = `failed to scan files in ${pkg.root}: ${String(e.cause)}`;
|
|
25
|
+
if (isVerbose)
|
|
26
|
+
yield* Console.log(`Warning: ${msg}`);
|
|
27
|
+
return { files: [], warning: msg };
|
|
28
|
+
})), Effect.map((result) => 'warning' in result
|
|
29
|
+
? result
|
|
30
|
+
: { files: result, warning: null }), Effect.map((r) => ({ pkg, files: r.files, warning: r.warning }))))));
|
|
31
|
+
return {
|
|
32
|
+
filesByPackage: pipe(results, Arr.map((r) => [r.pkg, r.files])),
|
|
33
|
+
warnings: pipe(results, Arr.filterMap((r) => (r.warning !== null ? Option.some(r.warning) : Option.none()))),
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
const parseAllFiles = (filesByPackage, isVerbose) => Effect.gen(function* () {
|
|
37
|
+
const fs = yield* FileSystem.FileSystem;
|
|
38
|
+
const exportParser = yield* ExportParser;
|
|
39
|
+
const importParser = yield* ImportParser;
|
|
40
|
+
const allFiles = pipe(filesByPackage, Arr.flatMap(([, files]) => files));
|
|
41
|
+
const fileResults = yield* Effect.all(pipe(allFiles, Arr.map((filePath) => pipe(fs.readFileString(filePath, 'utf-8'), Effect.either, Effect.flatMap((sourceResult) => {
|
|
42
|
+
if (sourceResult._tag === 'Left') {
|
|
43
|
+
const msg = `could not read ${filePath}: ${String(sourceResult.left)}`;
|
|
44
|
+
return isVerbose
|
|
45
|
+
? pipe(Console.log(`Warning: ${msg}`), Effect.map(() => ({
|
|
46
|
+
filePath,
|
|
47
|
+
exports: [],
|
|
48
|
+
imports: [],
|
|
49
|
+
warning: msg,
|
|
50
|
+
})))
|
|
51
|
+
: Effect.succeed({
|
|
52
|
+
filePath,
|
|
53
|
+
exports: [],
|
|
54
|
+
imports: [],
|
|
55
|
+
warning: msg,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
const source = sourceResult.right;
|
|
59
|
+
const parseExports = pipe(exportParser.parse(filePath, source), Effect.map((symbols) => ({
|
|
60
|
+
symbols,
|
|
61
|
+
warning: null,
|
|
62
|
+
})), Effect.catchTag('ParseError', (e) => {
|
|
63
|
+
const msg = `failed to parse exports in ${e.filePath}: ${e.message}`;
|
|
64
|
+
const result = { symbols: [], warning: msg };
|
|
65
|
+
return isVerbose
|
|
66
|
+
? pipe(Console.log(`Warning: ${msg}`), Effect.map(() => result))
|
|
67
|
+
: Effect.succeed(result);
|
|
68
|
+
}));
|
|
69
|
+
const parseImports = pipe(importParser.parse(filePath, source), Effect.map((symbols) => ({
|
|
70
|
+
symbols,
|
|
71
|
+
warning: null,
|
|
72
|
+
})), Effect.catchTag('ParseError', (e) => {
|
|
73
|
+
const msg = `failed to parse imports in ${e.filePath}: ${e.message}`;
|
|
74
|
+
const result = { symbols: [], warning: msg };
|
|
75
|
+
return isVerbose
|
|
76
|
+
? pipe(Console.log(`Warning: ${msg}`), Effect.map(() => result))
|
|
77
|
+
: Effect.succeed(result);
|
|
78
|
+
}));
|
|
79
|
+
return pipe(Effect.all({ exports: parseExports, imports: parseImports }), Effect.map(({ exports: exp, imports: imp }) => ({
|
|
80
|
+
filePath,
|
|
81
|
+
exports: exp.symbols,
|
|
82
|
+
imports: imp.symbols,
|
|
83
|
+
warning: exp.warning ?? imp.warning,
|
|
84
|
+
})));
|
|
85
|
+
})))));
|
|
86
|
+
const exportEntries = pipe(fileResults, Arr.filter((r) => r.exports.length > 0), Arr.map((r) => [r.filePath, r.exports]));
|
|
87
|
+
const importEntries = pipe(fileResults, Arr.filter((r) => r.imports.length > 0), Arr.map((r) => [r.filePath, r.imports]));
|
|
88
|
+
const warnings = pipe(fileResults, Arr.filterMap((r) => (r.warning !== null ? Option.some(r.warning) : Option.none())));
|
|
89
|
+
return {
|
|
90
|
+
allExports: new Map(exportEntries),
|
|
91
|
+
allImports: new Map(importEntries),
|
|
92
|
+
warnings,
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
const analyzeAndReport = (targetPackages, allPackages, allExports, allImports) => Effect.gen(function* () {
|
|
96
|
+
const graph = yield* ExportGraph;
|
|
97
|
+
const reporter = yield* Reporter;
|
|
98
|
+
const result = yield* graph.analyze(targetPackages, allExports, allImports);
|
|
99
|
+
const packageRoots = new Map(pipe(allPackages, Arr.map((p) => [p.name, p.root])));
|
|
100
|
+
const report = reporter.format(result, packageRoots);
|
|
101
|
+
yield* Console.log(report);
|
|
102
|
+
return { deadCount: result.deadExports.length, warnings: result.warnings };
|
|
103
|
+
});
|
|
104
|
+
// ─── Command ──────────────────────────────────────────────────────────────────
|
|
105
|
+
const command = Command.make('dead-export-finder', { packages, ignore, verbose }, ({ packages: packagesOpt, ignore: ignoreOpt, verbose: isVerbose }) => Effect.gen(function* () {
|
|
106
|
+
const startTime = Date.now();
|
|
107
|
+
const detector = yield* WorkspaceDetector;
|
|
108
|
+
const cwd = process.cwd();
|
|
109
|
+
const workspace = yield* detector.detect(cwd);
|
|
110
|
+
if (isVerbose) {
|
|
111
|
+
yield* Console.log(`Detected workspace type: ${workspace.type}`);
|
|
112
|
+
yield* Console.log(`Found ${workspace.packages.length} packages`);
|
|
113
|
+
}
|
|
114
|
+
const packageFilter = packagesOpt._tag === 'Some' ? new Set(packagesOpt.value) : null;
|
|
115
|
+
const targetPackages = packageFilter !== null
|
|
116
|
+
? pipe(workspace.packages, Arr.filter((p) => packageFilter.has(p.name)))
|
|
117
|
+
: [...workspace.packages];
|
|
118
|
+
const ignoreGlobs = ignoreOpt._tag === 'Some' ? ignoreOpt.value : [];
|
|
119
|
+
if (isVerbose && packageFilter !== null && targetPackages.length > 0) {
|
|
120
|
+
yield* Console.log(`Scoping to packages: ${pipe(targetPackages, Arr.map((p) => p.name), Arr.join(', '))}`);
|
|
121
|
+
}
|
|
122
|
+
const scanResult = yield* scanWorkspace(workspace, ignoreGlobs, isVerbose);
|
|
123
|
+
const parseResult = yield* parseAllFiles(scanResult.filesByPackage, isVerbose);
|
|
124
|
+
if (isVerbose) {
|
|
125
|
+
yield* Console.log(`Scanned ${parseResult.allExports.size} files with exports, ${parseResult.allImports.size} files with imports`);
|
|
126
|
+
}
|
|
127
|
+
const { deadCount, warnings: analysisWarnings } = yield* analyzeAndReport(targetPackages, [...workspace.packages], parseResult.allExports, parseResult.allImports);
|
|
128
|
+
const allWarnings = pipe(scanResult.warnings, Arr.appendAll(parseResult.warnings), Arr.appendAll(analysisWarnings));
|
|
129
|
+
if (allWarnings.length > 0 && !isVerbose) {
|
|
130
|
+
yield* Console.log(`\nWarning: ${allWarnings.length} issue(s) during analysis — results may be incomplete. Run with --verbose for details.`);
|
|
131
|
+
}
|
|
132
|
+
if (isVerbose) {
|
|
133
|
+
const elapsed = Date.now() - startTime;
|
|
134
|
+
yield* Console.log(`\nCompleted in ${elapsed}ms`);
|
|
135
|
+
}
|
|
136
|
+
if (deadCount > 0) {
|
|
137
|
+
return yield* new ExitWithCode({ code: 1 });
|
|
138
|
+
}
|
|
139
|
+
})).pipe(Command.withDescription('Find dead exports across monorepo package boundaries.'));
|
|
140
|
+
// ─── Runner ───────────────────────────────────────────────────────────────────
|
|
141
|
+
const cli = Command.run(command, {
|
|
142
|
+
name: 'Dead Export Finder',
|
|
143
|
+
version: '0.0.0',
|
|
144
|
+
});
|
|
145
|
+
cli(process.argv).pipe(Effect.catchTags({
|
|
146
|
+
ExitWithCode: (e) => Effect.sync(() => (process.exitCode = e.code)),
|
|
147
|
+
WorkspaceNotFoundError: (e) => Console.error(`error: workspace not found at ${e.cwd}`).pipe(Effect.zipRight(Effect.sync(() => {
|
|
148
|
+
process.exitCode = 1;
|
|
149
|
+
}))),
|
|
150
|
+
}), Effect.provide(AppLayer), NodeRuntime.runMain);
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACvF,YAAY,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAChG,YAAY,EACV,WAAW,EACX,cAAc,EACd,cAAc,EACd,UAAU,EACV,cAAc,GACf,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
// Public API
|
|
3
2
|
export { WorkspaceDetector, WorkspaceDetectorLive } from './lib/workspace-detector.js';
|
|
4
3
|
export { FileScanner, FileScannerLive } from './lib/file-scanner.js';
|
|
@@ -7,152 +6,3 @@ export { ImportParser, ImportParserLive } from './lib/import-parser.js';
|
|
|
7
6
|
export { ExportGraph, ExportGraphLive } from './lib/export-graph.js';
|
|
8
7
|
export { Reporter, ReporterLive } from './lib/reporter.js';
|
|
9
8
|
export { WorkspaceNotFoundError, ParseError, EntryPointResolutionError } from './lib/errors.js';
|
|
10
|
-
import { Command, Options } from '@effect/cli';
|
|
11
|
-
import { NodeContext, NodeRuntime } from '@effect/platform-node';
|
|
12
|
-
import { FileSystem } from '@effect/platform';
|
|
13
|
-
import { Console, Data, Effect, Layer, Array as Arr, Option, pipe } from 'effect';
|
|
14
|
-
import { WorkspaceDetector, WorkspaceDetectorLive } from './lib/workspace-detector.js';
|
|
15
|
-
import { FileScanner, FileScannerLive } from './lib/file-scanner.js';
|
|
16
|
-
import { ExportParser, ExportParserLive } from './lib/export-parser.js';
|
|
17
|
-
import { ImportParser, ImportParserLive } from './lib/import-parser.js';
|
|
18
|
-
import { ExportGraph, ExportGraphLive } from './lib/export-graph.js';
|
|
19
|
-
import { Reporter, ReporterLive } from './lib/reporter.js';
|
|
20
|
-
// ─── Exit code error ──────────────────────────────────────────────────────────
|
|
21
|
-
class ExitWithCode extends Data.TaggedError('ExitWithCode') {
|
|
22
|
-
}
|
|
23
|
-
// ─── Options ──────────────────────────────────────────────────────────────────
|
|
24
|
-
const packages = Options.text('packages').pipe(Options.withAlias('p'), Options.withDescription('Scope analysis to specific package names (repeat for multiple).'), Options.repeated, Options.optional);
|
|
25
|
-
const ignore = Options.text('ignore').pipe(Options.withAlias('i'), Options.withDescription('Glob patterns to exclude from scanning (repeat for multiple).'), Options.repeated, Options.optional);
|
|
26
|
-
const verbose = Options.boolean('verbose').pipe(Options.withAlias('v'), Options.withDescription('Print verbose output including timing and parse warnings.'), Options.withDefault(false));
|
|
27
|
-
// ─── Layer ────────────────────────────────────────────────────────────────────
|
|
28
|
-
const AppLayer = Layer.mergeAll(ExportParserLive, ImportParserLive, ExportGraphLive, ReporterLive, WorkspaceDetectorLive, FileScannerLive).pipe(Layer.provideMerge(NodeContext.layer));
|
|
29
|
-
const scanWorkspace = (workspace, ignoreGlobs, isVerbose) => Effect.gen(function* () {
|
|
30
|
-
const scanner = yield* FileScanner;
|
|
31
|
-
const results = yield* Effect.all(pipe(workspace.packages, Arr.map((pkg) => pipe(scanner.scan(pkg.root, ignoreGlobs), Effect.catchTag('GlobError', (e) => Effect.gen(function* () {
|
|
32
|
-
const msg = `failed to scan files in ${pkg.root}: ${String(e.cause)}`;
|
|
33
|
-
if (isVerbose)
|
|
34
|
-
yield* Console.log(`Warning: ${msg}`);
|
|
35
|
-
return { files: [], warning: msg };
|
|
36
|
-
})), Effect.map((result) => 'warning' in result
|
|
37
|
-
? result
|
|
38
|
-
: { files: result, warning: null }), Effect.map((r) => ({ pkg, files: r.files, warning: r.warning }))))));
|
|
39
|
-
return {
|
|
40
|
-
filesByPackage: pipe(results, Arr.map((r) => [r.pkg, r.files])),
|
|
41
|
-
warnings: pipe(results, Arr.filterMap((r) => (r.warning !== null ? Option.some(r.warning) : Option.none()))),
|
|
42
|
-
};
|
|
43
|
-
});
|
|
44
|
-
const parseAllFiles = (filesByPackage, isVerbose) => Effect.gen(function* () {
|
|
45
|
-
const fs = yield* FileSystem.FileSystem;
|
|
46
|
-
const exportParser = yield* ExportParser;
|
|
47
|
-
const importParser = yield* ImportParser;
|
|
48
|
-
const allFiles = pipe(filesByPackage, Arr.flatMap(([, files]) => files));
|
|
49
|
-
const fileResults = yield* Effect.all(pipe(allFiles, Arr.map((filePath) => pipe(fs.readFileString(filePath, 'utf-8'), Effect.either, Effect.flatMap((sourceResult) => {
|
|
50
|
-
if (sourceResult._tag === 'Left') {
|
|
51
|
-
const msg = `could not read ${filePath}: ${String(sourceResult.left)}`;
|
|
52
|
-
return isVerbose
|
|
53
|
-
? pipe(Console.log(`Warning: ${msg}`), Effect.map(() => ({
|
|
54
|
-
filePath,
|
|
55
|
-
exports: [],
|
|
56
|
-
imports: [],
|
|
57
|
-
warning: msg,
|
|
58
|
-
})))
|
|
59
|
-
: Effect.succeed({
|
|
60
|
-
filePath,
|
|
61
|
-
exports: [],
|
|
62
|
-
imports: [],
|
|
63
|
-
warning: msg,
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
const source = sourceResult.right;
|
|
67
|
-
const parseExports = pipe(exportParser.parse(filePath, source), Effect.map((symbols) => ({
|
|
68
|
-
symbols,
|
|
69
|
-
warning: null,
|
|
70
|
-
})), Effect.catchTag('ParseError', (e) => {
|
|
71
|
-
const msg = `failed to parse exports in ${e.filePath}: ${e.message}`;
|
|
72
|
-
const result = { symbols: [], warning: msg };
|
|
73
|
-
return isVerbose
|
|
74
|
-
? pipe(Console.log(`Warning: ${msg}`), Effect.map(() => result))
|
|
75
|
-
: Effect.succeed(result);
|
|
76
|
-
}));
|
|
77
|
-
const parseImports = pipe(importParser.parse(filePath, source), Effect.map((symbols) => ({
|
|
78
|
-
symbols,
|
|
79
|
-
warning: null,
|
|
80
|
-
})), Effect.catchTag('ParseError', (e) => {
|
|
81
|
-
const msg = `failed to parse imports in ${e.filePath}: ${e.message}`;
|
|
82
|
-
const result = { symbols: [], warning: msg };
|
|
83
|
-
return isVerbose
|
|
84
|
-
? pipe(Console.log(`Warning: ${msg}`), Effect.map(() => result))
|
|
85
|
-
: Effect.succeed(result);
|
|
86
|
-
}));
|
|
87
|
-
return pipe(Effect.all({ exports: parseExports, imports: parseImports }), Effect.map(({ exports: exp, imports: imp }) => ({
|
|
88
|
-
filePath,
|
|
89
|
-
exports: exp.symbols,
|
|
90
|
-
imports: imp.symbols,
|
|
91
|
-
warning: exp.warning ?? imp.warning,
|
|
92
|
-
})));
|
|
93
|
-
})))));
|
|
94
|
-
const exportEntries = pipe(fileResults, Arr.filter((r) => r.exports.length > 0), Arr.map((r) => [r.filePath, r.exports]));
|
|
95
|
-
const importEntries = pipe(fileResults, Arr.filter((r) => r.imports.length > 0), Arr.map((r) => [r.filePath, r.imports]));
|
|
96
|
-
const warnings = pipe(fileResults, Arr.filterMap((r) => (r.warning !== null ? Option.some(r.warning) : Option.none())));
|
|
97
|
-
return {
|
|
98
|
-
allExports: new Map(exportEntries),
|
|
99
|
-
allImports: new Map(importEntries),
|
|
100
|
-
warnings,
|
|
101
|
-
};
|
|
102
|
-
});
|
|
103
|
-
const analyzeAndReport = (targetPackages, allPackages, allExports, allImports) => Effect.gen(function* () {
|
|
104
|
-
const graph = yield* ExportGraph;
|
|
105
|
-
const reporter = yield* Reporter;
|
|
106
|
-
const result = yield* graph.analyze(targetPackages, allExports, allImports);
|
|
107
|
-
const packageRoots = new Map(pipe(allPackages, Arr.map((p) => [p.name, p.root])));
|
|
108
|
-
const report = reporter.format(result, packageRoots);
|
|
109
|
-
yield* Console.log(report);
|
|
110
|
-
return { deadCount: result.deadExports.length, warnings: result.warnings };
|
|
111
|
-
});
|
|
112
|
-
// ─── Command ──────────────────────────────────────────────────────────────────
|
|
113
|
-
const command = Command.make('dead-export-finder', { packages, ignore, verbose }, ({ packages: packagesOpt, ignore: ignoreOpt, verbose: isVerbose }) => Effect.gen(function* () {
|
|
114
|
-
const startTime = Date.now();
|
|
115
|
-
const detector = yield* WorkspaceDetector;
|
|
116
|
-
const cwd = process.cwd();
|
|
117
|
-
const workspace = yield* detector.detect(cwd);
|
|
118
|
-
if (isVerbose) {
|
|
119
|
-
yield* Console.log(`Detected workspace type: ${workspace.type}`);
|
|
120
|
-
yield* Console.log(`Found ${workspace.packages.length} packages`);
|
|
121
|
-
}
|
|
122
|
-
const packageFilter = packagesOpt._tag === 'Some' ? new Set(packagesOpt.value) : null;
|
|
123
|
-
const targetPackages = packageFilter !== null
|
|
124
|
-
? pipe(workspace.packages, Arr.filter((p) => packageFilter.has(p.name)))
|
|
125
|
-
: [...workspace.packages];
|
|
126
|
-
const ignoreGlobs = ignoreOpt._tag === 'Some' ? ignoreOpt.value : [];
|
|
127
|
-
if (isVerbose && packageFilter !== null && targetPackages.length > 0) {
|
|
128
|
-
yield* Console.log(`Scoping to packages: ${pipe(targetPackages, Arr.map((p) => p.name), Arr.join(', '))}`);
|
|
129
|
-
}
|
|
130
|
-
const scanResult = yield* scanWorkspace(workspace, ignoreGlobs, isVerbose);
|
|
131
|
-
const parseResult = yield* parseAllFiles(scanResult.filesByPackage, isVerbose);
|
|
132
|
-
if (isVerbose) {
|
|
133
|
-
yield* Console.log(`Scanned ${parseResult.allExports.size} files with exports, ${parseResult.allImports.size} files with imports`);
|
|
134
|
-
}
|
|
135
|
-
const { deadCount, warnings: analysisWarnings } = yield* analyzeAndReport(targetPackages, [...workspace.packages], parseResult.allExports, parseResult.allImports);
|
|
136
|
-
const allWarnings = pipe(scanResult.warnings, Arr.appendAll(parseResult.warnings), Arr.appendAll(analysisWarnings));
|
|
137
|
-
if (allWarnings.length > 0 && !isVerbose) {
|
|
138
|
-
yield* Console.log(`\nWarning: ${allWarnings.length} issue(s) during analysis — results may be incomplete. Run with --verbose for details.`);
|
|
139
|
-
}
|
|
140
|
-
if (isVerbose) {
|
|
141
|
-
const elapsed = Date.now() - startTime;
|
|
142
|
-
yield* Console.log(`\nCompleted in ${elapsed}ms`);
|
|
143
|
-
}
|
|
144
|
-
if (deadCount > 0) {
|
|
145
|
-
return yield* new ExitWithCode({ code: 1 });
|
|
146
|
-
}
|
|
147
|
-
})).pipe(Command.withDescription('Find dead exports across monorepo package boundaries.'));
|
|
148
|
-
// ─── Runner ───────────────────────────────────────────────────────────────────
|
|
149
|
-
const cli = Command.run(command, {
|
|
150
|
-
name: 'Dead Export Finder',
|
|
151
|
-
version: '0.0.0',
|
|
152
|
-
});
|
|
153
|
-
cli(process.argv).pipe(Effect.catchTags({
|
|
154
|
-
ExitWithCode: (e) => Effect.sync(() => (process.exitCode = e.code)),
|
|
155
|
-
WorkspaceNotFoundError: (e) => Console.error(`error: workspace not found at ${e.cwd}`).pipe(Effect.zipRight(Effect.sync(() => {
|
|
156
|
-
process.exitCode = 1;
|
|
157
|
-
}))),
|
|
158
|
-
}), Effect.provide(AppLayer), NodeRuntime.runMain);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wolfcola/dead-export-finder",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "Find dead exports across monorepo package boundaries",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"main": "./dist/index.js",
|
|
14
14
|
"types": "./dist/index.d.ts",
|
|
15
15
|
"bin": {
|
|
16
|
-
"dead-export-finder": "./dist/
|
|
16
|
+
"dead-export-finder": "./dist/cli.js"
|
|
17
17
|
},
|
|
18
18
|
"exports": {
|
|
19
19
|
".": {
|
package/dist/test-setup.d.ts
DELETED
package/dist/test-setup.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"test-setup.d.ts","sourceRoot":"","sources":["../src/test-setup.ts"],"names":[],"mappings":""}
|
package/dist/test-setup.js
DELETED