Fixes the silent near-edgeless-graph regression on any modern ESM
TypeScript project. Reported in #294 with full repro + root-cause
analysis.
### Why this matters
Under `moduleResolution: NodeNext` (or `Node16` / `Bundler` with
explicit extensions — the default for new TS-ESM projects since 2023),
TypeScript does NOT rewrite import specifiers during compilation:
// src/index.ts — real, idiomatic NodeNext source
import { x } from './config.js'; // on disk: config.ts
Before this fix, `probeWithExtensions` only tried APPENDING extensions
to the import specifier:
'./config.js' → not in fileSet
'./config.js.ts', './config.js.tsx', './config.js.js', ... → all miss
→ returns null → edge dropped at merge as dangling
Net result on the reporter's repro: a knowledge graph with hundreds of
file nodes and almost no `imports` edges between them — silently
removing exactly the dependency structure the graph is meant to show.
### Fix
New `NODENEXT_REWRITES` table maps each compiled-output extension to
the TypeScript source extensions that could have produced it:
.js → [.ts, .tsx, .js, .jsx]
.jsx → [.tsx, .jsx]
.mjs → [.mts, .mjs, .ts]
.cjs → [.cts, .cjs, .ts]
`probeWithExtensions` now applies the rewrite when the import already
ends with one of these extensions and no such file exists on disk. The
rewrite runs BEFORE the legacy append-extensions loop — otherwise
`./foo.js` would generate the nonsense candidate `foo.js.ts` and the
append loop would never reach the actual `foo.ts`.
### Disambiguation
If both `config.ts` and `config.js` exist on disk (rare, but possible
during a partial migration), `import './config.js'` still resolves to
the .js — that's an exact-disk match and what NodeNext compilation
actually does. The rewrite only kicks in when the .js doesn't exist.
### Tests
6 new tests in `test_extract_import_map.test.mjs`:
- The main #294 case (`.js → .ts`)
- `.jsx → .tsx` and `.mjs → .mts` rewrites
- Disambiguation when both `.ts` and `.js` exist on disk
- Pure-JS projects still work (real `.js → .js` imports)
- Historical no-extension probes unaffected
- Missing files still return null (rewrite can't invent targets)
Total: 202 tests passing (was 196).
Closes#294