Skip to content

fix: resolve JavaScript and TypeScript default imports - #56

Merged
joyful-ii-V-I merged 2 commits into
redhat-et:mainfrom
PollyBot13:fix/default-import-resolution
Sep 8, 2026
Merged

joyful-ii-V-I merged 2 commits into
redhat-et:mainfrom
PollyBot13:fix/default-import-resolution

Conversation

@PollyBot13

Copy link
Copy Markdown
Contributor

Summary

Resolve JS/TS default-import calls by module and exported identity, not by the importer's chosen local name.

For example, import save from './storage.js' must call the function exported as default by storage.js; an unrelated function named save is not evidence of that edge.

This extends the existing ES import/export fact tables to default imports, named default declarations, local identifier defaults, and export { local as default }. Existing lexical-shadow and module-ambiguity handling is reused. Named default classes use the same declaration seam to preserve constructor edges. The parser cache version and its quality mirror are bumped together.

Unsupported anonymous expressions remain unresolved. Conflicting default exports or module value bindings cannot select a function simply because it is the only function-shaped symbol.

Regression proof

The new sub-gate, called by tsimportprecisecheck.sh, covers TS/TSX/MTS/CTS and JS/JSX/MJS/CJS:

  • An actual exported function whose name differs from the local import, with a same-spelled decoy elsewhere.
  • Local identifier and clause defaults; named class compatibility.
  • Parameter/closure/block shadows, type-only imports, external imports and competing source/runtime modules.
  • Conflicting defaults and function/non-function binding collisions.
  • Real cold/warm cache equality and mutations that remove the target after caching.

The original regression fails on the current upstream-base binary; the fixed binary passes. Source review findings were reproduced or covered by added regression arms and repaired before publication.

Validation

  • Development, Release and ASan builds succeed using vendored dependencies with FETCHCONTENT_FULLY_DISCONNECTED=ON.
  • bash test/lib/jsdefaultimport.sh <binary> passes for all three build flavours. Existing jsimportalias.sh and jsimportfacts.sh pass in development and Release.
  • python3 test/pargates.py . ./build/ripwire -j 4: 571 gates, 565 pass, 3 skip, 3 fail on the first full run. The parser-identity fingerprint and two README example coordinates were then refreshed in a separate proof-reference commit; qschemetripcheck.sh, qextractionkeycheck.sh and readmeexamplecheck.sh pass after that refresh.
  • The remaining full-run failure, legendcoveragecheck.sh, scans installed host skills with bare --scan-skills; it passes with a disposable empty home. The legend baseline and host skills are unchanged.
  • The skipped formatting gate passes when rerun with clang-format 22, including its mutation controls. Two optional pre-change-binary comparison gates remain skipped; this change intentionally alters graph output.
  • Full-source ASan scan exits 0 with empty stderr. Three uncached maps and three cache-backed maps are byte-identical and valid XML.
  • Independent review completed; source findings fixed and rechecked. Diff and shell syntax checks pass.

Scope and limits

No new graph mechanism, general TypeScript semantic analysis, anonymous-symbol synthesis, barrel traversal, reassignment analysis or --uses behavior change. Existing named-import handling stays on its current path. The meaningful behavior change is confined to default bindings; reverting the commit restores the previous extraction/resolution behavior.

AI-assisted implementation with independent source review. Validation is static-source/real-CLI proof, not execution of the JavaScript programs.

@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: d7f48b52-35c6-433b-b59b-dc43464ba559

📥 Commits

Reviewing files that changed from the base of the PR and between af6d094 and 6e7db00.

📒 Files selected for processing (9)
  • README.md
  • src/graph.h
  • src/ingest_cache.h
  • src/ingest_jsimports.h
  • src/quality.h
  • test/lib/jsdefaultimport.sh
  • test/qschemetrip.hash
  • test/qschemetripcheck.sh
  • test/tsimportprecisecheck.sh

Included review availability: Your plan provides up to 4 included reviews per hour; 0 remain after this review.


📝 Summary

Summary by CodeRabbit

  • New Features

    • Improved JavaScript and TypeScript analysis for default imports and local default exports.
    • More accurately resolves default-import targets and avoids incorrect matches when exports are ambiguous.
    • Supports additional default-export forms, including classes and export clauses.
  • Bug Fixes

    • Improved handling of shadowed names, type-only imports, external modules, and cache consistency.
  • Documentation

    • Updated usage examples to reflect current source locations.
  • Tests

    • Added comprehensive coverage for default-import and default-export resolution scenarios.

Walkthrough

The change adds JavaScript and TypeScript default-import and default-export extraction, resolves unique bindings, marks ambiguous exports, invalidates older parser caches, and adds integration coverage.

Changes

Default binding resolution

Layer / File(s) Summary
Capture and disambiguate default bindings
src/ingest_jsimports.h, src/graph.h
Default imports and exports are recorded with the "default" name. Unique local bindings are resolved, while ambiguous bindings remain unresolved.
Cache invalidation and integration checks
src/ingest_cache.h, src/quality.h, test/lib/jsdefaultimport.sh, test/tsimportprecisecheck.sh, test/qschemetripcheck.sh, test/qschemetrip.hash, README.md
Parser versions advance from 81 to 82. Older cache data is invalidated. Integration tests cover default binding resolution, shadowing, ambiguity, and cache behavior. Documentation and recorded hashes are updated.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 6e7db

Default import/export resolution, ambiguity handling, cache invalidation, and regression coverage are aligned with the stated behavior; no merge-blocking risk remains.

Sequence Diagram(s)

sequenceDiagram
  participant JSModule
  participant captureJsImportFacts
  participant jsModuleBindingCount
  participant buildJsImportTables
  JSModule->>captureJsImportFacts: Parse default import/export syntax
  captureJsImportFacts->>jsModuleBindingCount: Count matching module-scope bindings
  jsModuleBindingCount-->>captureJsImportFacts: Return binding count
  captureJsImportFacts->>buildJsImportTables: Store resolved or ambiguous default facts
Loading

Suggested reviewers: joyful-ii-v-i

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 7 files. (2 skipped: 2… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: resolving JavaScript and TypeScript default imports.
Description check ✅ Passed The description directly explains default-import resolution, scope, regression coverage, and validation results.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 7 files. (2 skipped: 2 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@joyful-ii-V-I

Copy link
Copy Markdown
Collaborator

Thank you — and thank you for coming back. Your named-import fix from #45 shipped in v0.5.0 yesterday and you're named in the changelog for it; opening three more PRs the same evening is the best signal this project has had that the contribution path actually works.

Default imports are the right next gap. import X from "./m" is the spelling most JS/TS code actually uses, and #45 left it on the floor.

Queued behind #57's review — we're prioritising language coverage over new surface for the next couple of weeks, and this sits squarely in that. I'll give it the same treatment: full battery, ASan, and an adversarial corpus rather than just the fixture. If it holds up the way #45 did, it lands.

@joyful-ii-V-I
joyful-ii-V-I merged commit 635ef7a into redhat-et:main Sep 8, 2026
26 checks passed
@joyful-ii-V-I

Copy link
Copy Markdown
Collaborator

Merged — thank you, this is a careful fix and the reasoning in the description is the best part of it.

"Resolve by module and exported identity, not by the importer's chosen local name" is exactly the right
framing, and I verified it independently of your gate before merging, on a fresh fixture: a default
export persistToDisk in one module, an unrelated exported save in another, and
import save from './storage.js'.

origin/main   run() -> <c n="save"/>                          the DECOY, matched on the local name
this merge    run() -> <c n="persistToDisk" prov="import"/>   the real default export, with provenance

Three things you did that I want to name, because they are the parts that made this cheap to land:

You bumped kParserVer and kIngestParserVerMirror in the same commit. That mirror is the single
nastiest trap in this repo — CLAUDE.md documents a case where it was edited during a build and the binary
kept emitting the old value through repeated successful rebuilds. Getting it right unprompted is not
typical.

You left kCacheVersion alone and said why. New extraction facts on an unchanged record shape, so it
is not yours to move. That judgment is what let the merge conflict resolve cleanly (see below).

Your new coverage adds NO name to the absorb list. Putting the logic in test/lib/jsdefaultimport.sh
and calling it from the existing tsimportprecisecheck.sh means this PR does not touch the eight sites
that publish the gate count — with three lanes in flight adding gates this week, that mattered more than
you could have known. Worth copying whenever a "new gate" is really a new ARM of an existing one.

On the conflict. Both this branch and #57 (Ruby constant references) spent parser version 82. I
renumbered yours to 83 on merge and kept BOTH changelog entries so neither lineage is lost;
kIngestCacheVersionMirror stayed at main's 17 for exactly the reason you gave; and qschemetrip.hash
was re-pinned through its own UPDATE_GOLDEN path rather than by picking a side of the conflict, since a
hand-picked hash of source text would have been wrong either way.

Nothing was needed from you. Thanks again.

AnkitArya pushed a commit to AnkitArya/ripwire that referenced this pull request Sep 9, 2026
…ndriy Tyurnikov)

Round two of the Ruby constant work, on top of redhat-et#57. `User.find` now edges to the file that defines
`User`: a call whose receiver is a constant chain is a symbolic directive resolved through round one's
index. That is the Zeitwerk dependency proper — a Rails file almost never says `require`, it names the
constant and the autoloader loads the file on that first reference. Before this, `--impact=User` on a
Rails app named nobody.

CONFLICT RESOLUTION — seven files, and the interesting part is which ones git did NOT flag.

  kParserVer 83 -> 84. THIRD version collision today. main spent 83 on redhat-et#56's JS/TS default imports (itself
  renumbered from 82 on merge, because redhat-et#57 had taken 82). This PR also claimed 83. Renumbered to 84 with
  BOTH changelog entries kept and a line recording the renumber, so neither lineage is lost.
  kIngestParserVerMirror moved in the same edit — that pair is the nastiest trap in this codebase.

  test/regression.sh absorb list: took main's line VERBATIM and inserted `rubyrecvcheck` into it, rather
  than rebuilding it from parsed tokens. Rebuilding drops every name the filter misses and the trailing
  `; do`, which silently stops gates running — done exactly that once today. 561 names, and it correctly
  retains the two gates that landed after this PR branched.

  The eight gate-count sites did NOT conflict, and that is the hazard: main said 560, this PR said 559,
  and git would have merged one of them cleanly into a number that is wrong either way. Every count
  conflict was resolved to main's side FIRST and the number re-derived afterwards. Never carry a count
  across a rebase.

  docs/COMMANDS.md REGENERATED, not merged (it is generated output; docscommandscheck arm (G) checks which
  capture produced it): 4 lines, 161 samples intact. test/printf_parity.manifest re-pinned with ONLY the
  `help` label moving — the check that proves nothing else drifted.

  test/qschemetrip.hash re-pinned, kQSnapCacheScheme left at 8. This PR does not touch src/quality.h; the
  hash moved solely because the version MIRROR is inside the hashed region. Same call as redhat-et#56's merge.

Coupled gates verified: manifestcheck (561), qextractionkeycheck, qschemetripcheck, docscommandscheck,
printffmtparitycheck, rubyrecvcheck, rubyconstcheck.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.

2 participants