feat(egg-bundler): default to single-file (snapshot-eligible) bundle output - #5997
Conversation
The default `standalone` output emits a tiny `worker.js` loader that does
`require("./_turbopack__runtime.js")` and pulls in sibling chunks via `R.c(...)`
at runtime. A V8 startup snapshot builder forbids that user-land require of
sibling chunks, so a single self-contained file is needed.
Add an opt-in single-file mode (default stays `standalone`):
- `PackRunner` gains a `singleFile` option. When enabled it switches the
turbopack build to `output.type: "export"` and attaches `library: { name: "app" }`
to every entry, so @utoo/pack inlines all modules into one self-executing IIFE
(`((__UTOOPACK__)=>{...})([...modules])`) with no sibling-chunk require.
- Exposed through `BundlerConfig.pack.singleFile`, threaded by `Bundler`.
Verified by a real @utoo/pack build: the bundle emits a single `worker.js`
(no `_turbopack__runtime`/`R.c(`), passes `node --check`, and runs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughAdds a ChangesSingle-file bundler output
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces a singleFile option to the egg-bundler, enabling the generation of a single self-contained worker file with all modules inlined to support V8 startup snapshots. Feedback suggests simplifying the entry mapping configuration in PackRunner.ts by using object spreading to conditionally apply the library property, which reduces code duplication.
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.
| entry: singleFile | ||
| ? entries.map((e) => ({ name: e.name, import: e.filepath, library: { name: 'app' } })) | ||
| : entries.map((e) => ({ name: e.name, import: e.filepath })), |
There was a problem hiding this comment.
We can simplify the entry mapping to avoid duplicating the .map() call and the entry object structure. This improves maintainability and readability.
| entry: singleFile | |
| ? entries.map((e) => ({ name: e.name, import: e.filepath, library: { name: 'app' } })) | |
| : entries.map((e) => ({ name: e.name, import: e.filepath })), | |
| entry: entries.map((e) => ({ | |
| name: e.name, | |
| import: e.filepath, | |
| ...(singleFile ? { library: { name: 'app' } } : {}), | |
| })), |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## next #5997 +/- ##
=======================================
Coverage 84.89% 84.89%
=======================================
Files 669 669
Lines 19942 19942
Branches 3964 3964
=======================================
Hits 16929 16929
Misses 2588 2588
Partials 425 425 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
tools/egg-bundler/src/lib/PackRunner.ts (1)
156-158: 🚀 Performance & Scalability | 🔵 TrivialRemove hardcoded
library: { name: 'app' }to prevent global namespace collisionsHardcoding the library name causes all entries in
singleFilemode to overwrite one another on the global scope.@utoo/packdocumentation confirms thatlibrary.namedefines the global variable key for each bundle; distinct entries require distinct names to avoid collisions when exposed towindoworglobalThis.♻️ Use the entry name as the library name
entry: singleFile - ? entries.map((e) => ({ name: e.name, import: e.filepath, library: { name: 'app' } })) + ? entries.map((e) => ({ name: e.name, import: e.filepath, library: { name: e.name } })) : entries.map((e) => ({ name: e.name, import: e.filepath })),🤖 Prompt for 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. In `@tools/egg-bundler/src/lib/PackRunner.ts` around lines 156 - 158, The single-file entry mapping in PackRunner is hardcoding library.name to app, which causes global collisions between bundled entries. Update the entries map used in PackRunner so each bundle uses the entry’s own name for library.name instead of a fixed value, keeping the singleFile path and the non-singleFile path aligned with distinct names.Source: Linters/SAST tools
🤖 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-bundler/test/singleFile.realbuild.test.ts`:
- Line 1: The test file name uses camelCase instead of the repository’s
lowercase-with-hyphens convention. Rename the `singleFile.realbuild.test.ts`
test file to a hyphenated lowercase name such as
`single-file.realbuild.test.ts`, and update any references or imports that
depend on the old filename if applicable.
---
Nitpick comments:
In `@tools/egg-bundler/src/lib/PackRunner.ts`:
- Around line 156-158: The single-file entry mapping in PackRunner is hardcoding
library.name to app, which causes global collisions between bundled entries.
Update the entries map used in PackRunner so each bundle uses the entry’s own
name for library.name instead of a fixed value, keeping the singleFile path and
the non-singleFile path aligned with distinct names.
🪄 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: 85030aec-cbe0-47e4-b44d-efeb35923654
📒 Files selected for processing (5)
tools/egg-bundler/src/index.tstools/egg-bundler/src/lib/Bundler.tstools/egg-bundler/src/lib/PackRunner.tstools/egg-bundler/test/PackRunner.test.tstools/egg-bundler/test/singleFile.realbuild.test.ts
- Collapse the `entry` ternary into a single map that spreads `library` conditionally (Gemini / CodeRabbit): avoids duplicating the entry-shape builder across the standalone/single-file branches. - Rename test to `single-file.realbuild.test.ts` to follow the repo's lowercase-with-hyphens file naming convention (CodeRabbit). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Addressed both review comments in ec708d6:
Tests still green (18 passed). |
|
Thanks for the update, @killagu. The changes look great and address the feedback effectively. Collapsing the entry mapping logic is much cleaner, and the filename adjustment for the test file ensures consistency with the existing naming conventions. Everything looks good to go. |
|
🐇✨ If you found this review helpful, would you consider giving us a shout-out on X? Thank you for using CodeRabbit! ✅ Action performedComments resolved. Approval is disabled; enable |
Single-file (`output.type: "export"` + per-entry `library`) is now the
default output mode, since it is what V8 startup snapshots require (a
snapshot builder forbids the user-land `require` of sibling chunks that
the legacy standalone output relies on).
- PackRunner `singleFile` now defaults to `true`.
- Pass `pack: { singleFile: false }` to opt back into the legacy
multi-chunk standalone output.
- Update docs and tests: the default config now asserts export output
with a per-entry library; a dedicated test covers the standalone
opt-out; the real-build test exercises the default (no explicit flag).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Review against PR1 (egg-bundler single-file output) — ✅ 符合,一处需确认的偏差机制完全对齐 PR1:
验收点齐全(
一处与 PR1 描述的偏差,需 reviewer 自觉接受: 结论:作为 PR1 可合入,机制与测试均达标;唯一决策点是默认值翻转。 |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tools/egg-bundler/test/single-file.realbuild.test.ts (1)
88-107: 🩺 Stability & Availability | 🟠 MajorEnsure test execution environment meets the bundle's
node 22target requirement.The bundled output targets Node 22 (confirmed in
PackRunner.test.ts), but the test at line 106 executes the result using the ambientnodebinary. If a developer runs this on Node < 22, the test will fail due to runtime incompatibility rather than a bundler regression.While CI runs on Node 24 (compatible), local environments may drift. Guard the test execution or add a clear requirement:
// tools/egg-bundler/test/single-file.realbuild.test.ts import { version } from 'node:process'; const [major] = version.replace('v', '').split('.').map(Number); if (major < 22) { // Skip test if the runner's Node version < target return; }Alternatively, update the test description to explicitly state the Node >= 22 requirement.
🤖 Prompt for 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. In `@tools/egg-bundler/test/single-file.realbuild.test.ts` around lines 88 - 107, The single-file bundler test is running the generated worker.js with the ambient node binary without ensuring it matches the Node 22 runtime target. Update the test in single-file.realbuild.test.ts around the bundle() execution so it either skips/guards when process.version is below 22 or otherwise enforces a Node >= 22 runner, using the existing bundle test setup and assertions to keep the check aligned with the PackRunner Node 22 target.
🤖 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.
Outside diff comments:
In `@tools/egg-bundler/test/single-file.realbuild.test.ts`:
- Around line 88-107: The single-file bundler test is running the generated
worker.js with the ambient node binary without ensuring it matches the Node 22
runtime target. Update the test in single-file.realbuild.test.ts around the
bundle() execution so it either skips/guards when process.version is below 22 or
otherwise enforces a Node >= 22 runner, using the existing bundle test setup and
assertions to keep the check aligned with the PackRunner Node 22 target.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 9d47d066-a60b-4fba-a3e6-4197659b42f3
📒 Files selected for processing (4)
tools/egg-bundler/src/index.tstools/egg-bundler/src/lib/PackRunner.tstools/egg-bundler/test/PackRunner.test.tstools/egg-bundler/test/single-file.realbuild.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- tools/egg-bundler/src/lib/PackRunner.ts
- tools/egg-bundler/src/index.ts
… command Add the V8 startup snapshot entry-generation mechanism to @eggjs/egg-bundler (building on the single-file output default from #5997) plus egg-bin commands. - EntryGenerator emits a 3-mode worker entry driven by EGG_BUNDLE_SNAPSHOT: - normal: startEgg + listen (unchanged) - snapshot-build (EGG_BUNDLE_SNAPSHOT=build): startEgg({snapshot:true}), run snapshotWillSerialize hooks, register v8 setDeserializeMainFunction - restore main: setImmediate-deferred (ESM loader not ready at deserialize), installs require-based __EGG_MODULE_IMPORTER__/__RUNTIME_REQUIRE hooks so the egg loader avoids the missing dynamic import() callback, then resumes snapshotDidDeserialize and listens - Add a snapshot prelude generator; Bundler prepends it before the bundle IIFE in snapshot mode (skeleton placeholder; PR3 fills the lazy/stub mechanism) - BundlerConfig.snapshot forces single-file output and prelude prepend - egg-bin: add `snapshot build` (bundle + node --build-snapshot) and `snapshot start` (node --snapshot-blob); spawn (not fork) to avoid an IPC handle that would break --build-snapshot - Declare __RUNTIME_REQUIRE in @eggjs/typings global Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… command Add the V8 startup snapshot entry-generation mechanism to @eggjs/egg-bundler (building on the single-file output default from #5997) plus egg-bin commands. - EntryGenerator emits a 3-mode worker entry driven by EGG_BUNDLE_SNAPSHOT: - normal: startEgg + listen (unchanged) - snapshot-build (EGG_BUNDLE_SNAPSHOT=build): startEgg({snapshot:true}), run snapshotWillSerialize hooks, register v8 setDeserializeMainFunction - restore main: setImmediate-deferred (ESM loader not ready at deserialize), installs require-based __EGG_MODULE_IMPORTER__/__RUNTIME_REQUIRE hooks so the egg loader avoids the missing dynamic import() callback, then resumes snapshotDidDeserialize and listens - Add a snapshot prelude generator; Bundler prepends it before the bundle IIFE in snapshot mode (skeleton placeholder; PR3 fills the lazy/stub mechanism) - BundlerConfig.snapshot forces single-file output and prelude prepend - egg-bin: add `snapshot build` (bundle + node --build-snapshot) and `snapshot start` (node --snapshot-blob); spawn (not fork) to avoid an IPC handle that would break --build-snapshot - Declare __RUNTIME_REQUIRE in @eggjs/typings global Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… command Add the V8 startup snapshot entry-generation mechanism to @eggjs/egg-bundler (building on the single-file output default from #5997) plus egg-bin commands. - EntryGenerator emits a 3-mode worker entry driven by EGG_BUNDLE_SNAPSHOT: - normal: startEgg + listen (unchanged) - snapshot-build (EGG_BUNDLE_SNAPSHOT=build): startEgg({snapshot:true}), run snapshotWillSerialize hooks, register v8 setDeserializeMainFunction - restore main: setImmediate-deferred (ESM loader not ready at deserialize), installs require-based __EGG_MODULE_IMPORTER__/__RUNTIME_REQUIRE hooks so the egg loader avoids the missing dynamic import() callback, then resumes snapshotDidDeserialize and listens - Add a snapshot prelude generator; Bundler prepends it before the bundle IIFE in snapshot mode (skeleton placeholder; PR3 fills the lazy/stub mechanism) - BundlerConfig.snapshot forces single-file output and prelude prepend - egg-bin: add `snapshot build` (bundle + node --build-snapshot) and `snapshot start` (node --snapshot-blob); spawn (not fork) to avoid an IPC handle that would break --build-snapshot - Declare __RUNTIME_REQUIRE in @eggjs/typings global Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… command Add the V8 startup snapshot entry-generation mechanism to @eggjs/egg-bundler (building on the single-file output default from #5997) plus egg-bin commands. - EntryGenerator emits a 3-mode worker entry driven by EGG_BUNDLE_SNAPSHOT: - normal: startEgg + listen (unchanged) - snapshot-build (EGG_BUNDLE_SNAPSHOT=build): startEgg({snapshot:true}), run snapshotWillSerialize hooks, register v8 setDeserializeMainFunction - restore main: setImmediate-deferred (ESM loader not ready at deserialize), installs require-based __EGG_MODULE_IMPORTER__/__RUNTIME_REQUIRE hooks so the egg loader avoids the missing dynamic import() callback, then resumes snapshotDidDeserialize and listens - Add a snapshot prelude generator; Bundler prepends it before the bundle IIFE in snapshot mode (skeleton placeholder; PR3 fills the lazy/stub mechanism) - BundlerConfig.snapshot forces single-file output and prelude prepend - egg-bin: add `snapshot build` (bundle + node --build-snapshot) and `snapshot start` (node --snapshot-blob); spawn (not fork) to avoid an IPC handle that would break --build-snapshot - Declare __RUNTIME_REQUIRE in @eggjs/typings global Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… command Add the V8 startup snapshot entry-generation mechanism to @eggjs/egg-bundler (building on the single-file output default from #5997) plus egg-bin commands. - EntryGenerator emits a 3-mode worker entry driven by EGG_BUNDLE_SNAPSHOT: - normal: startEgg + listen (unchanged) - snapshot-build (EGG_BUNDLE_SNAPSHOT=build): startEgg({snapshot:true}), run snapshotWillSerialize hooks, register v8 setDeserializeMainFunction - restore main: setImmediate-deferred (ESM loader not ready at deserialize), installs require-based __EGG_MODULE_IMPORTER__/__RUNTIME_REQUIRE hooks so the egg loader avoids the missing dynamic import() callback, then resumes snapshotDidDeserialize and listens - Add a snapshot prelude generator; Bundler prepends it before the bundle IIFE in snapshot mode (skeleton placeholder; PR3 fills the lazy/stub mechanism) - BundlerConfig.snapshot forces single-file output and prelude prepend - egg-bin: add `snapshot build` (bundle + node --build-snapshot) and `snapshot start` (node --snapshot-blob); spawn (not fork) to avoid an IPC handle that would break --build-snapshot - Declare __RUNTIME_REQUIRE in @eggjs/typings global Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… command Add the V8 startup snapshot entry-generation mechanism to @eggjs/egg-bundler (building on the single-file output default from #5997) plus egg-bin commands. - EntryGenerator emits a 3-mode worker entry driven by EGG_BUNDLE_SNAPSHOT: - normal: startEgg + listen (unchanged) - snapshot-build (EGG_BUNDLE_SNAPSHOT=build): startEgg({snapshot:true}), run snapshotWillSerialize hooks, register v8 setDeserializeMainFunction - restore main: setImmediate-deferred (ESM loader not ready at deserialize), installs require-based __EGG_MODULE_IMPORTER__/__RUNTIME_REQUIRE hooks so the egg loader avoids the missing dynamic import() callback, then resumes snapshotDidDeserialize and listens - Add a snapshot prelude generator; Bundler prepends it before the bundle IIFE in snapshot mode (skeleton placeholder; PR3 fills the lazy/stub mechanism) - BundlerConfig.snapshot forces single-file output and prelude prepend - egg-bin: add `snapshot build` (bundle + node --build-snapshot) and `snapshot start` (node --snapshot-blob); spawn (not fork) to avoid an IPC handle that would break --build-snapshot - Declare __RUNTIME_REQUIRE in @eggjs/typings global Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… command (#5998) ## Motivation `@eggjs/egg-bundler` now defaults to single-file (snapshot-eligible) output (#5997). This PR adds the mechanism that turns that bundle into a **V8 startup snapshot**: a 3-mode generated entry, an auto-prepended runtime prelude, an `egg-bin snapshot build` command, and `egg-scripts start --snapshot-blob` to boot from the blob. egg core already has the snapshot lifecycle (`buildSnapshot`/`restoreSnapshot`, `snapshotWillSerialize`/`snapshotDidDeserialize`, `snapshot:true` stopping at `configWillLoad`). This wires egg-bundler + egg-bin + egg-scripts to drive it. ## Scope - **3-mode worker entry** (`EntryGenerator`), selected at runtime by `EGG_BUNDLE_SNAPSHOT`: - **normal** — `startEgg` + `listen` (unchanged behavior). - **snapshot-build** (`EGG_BUNDLE_SNAPSHOT=build`) — `startEgg({ snapshot: true })`, run `snapshotWillSerialize` hooks, then `v8.startupSnapshot.setDeserializeMainFunction(...)`. - **restore main** (inside the deserialize callback) — deferred via `setImmediate` (the Node ESM loader isn't ready when the callback runs), installs `require`-based `__EGG_MODULE_IMPORTER__` / `__RUNTIME_REQUIRE` hooks (via `process.getBuiltinModule('node:module')`, with an eval-require fallback for Node < 22.3) so the egg loader avoids the missing dynamic `import()` callback, resumes `snapshotDidDeserialize`, `listen`s, and emits an `egg-ready` IPC message for daemon readiness. - **Snapshot prelude** (`prelude.ts`) — `Bundler` prepends it before the bundle IIFE in snapshot mode so it runs before any module loads. Skeleton/placeholder; PR3 fills the lazy / native-binding-stub mechanism. - **`BundlerConfig.snapshot`** — forces single-file output (even if `pack.singleFile: false`) and triggers the prelude prepend. - **`egg-bin snapshot build`** — bundle in snapshot mode, then `node --snapshot-blob X --build-snapshot worker.js` (clean env so the ts-node loader isn't applied to the bundle; verifies the blob exists). Uses `spawn` (not `fork`) so no IPC handle breaks `--build-snapshot`. - **`egg-scripts start --snapshot-blob <blob>`** — boots the single self-contained snapshot process (no egg-cluster), reusing the existing daemon / stdout-stderr / signal lifecycle. `egg-scripts stop` recognizes snapshot processes by `--snapshot-blob` + `--title`. - `__RUNTIME_REQUIRE` declared in `@eggjs/typings` global; shared bundle-option helpers extracted to `bundleOptions.ts` (deduped from `bundle.ts`). > `snapshot start` lives in `@eggjs/scripts` (the production launcher), not `egg-bin` (dev/build tooling): a deployed app that ships a blob has egg-scripts available but usually not egg-bin. ## Boundary This PR is the entry/prelude skeleton + commands. The lazy/stub mechanism that converges non-serializable native bindings lands in PR3, so a live `egg-scripts start --snapshot-blob` may still hit native-binding errors after `setDeserializeMainFunction` until then. The egg-scripts daemon/stop path is unit-tested at the spawn-arg / process-match level; end-to-end daemon boot is verifiable once restore works (PR3). ## Test evidence - `tools/egg-bundler`: `prelude.test.ts`; `Bundler.test.ts` snapshot-mode cases (forces single-file even with `pack.singleFile:false`, prepends prelude, fails fast on missing worker.js); `EntryGenerator.test.ts` 3-mode + egg-ready assertions + regenerated canonical snapshot. - `tools/egg-bin`: `snapshot.test.ts` (build bundles + spawns `--build-snapshot` with clean env; `--skip-bundle`; custom `--blob`; `--dry-run`; non-zero exit / missing-blob rejections); `bundleOptions.test.ts`; existing `bundle.test.ts` green after the helper extraction. - `tools/scripts`: `snapshot-start.test.ts` (boots via `node --snapshot-blob`, no cluster bin; `--port` → PORT env); `snapshot-stop.test.ts` (stop matches snapshot processes by title). - typecheck + oxlint clean on all three packages. Pre-existing, unrelated to this PR: a few `EntryGenerator`/`ManifestLoader` tests fail locally on macOS due to `/var`→`/private/var` realpath (identical on the base branch). 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added V8 startup-snapshot workflow: `snapshot build`, and snapshot boot support for the start/stop flow. * Implemented snapshot prelude injection for single-file worker bundles and added a runtime require helper for snapshot restores. * Exposed additional command/option entry points and shared bundle options (mode/framework selection, `--pack-alias`). * **Bug Fixes** * Fixed port handling so `PORT=0` is treated as an explicit value. * Improved validation and error reporting when expected snapshot artifacts are missing. * **Tests** * Added and expanded unit/integration tests for snapshot commands, bundler/prelude/entry generation, and bundle options. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Motivation
@eggjs/egg-bundlerpreviously emitted @utoo/pack'sstandaloneoutput: a tinyworker.jsloader that doesrequire("./_turbopack__runtime.js")and pulls in sibling chunks at runtime viaR.c(...).A V8 startup snapshot builder forbids user-land
requireof sibling chunks — the snapshot must be a single self-contained file. Since snapshot support is the direction egg-bundler is built for, this PR makes single-file output the default.Scope
Single-file output is now the default.
PackRunnersets two things on the @utoo/pack build config:output.type:"export"(was"standalone")library: { name: "app" }With
export+ per-entrylibrary, @utoo/pack inlines every module into one self-executing IIFE (((__UTOOPACK__)=>{...})([...modules])) — the emittedworker.jscarries no sibling-chunk require and is snapshot-eligible. (A NAPI-RS loader helperindex.*.jsis normal.)Opt-out preserved. Pass
pack: { singleFile: false }(BundlerConfig.pack.singleFile) to fall back to the legacy multi-chunk standalone output.Test evidence
test/PackRunner.test.ts: the default config now assertsexportoutput with a per-entrylibrary; a dedicated test assertssingleFile: falseproduces the legacystandaloneoutput with nolibrary.test/single-file.realbuild.test.ts: a real @utoo/pack build throughbundle()with nopackoption (i.e. the default) asserts the output dir holds exactly oneworker.js(no_turbopack__runtime.jssibling),worker.jsmatches neither_turbopack__runtimenorR.c(,node --check worker.jspasses, and the inlined module runs.Manual verification of the acceptance checks on a default bundle:
dist/contains onlyworker.js(oneworker*.js, no_turbopack__runtime.js)grep -c "_turbopack__runtime\|R.c(" worker.js→0node --check worker.js→ PASS, and direct execution prints the inlined module's value🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
worker.jssuitable for V8 startup snapshots.Bug Fixes
Tests
singleFile: false.