Version: graphifyy 0.9.62 (PyPI) · Python 3.13.3 · macOS (darwin) · TypeScript extractor (tree-sitter)
Summary
A static import of an npm package subpath (next/image, next/link) produces no edge at all, while a bare import of the same package (next) resolves correctly to the package.json node.
The importing file keeps its node, so nothing looks wrong — the file is simply disconnected from the framework it depends on. graphify query "what uses next/image?" returns nothing, with no warning.
This is a big blind spot for Next.js / React codebases, where next/image, next/link and next/navigation carry most of the framework coupling. In a real 327-node Next.js site I measured 21 dropped import edges across 7 subpaths (next/link ×10, next/navigation ×3, next/server ×2, next/image ×2, next/headers ×2, next/og ×1, next/font/google ×1).
Note this is not the same as #3084, which reports that bare static imports also dangle — on 0.9.62 the bare case works (verified below), so that half appears to have been fixed since 0.9.39. The subpath case remains.
Minimal reproduction
Three files in an empty directory:
package.json
{
"name": "mre-subpath-import",
"version": "1.0.0",
"dependencies": { "next": "16.2.7", "react": "19.2.4" }
}
bare.ts
import type { Metadata } from "next";
import { useState } from "react";
export const meta: Metadata = {};
export const s = useState;
subpath.ts
import Image from "next/image";
import Link from "next/link";
export const i = Image;
export const l = Link;
Then:
graphify extract . --code-only
Actual result
All four import edges should exist; only the two bare ones do.
NODES: ['bare.ts', 'dependencies', 'i', 'l', 'meta', 'name', 'next', 'next',
'package.json', 'react', 'react', 's', 'subpath.ts', 'version']
IMPORT EDGES:
package.json --imports--> next
package.json --imports--> react
bare.ts --imports_from--> next <-- bare resolves
bare.ts --imports_from--> react <-- bare resolves
<-- subpath.ts has NO import edges
subpath.ts is present as a node with zero outbound import edges.
Expected result
A subpath specifier should resolve to its owning package node, so that
subpath.ts --imports_from--> next exists (ideally twice, or once with the
subpath retained as edge metadata).
Stripping the specifier to its package root before canonicalization would cover
the common cases: next/image -> next, @scope/pkg/sub -> @scope/pkg
(first two segments for scoped packages).
Why it matters
Framework surface is exactly what an architecture graph should show. For a
Next.js project the questions you most want to ask — which routes use
next/headers, which pages client-navigate via next/link, which components
render next/image — all silently return nothing. An empty result is
indistinguishable from "no usages", so the gap is invisible unless you already
know the answer.
Version: graphifyy 0.9.62 (PyPI) · Python 3.13.3 · macOS (darwin) · TypeScript extractor (tree-sitter)
Summary
A static import of an npm package subpath (
next/image,next/link) produces no edge at all, while a bare import of the same package (next) resolves correctly to thepackage.jsonnode.The importing file keeps its node, so nothing looks wrong — the file is simply disconnected from the framework it depends on.
graphify query "what uses next/image?"returns nothing, with no warning.This is a big blind spot for Next.js / React codebases, where
next/image,next/linkandnext/navigationcarry most of the framework coupling. In a real 327-node Next.js site I measured 21 dropped import edges across 7 subpaths (next/link×10,next/navigation×3,next/server×2,next/image×2,next/headers×2,next/og×1,next/font/google×1).Note this is not the same as #3084, which reports that bare static imports also dangle — on 0.9.62 the bare case works (verified below), so that half appears to have been fixed since 0.9.39. The subpath case remains.
Minimal reproduction
Three files in an empty directory:
package.json{ "name": "mre-subpath-import", "version": "1.0.0", "dependencies": { "next": "16.2.7", "react": "19.2.4" } }bare.tssubpath.tsThen:
graphify extract . --code-onlyActual result
All four import edges should exist; only the two bare ones do.
subpath.tsis present as a node with zero outbound import edges.Expected result
A subpath specifier should resolve to its owning package node, so that
subpath.ts --imports_from--> nextexists (ideally twice, or once with thesubpath retained as edge metadata).
Stripping the specifier to its package root before canonicalization would cover
the common cases:
next/image->next,@scope/pkg/sub->@scope/pkg(first two segments for scoped packages).
Why it matters
Framework surface is exactly what an architecture graph should show. For a
Next.js project the questions you most want to ask — which routes use
next/headers, which pages client-navigate vianext/link, which componentsrender
next/image— all silently return nothing. An empty result isindistinguishable from "no usages", so the gap is invisible unless you already
know the answer.