Skip to content

chore: upgrade to TypeScript 7 - #99

Draft
ibgreen wants to merge 1 commit into
masterfrom
codex/upgrade-typescript-7
Draft

chore: upgrade to TypeScript 7#99
ibgreen wants to merge 1 commit into
masterfrom
codex/upgrade-typescript-7

Conversation

@ibgreen

@ibgreen ibgreen commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Summary

  • upgrade the build to TypeScript 7.0.2 via @typescript/native
  • replace ocular-build with a small explicit build pipeline
  • preserve extensionless imports in every TypeScript source file
  • rewrite only emitted relative module specifiers so the published ESM and declarations remain Node-compatible
  • continue generating the CommonJS entry points with esbuild

Why the build pipeline needs a workaround

The previous build depended on @vis.gl/ts-plugins through ts-patch. Its append-extension transformer ran inside the JavaScript TypeScript compiler and changed relative module specifiers in both JavaScript and declaration output.

TypeScript 7 uses the native compiler. That compiler cannot be patched by ts-patch and does not load the JavaScript custom transformers used by @vis.gl/ts-plugins, so the old ocular-build path cannot perform this repository's required emit transformation.

An earlier version of this PR worked around that limitation by spelling emitted .js extensions in the .ts source. That couples source authoring to one output format and creates unnecessary risk for tests, bundlers, and future build targets. This revision removes all of that source churn: modules/*/src is byte-for-byte unchanged from master, and TypeScript uses moduleResolution: "bundler" to continue resolving the existing extensionless imports.

Post-emission solution

The new build has three explicit stages:

  1. TypeScript 7 performs project-reference type checking and emits ESM JavaScript, declarations, and their source maps.
  2. A standalone, syntax-aware postprocessor updates relative module specifiers in the emitted .js and .d.ts files.
  3. esbuild generates the package CommonJS entry points from the corrected ESM output.

The postprocessor deliberately does not use textual search-and-replace. It uses the retained TypeScript 6 compatibility package only as a JavaScript parser, walks module-specifier syntax nodes (static imports/exports, dynamic imports, require, import types, and import-equals), and ignores comments, ordinary strings, package imports, and specifiers that already have extensions.

For every extensionless relative specifier, it checks the emitted filesystem before changing anything:

  • a direct emitted file becomes ./target.js
  • an emitted directory index becomes ./target/index.js
  • a missing target fails the build
  • multiple possible targets fail the build as ambiguous

The same transformation is applied to declaration files. When a suffix is inserted, the corresponding JavaScript or declaration source-map mappings are shifted by the exact inserted column count, preserving mapping accuracy. TypeScript 6 is therefore a narrow parser/tooling compatibility dependency; TypeScript 7 remains the compiler used for checking and emission.

This keeps the workaround isolated at the boundary where the runtime requirement actually exists: generated package artifacts, not authored TypeScript.

Validation

  • yarn install --immutable
  • yarn build
    • rewrote 323 relative specifiers across 111 emitted files
  • verified 204 emitted .js/.d.ts files contain no extensionless relative module specifiers
  • decoded and validated ordering in 219 generated source maps
  • loaded all 30 ESM and CommonJS package entry points
  • yarn lint
  • yarn test node: 2,719 passing
  • yarn test ci: full browser suite passing
  • verified modules/*/src has no diff from master

The rewriter also has focused coverage for JavaScript output, declaration output, source-map adjustment, Node ESM loading, and failure on an unresolved emitted specifier.

@ibgreen
ibgreen force-pushed the codex/upgrade-typescript-7 branch from 35f08bc to 572792a Compare July 29, 2026 16:45
@ibgreen
ibgreen force-pushed the codex/upgrade-typescript-7 branch from 572792a to 71c1fc0 Compare July 29, 2026 18:52
@chrisgervang

Copy link
Copy Markdown

TypeScript 6 is therefore a narrow parser/tooling compatibility dependency; TypeScript 7 remains the compiler used for checking and emission.

Interesting approach. I like that it doesn't burden us with maintaining a complex parser.

This does feel like something the TS community would ideally solve for us in the long term.. we couldn't be the only project trying to emit TS, JS, d.ts, ESM, and CommonJS.. right?

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