Skip to content

perf(egg-bin): boot TypeScript via @oxc-node/core instead of ts-node#6007

Merged
killagu merged 5 commits into
nextfrom
feat/egg-bin-oxc-loader
Jun 28, 2026
Merged

perf(egg-bin): boot TypeScript via @oxc-node/core instead of ts-node#6007
killagu merged 5 commits into
nextfrom
feat/egg-bin-oxc-loader

Conversation

@killagu

@killagu killagu commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Motivation

Every forked egg-bin CLI (and every egg-bin dev/test/cov child) booted TypeScript through ts-node/register + a hardcoded ts-node/esm --loader. ts-node is a JS-based TS compiler and the deprecated --loader hook is slow to spin up — on Windows CI this loader startup dominates the Test bin job (tens of seconds per fork, ~109 forks).

The rest of the repo already moved its runtime .ts transpilation to @oxc-node/core/register (Rust/oxc) in #5965. This PR brings egg-bin in line.

What changed

  • baseCommand.ts — default --tscompiler is now @oxc-node/core/register. oxc installs both a CJS require hook (via pirates) and an ESM module.register() hook from a single --import, so:
    • it is injected as one --import for CJS and ESM apps (its export is import-only, so it is resolved through the package main rather than CJS-resolved / --required);
    • ESM apps no longer get a separate ts-node/esm --loader.
    • tsconfig-paths/register is still injected (oxc does not resolve tsconfig paths).
  • Backward compatible: legacy compilers keep their exact old path. --tscompiler=ts-node/register, @swc-node/register, esbuild-register, pkg.egg.tscompiler and TS_COMPILER all still resolve a CJS register + the ts-node/esm ESM loader, unchanged. ts-node stays a dependency for explicit opt-in.
  • test/coffee.ts — the test harness forks the egg-bin CLI itself via --import @oxc-node/core/register (this is the part that paid the Windows tax).
  • Help summary + the one assertion that pinned ts-node/register updated; stale ts-node/esm comments refreshed.
  • Add @oxc-node/core (catalog:^0.1.0, decorator-metadata-correct).

Test evidence

Ran the full egg-bin suite built, CI-style (build → vitest run) on both the pristine next (ts-node) and this branch (oxc), then diffed the per-test pass/fail sets:

total passed failed skipped
next (ts-node) 125 86 0 39
this PR (oxc) 125 86 0 39

Regressions: none. Test set: identical. Failures: none. Decorator-heavy fixtures, ESM/CJS apps, and the explicit --tscompiler override tests all still pass. tsgo typecheck and oxlint --type-aware clean.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Updated TypeScript startup to use import-based runtime registration via @oxc-node/core/register, and refreshed --tscompiler help text.
    • Aligned forked TypeScript CLI startup to match the new runtime registration approach.
  • Bug Fixes
    • Improved consistency across command, snapshot, and test flows for ESM vs CommonJS loader behavior, with clearer snapshot messaging.
  • Tests
    • Added Vitest coverage for the legacy (non-oxc) --tscompiler initialization path with environment isolation.
    • Updated related help-text/comment expectations.
  • Chores
    • Updated tooling/template dependencies and refreshed Windows CI concurrency guidance (no behavior change).

Replace `ts-node/register` (CJS) + the hardcoded `ts-node/esm` `--loader` with a
single `--import @oxc-node/core/register`, the Rust/oxc loader already used by
the root test suite (#5965). oxc installs both a CJS require hook and an ESM
`module.register()` hook in one shot, so ESM apps no longer need a separate
loader. This removes the slow ts-node startup every forked egg-bin CLI paid
(tens of seconds on Windows CI).

- baseCommand: default `tscompiler` is now `@oxc-node/core/register`; its
  import-only export is injected via `--import` (resolved through the package
  main, since it cannot be CJS-resolved). Legacy compilers (ts-node, swc,
  esbuild) keep their CJS register + ts-node/esm loader path, so explicit
  `--tscompiler=...` overrides behave exactly as before. tsconfig-paths/register
  is still injected (oxc does not resolve tsconfig `paths`).
- test/coffee.ts: fork the egg-bin CLI via `--import @oxc-node/core/register`.
- add `@oxc-node/core` dependency; keep `ts-node` for explicit opt-in.

Full egg-bin suite identical before/after (built, CI-style): 86 passed,
0 failed, 39 skipped — zero regressions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 27, 2026 17:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2d3126b3-fbac-4954-bb34-2eab90eaa4ab

📥 Commits

Reviewing files that changed from the base of the PR and between 6f2a31f and a5cd88d.

📒 Files selected for processing (1)
  • tools/egg-bin/vitest.config.ts
✅ Files skipped from review due to trivial changes (1)
  • tools/egg-bin/vitest.config.ts

📝 Walkthrough

Walkthrough

Replaces ts-node bootstrapping with @oxc-node/core in egg-bin, updates related comments and tests, and refreshes the tegg template dependency version and Windows CI guidance.

Changes

oxc-node/core as default TS compiler

Layer / File(s) Summary
Dependency and compiler switching
tools/egg-bin/package.json, tools/egg-bin/src/baseCommand.ts
Adds @oxc-node/core to dependencies, updates the --tscompiler help text, and changes #afterInit() to default to @oxc-node/core/register, inject register.mjs via --import for oxc compilers, and skip the ts-node/esm branch when oxc is active.
Fork startup and comment updates
tools/egg-bin/test/coffee.ts, tools/egg-bin/src/commands/snapshot.ts, tools/egg-bin/src/commands/test.ts, tools/egg-bin/test/my-egg-bin.test.ts
Replaces the forked process loader setup with --import @oxc-node/core/register`` and updates comments to describe the new startup path.
Non-oxc compiler test
tools/egg-bin/test/commands/test-tscompiler.test.ts
Adds a dry-run test for an explicit non-oxc --tscompiler value, captures output, and restores process.env after execution.
Template version and CI note
tools/create-egg/src/templates/tegg/package.json, tools/egg-bin/vitest.config.ts
Bumps the tegg template @oxc-node/core devDependency version and updates the Windows CI concurrency guidance comment.

Sequence Diagram(s)

sequenceDiagram
  participant TestRunner as Test runner
  participant BaseCommand as BaseCommand#afterInit
  participant Node as Node process
  participant OxcRegister as `@oxc-node/core/register`
  participant LegacyCompiler as legacy tscompiler

  TestRunner->>BaseCommand: start egg-bin with TypeScript enabled
  BaseCommand->>Node: set NODE_OPTIONS / tscompiler defaults
  alt oxc compiler path
    BaseCommand->>OxcRegister: inject register.mjs via --import
  else legacy compiler path
    BaseCommand->>LegacyCompiler: resolve CJS register entry
    BaseCommand->>Node: inject legacy register via formatImportModule()
  end
  BaseCommand->>Node: add tsconfig-paths/register
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

chore: gitAction

Suggested reviewers

  • fengmk2

Poem

🐇 I hopped to oxc with a tidy little bounce,
--import now speaks the magic ounce.
Tests and notes all line up bright,
Windows sleeps a bit more light.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: switching egg-bin TypeScript bootstrapping from ts-node to @oxc-node/core.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/egg-bin-oxc-loader

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request replaces the default TypeScript compiler/loader in egg-bin from ts-node to @oxc-node/core. This change significantly improves startup performance by registering both CommonJS and ES Module hooks via a single --import flag. The review feedback recommends improving the robustness of the compiler detection logic in baseCommand.ts by replacing the broad .includes() check with a more precise string match or prefix check.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread tools/egg-bin/src/baseCommand.ts Outdated
// let child process auto require ts-node too
this.addNodeOptions(this.formatImportModule(tsNodeRegister));
flags.tscompiler = flags.tscompiler ?? '@oxc-node/core/register';
isOxcCompiler = flags.tscompiler.includes('@oxc-node/core');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Avoid using broad substring checks like 'includes()' on file paths or package specifiers to prevent incorrect matches on similarly named directories or files. Instead, match against exact path segments or precise specifier prefixes.

Suggested change
isOxcCompiler = flags.tscompiler.includes('@oxc-node/core');
isOxcCompiler = flags.tscompiler === '@oxc-node/core/register' || flags.tscompiler.startsWith('@oxc-node/core/');
References
  1. When filtering file paths, avoid broad substring checks like 'includes("node_modules")'. Instead, match against exact path segments to prevent incorrect matches on similarly named directories or files.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — addressed in a674b3f. The detection now matches the exact specifier or a @oxc-node/core/ subpath prefix instead of a loose includes(), so a similarly named compiler won't be misdetected as oxc. Also added an in-process test covering the explicit non-oxc --tscompiler register branch.

@codecov

codecov Bot commented Jun 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.11765% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 81.92%. Comparing base (5134d9c) to head (a5cd88d).

Files with missing lines Patch % Lines
tools/egg-bin/src/baseCommand.ts 94.11% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             next    #6007      +/-   ##
==========================================
- Coverage   81.99%   81.92%   -0.07%     
==========================================
  Files         676      677       +1     
  Lines       20522    20639     +117     
  Branches     4060     4096      +36     
==========================================
+ Hits        16826    16908      +82     
- Misses       3189     3217      +28     
- Partials      507      514       +7     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@socket-security

socket-security Bot commented Jun 27, 2026

Copy link
Copy Markdown

Dependency limit exceeded — report not shown.

This pull request scan exceeded the 10,000-dependency limit applied to this scan, so the results are incomplete and may be inaccurate. To avoid reporting false positives, Socket has not posted a report.

Upgrade your plan to raise the dependency limit and get complete reports, or view the partial scan in the dashboard.

Socket is always free for open source. If this is a non-commercial open source project, contact us to request a free Team account.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 27, 2026

Copy link
Copy Markdown

Deploying egg with  Cloudflare Pages  Cloudflare Pages

Latest commit: a5cd88d
Status: ✅  Deploy successful!
Preview URL: https://bc4cdc21.egg-cci.pages.dev
Branch Preview URL: https://feat-egg-bin-oxc-loader.egg-cci.pages.dev

View logs

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 27, 2026

Copy link
Copy Markdown

Deploying egg-v3 with  Cloudflare Pages  Cloudflare Pages

Latest commit: a5cd88d
Status: ✅  Deploy successful!
Preview URL: https://2e4caf1f.egg-v3.pages.dev
Branch Preview URL: https://feat-egg-bin-oxc-loader.egg-v3.pages.dev

View logs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tools/egg-bin/src/baseCommand.ts`:
- Around line 233-240: The default oxc loader resolution in baseCommand.ts is
still using the app’s module resolution path via flags.tscompiler and
cjsResolve(), which allows apps to shadow egg-bin’s bundled `@oxc-node/core`.
Update the loader lookup in the oxc compiler branch so egg-bin resolves its own
bundled `@oxc-node/core` package instead of relying on the app’s install, and keep
the register.mjs injection logic tied to that resolved package root. Verify the
change around isOxcCompiler, cjsResolve('`@oxc-node/core`'), and the default
'`@oxc-node/core/register`' fallback.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 27bbd879-6f2a-4a4c-9223-4d41be56c0c9

📥 Commits

Reviewing files that changed from the base of the PR and between 5134d9c and 1544072.

📒 Files selected for processing (6)
  • tools/egg-bin/package.json
  • tools/egg-bin/src/baseCommand.ts
  • tools/egg-bin/src/commands/snapshot.ts
  • tools/egg-bin/src/commands/test.ts
  • tools/egg-bin/test/coffee.ts
  • tools/egg-bin/test/my-egg-bin.test.ts

Comment thread tools/egg-bin/src/baseCommand.ts Outdated
killagu and others added 2 commits June 28, 2026 01:30
- Detect oxc by exact specifier / `@oxc-node/core/` prefix instead of a loose
  `includes()` substring, so a similarly named compiler can't be misdetected
  (review feedback from gemini-code-assist).
- Add an in-process `test --dry-run` case exercising an explicit non-oxc
  `--tscompiler` (the ts-node/swc/esbuild CJS-register branch), restoring patch
  coverage on baseCommand.ts now that oxc — not that branch — is the default.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When `--tscompiler` is not specified, resolve the bundled `@oxc-node/core`
from egg-bin's own install instead of the app's `baseDir` first. Otherwise an
app pinning an older `@oxc-node/core` (e.g. the tegg template's `^0.0.35`, below
the `>=0.1.0` decorator-metadata floor) would shadow the bundled copy and could
crash on startup. An explicit `--tscompiler=@oxc-node/core/...` keeps the normal
app-first lookup.

Also bump the create-egg tegg template to `@oxc-node/core@^0.1.0`.

Review feedback from coderabbitai.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 27, 2026 17:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

The 2-worker cap was set to bound vitest-in-vitest child-process contention
under the slow ts-node/esm fork startup. Now that children boot via
@oxc-node/core/register, the per-fork loader tax is much lower, so 4 workers
fit and shorten the Windows `Test bin` job.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tools/egg-bin/vitest.config.ts`:
- Line 23: The Windows CI worker cap is currently hidden inside a conditional
spread in vitest.config.ts, so the benchmark parser cannot read it and falls
back to availableParallelism. Update scripts/ci-test-benchmark/environment.js to
recognize this machine-readable conditional maxWorkers shape, or refactor the
vitest config around isWindowsCI/maxWorkers so the cap is exposed in a direct
form the parser can extract.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7e07c2c4-01ad-4416-887c-308ade621558

📥 Commits

Reviewing files that changed from the base of the PR and between b6f12af and 6f2a31f.

📒 Files selected for processing (1)
  • tools/egg-bin/vitest.config.ts

Comment thread tools/egg-bin/vitest.config.ts Outdated
Raising it to 4 oversaturated the 4-vCPU Windows runner: these vitest-in-vitest
forks are CPU-bound (each spawns its own vitest), so 4 workers made every case
slower and `test.test.ts > should success with some files` timed out at 120s
(total ~29m, no speedup over the 2-worker baseline). Revert to 2; oxc already
trims the per-fork loader startup, which is the part that was actually loader-bound.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 27, 2026 18:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@killagu

killagu commented Jun 27, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Reviews resumed.

@killagu killagu merged commit fcd685e into next Jun 28, 2026
25 of 26 checks passed
@killagu killagu deleted the feat/egg-bin-oxc-loader branch June 28, 2026 00:29
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