Skip to content

Tracking: split #5863 into 19 small PRs #5871

@killagu

Description

@killagu

Goal

Split #5863 (a 4812-line monolithic PR adding `@eggjs/egg-bundler` for turbopack-based application bundling) into 19 small, independently-reviewable PRs.

#5863 stays open as the reference / smoke-test PR. The 19 small PRs land incrementally.

Architecture (5 layers — see #5863)

  1. Manifest pre-computation — `ManifestLoader` spawns `generate-manifest.mjs` (with tsx loader injected) to dump file discovery / resolveCache / tegg metadata; realpath → `node_modules/` normalization.
  2. Externals resolution — `ExternalsResolver` excludes native addons / ESM-only-without-require / peerDeps. Final commit removes `@eggjs/*` from always-external so the framework itself can be bundled.
  3. Entry generation — `EntryGenerator` writes a synthetic `worker.entry.ts` with sorted static imports, inlined `MANIFEST_DATA`, dual-keyed `BUNDLE_MAP`, runtime baseDir via `process.argv[1]`, and `startEgg({mode:'single'})` + `app.listen()`.
  4. Build orchestration — `Bundler` wires the 4 layers; `PackRunner` wraps `@utoo/pack/cjs/commands/build.js` with pre-written tsconfig (decorators) + `package.json {type:'commonjs'}`.
  5. Runtime support APIs — `@eggjs/utils.setBundleModuleLoader`, `@eggjs/core.ManifestStore.fromBundle`, `ManifestStore.setBundleStore`. State on `globalThis` to cross bundled/external module-instance boundaries.

Plus plugin compatibility patches (onerror/development/watcher) that inline templates and replace path-based references with class imports.

cnpmcore E2E result on #5863: externals 76 → 12, HTTP 200.

Batches & status

Batch 1 — independent of unmerged upstream (4 PRs, OPEN)

Batch 2 — Group A continued (opens after #5867 merges)

  • `feat(core): add ManifestStore.fromBundle for bundled artifacts` — packages/core/src/loader/manifest.ts
  • `feat(core): add ManifestStore.setBundleStore hook` — globalThis-stored bundle store registration

Batch 3 — Group C base (opens after Batch 2 merges)

  • `feat(bundler): scaffold @eggjs/egg-bundler package` — package.json + tsconfig + tsdown + vitest + workspace catalog (`@utoo/pack`, `tsx`)
  • `feat(bundler): add ExternalsResolver` — native addon / ESM-only / peerDeps detection with force/inline overrides
  • `feat(bundler): add ManifestLoader with realpath normalization` — workspace-link / .pnpm path → `node_modules/` longest-prefix matching
  • `feat(bundler): add generate-manifest subprocess with tsx loader injection` — spawn (not fork) + `--import` tsx + framework entry from `exports['.']`
  • `feat(bundler): define BundlerConfig public API` — type surface + placeholder `bundle()`

Batch 4 — Group C orchestration (opens after Batch 3 merges)

  • `feat(bundler): add EntryGenerator with sorted static imports` — deterministic worker.entry.ts, internal/external split, `process.argv[1]` baseDir, port listen
  • `feat(bundler): add PackRunner wrapper over @utoo/pack` — pre-write tsconfig + package.json, wrapped `{config}` arg, UMD-shaped externals
  • `feat(bundler): add Bundler orchestrator` — wires 4 layers, named error wrapping, writes `bundle-manifest.json`, patches output package.json name
  • `test(bundler): verify no-fs-scan contract and bundle determinism` — short-circuit assertions + byte-identical-across-runs assertions
  • `test(bundler): add tegg-app fixture with HTTPController + service` — `@SingletonProto` + `@HTTPController` exposing GET /tegg/hello

Batch 5 — CLI + final integration (opens after Batch 4 merges)

  • `feat(egg-bin): add bundle subcommand` — thin wrapper over `@eggjs/egg-bundler.bundle()` with `--output/--manifest/--framework/--mode/--no-tegg/--force-external/--inline-external`
  • `feat(bundler): remove @eggjs/* from auto-externals` — lets the framework itself be bundled
  • `feat(bundler): post-process turbopack output to fix import.meta.url/dirname` — Bundler `#patchImportMetaUrl` step

Improvements over #5863

These split PRs fix issues present in #5863:

  • E1 typecheck: removing the `@eggjs/*` external rule leaves `#isEsmOnly` and `#hasRequireCondition` as dead code, breaking `tsgo --noEmit`. Our split removes both methods as part of the same commit.
  • C9 deterministic test flake (~60% rate): `deterministic.test.ts` races with `integration.test.ts` over `minimal-app/.egg/`. Our split filters `.egg/` and `.egg-bundle/` at `fs.cp` time so the race is impossible.

Workflow

After each batch's PRs merge to `next`, the next batch's branches will be extracted from local stacks (`split/group-a-runtime`, `split/c-orchestration`) and pushed. Each branch is rebased onto the current `next` HEAD before opening. Stacked PRs are not opened in advance — only PRs whose base is the current `next` (no unmerged dependencies) are opened.

🤖 Tracking issue generated alongside batch-1 PR creation by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions