Skip to content

package.json dependencies produce two unlinked nodes with the same label #4569

Open

Description

@BiSide

Summary

For each entry under dependencies / devDependencies / peerDependencies, extractors/json_config.py emits two nodes with the same label:

  • the generic key node <stem>_dependencies_react, created by walk_object and reached through dependencies --contains-->
  • the external ref node ref_react, created by the dependency branch and reached through package.json --imports-->

No edge connects them. Source files that import the package (import React from "react") resolve to ref_react, so the manifest's own entry for the dependency is cut off from the code that uses it.

Reproduction (graphifyy 0.9.84; json_config.py is identical in 0.9.92)

package.json:

{
  "name": "demo",
  "dependencies": {
    "react": "^19.0.0"
  }
}
from pathlib import Path
from graphify.extractors.json_config import extract_json
r = extract_json(Path("package.json"))

Nodes (stem shortened):

<stem>_package_json            'package.json'  code
<stem>_package_name            'name'          code
<stem>_package_dependencies    'dependencies'  code
<stem>_package_dependencies_react  'react'     code     <- key node
ref_react                      'react'         concept  <- ref node

Edges:

<stem>_package_json         -contains-> <stem>_package_name
<stem>_package_json         -contains-> <stem>_package_dependencies
<stem>_package_dependencies -contains-> <stem>_package_dependencies_react
<stem>_package_json         -imports->  ref_react

Neither react node has an edge to the other.

Impact on a real graph

On a small Vite + React project, community detection put the two react nodes (and the two react-dom nodes) in different communities: the key nodes went to "package manifest" and the ref nodes to "SPA entry" with main.tsx. Each @types/* devDependency also got a near-isolated key node, so suggest_questions lists them as weakly connected ("possible documentation gaps"). Cross-community bridge scores for devDependencies / index.html are inflated by the split.

Cause

The #1764 fix (comment at the parent_key in _DEP_KEYS branch) moved the imports edge source from key_nid to file_nid, to avoid the self-referential-looking react --imports--> react. That edge was the only link between the key node and the ref node, so they are now disconnected.

Suggested fix

Link the two nodes again, with a relation that does not read as a self-import:

dep_nid = _make_id("ref", key)
if dep_nid:
    add_node(dep_nid, key, line, file_type="concept")
    add_edge(file_nid, dep_nid, "imports", line, context="import")
    add_edge(key_nid, dep_nid, "declares", line)  # reconnect the manifest entry

The node ids still differ, so the J-4 namespacing and the #1764 self-loop concern both hold. An alternative is to skip creating the key node for pairs under a _DEP_KEYS parent, so that ref_* is the only node per dependency, but that drops the version-range location.

Activity

  1. github-actions commented on Oct 19, 2026

    @github-actions

    Thanks for opening this issue, @BiSide. A maintainer will take a look soon.

    If you would like to discuss it in real time, come say hi on our Discord server. For longer-form questions and ideas there is also GitHub Discussions.

    To help us triage, please make sure the report includes what you expected, what actually happened, and the steps (and a small sample) to reproduce it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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