[Disclaimer] This issue was partially created by AI, and audited and verified by me (If this is not appreciated I am happy to remove the issue)
I am also working on a fix for the issue, just want to make sure this is not something out of scope for the project.
missingNodeModulesDetection doesn't work with pnpm strict node_modules
Problem
missingNodeModulesDetection flags every workspace-specific dependency as "missing" in a pnpm monorepo.
pnpm uses a strict node_modules layout by default: workspace dependencies are only symlinked into each workspace's own node_modules/, not the root. rev-dep's missingNodeModulesDetection only checks the CWD resolver's nodeModules map, so any dependency declared in a workspace package.json (but absent from the root node_modules/) gets flagged as missing.
In a monorepo with 100+ workspaces this produces hundreds of false positives, making the check unusable.
orphanFilesDetection has a related issue: workspace src/index.ts files appear orphaned because they're only reachable via cross-package imports that the orphan analysis doesn't trace through.
Reproduction
pnpm monorepo, strict node_modules (default pnpm behavior):
my-monorepo/
package.json # root — no @ai-sdk/cerebras here
node_modules/ # only root-level deps hoisted here
packages/
llm-clients/
package.json # "@ai-sdk/cerebras": "^4.0.0"
node_modules/
@ai-sdk/cerebras/ # symlinked by pnpm ✓
src/
index.ts # import { ... } from '@ai-sdk/cerebras'
Config (single rule or per-workspace — same result):
Result:
❌ Missing Node Modules Issues (1):
- @ai-sdk/cerebras (imported from: packages/llm-clients/src/index.ts)
Expected: no issues — the package is declared and installed in the workspace.
Workarounds tested (none worked)
- Per-workspace rule with
path: "packages/llm-clients" — same false positive
--package-json packages/llm-clients/package.json CLI flag — same
- Direct version
"^4.0.0" instead of pnpm "catalog:" — same
Root cause
In nodeModules.go (~lines 168-174), GetMissingNodeModulesFromTree receives only the CWD resolver's node modules:
resolverForCwd := resolverManager.GetResolverForFile(cwd)
cwdNodeModules := resolverForCwd.nodeModules
// ...
GetMissingNodeModulesFromTree(tree, cwdNodeModules, ...)
The CWD resolver points at the root package.json, so only root-level dependencies are in cwdNodeModules. Workspace dependencies are held by their own resolvers but never consulted.
The monorepo infrastructure already handles this — ResolverManager creates a resolver per workspace and CollectAllNodeModules() (lines 288-306) aggregates them. It's just not called here.
Proposed fix
missingNodeModulesDetection (small change)
Replace the CWD-only lookup with the existing aggregation method:
// Before:
resolverForCwd := resolverManager.GetResolverForFile(cwd)
cwdNodeModules = resolverForCwd.nodeModules
// After:
cwdNodeModules := resolverManager.CollectAllNodeModules()
CollectAllNodeModules() already iterates all resolvers (root + workspace packages) and merges their nodeModules maps. This single change should fix the false positives.
A more precise alternative: for each flagged import, look up the resolver for the importing file (GetResolverForFile(importingFile)) and check that resolver's nodeModules. This would be per-file accurate rather than a global merge, but the global merge is simpler and sufficient since a "missing" module that exists in any workspace resolver is clearly installed.
orphanFilesDetection (medium change)
When followMonorepoPackages is enabled, automatically treat each workspace's entry points (derived from package.json exports/main fields) as valid entry points for the orphan analysis. This would require:
- In
MonorepoContext.FindWorkspacePackages() — extract entry points from each workspace's package.json
- Pass these as additional
validEntryPoints to FindOrphanFiles()
- Estimated 2-3 functions to modify
Environment
- rev-dep: 2.9.1
- pnpm: 10.x (strict node_modules layout)
- Config version: 1.5
[Disclaimer] This issue was partially created by AI, and audited and verified by me (If this is not appreciated I am happy to remove the issue)
I am also working on a fix for the issue, just want to make sure this is not something out of scope for the project.
missingNodeModulesDetection doesn't work with pnpm strict node_modules
Problem
missingNodeModulesDetectionflags every workspace-specific dependency as "missing" in a pnpm monorepo.pnpm uses a strict
node_moduleslayout by default: workspace dependencies are only symlinked into each workspace's ownnode_modules/, not the root. rev-dep'smissingNodeModulesDetectiononly checks the CWD resolver'snodeModulesmap, so any dependency declared in a workspacepackage.json(but absent from the rootnode_modules/) gets flagged as missing.In a monorepo with 100+ workspaces this produces hundreds of false positives, making the check unusable.
orphanFilesDetectionhas a related issue: workspacesrc/index.tsfiles appear orphaned because they're only reachable via cross-package imports that the orphan analysis doesn't trace through.Reproduction
pnpm monorepo, strict
node_modules(default pnpm behavior):Config (single rule or per-workspace — same result):
{ "configVersion": "1.5", "rules": [ { "path": "packages/llm-clients", "followMonorepoPackages": true, "missingNodeModulesDetection": { "enabled": true } } ] }Result:
Expected: no issues — the package is declared and installed in the workspace.
Workarounds tested (none worked)
path: "packages/llm-clients"— same false positive--package-json packages/llm-clients/package.jsonCLI flag — same"^4.0.0"instead of pnpm"catalog:"— sameRoot cause
In
nodeModules.go(~lines 168-174),GetMissingNodeModulesFromTreereceives only the CWD resolver's node modules:The CWD resolver points at the root
package.json, so only root-level dependencies are incwdNodeModules. Workspace dependencies are held by their own resolvers but never consulted.The monorepo infrastructure already handles this —
ResolverManagercreates a resolver per workspace andCollectAllNodeModules()(lines 288-306) aggregates them. It's just not called here.Proposed fix
missingNodeModulesDetection (small change)
Replace the CWD-only lookup with the existing aggregation method:
CollectAllNodeModules()already iterates all resolvers (root + workspace packages) and merges theirnodeModulesmaps. This single change should fix the false positives.A more precise alternative: for each flagged import, look up the resolver for the importing file (
GetResolverForFile(importingFile)) and check that resolver'snodeModules. This would be per-file accurate rather than a global merge, but the global merge is simpler and sufficient since a "missing" module that exists in any workspace resolver is clearly installed.orphanFilesDetection (medium change)
When
followMonorepoPackagesis enabled, automatically treat each workspace's entry points (derived frompackage.jsonexports/mainfields) as valid entry points for the orphan analysis. This would require:MonorepoContext.FindWorkspacePackages()— extract entry points from each workspace'spackage.jsonvalidEntryPointstoFindOrphanFiles()Environment