Skip to content

TypeScript-first absolute imports - #2

Merged
will-rice merged 3 commits into
mainfrom
wr/typescript-first
Aug 31, 2026
Merged

will-rice merged 3 commits into
mainfrom
wr/typescript-first

Conversation

@will-rice

@will-rice will-rice commented Aug 31, 2026 •

Copy link
Copy Markdown
Owner

Two mechanical steps, same intent:

  1. Relative import specifiers name the real .ts files instead of the emitted .js names.
  2. All internal specifiers become absolute Node subpath imports — package.json "imports" maps #* to ./src/*, so ../types.js is now #types.ts, ./ringBuffer.js is #engine/ringBuffer.ts, and tests/demo import #index.ts.

Node, tsc (NodeNext), vitest, and tsdown all resolve the # map natively — no alias/rewrite layer. Bare node src/index.ts now runs via type stripping (the property erasableSyntaxOnly was adopted for), and dist is bundled as before with no specifiers to leak. The vendored Emscripten module keeps its .js name — that file is actual JavaScript. Full gate green; refactor-only, no release.

Relative specifiers now name the files that exist, not the files emit
would produce. rewriteRelativeImportExtensions keeps any tsc emit
runtime-correct, tsdown resolves the specifiers when bundling (dist is
unchanged), and bare Node can now execute the source directly via type
stripping — the property erasableSyntaxOnly was adopted for. The
generated Emscripten module keeps its .js specifier: that file really
is JavaScript.
package.json "imports" maps #* to ./src/*, so every internal specifier
is absolute (#engine/session.ts) instead of relative, named by its real
.ts file. Node, tsc (NodeNext), vitest, and tsdown resolve the # map
natively — bare Node still executes the source via type stripping, and
the bundled dist is unchanged. rewriteRelativeImportExtensions is gone:
with no relative imports left it only produced TS2877 on # specifiers.
@will-rice will-rice changed the title Import by real .ts extensions TypeScript-first absolute imports Aug 31, 2026
Both the TypeScript team's module guidance and the prevailing package
guides recommend declaration maps so a consumer's go-to-definition lands
in real .ts source rather than a declaration bundle. tsdown emits
.d.ts.map and .js.map (sourcesContent embedded); src ships in the
tarball so the maps resolve. Size cost is negligible next to the bundled
models.
@will-rice
will-rice merged commit f03f041 into main Aug 31, 2026
2 checks passed
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 0.2.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

@will-rice
will-rice deleted the wr/typescript-first branch August 31, 2026 23:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant