-
-
Notifications
You must be signed in to change notification settings - Fork 8
Description
After battle testing the cli with storybook's monorepo, I found that we need to improve the duplicate deps detection.
This affects users while debugging dependency issues, especially in monorepos.
Many duplicate dependencies show "via the following 0 package(s)" instead of listing the actual parent packages that depend on them. This makes it difficult for users to understand which packages are causing duplicate dependencies and how to resolve them.
When running e18e analyze on projects (especially monorepos), duplicate dependency messages often show zero parent packages:
❌ Broken Example:
[duplicate dependency] @angular-devkit/build-angular has 2 installed versions:
│ via the following 0 package(s)
│ 19.2.19 via the following 0 package(s)
✅ Working Example (for reference):
Some dependencies do show parents correctly:
[duplicate dependency] jsonc-parser has 2 installed versions:
│ 3.3.1 via the following 0 package(s)
│ 3.2.0 via the following 1 package(s) nx@22.1.3
[duplicate dependency] tslib has 3 installed versions:
│ 2.8.1 via the following 0 package(s)
│ via the following 8 package(s) @nx/devkit@22.1.3, @emnapi/wasi-threads@1.1.0, ...
All duplicate dependencies should show their parent packages (the packages that directly depend on them), even when:
- Dependencies are hoisted to the root
node_modules - Dependencies appear in the lockfile but aren't directly referenced in a
package.json - Dependencies are nested deep in the dependency tree
- Dependencies are root-level dependencies
Expected Output:
[duplicate dependency] @angular-devkit/build-angular has 2 installed versions:
│ 19.2.18 via the following 2 package(s) @storybook/angular@8.0.0, @storybook/builder-webpack5@8.0.0
│ 19.2.19 via the following 1 package(s) @storybook/core-server@8.0.0
The root cause is that computeParents function in src/analyze/duplicate-dependencies.ts uses lockparse's traverse function to walk the dependency tree, but there are several issues:
Issue 1: Early Return Condition
Location: src/analyze/duplicate-dependencies.ts:76
const visitorFn: VisitorFn = (node, parent, _path) => {
if (!duplicateDependencies.has(node.name) || !parent) {
return; // ❌ Skips nodes without parent or not in map
}
// ... rest of logic
};Problem: This condition causes the function to skip nodes that:
- ❌ Are not in the
duplicateDependenciesmap (but should be tracked) - ❌ Don't have a parent (root-level dependencies)
Issue 2: Traversal Limitations
The lockparse traverse function may not capture all parent relationships correctly, especially for:
| Scenario | Impact |
|---|---|
| Hoisted dependencies | Dependencies moved to root node_modules lose parent context |
| Unreferenced dependencies | Dependencies in lockfile but not in package.json aren't tracked |
| Monorepo edge cases | Complex workspace structures confuse traversal |
Issue 3: Missing Reverse Dependency Map
The current implementation relies solely on forward traversal. A reverse dependency map (package@version → Set<parent packages>) would provide a more reliable way to track parent relationships.