vfs: integrate with CJS and ESM module loaders#63653
Conversation
|
Review requested:
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #63653 +/- ##
==========================================
- Coverage 91.96% 90.32% -1.64%
==========================================
Files 379 732 +353
Lines 166638 237271 +70633
Branches 25497 44727 +19230
==========================================
+ Hits 153242 214325 +61083
- Misses 13099 14656 +1557
- Partials 297 8290 +7993
🚀 New features to boost your workflow:
|
|
@joyeecheung take a look, should be easier to review. |
Restore the "DO NOT depend on the patchability" warnings in esm/load.js and esm/resolve.js that were dropped along with the fs imports. The warning still applies; it now also points at node:vfs as one of the formal hook mechanisms callers should reach for instead. Addresses review feedback from @jsumners-nr in nodejs#63653
6321e08 to
51b033a
Compare
joyeecheung
left a comment
There was a problem hiding this comment.
A design question recently occurred to me: have we explored the versioning of the mounting?
what do you mean? You mean multiple vfs layers on top of each other? |
For the stacks to have some kind of version number/ID to identify the current status? BTW I just noticed that there's no mention of |
I did purge them when doing the splitting; I forgot to bring them back. I'll add them to this PR. |
No but we totally should. |
Each VirtualFileSystem now exposes a per-process monotonically increasing `layerId`, assigned at construction. The id is stable across mount/unmount cycles for the lifetime of the instance and surfaces in: - debug() output for register / deregister so the layer stack is visible when NODE_DEBUG=vfs is enabled; - the overlap ERR_INVALID_STATE message, which now names the layer ids of the conflicting mounts. The id is the building block for tagging cache entries with the owning VFS, which a follow-up will use to replace the global loader-cache flush in deregisterVFS with a scoped purge. Refs: nodejs#63653 Signed-off-by: Matteo Collina <hello@matteocollina.com>
51b033a to
294a19c
Compare
Replace the global loader-cache flush in deregisterVFS with a scope-purge that only drops entries owned by the unmounting VFS. Per-layer ownership is determined two ways: - For CJS-style filename-keyed caches (Module._cache, Module._pathCache, the CJS stat cache, the helpers.js realpath cache, and the package.json caches) entries are filtered with `vfs.shouldHandle(filename)`. __filename stays a clean absolute path so user code that does `path.dirname(__filename)` or similar is unaffected. - For the ESM cascaded loader's loadCache, entries are tagged at resolve time: when finalizeResolution() detects the resolved path is VFS-owned (via the new loaderGetLayerForPath hook), it appends `?vfs-layer=<id>` to the URL. The tag surfaces in `import.meta.url`, matching the cache-busting pattern used by HMR tooling. On deregister, entries whose URL carries the tag for the unmounting layer are deleted. Multi-mount setups no longer pay the cross-VFS cache-warmup penalty when a single VFS unmounts, and ESM modules loaded from a VFS become reachable for purge instead of leaking forever in the cascaded loader. New helpers exposed for VFS: - cjs/loader.js: clearStatCacheForVFS - helpers.js: purgeRealpathCacheForVFS, loaderGetLayerForPath - package_json_reader.js: purgePackageJSONCacheForVFS Adds test-vfs-scoped-cache-purge covering both the multi-mount isolation and the import.meta.url tag visibility. Refs: nodejs#63653 Signed-off-by: Matteo Collina <hello@matteocollina.com>
|
@joyeecheung PTAL |
75bb5c7 to
99a5a5c
Compare
4dc22f7 to
4bb502d
Compare
4bb502d to
10412dc
Compare
|
@joyeecheung PTAL |
572c4bc to
efc7db4
Compare
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
|
@jasnell @joyeecheung updated with all your feedbacks. |
Co-authored-by: James M Snell <jasnell@gmail.com> Signed-off-by: Matteo Collina <hello@matteocollina.com>
Each VirtualFileSystem now tracks the absolute filenames it has handled (ownedFilenames) and the Module._pathCache keys that resolve to a VFS-owned filename (ownedPathCacheKeys). Recording happens at routing time inside the loader overrides (findVFSForStat / findVFSForRead / findVFSWith, the inline package.json override loops, and a pathCache write recorder installed on the cjs loader). On deregister, purgeLoaderCachesForVFS walks the per-VFS sets and removes entries from Module._cache, Module._pathCache, the stat / realpath / package.json caches in O(owned) instead of scanning every cache and calling vfs.shouldHandle() on each entry. The pathCache recorder uses a Set.has() lookup over activeVFSList so the per-write overhead is M cheap Set checks rather than M path normalizations. Also encapsulates the previous direct access to Module._cache / Module._pathCache from internal/vfs/setup.js behind the cjs loader's new purgeModuleCacheForVFS / setPathCacheWriteRecorder / cachePathResolution helpers, so the loader's private data structures stay owned by the loader module. Signed-off-by: Matteo Collina <hello@matteocollina.com>
61ada45 to
c65d084
Compare
| function clearStatCache() { | ||
| if (statCache !== null) { | ||
| statCache = new SafeMap(); | ||
| } |
There was a problem hiding this comment.
Nit: could this skip the conditional check and just always set to a new Map?
Makes `require()` and `import` resolve files served by `node:vfs`. Before this PR, mounted VFS files were only visible through `fs.*`; the loaders went straight to the real filesystem.
Mechanism: toggleable wrappers in the loaders. Null fast-path when no VFS is mounted; otherwise the VFS answers stat / read / realpath / package.json.
User-visible side effects:
Review guide
Suggested reading order:
Tests (all gated by `--experimental-vfs`): `test-vfs-require`, `test-vfs-import`, `test-vfs-module-hooks`, `test-vfs-package-json`, `test-vfs-package-json-cache`, `test-vfs-invalid-package-json`, `test-vfs-module-hooks-cleanup`, `test-vfs-layer-id`, `test-vfs-scoped-cache-purge`.
Out of scope
SEA + VFS, migrating the C++ `package_configs_` cache, broader permission-model integration.