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
duplicateDependencies map (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.
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 analyzeon projects (especially monorepos), duplicate dependency messages often show zero parent packages:❌ Broken Example:
✅ Working Example (for reference):
Some dependencies do show parents correctly:
All duplicate dependencies should show their parent packages (the packages that directly depend on them), even when:
node_modulespackage.jsonExpected Output:
The root cause is that
computeParentsfunction insrc/analyze/duplicate-dependencies.tsuseslockparse'straversefunction to walk the dependency tree, but there are several issues:Issue 1: Early Return Condition
Location:
src/analyze/duplicate-dependencies.ts:76Problem: This condition causes the function to skip nodes that:
duplicateDependenciesmap (but should be tracked)Issue 2: Traversal Limitations
The
lockparsetraversefunction may not capture all parent relationships correctly, especially for:node_moduleslose parent contextpackage.jsonaren't trackedIssue 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.