mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
186 lines
9.5 KiB
Markdown
186 lines
9.5 KiB
Markdown
# TypeScript 7 toolchain and migration evaluation
|
||||
|
|
|
|||
|
|
The workspace uses TypeScript **7.0.2** for `tsc` commands, including type checks,
|
|||
|
|
JavaScript emission, declaration emission, and the web build's final type gate.
|
|||
|
|
|
|||
|
|
## Dependency layout
|
|||
|
|
|
|||
|
|
Every workspace declares `@typescript/native: npm:typescript@7.0.2`, which
|
|||
|
|
supplies `tsc` and the pinned native APIs.
|
|||
|
|
|
|||
|
|
Only `apps/web` retains `typescript: npm:@typescript/typescript6@6.0.2` for
|
|||
|
|
Next 16.2.6 configuration/type generation. It supplies the legacy JavaScript
|
|||
|
|
compiler API and `tsc6`, without competing for `tsc`. Next still imports the old
|
|||
|
|
API; removing this last alias requires a separate framework compatibility change.
|
|||
|
|
This follows Microsoft's [side-by-side installation guidance](https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/).
|
|||
|
|
|
|||
|
|
Repository-owned guards and tests no longer import `typescript`:
|
|||
|
|
|
|||
|
|
- Guards and AST-based tests use `@typescript/native/unstable/ast` and
|
|||
|
|
`@typescript/native/unstable/sync`.
|
|||
|
|
- Semantic regression tests request diagnostics from the native compiler,
|
|||
|
|
preserving their error-code and source-position assertions.
|
|||
|
|
- Desktop snippets use Node 24's type stripping. Daemon/packaged test harnesses
|
|||
|
|
use esbuild for ESM/CommonJS emission, without pretending this is type checking.
|
|||
|
|
- Guard parsing uses an explicitly scoped native process, no libraries or import
|
|||
|
|
resolution, and immutable in-memory projects. Regression tests ensure changed
|
|||
|
|
contents and TS/TSX syntax cannot reuse stale ASTs. Native processes and
|
|||
|
|
snapshots are released on success and failure. No virtual fixture is written
|
|||
|
|
to disk.
|
|||
|
|
|
|||
|
|
The `unstable/*` API is deliberately pinned to 7.0.2. Compiler upgrades must run
|
|||
|
|
the focused guard and semantic-diagnostic tests, not just `tsc --noEmit`.
|
|||
|
|
`pnpm exec tsc --version` reports `7.0.2`. The remaining compatibility dependency
|
|||
|
|
is isolated to Next; it is not used for repository type checks or JS emission.
|
|||
|
|
|
|||
|
|
## Web build gate
|
|||
|
|
|
|||
|
|
Run `pnpm --filter @open-design/web build`, which executes:
|
|||
|
|
|
|||
|
|
```sh
|
|||
|
|
next build && tsc -b --noEmit
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Next generates route types and application assets. Its old in-process type check
|
|||
|
|
is disabled in `next.config.ts`; native `tsc` checks the project, including the
|
|||
|
|
generated types, before the package build can succeed. A type error therefore
|
|||
|
|
still fails the build. Calling `next build` directly bypasses this final gate;
|
|||
|
|
use the package build entry point, including for packaging and CI.
|
|||
|
|
|
|||
|
|
The standalone `typecheck` script remains `tsc -b --noEmit`. The project-level
|
|||
|
|
compiler options explicitly list ambient types that older compilers loaded
|
|||
|
|
implicitly. The e2e alias uses a relative `paths` target instead of the removed
|
|||
|
|
`baseUrl` option. No strictness checks were disabled for this migration.
|
|||
|
|
|
|||
|
|
## Evaluation scope
|
|||
|
|
|
|||
|
|
Baseline: commit `287009d37f`, with the original mix of TypeScript 5.9.3 and
|
|||
|
|
6.0.3. Measurements ran sequentially in the same worktree on an Apple M5 Pro
|
|||
|
|
(15 logical CPUs, 48 GiB RAM), macOS arm64, Node 24.13.0, pnpm 10.33.2.
|
|||
|
|
Dependencies and workspace build prerequisites were installed before measuring.
|
|||
|
|
|
|||
|
|
The timings below were captured at the initial compiler migration, before the
|
|||
|
|
follow-up guard/test API migration. They are single-run wall-clock observations, including
|
|||
|
|
pnpm orchestration. Web builds use the default static-export mode and start
|
|||
|
|
without `.next` caches; the native run also discards the separate web
|
|||
|
|
`tsconfig.tsbuildinfo` before the build. Full type checking starts without that
|
|||
|
|
incremental cache. These are not fresh OS filesystem-cache measurements or CI
|
|||
|
|
runner predictions. Existing generated package outputs are present on both sides.
|
|||
|
|
|
|||
|
|
| Command | Original | TS 7.0.2 | Speedup | Time saved |
|
|||
|
|
| --- | ---: | ---: | ---: | ---: |
|
|||
|
|
| `pnpm typecheck` | 46.87 s | 10.90 s | 4.30x | 76.8% |
|
|||
|
|
| `pnpm --filter @open-design/daemon build` | 6.11 s | 1.50 s | 4.08x | 75.5% |
|
|||
|
|
| `pnpm --filter @open-design/desktop build` | 0.93 s | 0.28 s | 3.36x | 70.3% |
|
|||
|
|
| `pnpm --filter @open-design/web build` | 36.65 s | 16.79 s | 2.18x | 54.2% |
|
|||
|
|
|
|||
|
|
The baseline Next build reported 20.8 s in its TypeScript stage. The remaining
|
|||
|
|
bundling and prerendering work explains why the full web build improves less
|
|||
|
|
than type checking alone. Workspace orchestration and esbuild work similarly
|
|||
|
|
limit the full-repository speedup.
|
|||
|
|
|
|||
|
|
## Repeated compiler-only comparison
|
|||
|
|
|
|||
|
|
To separate compiler performance from pnpm orchestration, each compiler ran
|
|||
|
|
three times in alternating order on the same migrated sources and installed
|
|||
|
|
workspace outputs. Both checks passed in every round. Web incremental caching
|
|||
|
|
was disabled; daemon does not enable it. The comparison uses the original
|
|||
|
|
5.9.3 compiler for both apps versus native 7.0.2, with each compiler's bundled
|
|||
|
|
standard library and default worker settings.
|
|||
|
|
|
|||
|
|
| Project | TS 5.9.3 median | TS 7.0.2 median | Speedup |
|
|||
|
|
| --- | ---: | ---: | ---: |
|
|||
|
|
| `apps/web` | 22.31 s | 2.51 s | 8.90x |
|
|||
|
|
| `apps/daemon` | 4.56 s | 1.52 s | 3.00x |
|
|||
|
|
|
|||
|
|
Commands from each app directory:
|
|||
|
|
|
|||
|
|
```sh
|
|||
|
|
# Run with the baseline 5.9.3 compiler, then the native 7.0.2 compiler.
|
|||
|
|
# Web:
|
|||
|
|
tsc -p tsconfig.json --noEmit --incremental false
|
|||
|
|
# Daemon:
|
|||
|
|
tsc -p tsconfig.json --noEmit
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The web measurements ranged from 21.42–25.24 s (5.9.3) and 2.32–2.92 s (7.0.2).
|
|||
|
|
The daemon measurements ranged from 4.55–4.66 s and 1.51–1.55 s respectively.
|
|||
|
|
This supports a substantial improvement on this machine without implying the
|
|||
|
|
same multiplier for every package or CI runner.
|
|||
|
|
|
|||
|
|
## Validation
|
|||
|
|
|
|||
|
|
- Frozen-lockfile installation and postinstall builds passed. All 27 child
|
|||
|
|
workspaces plus the root resolve `tsc --version` to 7.0.2.
|
|||
|
|
- Full workspace `pnpm typecheck` and repository `pnpm guard` passed.
|
|||
|
|
- Focused existing tests cover deliverable selection and consumers of the legacy
|
|||
|
|
compiler API: 8 files, 150 tests passed across daemon, web, desktop, packaged,
|
|||
|
|
and e2e packages.
|
|||
|
|
- Default static-export and standalone web builds, web sidecar, daemon, desktop,
|
|||
|
|
packaged, and Closure builds passed.
|
|||
|
|
- A temporary TS2322 error was injected into a web source file: the complete
|
|||
|
|
package build exited nonzero at the native compiler gate. The probe was removed
|
|||
|
|
and the subsequent standalone build passed.
|
|||
|
|
- Daemon and desktop emitted-file sets are unchanged (2,301 and 105 files).
|
|||
|
|
Desktop JavaScript output is byte-identical. Daemon JavaScript differs only
|
|||
|
|
in the migrated deliverable-selection expression; other changes are confined
|
|||
|
|
to declarations and source maps.
|
|||
|
|
|
|||
|
|
This evaluation does not measure application runtime speed, editor latency,
|
|||
|
|
peak memory, Windows/Linux execution, or signed installer production. Full daemon
|
|||
|
|
and web test suites were not run; focused tests follow repository guidance.
|
|||
|
|
|
|||
|
|
## Follow-up: remove repository-owned legacy API usage
|
|||
|
|
|
|||
|
|
The follow-up migration keeps the compatibility alias only in `apps/web` for
|
|||
|
|
Next. Full type checking, web build, and all guards passed after removal from the
|
|||
|
|
other 27 manifests. Focused follow-up validation passed 149 tests across 11
|
|||
|
|
files: 42 e2e guard/scope tests, 72 web tests, 10 desktop tests, 12 packaged tests,
|
|||
|
|
and 13 daemon tests. Seven new guard tests cover import-edge preservation,
|
|||
|
|
source changes, TS/TSX parsing, old-tree stability, and failure cleanup.
|
|||
|
|
|
|||
|
|
The final native-API guard run took 20.54 s on the same machine. This is not a
|
|||
|
|
claim of improvement over the old JS parser: native AST extraction involves
|
|||
|
|
process/protocol overhead, unlike the compiler-only benchmarks above. One
|
|||
|
|
native process per full guard run and a one-entry AST cache avoid repeatedly
|
|||
|
|
starting the compiler or parsing identical consecutive inputs. Native snapshots
|
|||
|
|
are immutable; tests caught stale results when virtual paths were reused, so
|
|||
|
|
each distinct parse now opens a fresh virtual project and closes its predecessor.
|
|||
|
|
|
|||
|
|
|
|||
|
|
## Guard performance follow-up
|
|||
|
|
|
|||
|
|
CI exposed a regression in the first API migration: the layout-policy step took
|
|||
|
|
131 s in run `39649012290` (52 s in run `39651364456`), versus 18 s in the nearby
|
|||
|
|
legacy run `39648868125`. Per-file immutable project creation is unsuitable for
|
|||
|
|
repository scans even though it protects correctness.
|
|||
|
|
|
|||
|
|
Cross-app and web import isolation now collect the same inputs and parse them
|
|||
|
|
in batches of at most 256 files. Each batch owns one immutable parse-only project
|
|||
|
|
and snapshot, with no library or dependency resolution. Native ASTs are fetched
|
|||
|
|
lazily; the client cache retains only the immediately preceding tree so adjacent
|
|||
|
|
consumers do not transfer it twice. Each batch closes its native process and
|
|||
|
|
snapshot, including on failure. Changed or unlisted source contents still use
|
|||
|
|
isolated immutable projects. The checking rules and traversal are unchanged.
|
|||
|
|
|
|||
|
|
Three alternating complete `pnpm guard` runs on the same macOS machine compared
|
|||
|
|
legacy commit `8d820a6989` with this optimization:
|
|||
|
|
|
|||
|
|
| Variant | Round 1 | Round 2 | Round 3 | Median |
|
|||
|
|
| --- | ---: | ---: | ---: | ---: |
|
|||
|
|
| Legacy JS API | 8.37 s | 8.38 s | 8.42 s | 8.38 s |
|
|||
|
|
| Bounded native batches | 7.43 s | 7.18 s | 6.75 s | 7.18 s |
|
|||
|
|
|
|||
|
|
All runs passed. The median wall time improved by 14.3% over the legacy guard.
|
|||
|
|
These are warm local observations, not an all-platform performance guarantee.
|
|||
|
|
`/usr/bin/time -l` reported maximum resident sizes of 642–648 MB for legacy and
|
|||
|
|
670–716 MB for the final native implementation (decimal units). This is a modest
|
|||
|
|
memory increase, not a memory saving. An unbounded batch prototype reached
|
|||
|
|
2.36 GB and was rejected. No process-wide heap limit or forced GC was added.
|
|||
|
|
|
|||
|
|
Validation includes full guard/typecheck and 41 focused tests across five files,
|
|||
|
|
including batch-boundary traversal, changed content, TS/TSX handling, nested
|
|||
|
|
failure cleanup, and old-tree stability after releasing a batch. Linux CI timing
|
|||
|
|
must be checked independently; local measurements alone do not close the CI
|
|||
|
|
performance regression.
|