Skip to content

Duplicate Dependency Detection - Missing Parent Package Tracking #165

@dreyfus92

Description

@dreyfus92

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions