Skip to content

fix: record local alias for dynamic import() destructuring renames#1970

Open
carlos-alm wants to merge 1 commit into
fix/issue-1823-barrel-re-export-with-rename-export-x-as-yfrom
fix/issue-1824-dynamic-import-destructuring-rename-const-a-b
Open

fix: record local alias for dynamic import() destructuring renames#1970
carlos-alm wants to merge 1 commit into
fix/issue-1823-barrel-re-export-with-rename-export-x-as-yfrom
fix/issue-1824-dynamic-import-destructuring-rename-const-a-b

Conversation

@carlos-alm

Copy link
Copy Markdown
Contributor

Summary

  • const { a: b } = await import(...) recorded the original exported name (a) instead of the local binding actually used at call sites (b) — so a subsequent call to b(...) never resolved to the imported symbol. The pair_pattern branch of extractDynamicImportNames (TS/WASM) and extract_dynamic_import_names (Rust) preferred tree-sitter's key field (the name exported by the target module) over value (the local binding) — the same class of bug fixed for static import { X as Y } specifiers in codegraph exports does not credit consumers that import via a rename (import { X as Y }) #1730.
  • Both engines now record the local alias in names, with the local → original mapping recorded in Import.renamedImports — reusing the exact mechanism codegraph exports does not credit consumers that import via a rename (import { X as Y }) #1730/Barrel re-export with rename (export { X as Y } from ...) is not tracked, breaking barrel resolution #1823 already threaded through call resolution, barrel tracing, and the incremental path. No resolver changes were needed, only extraction, since that infrastructure already exists and is generic across static/dynamic imports.
  • Also handles the combined rename + default-value case ({ a: b = defaultValue }) and falls back to the previous key-based behavior for nested patterns ({ foo: { nested } }), which have no single local binding to extract.
  • As a side effect of fixing the shared Rust collect_object_pattern_names helper, CJS require() destructuring renames (const { a: b } = require('./mod')) are now also correct on the native engine, bringing it in line with WASM's already-correct extractCjsRequireBinding.

Test plan

  • npx vitest run tests/parsers/javascript.test.ts — 208/208 pass, including 5 new tests for the rename/default/parens+as-cast cases
  • npx vitest run tests/integration/issue-1824-dynamic-import-destructure-rename.test.ts — new integration test, WASM + native variants, 6/6 pass
  • Verified the new integration test actually fails without the fix (rebuilt dist/ from the pre-fix source, confirmed 2 failures; restored the fix and rebuilt, confirmed all pass)
  • cargo test --lib — 529 passed, including 3 existing Rust unit tests updated (they previously asserted the buggy behavior as expected output, per the "never document bugs as expected behavior" rule)
  • npm test — full suite green (3756 passed, 30 skipped, 2 todo)
  • npm run lint — clean
  • codegraph diff-impact --staged -T — confirmed blast radius is scoped to extractDynamicImportNames/collectDynamicImport/handleDynamicImportCall and their callers only

Filed #1969 (WASM dynamic-import destructuring misses shorthand-default and rest-element bindings that native already handles) — a distinct, pre-existing parity gap discovered while reading the Rust/WASM object-pattern loops side by side, out of scope for this fix.

Stacked on #1823 (base branch fix/issue-1823-barrel-re-export-with-rename-export-x-as-y) — only the extractor/types/test diff is this issue's change.

Fixes #1824

const { a: b } = await import(...) recorded the original exported name
(a) instead of the local binding actually used at call sites (b), so
calls like b(...) never resolved to the imported symbol. The
pair_pattern branch of extractDynamicImportNames (TS/WASM) and
extract_dynamic_import_names (Rust) preferred tree-sitter's `key` field
over `value` - the same class of bug fixed for static
`import { X as Y }` specifiers in #1730.

Both engines now record the local alias in `names`, with the
local -> original mapping in `Import.renamedImports` so call-edge
resolution can still find the target module's real export. This reuses
the renamedImports mechanism #1730/#1823 already threaded through
resolution, so no resolver changes were needed - only extraction.

The Rust fix also corrects the same key-vs-value bug in CJS require()
destructuring (which shares collect_object_pattern_names), bringing it
in line with WASM's already-correct extractCjsRequireBinding.

Impact: 3 functions changed, 10 affected
@greptile-apps

greptile-apps Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes dynamic import destructuring aliases in both JavaScript extractors. The main changes are:

  • Records the local alias in dynamic import names.
  • Stores local-to-original mappings in renamedImports.
  • Mirrors the fix in the Rust/native extractor.
  • Adds parser and integration tests for renamed destructuring.

Confidence Score: 4/5

The alias flow works for identifier keys, but quoted or computed destructuring keys can still miss dynamic import edges.

  • The changed extractors now pass renamedImports into existing import resolution paths.
  • Identifier aliases, mixed bindings, defaults, and cast wrappers are covered by the new tests.
  • Raw key text is still stored for non-identifier property keys, which can make callers receive incomplete graph results.

src/extractors/javascript.ts; crates/codegraph-core/src/extractors/javascript.rs

Important Files Changed

Filename Overview
src/extractors/javascript.ts Adds renamedImports for dynamic import destructuring aliases, with a remaining raw-key issue for quoted and computed property names.
crates/codegraph-core/src/extractors/javascript.rs Mirrors dynamic import alias extraction in the native parser, with the same raw-key issue for quoted and computed property names.
src/types.ts Documents that renamedImports also applies to dynamic import destructuring aliases.
tests/integration/issue-1824-dynamic-import-destructure-rename.test.ts Adds end-to-end coverage for renamed dynamic import destructuring across WASM and native engines.
tests/parsers/javascript.test.ts Adds parser coverage for identifier-key dynamic import aliases, defaults, mixed bindings, and cast wrappers.

Fix All in Claude Code

Reviews (1): Last reviewed commit: "fix: record local alias for dynamic impo..." | Re-trigger Greptile

) {
localNode = value;
} else if (value?.type === 'assignment_pattern') {
// { imported: local = defaultValue } — the local binding is the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Raw Literal Keys Miss Exports

When a dynamic import uses a valid quoted or computed destructuring key like const { 'foo-bar': local } = await import('./mod.js'), this stores the raw key text as imported. The resolver then looks for an export named 'foo-bar' or ['foo-bar'] instead of foo-bar, so calls through local() stay unresolved and the real export can still be marked dead.

Fix in Claude Code

match (local_node, key) {
(Some(local_node), Some(key)) => {
let local_text = node_text(&local_node, source).to_string();
let key_text = node_text(&key, source).to_string();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Raw Literal Keys Miss Exports

When the native extractor sees a dynamic import destructure such as const { 'foo-bar': local } = await import('./mod.js'), node_text(&key, source) includes the quotes or computed-key syntax. renamed_imports.imported then no longer matches the target export name, so the alias call edge is not created and the exported symbol can be reported as unused.

Fix in Claude Code

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Codegraph Impact Analysis

3 functions changed10 callers affected across 1 files

  • collectDynamicImport in src/extractors/javascript.ts:805 (8 transitive callers)
  • handleDynamicImportCall in src/extractors/javascript.ts:1554 (3 transitive callers)
  • extractDynamicImportNames in src/extractors/javascript.ts:4201 (10 transitive callers)

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant