Skip to content

feat(bundler): inject manifest loader fs into worker#5944

Open
killagu wants to merge 4 commits intonextfrom
agent/egg-dev/8cf5cc33
Open

feat(bundler): inject manifest loader fs into worker#5944
killagu wants to merge 4 commits intonextfrom
agent/egg-dev/8cf5cc33

Conversation

@killagu
Copy link
Copy Markdown
Contributor

@killagu killagu commented May 10, 2026

Summary

  • include the prerequisite ManifestLoaderFS work from feat(core): add manifest-backed loader fs #5943 for this branch
  • thread loaderFS through startEgg and EggCore into EggLoader
  • generate bundled worker entries that create ManifestLoaderFS from the inline manifest store and pass it via loader options
  • update entry generator/runtime tests and the canonical worker snapshot

Tests

  • pnpm exec vitest run tools/egg-bundler/test/EntryGenerator.test.ts packages/core/test/egg.test.ts packages/core/test/loader/manifest_loader_fs.test.ts -u (root run picked up the two core files)
  • pnpm exec vitest run test/EntryGenerator.test.ts -u from tools/egg-bundler
  • pnpm --filter @eggjs/core typecheck
  • pnpm --filter egg typecheck
  • pnpm --filter @eggjs/egg-bundler typecheck
  • pnpm exec oxlint --type-aware packages/core/src/egg.ts packages/core/test/egg.test.ts packages/egg/src/lib/start.ts tools/egg-bundler/src/lib/EntryGenerator.ts tools/egg-bundler/test/EntryGenerator.test.ts (0 errors; existing warnings in untouched sections of core files)

Summary by CodeRabbit

  • New Features
    • Added a configurable loaderFS option enabling manifest-backed filesystem behavior and bundled-app file resolution; bundler now wires this through startup.
  • Tests
    • Added and extended tests for manifest-backed FS behavior, coverage discovery, and generated bundler runtime hooks.
  • Chores
    • Added multimatch dependency for improved pattern matching.

Review Change Stack

Copilot AI review requested due to automatic review settings May 10, 2026 03:36
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 10, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e689f76a-1012-4e83-a6a2-afeee67df987

📥 Commits

Reviewing files that changed from the base of the PR and between 8cef622 and e153fd4.

📒 Files selected for processing (2)
  • packages/core/src/loader/egg_loader.ts
  • packages/core/test/egg.test.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/core/test/egg.test.ts

📝 Walkthrough

Walkthrough

Adds ManifestLoaderFS (manifest-backed LoaderFS), threads an injectable loaderFS option through EggCore/startup, updates EggLoader to prefer/wrap with ManifestLoaderFS, updates bundler output to instantiate/pass ManifestLoaderFS, and adds tests and a multimatch dependency.

Changes

Manifest-Backed Loader Filesystem

Layer / File(s) Summary
Public API & Type Contracts
packages/core/src/egg.ts, packages/egg/src/lib/start.ts, packages/core/package.json
EggCoreOptions and StartEggOptions gain loaderFS?: LoaderFS. multimatch added to packages/core dependencies.
ManifestLoaderFS Implementation
packages/core/src/loader/loader_fs.ts
New exported ManifestLoaderFS implements LoaderFS: virtualized exists, stat, realpath, readJSON, glob, and loadFile against a ManifestStore, with bundle-module lookup and fallback to a real FS.
EggLoader Integration
packages/core/src/loader/egg_loader.ts
EggLoader now selects/constructs ManifestLoaderFS for matching bundle stores, uses loaderFS.exists()/readJSON() for plugin package checks, and records convention package.json files into manifest.fileDiscovery while threading a resolve-cache set.
EggCore & Startup Forwarding
packages/core/src/egg.ts, packages/egg/src/lib/start.ts
EggCore and startup pass options.loaderFS into the loader/startup flow so callers may inject a LoaderFS implementation.
Bundler EntryGenerator
tools/egg-bundler/src/lib/EntryGenerator.ts
Generated worker entries now create a bundle ManifestStore, instantiate ManifestLoaderFS, register the bundle store, and call startEgg with loaderFS: __loaderFS.
Tests & Validation
packages/core/test/..., tools/egg-bundler/test/...
New and updated tests: manifest_loader_fs.test.ts covers manifest-backed behavior, fallback, precedence, and empty-dir globbing; egg core and bundler tests updated to assert loaderFS forwarding and generated code changes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • eggjs/egg#5867: Related — global bundle module loader hookup used by ManifestLoaderFS.
  • eggjs/egg#5922: Related — overlapping changes to manifest generation and resolve/cache handling in EggLoader.
  • eggjs/egg#5844: Related — introduces ManifestStore and earlier manifest-backed resolution work this change builds on.

Suggested reviewers

  • fengmk2
  • gxkl
  • akitaSummer
  • jerryliang64

Poem

🐰 I found a map inside a tiny chest,
ManifestLoaderFS helps files find rest,
Virtual stats hum where real disks sleep,
Bundled modules leap while fallbacks keep,
Tests hop through paths — the bundle's blessed.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main feature: injecting a manifest loader filesystem into the bundled worker, which aligns with the primary changes across the codebase.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 agent/egg-dev/8cf5cc33

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 and usage tips.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 10, 2026

Deploying egg with  Cloudflare Pages  Cloudflare Pages

Latest commit: e153fd4
Status: ✅  Deploy successful!
Preview URL: https://31eb13e9.egg-cci.pages.dev
Branch Preview URL: https://agent-egg-dev-8cf5cc33.egg-cci.pages.dev

View logs

@codecov
Copy link
Copy Markdown

codecov Bot commented May 10, 2026

Codecov Report

❌ Patch coverage is 91.58879% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.25%. Comparing base (0dec2c9) to head (e153fd4).

Files with missing lines Patch % Lines
packages/core/src/loader/loader_fs.ts 90.32% 16 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             next    #5944      +/-   ##
==========================================
+ Coverage   85.21%   85.25%   +0.04%     
==========================================
  Files         669      669              
  Lines       19304    19508     +204     
  Branches     3787     3847      +60     
==========================================
+ Hits        16449    16631     +182     
- Misses       2463     2483      +20     
- Partials      392      394       +2     

☔ View full report in Codecov by Sentry.
📢 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.

Copy link
Copy Markdown
Contributor

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

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 introduces ManifestLoaderFS, a new filesystem abstraction that allows the Egg loader to resolve and load files using a manifest, prioritizing bundled modules over the real filesystem. The implementation is integrated into EggCore and EggLoader, and the egg-bundler tool is updated to leverage this for generated entries. Feedback focuses on performance and logic improvements within ManifestLoaderFS and EggLoader. Specifically, it is recommended to replace linear scans (O(N)) with pre-calculated data structures like Set to improve efficiency in methods like isManifestFile and isManifestDirectory. Additionally, the reviewer suggests using exact path segment matching instead of startsWith to avoid over-broad directory matching and optimizing the manifest generation loop in EggLoader to prevent O(N^2) complexity.

Comment thread packages/core/src/loader/loader_fs.ts
Comment thread packages/core/src/loader/egg_loader.ts Outdated
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 10, 2026

Deploying egg-v3 with  Cloudflare Pages  Cloudflare Pages

Latest commit: e153fd4
Status: ✅  Deploy successful!
Preview URL: https://b96f1b8e.egg-v3.pages.dev
Branch Preview URL: https://agent-egg-dev-8cf5cc33.egg-v3.pages.dev

View logs

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR wires a manifest-backed LoaderFS through the single-process startEgg() path and updates the egg-bundler worker entry to construct and pass a ManifestLoaderFS instance derived from the inlined bundled ManifestStore. This enables bundled workers to perform loader filesystem operations against the startup manifest/bundle map instead of relying on the host filesystem.

Changes:

  • Add loaderFS as an option on startEgg() / EggCore, and pass it into EggLoader.
  • Introduce ManifestLoaderFS (manifest-first LoaderFS) and have EggLoader default to it when a manifest/bundle store is present.
  • Update egg-bundler’s generated worker entry + tests/snapshots to create ManifestLoaderFS from the inlined manifest store and pass it to startEgg().

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tools/egg-bundler/test/EntryGenerator.test.ts Updates expectations/runtime test to validate ManifestLoaderFS creation and loaderFS threading into startEgg().
tools/egg-bundler/test/snapshots/EntryGenerator.worker.canonical.snap Updates canonical worker entry snapshot to include ManifestLoaderFS and loaderFS option.
tools/egg-bundler/src/lib/EntryGenerator.ts Generates worker entry code that instantiates ManifestLoaderFS from the bundled manifest store and passes it to startEgg().
packages/egg/src/lib/start.ts Adds loaderFS?: LoaderFS to StartEggOptions and threads it through Agent/Application construction.
packages/core/test/loader/manifest_loader_fs.test.ts Adds coverage for manifest-backed LoaderFS behavior (stat/realpath/glob/readJSON/loadFile + fallback).
packages/core/test/loader/manifest_coverage.test.ts Extends coverage assertions to ensure plugin package.json metadata is captured in manifest discovery.
packages/core/test/egg.test.ts Adds a regression test asserting EggCore({ loaderFS }) passes through to EggLoader.
packages/core/test/snapshots/index.test.ts.snap Updates public export snapshot to include ManifestLoaderFS.
packages/core/src/loader/loader_fs.ts Implements ManifestLoaderFS and supporting glob/filter utilities.
packages/core/src/loader/egg_loader.ts Defaults/wraps loaderFS with ManifestLoaderFS, switches plugin metadata reads to loaderFS, and collects package.json into manifest discovery.
packages/core/src/egg.ts Adds loaderFS?: LoaderFS to EggCoreOptions and forwards into EggLoader options.
packages/core/package.json Adds multimatch dependency used for manifest-backed glob matching.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
packages/core/src/loader/egg_loader.ts (2)

98-101: 💤 Low value

Constructor + post-manifest wiring is consistent — minor note on bundle vs. local-manifest divergence.

When bundleStore?.baseDir === this.options.baseDir, loaderFS becomes new ManifestLoaderFS(bundleStore) and the post-manifest wrap is skipped (instanceof check). That means in bundled mode the FS layer reads from bundleStore, while this.manifest (loaded/created locally) is used by resolveModule. This is fine if the loaded manifest mirrors the bundle store, but worth a brief inline comment so future readers don’t assume this.manifest and the FS layer share a single manifest source in this branch.

Also applies to: 182-184

🤖 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 `@packages/core/src/loader/egg_loader.ts` around lines 98 - 101, The code sets
this.loaderFS to new ManifestLoaderFS(bundleStore) when
ManifestStore.getBundleStore().baseDir === this.options.baseDir, which means the
FS layer will read from the bundle store while this.manifest (used by
resolveModule) may be the locally loaded/created manifest; add a brief inline
comment next to the assignment (near ManifestStore.getBundleStore,
ManifestLoaderFS, RealLoaderFS and the post-manifest instanceof check around
resolveModule) explaining that in bundled mode the loaderFS and this.manifest
can diverge and that resolveModule relies on this.manifest while
ManifestLoaderFS reads from the bundle store so readers don’t assume a single
shared manifest source.

1848-1858: 💤 Low value

LGTM — minor: the resolveCache scan is O(n) per call.

Object.values(manifest.resolveCache).includes(fileKey) is linear in resolveCache size and runs once per load unit. This only affects manifest generation (not request hot paths), so it's fine, but a Set<string> of resolveCache values built once in #collectConventionalDynamicFiles would avoid the repeated scan if this list grows.

♻️ Optional: precompute the value set once
   `#collectConventionalDynamicFiles`(manifest: StartupManifest): void {
+    const resolveCacheTargets = new Set(Object.values(manifest.resolveCache).filter((v): v is string => typeof v === 'string'));
     for (const unit of this.getLoadUnits()) {
-      this.#collectConventionFile(manifest, path.join(unit.path, 'package.json'));
+      this.#collectConventionFile(manifest, path.join(unit.path, 'package.json'), resolveCacheTargets);
       ...
🤖 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 `@packages/core/src/loader/egg_loader.ts` around lines 1848 - 1858, The current
check in `#collectConventionFile` uses
Object.values(manifest.resolveCache).includes(fileKey) which is O(n) per call;
change the approach by precomputing a Set of manifest.resolveCache values once
in `#collectConventionalDynamicFiles` (e.g., const resolvedSet = new
Set(Object.values(manifest.resolveCache))) and pass that Set into
`#collectConventionFile` (or attach it to the manifest under a temporary property)
so `#collectConventionFile` can do resolvedSet.has(fileKey) instead of a repeated
linear scan; update all call sites of `#collectConventionFile` in
`#collectConventionalDynamicFiles` to provide the Set and remove the
Object.values(...).includes usage.
packages/core/src/loader/loader_fs.ts (1)

178-204: ⚖️ Poor tradeoff

Optional: precompute the set of manifest directories.

#isManifestDirectory runs an O(D × F) scan over fileDiscovery (and another O(R) over resolveCache) on every call, and it is reached from every exists/stat/realpath/loadFile/readJSON invocation plus glob via #getManifestEntry and #listManifestFilesUnder. This is a hot path with repeated scans for typical loader operations.

If you want to keep it O(1), precompute a Set<string> of every directory that the manifest covers in the constructor (fileDiscovery keys + parent directories of every dir/file combination and each non-null resolveCache target), then this method becomes a single set.has(rel) lookup. Defer until profiling shows it matters.

🤖 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 `@packages/core/src/loader/loader_fs.ts` around lines 178 - 204, Precompute a
Set of manifest directories in the class constructor and make
`#isManifestDirectory` do a single lookup: build a Set<string> (e.g.,
manifestDirs) from this.#manifest.data.fileDiscovery keys plus the parent
directories of every path.posix.join(dir, file) and every non-null target in
this.#manifest.data.resolveCache; update the constructor to populate that Set
and change `#isManifestDirectory`(rel: string) to return
this.manifestDirs.has(rel) (with the empty-string case still delegating to
this.#hasManifestData() if needed) so you avoid the O(D×F) scans on each call.
🤖 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.

Nitpick comments:
In `@packages/core/src/loader/egg_loader.ts`:
- Around line 98-101: The code sets this.loaderFS to new
ManifestLoaderFS(bundleStore) when ManifestStore.getBundleStore().baseDir ===
this.options.baseDir, which means the FS layer will read from the bundle store
while this.manifest (used by resolveModule) may be the locally loaded/created
manifest; add a brief inline comment next to the assignment (near
ManifestStore.getBundleStore, ManifestLoaderFS, RealLoaderFS and the
post-manifest instanceof check around resolveModule) explaining that in bundled
mode the loaderFS and this.manifest can diverge and that resolveModule relies on
this.manifest while ManifestLoaderFS reads from the bundle store so readers
don’t assume a single shared manifest source.
- Around line 1848-1858: The current check in `#collectConventionFile` uses
Object.values(manifest.resolveCache).includes(fileKey) which is O(n) per call;
change the approach by precomputing a Set of manifest.resolveCache values once
in `#collectConventionalDynamicFiles` (e.g., const resolvedSet = new
Set(Object.values(manifest.resolveCache))) and pass that Set into
`#collectConventionFile` (or attach it to the manifest under a temporary property)
so `#collectConventionFile` can do resolvedSet.has(fileKey) instead of a repeated
linear scan; update all call sites of `#collectConventionFile` in
`#collectConventionalDynamicFiles` to provide the Set and remove the
Object.values(...).includes usage.

In `@packages/core/src/loader/loader_fs.ts`:
- Around line 178-204: Precompute a Set of manifest directories in the class
constructor and make `#isManifestDirectory` do a single lookup: build a
Set<string> (e.g., manifestDirs) from this.#manifest.data.fileDiscovery keys
plus the parent directories of every path.posix.join(dir, file) and every
non-null target in this.#manifest.data.resolveCache; update the constructor to
populate that Set and change `#isManifestDirectory`(rel: string) to return
this.manifestDirs.has(rel) (with the empty-string case still delegating to
this.#hasManifestData() if needed) so you avoid the O(D×F) scans on each call.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ead44421-7024-4838-a71f-a368b7fd3a69

📥 Commits

Reviewing files that changed from the base of the PR and between 0dec2c9 and 165a360.

⛔ Files ignored due to path filters (2)
  • packages/core/test/__snapshots__/index.test.ts.snap is excluded by !**/*.snap
  • tools/egg-bundler/test/__snapshots__/EntryGenerator.worker.canonical.snap is excluded by !**/*.snap
📒 Files selected for processing (10)
  • packages/core/package.json
  • packages/core/src/egg.ts
  • packages/core/src/loader/egg_loader.ts
  • packages/core/src/loader/loader_fs.ts
  • packages/core/test/egg.test.ts
  • packages/core/test/loader/manifest_coverage.test.ts
  • packages/core/test/loader/manifest_loader_fs.test.ts
  • packages/egg/src/lib/start.ts
  • tools/egg-bundler/src/lib/EntryGenerator.ts
  • tools/egg-bundler/test/EntryGenerator.test.ts

Copilot AI review requested due to automatic review settings May 10, 2026 04:49
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

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