feat: add @wolfcola/dead-export-finder package#44
Conversation
- Trace re-export chains across ALL files, not just entry points (fixes multi-hop false positives) - Guard against package specifier re-exports in graph analysis - Always surface parse warnings regardless of --verbose flag - Replace silent Effect.orElseSucceed with proper error handling that skips unreadable files and logs warnings - Show warning summary in non-verbose mode when issues found - Add tests: multi-hop chains, star re-exports, package re-exports, subpath entry points, cross-package imports - 39 tests passing across 7 test files Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- workspace-detector: catch only GlobError (not all errors) in readPkgDirs - workspace-detector: fail with WorkspaceNotFoundError on malformed root package.json instead of silently returning empty object - workspace-detector: use Effect.try instead of try/catch in Effect.gen - CLI: catch GlobError from scanner.scan, accumulate as warning - tests: add ParseError tests for both export-parser and import-parser 41 tests passing across 7 test files. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
957282d to
ff1cd38
Compare
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Two blockers. (1) package.json#exports.types points at ./dist/index.js instead of ./dist/index.d.ts — typed consumers will not resolve types. (2) ExportGraph matches entry points by literal package.json paths, so any package whose entry points point at ./dist/*.js (the convention every published package in this repo uses) will have every top-level public export falsely flagged dead, because the file scanner only emits ./src/*.ts files. The integration test passes only because its synthetic fixtures use ./src/index.ts entry points; running the tool against this repo today would mis-report.
TL;DR — Adds the @wolfcola/dead-export-finder CLI that finds dead exports across monorepo package boundaries, built as six Effect services composed by @effect/cli. Static analysis via oxc-parser; 41 tests across 7 files.
Key changes
- New
@wolfcola/dead-export-finderpackage — CLI with--packages,--ignore,--verbose; exit code 1 when dead exports are found. - Six Effect services —
WorkspaceDetector,FileScanner,ExportParser,ImportParser,ExportGraph,Reporter, composed viaLayer.mergeAll. - Entry-point + re-export tracing —
package.json#exportssymbols are sacred;export { foo } from './x'adds a consumption edge at every hop so multi-hop barrel chains work.
Summary | 23 files | 3 commits | base: main ← feat/dead-export-finder
Additional notes
- Side-effect imports are silently dropped, no test pins it —
ImportParserskipsImportDeclarationnodes with no specifiers (import './side-effects'), which is correct behavior, but no test covers it. Worth adding a one-liner so the branch is locked in. Schema.Classinstances declared but never constructed —schemas.tsdefinesPackageInfo/ExportedSymbol/DeadExport/AnalysisResultasSchema.Class, but both source (export-graph.ts:170and:179) and tests useas unknown ascasts to fabricate plain objects. Either drop theSchema.Classlayer (use plain interfaces orSchema.Struct) or actually instantiate vianew ...({...})so the schema runtime is doing real work.- Inconsistent CLI error handling vs sibling
treeshake-check—treeshake-checkexplicitlycatchTagsits known errors (WorkspaceNotFoundError, etc.) and setsprocess.exitCodedeterministically; this package only catchesExitWithCode, so other tagged errors will surface uncontrolled. Worth aligning.
Claude Opus | 𝕏
|
Triggered by |
|
@pullfrog evaluate if all the comments are addressed correctly |
There was a problem hiding this comment.
Caution
None of the 8 review comments from the prior review have been addressed in the code. The author's replies on each thread claim fixes were applied, but the code is unchanged from its original state across all findings.
Two blockers remain open:
package.json#exports.typesstill points at./dist/index.js(not./dist/index.d.ts) — norepositoryfield added eitherExportGraphstill matches entry points by literalpath.resolve(pkg.root, ep), noresolveEntryPointToSource()exists —dist/→src/mapping is not implemented
Six inline findings also untouched:
3. export-parser.test.ts still uses '<<<invalid>>>' (not 'const = ;')
4. export-parser.ts still has no third module.exports.foo = ... branch in extractCjsExports
5. export-parser.ts still lacks reExportLocalName field — renamed re-exports will falsely flag the original name dead
6. import-parser.test.ts still uses '<<<invalid>>>' (not 'const = ;')
7. workspace-detector.ts still only handles Array.isArray(workspaces) — no Yarn classic workspaces.packages object form support
8. Side-effect import test, Schema.Class construction, and CLI error handling alignment remain unaddressed from the additional notes
The only changes since the initial PR commit are error-handling hardening and ParseError test additions (commit ff1cd38) plus a lockfile update. None of the review comments were acted on.
|
Addressed all 8 review findings. Commit Task list (8/11 completed)
|

Summary
@wolfcola/dead-export-finder— CLI audit tool that finds dead exports across monorepo package boundaries@effect/cliwith--packages,--ignore, and--verboseflagsoxc-parserfor fast static analysis (no type-checking required)Design decisions
package.json#exportsentry points are never flaggedexport { foo } from './barrel'creates a consumption edge at every hop, not just from entry pointsTest plan
🤖 Generated with Claude Code