Skip to content

vfs: integrate with CJS and ESM module loaders#63653

Open
mcollina wants to merge 4 commits into
nodejs:mainfrom
mcollina:vfs-module-loader-integration
Open

vfs: integrate with CJS and ESM module loaders#63653
mcollina wants to merge 4 commits into
nodejs:mainfrom
mcollina:vfs-module-loader-integration

Conversation

@mcollina
Copy link
Copy Markdown
Member

Route loader fs and package.json operations through toggleable wrappers so the VFS can resolve and load modules from mounted paths.

Route loader fs and package.json operations through toggleable
wrappers so the VFS can resolve and load modules from mounted paths.
@nodejs-github-bot
Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/loaders

@nodejs-github-bot nodejs-github-bot added lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. labels May 30, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented May 30, 2026

Codecov Report

❌ Patch coverage is 90.56604% with 60 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.34%. Comparing base (6f29e1a) to head (b51fe1f).
⚠️ Report is 5 commits behind head on main.

Files with missing lines Patch % Lines
lib/internal/vfs/setup.js 85.54% 59 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #63653      +/-   ##
==========================================
+ Coverage   90.32%   90.34%   +0.01%     
==========================================
  Files         732      732              
  Lines      236398   237026     +628     
  Branches    44504    44672     +168     
==========================================
+ Hits       213530   214138     +608     
- Misses      14575    14593      +18     
- Partials     8293     8295       +2     
Files with missing lines Coverage Δ
lib/internal/modules/cjs/loader.js 98.15% <100.00%> (+0.09%) ⬆️
lib/internal/modules/esm/get_format.js 94.83% <100.00%> (ø)
lib/internal/modules/esm/load.js 91.24% <100.00%> (-0.16%) ⬇️
lib/internal/modules/esm/resolve.js 99.03% <100.00%> (-0.02%) ⬇️
lib/internal/modules/helpers.js 98.75% <100.00%> (-0.16%) ⬇️
lib/internal/modules/package_json_reader.js 99.47% <100.00%> (+0.02%) ⬆️
lib/internal/vfs/setup.js 86.14% <85.54%> (-0.55%) ⬇️

... and 34 files with indirect coverage changes

🚀 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.

mcollina added 2 commits May 30, 2026 09:06
- Normalize setLoaderFsOverrides / setLoaderPackageOverrides to coerce
  missing fields to null so calling with {} actually clears them.
- Add clearRealpathCache helper, called from deregisterVFS so paths that
  overlap a removed mount are re-resolved against the real filesystem.
- Uninstall loader overrides when the last VFS unmounts so the fast
  path is restored; reset hooksInstalled so a future mount reinstalls.
- findVFSPackageJSON now reports the topmost candidate path as a
  sentinel; getPackageScopeConfig returns it instead of dirname so the
  caller's "not found" cache mirrors the C++ binding's contract.
- legacyMainResolve now throws ERR_MODULE_NOT_FOUND with the resolved
  initial candidate path (pkgPath + main) to match the C++ message.
- getFormatOfExtensionlessFile only swallows ENOENT; EACCES/ELOOP and
  any other error propagate instead of being silently labeled as JS.
- Validate package.json field types in serializePackageJSON and let the
  schema error escape the readPackageJSON override unswallowed.
- Add test-vfs-module-hooks-cleanup covering register/deregister cycles,
  malformed schema rejection, and missing-main error propagation.
Address the round-2 adversarial review:

- ERR_MODULE_NOT_FOUND: pass undefined for the third (`exactUrl`) arg
  so the message uses "package" and err.url isn't overwritten. The
  constructor enforces strict arity; the arg must be present.
- serializePackageJSON now matches the C++ binding's laxity:
  non-string `main` is silently omitted (matches USE(value.get_string)),
  unknown `type` strings fall back to 'none', non-string `name` / `type`
  throw ERR_INVALID_PACKAGE_CONFIG, top-level null / non-object roots
  throw too. Old `validateOptionalString` helper removed.
- ESM LoadCache is now cleared from clearLoaderCaches() so dynamic
  import() after re-mount sees fresh module jobs.
- deregisterVFS still flushes loader caches on every unmount (we can't
  tell which entries belonged to the VFS going away), but only
  uninstalls the loader override hooks when the last VFS is removed.
- test-vfs-module-hooks-cleanup: dropped the assertion that the strict
  schema rejects `{"main": 42}` (would diverge from real-fs behavior);
  added a top-level-null case, a non-string-main lenient case, a multi-
  mount partial-deregister case, and rewrote the legacyMainResolve case
  to use bare-specifier resolution from an ESM entry inside the VFS so
  the override is actually exercised.
@mcollina
Copy link
Copy Markdown
Member Author

@joyeecheung take a look, should be easier to review.

Address the round-3 adversarial review:

- legacyMainResolve override: when `main` is missing/non-string, use
  pkgPath/index.js as the candidate path in the thrown
  ERR_MODULE_NOT_FOUND, matching src/node_file.cc:3927-4005. Previously
  the bare pkgPath was used, diverging from the native binding.
- getPackageType override: route through serializePackageJSON so a
  malformed `type` (non-string) throws ERR_INVALID_PACKAGE_CONFIG just
  like readPackageJSON and getPackageScopeConfig already do. Closes
  the divergence where the same package.json answered differently
  depending on which override was consulted.
- clearLoaderCaches no longer clears the ESM cascaded loader's
  loadCache. Clearing it mid-flight can let a second ModuleJob be
  created for the same URL, breaking module identity. The trade-off
  is documented in a comment: ESM modules loaded from a VFS path
  remain cached after unmount, consistent with how Node.js caches
  ESM modules everywhere else.
- test-vfs-module-hooks-cleanup: tighten the legacyMainResolve case to
  assert err.url is undefined and the message starts with "Cannot find
  package " (previously only asserted err.url !== 'package', which is
  a weak negative).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants