-
-
Notifications
You must be signed in to change notification settings - Fork 36.2k
vfs: integrate with CJS and ESM module loaders #63653
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
66713f6
90d8eb4
b28a077
d11997b
f94cdbe
ee06f2e
7bedf50
133a8a1
cefef62
722e10d
e7dd654
de17e81
a17ee29
023516e
e378435
7e94c3d
2203b59
f2f7b04
2462d17
902254d
2716996
6fafe07
6e3f1b9
a95717a
ffd68a2
7b6e3de
2e463ca
5b71a94
30d5115
337c130
0d5fb61
0a98b98
4c417d0
6d8b5f5
bdf79b2
018a6da
3b4d428
b993d9e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| 'use strict'; | ||
| const path = require('path'); | ||
| const { pathToFileURL } = require('url'); | ||
| const common = require('../common.js'); | ||
|
|
||
| const bench = common.createBenchmark(main, { | ||
| type: ['cjs', 'esm'], | ||
| files: [1e2, 1e3], | ||
| n: [10], | ||
| }, { flags: ['--experimental-vfs', '--no-warnings'] }); | ||
|
|
||
| // Builds a module graph of `files` packages, each with its own package.json, | ||
| // an index requiring a package-local file and a shared root module, and an | ||
| // entry point that pulls in every package. | ||
| function buildGraph(layer, files, type) { | ||
| const entryRequires = []; | ||
| if (type === 'esm') { | ||
| layer.writeFileSync('/package.json', '{"type":"module"}'); | ||
| } | ||
| layer.writeFileSync('/shared.js', | ||
| type === 'cjs' ? 'module.exports = 0;' : 'export default 0;'); | ||
| for (let i = 0; i < files; i++) { | ||
| layer.mkdirSync(`/${i}`, { recursive: true }); | ||
| if (type === 'cjs') { | ||
| layer.writeFileSync(`/${i}/package.json`, '{"main":"index.js"}'); | ||
| layer.writeFileSync(`/${i}/lib.js`, 'module.exports = 1;'); | ||
| layer.writeFileSync( | ||
| `/${i}/index.js`, | ||
| 'require("./lib.js"); require("../shared.js"); module.exports = __filename;'); | ||
| entryRequires.push(`require('./${i}/');`); | ||
| } else { | ||
| layer.writeFileSync(`/${i}/package.json`, '{"type":"module"}'); | ||
| layer.writeFileSync(`/${i}/lib.js`, 'export default 1;'); | ||
| layer.writeFileSync( | ||
| `/${i}/index.js`, | ||
| 'import "./lib.js"; import "../shared.js"; export default import.meta.url;'); | ||
| entryRequires.push(`import './${i}/index.js';`); | ||
| } | ||
| } | ||
| layer.writeFileSync('/entry.js', entryRequires.join('\n')); | ||
| } | ||
|
|
||
| async function main({ n, type, files }) { | ||
| const vfs = require('node:vfs'); | ||
| const layer = vfs.create(); | ||
| buildGraph(layer, files, type); | ||
|
|
||
| bench.start(); | ||
| for (let i = 0; i < n; i++) { | ||
| const mountPoint = layer.mount(); | ||
| const entry = path.join(mountPoint, 'entry.js'); | ||
| if (type === 'cjs') { | ||
| require(entry); | ||
| } else { | ||
| await import(pathToFileURL(entry).href); | ||
| } | ||
| // Unmounting purges the module caches for the mount prefix, so every | ||
| // iteration is a cold load of the full graph. | ||
| layer.unmount(); | ||
| } | ||
| bench.end(n * files); | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -61,6 +61,11 @@ callback-based, and promise-based file system methods that mirror the | |||||||||||||||||||||||||||||||||
| shape of the [`node:fs`][] API. All paths are POSIX-style and absolute | ||||||||||||||||||||||||||||||||||
| (starting with `/`). | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| By default, the file tree is private to the VFS instance. To expose | ||||||||||||||||||||||||||||||||||
| it through the global `node:fs` module, `require()`, and `import`, | ||||||||||||||||||||||||||||||||||
| call [`vfs.mount()`][]; call [`vfs.unmount()`][] (or rely on a | ||||||||||||||||||||||||||||||||||
| `using` declaration) to detach again. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ## `vfs.create([provider][, options])` | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| <!-- YAML | ||||||||||||||||||||||||||||||||||
|
|
@@ -107,6 +112,96 @@ added: v26.4.0 | |||||||||||||||||||||||||||||||||
| * `emitExperimentalWarning` {boolean} Whether to emit the experimental | ||||||||||||||||||||||||||||||||||
| warning. **Default:** `true`. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ### `vfs.mount()` | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| <!-- YAML | ||||||||||||||||||||||||||||||||||
| added: REPLACEME | ||||||||||||||||||||||||||||||||||
| --> | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| * Returns: {string} The absolute mount point. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Mounts the virtual file system and returns the resulting mount point. | ||||||||||||||||||||||||||||||||||
| After mounting, files in the VFS can be accessed through the | ||||||||||||||||||||||||||||||||||
| `node:fs` module and resolved through `require()` and `import` | ||||||||||||||||||||||||||||||||||
| using paths under the returned mount point. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Mount points always live inside a reserved namespace that cannot have child file system entries, | ||||||||||||||||||||||||||||||||||
| so virtual paths never conflate with (or shadow) real paths. The virtual path scheme is subject to | ||||||||||||||||||||||||||||||||||
| change and users should not manually construct them based on assumptions. Instead, obtain | ||||||||||||||||||||||||||||||||||
| them from what `vfs.mount()` returns or `vfs.mountPoint`. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ```cjs | ||||||||||||||||||||||||||||||||||
| const vfs = require('node:vfs'); | ||||||||||||||||||||||||||||||||||
| const fs = require('node:fs'); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const myVfs = vfs.create(); | ||||||||||||||||||||||||||||||||||
| myVfs.writeFileSync('/data.txt', 'Hello'); | ||||||||||||||||||||||||||||||||||
| const mountPoint = myVfs.mount(); | ||||||||||||||||||||||||||||||||||
| // e.g. '/dev/null/vfs/0' | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| fs.readFileSync(`${mountPoint}/data.txt`, 'utf8'); // 'Hello' | ||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Each `VirtualFileSystem` instance may be mounted at most once at a | ||||||||||||||||||||||||||||||||||
| time. Attempting to mount an already-mounted instance throws | ||||||||||||||||||||||||||||||||||
| `ERR_INVALID_STATE`. Because each instance mounts inside its own | ||||||||||||||||||||||||||||||||||
| per-layer namespace, mounts from different instances can never | ||||||||||||||||||||||||||||||||||
| overlap. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| The VFS supports the [Explicit Resource Management][] proposal. Use | ||||||||||||||||||||||||||||||||||
| a `using` declaration to unmount automatically when leaving scope: | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ```cjs | ||||||||||||||||||||||||||||||||||
| const vfs = require('node:vfs'); | ||||||||||||||||||||||||||||||||||
| const fs = require('node:fs'); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| let mountPoint; | ||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||
| using myVfs = vfs.create(); | ||||||||||||||||||||||||||||||||||
| myVfs.writeFileSync('/data.txt', 'Hello'); | ||||||||||||||||||||||||||||||||||
| mountPoint = myVfs.mount(); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| fs.readFileSync(`${mountPoint}/data.txt`, 'utf8'); // 'Hello' | ||||||||||||||||||||||||||||||||||
| } // VFS is automatically unmounted here | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| fs.existsSync(`${mountPoint}/data.txt`); // false | ||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ### `vfs.unmount()` | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| <!-- YAML | ||||||||||||||||||||||||||||||||||
| added: REPLACEME | ||||||||||||||||||||||||||||||||||
| --> | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Unmounts the virtual file system. After unmounting, virtual files | ||||||||||||||||||||||||||||||||||
| are no longer reachable through `node:fs`, `require()`, or `import`. | ||||||||||||||||||||||||||||||||||
| The same instance may be mounted again by calling `mount()`. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| This method is idempotent: calling `unmount()` on a VFS that is not | ||||||||||||||||||||||||||||||||||
| currently mounted has no effect. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ### `vfs.mounted` | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| <!-- YAML | ||||||||||||||||||||||||||||||||||
| added: REPLACEME | ||||||||||||||||||||||||||||||||||
| --> | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| * {boolean} | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| `true` while the VFS is mounted; `false` otherwise. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ### `vfs.mountPoint` | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| <!-- YAML | ||||||||||||||||||||||||||||||||||
| added: REPLACEME | ||||||||||||||||||||||||||||||||||
| --> | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| * {string | null} | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| The current mount point as an absolute string (the value returned by | ||||||||||||||||||||||||||||||||||
| the last [`vfs.mount()`][] call), or `null` when the VFS is not | ||||||||||||||||||||||||||||||||||
| mounted. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ### `vfs.provider` | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| <!-- YAML | ||||||||||||||||||||||||||||||||||
|
|
@@ -195,15 +290,99 @@ The promise namespace mirrors `fs.promises` and includes `readFile`, | |||||||||||||||||||||||||||||||||
| `access`, `rm`, `truncate`, `link`, `mkdtemp`, `chmod`, `chown`, `lchown`, | ||||||||||||||||||||||||||||||||||
| `utimes`, `lutimes`, `open`, `lchmod`, and `watch`. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ## Module loader integration | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Once a `VirtualFileSystem` is mounted, paths under the mount point | ||||||||||||||||||||||||||||||||||
| participate in module resolution and loading. The [CommonJS | ||||||||||||||||||||||||||||||||||
| resolution algorithm][] used by [`require()`][] and | ||||||||||||||||||||||||||||||||||
| [`require.resolve()`][] and the [ES modules resolution algorithm][] | ||||||||||||||||||||||||||||||||||
| used by `import` and [`import.meta.resolve()`][] are unchanged; | ||||||||||||||||||||||||||||||||||
| instead, every file system operation those algorithms perform | ||||||||||||||||||||||||||||||||||
| (existence probes, file reads, `package.json` lookups, real-path | ||||||||||||||||||||||||||||||||||
| resolution) is dispatched on the path being probed: paths under a | ||||||||||||||||||||||||||||||||||
| mount point are served by the owning VFS, and all other paths are | ||||||||||||||||||||||||||||||||||
| served by the real file system. Files served from the VFS therefore | ||||||||||||||||||||||||||||||||||
| behave as first-class modules: `package.json` is honoured, | ||||||||||||||||||||||||||||||||||
| conditional [`"exports"`][] / [`"imports"`][] work, and so on. | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+305
to
+306
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
These look like random hand-wavy examples that seem to be meaningful but actually clarify nothing again - if we have to explain with an example it's better to provide a snippet etc. to make it more concrete. |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Because mounted paths live in a reserved namespace that cannot exist | ||||||||||||||||||||||||||||||||||
| on disk, any given path is served either by exactly one VFS or by | ||||||||||||||||||||||||||||||||||
| the real file system, never both. There is no search order or | ||||||||||||||||||||||||||||||||||
| fallback between the two: if a path under a mount point does not | ||||||||||||||||||||||||||||||||||
| exist in the VFS, resolution fails with `ENOENT` without consulting | ||||||||||||||||||||||||||||||||||
| the disk, and a mounted layer never shadows a real directory. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| For resolution purposes the mount point behaves as a file system | ||||||||||||||||||||||||||||||||||
| root: `package.json` scope lookups (for [`"type"`][], | ||||||||||||||||||||||||||||||||||
| [`"exports"`][], [`"imports"`][], and the CommonJS directory | ||||||||||||||||||||||||||||||||||
| [`"main"`][]) and [loading from `node_modules` folders][] stop at | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+316
to
+318
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Again we should not enumerate them here. If we must, simply linking to the documentation in packages is better. |
||||||||||||||||||||||||||||||||||
| the mount point instead of continuing into the real file system | ||||||||||||||||||||||||||||||||||
| ([the global folders][], such as `NODE_PATH`, are legacy CommonJS | ||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The "instead of continuing into the real file system" part is leading users to spare a thought about a case that is not practical in the first place, because the mount point cannot actually have real files in the file system, no continuation can be performed even if Node.js wants to. I think it's clearer to simply list an example lookup, for example in |
||||||||||||||||||||||||||||||||||
| behavior and still apply to `require()`). Absolute specifiers may | ||||||||||||||||||||||||||||||||||
| cross the boundary in either direction: a module on the real file | ||||||||||||||||||||||||||||||||||
| system can `require()` a mounted path, and a virtual module can | ||||||||||||||||||||||||||||||||||
| `require()` a real one. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ```cjs | ||||||||||||||||||||||||||||||||||
| const vfs = require('node:vfs'); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const myVfs = vfs.create(); | ||||||||||||||||||||||||||||||||||
| myVfs.mkdirSync('/lib'); | ||||||||||||||||||||||||||||||||||
| myVfs.writeFileSync('/lib/greet.js', 'module.exports = () => "hi";'); | ||||||||||||||||||||||||||||||||||
| myVfs.writeFileSync( | ||||||||||||||||||||||||||||||||||
| '/lib/package.json', '{"main": "./greet.js"}'); | ||||||||||||||||||||||||||||||||||
| const mountPoint = myVfs.mount(); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const greet = require(`${mountPoint}/lib`); | ||||||||||||||||||||||||||||||||||
| console.log(greet()); // 'hi' | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| myVfs.unmount(); | ||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| For ECMAScript modules, convert mounted paths to `file:` URLs before | ||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Non-blocking: I wonder if we should simply supply |
||||||||||||||||||||||||||||||||||
| passing them to dynamic `import()`. This keeps VFS imports portable on | ||||||||||||||||||||||||||||||||||
| Windows, where mounted paths use Windows path syntax. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ```mjs | ||||||||||||||||||||||||||||||||||
| import { pathToFileURL } from 'node:url'; | ||||||||||||||||||||||||||||||||||
| import vfs from 'node:vfs'; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const myVfs = vfs.create(); | ||||||||||||||||||||||||||||||||||
| myVfs.writeFileSync('/mod.mjs', 'export const value = 42;'); | ||||||||||||||||||||||||||||||||||
| const mountPoint = myVfs.mount(); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const { value } = await import( | ||||||||||||||||||||||||||||||||||
| pathToFileURL(`${mountPoint}/mod.mjs`).href, | ||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||
| console.log(value); // 42 | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| myVfs.unmount(); | ||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Module identity follows the path: `__filename` and `module.filename` | ||||||||||||||||||||||||||||||||||
| are the plain absolute path of the module under the mount point, and | ||||||||||||||||||||||||||||||||||
| `import.meta.url` is the corresponding `file:` URL, with no synthetic | ||||||||||||||||||||||||||||||||||
| decorations. Importing the same virtual path repeatedly, including | ||||||||||||||||||||||||||||||||||
| through `import.meta.resolve()`, yields the same module instance, | ||||||||||||||||||||||||||||||||||
| exactly as for real files. | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+362
to
+367
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
The current description is mixing CommonJS and ESM. It's better to describe different cases more clearly. |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Calling [`vfs.unmount()`][] invalidates the modules that were loaded | ||||||||||||||||||||||||||||||||||
| from the mount point: a subsequent `require()` or `import` of a path | ||||||||||||||||||||||||||||||||||
| under a re-created mount re-reads the file from the newly mounted | ||||||||||||||||||||||||||||||||||
| VFS rather than returning a stale module. Modules loaded from other | ||||||||||||||||||||||||||||||||||
| VFS instances or from the real file system are unaffected. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Mounting and unmounting do not invalidate ESM modules that are | ||||||||||||||||||||||||||||||||||
| already executing. As with any other module-system teardown, | ||||||||||||||||||||||||||||||||||
| unmounting a VFS while the import graph below it is still loading is | ||||||||||||||||||||||||||||||||||
| the caller's responsibility to avoid. | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+375
to
+378
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
You could technically unmount CJS while it is executing too (if you make the vfs object reference available e.g. with globals, then call unmount at the top level of a CJS module being loaded from that VFS). |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ## Class: `VirtualProvider` | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| <!-- YAML | ||||||||||||||||||||||||||||||||||
| added: v26.4.0 | ||||||||||||||||||||||||||||||||||
| --> | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| The base class for all VFS providers. Subclasses implement the essential | ||||||||||||||||||||||||||||||||||
| primitives (`open`, `stat`, `readdir`, `mkdir`, `rmdir`, `unlink`, | ||||||||||||||||||||||||||||||||||
| `rename`, ...) and inherit default implementations of the derived | ||||||||||||||||||||||||||||||||||
| The base class for all VFS providers. Subclasses implement the essential | ||||||||||||||||||||||||||||||||||
| primitives (such as `open`, `stat`, `readdir`, `mkdir`, `rmdir`, `unlink`, | ||||||||||||||||||||||||||||||||||
| `rename`, etc.) and inherit default implementations of the derived | ||||||||||||||||||||||||||||||||||
|
|
@@ -318,10 +497,24 @@ fields use synthetic but stable values: | |||||||||||||||||||||||||||||||||
| * `blocks` is `Math.ceil(size / 512)`. | ||||||||||||||||||||||||||||||||||
| * Times default to the moment the entry was created/last modified. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| [CommonJS resolution algorithm]: modules.md#all-together | ||||||||||||||||||||||||||||||||||
| [ES modules resolution algorithm]: esm.md#resolution-algorithm | ||||||||||||||||||||||||||||||||||
| [Explicit Resource Management]: https://github.com/tc39/proposal-explicit-resource-management | ||||||||||||||||||||||||||||||||||
| [`"exports"`]: packages.md#exports | ||||||||||||||||||||||||||||||||||
| [`"imports"`]: packages.md#imports | ||||||||||||||||||||||||||||||||||
| [`"main"`]: packages.md#main | ||||||||||||||||||||||||||||||||||
| [`"type"`]: packages.md#type | ||||||||||||||||||||||||||||||||||
| [`MemoryProvider`]: #class-memoryprovider | ||||||||||||||||||||||||||||||||||
| [`RealFSProvider`]: #class-realfsprovider | ||||||||||||||||||||||||||||||||||
| [`VirtualFileSystem`]: #class-virtualfilesystem | ||||||||||||||||||||||||||||||||||
| [`VirtualProvider`]: #class-virtualprovider | ||||||||||||||||||||||||||||||||||
| [`fs.BigIntStats`]: fs.md#class-fsbigintstats | ||||||||||||||||||||||||||||||||||
| [`fs.Stats`]: fs.md#class-fsstats | ||||||||||||||||||||||||||||||||||
| [`import.meta.resolve()`]: esm.md#importmetaresolvespecifier | ||||||||||||||||||||||||||||||||||
| [`node:fs`]: fs.md | ||||||||||||||||||||||||||||||||||
| [`require()`]: modules.md#requireid | ||||||||||||||||||||||||||||||||||
| [`require.resolve()`]: modules.md#requireresolverequest-options | ||||||||||||||||||||||||||||||||||
| [`vfs.mount()`]: #vfsmount | ||||||||||||||||||||||||||||||||||
| [`vfs.unmount()`]: #vfsunmount | ||||||||||||||||||||||||||||||||||
| [loading from `node_modules` folders]: modules.md#loading-from-node_modules-folders | ||||||||||||||||||||||||||||||||||
| [the global folders]: modules.md#loading-from-the-global-folders | ||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These implementation details should not be enumerated in the docs.