Skip to content

TS/JS AST extractor creates no node for export const with a scalar initializer #2266

Closed

Description

@drmzperx

TS/JS AST extractor creates no node for export const with a scalar initializer

Version: graphifyy 0.9.26
Environment: Python 3.12.13, Linux x86_64 (glibc 2.31)

Summary

graphify.extract.extract() emits a node for an exported const only when the initializer is a composite or callable expression. Exported constants initialized to a scalar — string, number, boolean, template literal, member expression, logical expression, or ternary — produce no node at all, so they are absent from the graph and any edge referencing them dangles.

This silently drops a large share of a typical TS codebase's public surface: env-var constants, feature flags, string prefixes, numeric limits.

Reproducer

mkdir -p /tmp/gr2 && cd /tmp/gr2
cat > w.ts <<'EOF'
export const W1 = 42;
export const W2 = `tpl${x}`;
export const W3 = [1, 2, 3];
export const W4 = () => 1;
export const W5 = function () { return 1; };
export const W6 = new Map();
export const W7 = foo();
export const W8 = { a: 1 };
export const W9 = true;
export const W10 = process.env.X;
export const W11 = process.env.X ?? 'fallback';
export const W12 = cond ? 'a' : 'b';
EOF
python -c "
from pathlib import Path
from graphify.extract import extract
r = extract([Path('/tmp/gr2/w.ts')], cache_root=Path('/tmp/gr2'))
print(sorted(n['id'] for n in r['nodes']))
"

Actual:

['w', 'w_w3', 'w_w4', 'w_w5', 'w_w6', 'w_w7', 'w_w8']
initializer node created
number / string / boolean literal no
template literal no
member expression (process.env.X) no
logical (??, ||) no
ternary no
array literal yes
object literal yes
arrow function yes
function expression yes
new expression yes
call expression yes

Knock-on effect: dangling import edges

A different module importing the missing symbol still gets an edge, which then dangles:

cat > /tmp/gr2/a.ts <<'EOF'
export const A_PREFIX = process.env.A_PREFIX ?? 'X>';
export const A_MAX = Number(process.env.A_MAX || 10);
EOF
cat > /tmp/gr2/b.ts <<'EOF'
import { A_PREFIX, A_MAX } from './a';
EOF

yields b --imports--> a_a_prefix where a_a_prefix does not exist, while a_a_max resolves — because Number(...) is a call expression and process.env.X ?? 'X>' is not. This is one of the contributors to the spurious GRAPH HEALTH WARNING reported separately.

Real-world case

In a Next.js repo, lib/send.ts exports two adjacent constants:

export const SEND_PREFIX = process.env.SEND_PREFIX ?? 'PMW>';         // no node
export const SEND_MAX_LEN = Number(process.env.SEND_MAX_LEN || 190);  // node created

SEND_PREFIX is a semantically important value (the message-attribution prefix, referenced by name in the project's own design docs), yet it is queryable only through the doc-derived semantic nodes — graphify explain "SEND_PREFIX" finds no code symbol, and the import edge from lib/send.test.ts dangles.

Expected

An exported binding should produce a node regardless of its initializer's expression type. If the current behaviour is intentional (e.g. to avoid noise from trivial locals), it should at minimum apply only to non-exported declarations, since exported constants are part of a module's public API and are exactly what cross-file imports edges point at.

Activity

  1. drmzperx commented on Jul 28, 2026

    @drmzperx
    Author

    Related: #2265 — the dangling import edges described under "knock-on effect" are counted there as part of the spurious GRAPH HEALTH WARNING.

  2. safishamsi commented on Jul 30, 2026

    @safishamsi
    Member

    Fixed in v0.9.31 (#2300, thanks @oleksii-tumanov). export const X = <scalar> now emits a graph node, so a named import of a scalar export is no longer left dangling. https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.31

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