| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent b53f8f7 commit 7bc93a6
78 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1450,6 +1450,16 @@ The flag may be specified more than once; tests must contain **every** | |||
| 1450 | 1450 | filter value to run. See [Test tags][] for details on declaring and | |
| 1451 | 1451 | inheriting tags. | |
| 1452 | 1452 | ||
| 1453 | + ### `--experimental-vfs` | ||
| 1454 | + | ||
| 1455 | + <!-- YAML | ||
| 1456 | + added: REPLACEME | ||
| 1457 | + --> | ||
| 1458 | + | ||
| 1459 | + > Stability: 1 - Experimental | ||
| 1460 | + | ||
| 1461 | + Enable the experimental [`node:vfs`][] module. | ||
| 1462 | + | ||
| 1453 | 1463 | ### `--experimental-vm-modules` | |
| 1454 | 1464 | ||
| 1455 | 1465 | <!-- YAML | |
@@ -3783,6 +3793,7 @@ one is included in the list below. | |||
| 3783 | 3793 | * `--experimental-stream-iter` | |
| 3784 | 3794 | * `--experimental-test-isolation` | |
| 3785 | 3795 | * `--experimental-top-level-await` | |
| 3796 | + * `--experimental-vfs` | ||
| 3786 | 3797 | * `--experimental-vm-modules` | |
| 3787 | 3798 | * `--experimental-wasi-unstable-preview1` | |
| 3788 | 3799 | * `--force-context-aware` | |
@@ -4426,6 +4437,7 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12 | |||
| 4426 | 4437 | [`node:ffi`]: ffi.md | |
| 4427 | 4438 | [`node:sqlite`]: sqlite.md | |
| 4428 | 4439 | [`node:stream/iter`]: stream_iter.md | |
| 4440 | + [`node:vfs`]: vfs.md | ||
| 4429 | 4441 | [`process.setUncaughtExceptionCaptureCallback()`]: process.md#processsetuncaughtexceptioncapturecallbackfn | |
| 4430 | 4442 | [`tls.DEFAULT_MAX_VERSION`]: tls.md#tlsdefault_max_version | |
| 4431 | 4443 | [`tls.DEFAULT_MIN_VERSION`]: tls.md#tlsdefault_min_version | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -65,6 +65,7 @@ | |||
| 65 | 65 | * [URL](url.md) | |
| 66 | 66 | * [Utilities](util.md) | |
| 67 | 67 | * [V8](v8.md) | |
| 68 | + * [Virtual File System](vfs.md) | ||
| 68 | 69 | * [VM](vm.md) | |
| 69 | 70 | * [WASI](wasi.md) | |
| 70 | 71 | * [Web Crypto API](webcrypto.md) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,310 @@ | |||
| 1 | + # Virtual File System | ||
| 2 | + | ||
| 3 | + <!--introduced_in=REPLACEME--> | ||
| 4 | + | ||
| 5 | + <!-- YAML | ||
| 6 | + added: REPLACEME | ||
| 7 | + --> | ||
| 8 | + | ||
| 9 | + > Stability: 1 - Experimental | ||
| 10 | + | ||
| 11 | + <!-- source_link=lib/vfs.js --> | ||
| 12 | + | ||
| 13 | + The `node:vfs` module provides an in-memory virtual file system with a | ||
| 14 | + `node:fs`-like API. It is useful for tests, fixtures, embedded assets, and other | ||
| 15 | + scenarios where you need a self-contained file system without touching the | ||
| 16 | + actual file-system. | ||
| 17 | + | ||
| 18 | + To access it: | ||
| 19 | + | ||
| 20 | + ```mjs | ||
| 21 | + import vfs from 'node:vfs'; | ||
| 22 | + ``` | ||
| 23 | + | ||
| 24 | + ```cjs | ||
| 25 | + const vfs = require('node:vfs'); | ||
| 26 | + ``` | ||
| 27 | + | ||
| 28 | + This module is only available under the `node:` scheme, and only when Node.js | ||
| 29 | + is started with the `--experimental-vfs` flag. | ||
| 30 | + | ||
| 31 | + ## Basic usage | ||
| 32 | + | ||
| 33 | + ```cjs | ||
| 34 | + const vfs = require('node:vfs'); | ||
| 35 | + | ||
| 36 | + const myVfs = vfs.create(); | ||
| 37 | + myVfs.mkdirSync('/dir', { recursive: true }); | ||
| 38 | + myVfs.writeFileSync('/dir/hello.txt', 'Hello, VFS!'); | ||
| 39 | + | ||
| 40 | + console.log(myVfs.readFileSync('/dir/hello.txt', 'utf8')); // 'Hello, VFS!' | ||
| 41 | + ``` | ||
| 42 | + | ||
| 43 | + `vfs.create()` returns a [`VirtualFileSystem`][] instance backed by a | ||
| 44 | + [`MemoryProvider`][] by default. The instance exposes synchronous, | ||
| 45 | + callback-based, and promise-based file system methods that mirror the | ||
| 46 | + shape of the [`node:fs`][] API. All paths are POSIX-style and absolute | ||
| 47 | + (starting with `/`). | ||
| 48 | + | ||
| 49 | + ## `vfs.create([provider][, options])` | ||
| 50 | + | ||
| 51 | + <!-- YAML | ||
| 52 | + added: REPLACEME | ||
| 53 | + --> | ||
| 54 | + | ||
| 55 | + * `provider` {VirtualProvider} The provider to use. **Default:** | ||
| 56 | + `new MemoryProvider()`. | ||
| 57 | + * `options` {Object} | ||
| 58 | + * `emitExperimentalWarning` {boolean} Whether to emit the experimental | ||
| 59 | + warning when the instance is created. **Default:** `true`. | ||
| 60 | + * Returns: {VirtualFileSystem} | ||
| 61 | + | ||
| 62 | + Convenience factory equivalent to `new VirtualFileSystem(provider, options)`. | ||
| 63 | + | ||
| 64 | + ```cjs | ||
| 65 | + const vfs = require('node:vfs'); | ||
| 66 | + | ||
| 67 | + // Default in-memory provider | ||
| 68 | + const memoryVfs = vfs.create(); | ||
| 69 | + | ||
| 70 | + // Explicit provider | ||
| 71 | + const realVfs = vfs.create(new vfs.RealFSProvider('/tmp/sandbox')); | ||
| 72 | + ``` | ||
| 73 | + | ||
| 74 | + ## Class: `VirtualFileSystem` | ||
| 75 | + | ||
| 76 | + <!-- YAML | ||
| 77 | + added: REPLACEME | ||
| 78 | + --> | ||
| 79 | + | ||
| 80 | + A `VirtualFileSystem` wraps a [`VirtualProvider`][] and exposes a | ||
| 81 | + `node:fs`-like API. Each instance maintains its own file tree. | ||
| 82 | + | ||
| 83 | + ### `new VirtualFileSystem([provider][, options])` | ||
| 84 | + | ||
| 85 | + <!-- YAML | ||
| 86 | + added: REPLACEME | ||
| 87 | + --> | ||
| 88 | + | ||
| 89 | + * `provider` {VirtualProvider} The provider to use. **Default:** | ||
| 90 | + `new MemoryProvider()`. | ||
| 91 | + * `options` {Object} | ||
| 92 | + * `emitExperimentalWarning` {boolean} Whether to emit the experimental | ||
| 93 | + warning. **Default:** `true`. | ||
| 94 | + | ||
| 95 | + ### `vfs.provider` | ||
| 96 | + | ||
| 97 | + <!-- YAML | ||
| 98 | + added: REPLACEME | ||
| 99 | + --> | ||
| 100 | + | ||
| 101 | + * {VirtualProvider} | ||
| 102 | + | ||
| 103 | + The provider backing this VFS instance. | ||
| 104 | + | ||
| 105 | + ### `vfs.readonly` | ||
| 106 | + | ||
| 107 | + <!-- YAML | ||
| 108 | + added: REPLACEME | ||
| 109 | + --> | ||
| 110 | + | ||
| 111 | + * {boolean} | ||
| 112 | + | ||
| 113 | + `true` when the underlying provider is read-only. | ||
| 114 | + | ||
| 115 | + ### APIs | ||
| 116 | + | ||
| 117 | + `VirtualFileSystem` implements the following methods, with the same | ||
| 118 | + signatures as their [`node:fs`][] counterparts: | ||
| 119 | + | ||
| 120 | + #### Synchronous API | ||
| 121 | + | ||
| 122 | + * `existsSync(path)` | ||
| 123 | + * `statSync(path[, options])` | ||
| 124 | + * `lstatSync(path[, options])` | ||
| 125 | + * `readFileSync(path[, options])` | ||
| 126 | + * `writeFileSync(path, data[, options])` | ||
| 127 | + * `appendFileSync(path, data[, options])` | ||
| 128 | + * `readdirSync(path[, options])` | ||
| 129 | + * `mkdirSync(path[, options])` | ||
| 130 | + * `rmdirSync(path)` | ||
| 131 | + * `unlinkSync(path)` | ||
| 132 | + * `renameSync(oldPath, newPath)` | ||
| 133 | + * `copyFileSync(src, dest[, mode])` | ||
| 134 | + * `realpathSync(path[, options])` | ||
| 135 | + * `readlinkSync(path[, options])` | ||
| 136 | + * `symlinkSync(target, path[, type])` | ||
| 137 | + * `accessSync(path[, mode])` | ||
| 138 | + * `rmSync(path[, options])` | ||
| 139 | + * `truncateSync(path[, len])` | ||
| 140 | + * `ftruncateSync(fd[, len])` | ||
| 141 | + * `linkSync(existingPath, newPath)` | ||
| 142 | + * `chmodSync(path, mode)` | ||
| 143 | + * `chownSync(path, uid, gid)` | ||
| 144 | + * `utimesSync(path, atime, mtime)` | ||
| 145 | + * `lutimesSync(path, atime, mtime)` | ||
| 146 | + * `mkdtempSync(prefix)` | ||
| 147 | + * `opendirSync(path[, options])` | ||
| 148 | + * `openAsBlob(path[, options])` | ||
| 149 | + * File-descriptor ops: `openSync`, `closeSync`, `readSync`, `writeSync`, | ||
| 150 | + `fstatSync` | ||
| 151 | + * Streams: `createReadStream`, `createWriteStream` | ||
| 152 | + * Watchers: `watch`, `watchFile`, `unwatchFile` | ||
| 153 | + | ||
| 154 | + #### Callback API | ||
| 155 | + | ||
| 156 | + `readFile`, `writeFile`, `stat`, `lstat`, `readdir`, `realpath`, `readlink`, | ||
| 157 | + `access`, `open`, `close`, `read`, `write`, `rm`, `fstat`, `truncate`, | ||
| 158 | + `ftruncate`, `link`, `mkdtemp`, `opendir`. Each takes a Node.js-style | ||
| 159 | + callback `(err, ...result) => {}`. | ||
| 160 | + | ||
| 161 | + #### Promise API | ||
| 162 | + | ||
| 163 | + `vfs.promises` exposes the promise-based variants: | ||
| 164 | + | ||
| 165 | + ```cjs | ||
| 166 | + const vfs = require('node:vfs'); | ||
| 167 | + | ||
| 168 | + async function example() { | ||
| 169 | + const myVfs = vfs.create(); | ||
| 170 | + await myVfs.promises.writeFile('/file.txt', 'hello'); | ||
| 171 | + const data = await myVfs.promises.readFile('/file.txt', 'utf8'); | ||
| 172 | + return data; | ||
| 173 | + } | ||
| 174 | + example(); | ||
| 175 | + ``` | ||
| 176 | + | ||
| 177 | + The promise namespace mirrors `fs.promises` and includes `readFile`, | ||
| 178 | + `writeFile`, `appendFile`, `stat`, `lstat`, `readdir`, `mkdir`, `rmdir`, | ||
| 179 | + `unlink`, `rename`, `copyFile`, `realpath`, `readlink`, `symlink`, | ||
| 180 | + `access`, `rm`, `truncate`, `link`, `mkdtemp`, `chmod`, `chown`, `lchown`, | ||
| 181 | + `utimes`, `lutimes`, `open`, `lchmod`, and `watch`. | ||
| 182 | + | ||
| 183 | + ## Class: `VirtualProvider` | ||
| 184 | + | ||
| 185 | + <!-- YAML | ||
| 186 | + added: REPLACEME | ||
| 187 | + --> | ||
| 188 | + | ||
| 189 | + The base class for all VFS providers. Subclasses implement the essential | ||
| 190 | + primitives (`open`, `stat`, `readdir`, `mkdir`, `rmdir`, `unlink`, | ||
| 191 | + `rename`, ...) and inherit default implementations of the derived | ||
| 192 | + The base class for all VFS providers. Subclasses implement the essential | ||
| 193 | + primitives (such as `open`, `stat`, `readdir`, `mkdir`, `rmdir`, `unlink`, | ||
| 194 | + `rename`, etc.) and inherit default implementations of the derived | ||
| 195 | + methods (such as `readFile`, `writeFile`, `exists`, `copyFile`, `access`, etc.). | ||
| 196 | + | ||
| 197 | + ### Capability flags | ||
| 198 | + | ||
| 199 | + * `provider.readonly` {boolean} **Default:** `false`. | ||
| 200 | + * `provider.supportsSymlinks` {boolean} **Default:** `false`. | ||
| 201 | + * `provider.supportsWatch` {boolean} **Default:** `false`. | ||
| 202 | + | ||
| 203 | + ### Creating custom providers | ||
| 204 | + | ||
| 205 | + ```cjs | ||
| 206 | + const { VirtualProvider } = require('node:vfs'); | ||
| 207 | + | ||
| 208 | + class StaticProvider extends VirtualProvider { | ||
| 209 | + get readonly() { return true; } | ||
| 210 | + | ||
| 211 | + statSync(path) { /* ... */ } | ||
| 212 | + openSync(path, flags) { /* ... */ } | ||
| 213 | + readdirSync(path, options) { /* ... */ } | ||
| 214 | + // ... | ||
| 215 | + } | ||
| 216 | + ``` | ||
| 217 | + | ||
| 218 | + The base class throws `ERR_METHOD_NOT_IMPLEMENTED` for any primitive | ||
| 219 | + that has not been overridden, and rejects writes from a `readonly` | ||
| 220 | + provider with `EROFS`. | ||
| 221 | + | ||
| 222 | + ## Class: `MemoryProvider` | ||
| 223 | + | ||
| 224 | + <!-- YAML | ||
| 225 | + added: REPLACEME | ||
| 226 | + --> | ||
| 227 | + | ||
| 228 | + The default in-memory provider. Stores files, directories, and symbolic | ||
| 229 | + links in a `Map`-backed tree, supports symlinks (`supportsSymlinks === | ||
| 230 | + true`), and supports watching (`supportsWatch === true`). | ||
| 231 | + | ||
| 232 | + ### `memoryProvider.setReadOnly()` | ||
| 233 | + | ||
| 234 | + <!-- YAML | ||
| 235 | + added: REPLACEME | ||
| 236 | + --> | ||
| 237 | + | ||
| 238 | + Locks the provider into read-only mode. Subsequent writes through any | ||
| 239 | + [`VirtualFileSystem`][] using this provider throw `EROFS`. There is no | ||
| 240 | + way to revert the provider to writable. | ||
| 241 | + | ||
| 242 | + ```cjs | ||
| 243 | + const vfs = require('node:vfs'); | ||
| 244 | + | ||
| 245 | + const provider = new vfs.MemoryProvider(); | ||
| 246 | + const myVfs = vfs.create(provider); | ||
| 247 | + myVfs.writeFileSync('/seed.txt', 'initial'); | ||
| 248 | + | ||
| 249 | + provider.setReadOnly(); | ||
| 250 | + | ||
| 251 | + myVfs.writeFileSync('/x.txt', 'fail'); // throws EROFS | ||
| 252 | + ``` | ||
| 253 | + | ||
| 254 | + ## Class: `RealFSProvider` | ||
| 255 | + | ||
| 256 | + <!-- YAML | ||
| 257 | + added: REPLACEME | ||
| 258 | + --> | ||
| 259 | + | ||
| 260 | + A provider that wraps a directory (i.e. one on the actual file system) and exposes its | ||
| 261 | + contents through the VFS API. All VFS paths are resolved relative to | ||
| 262 | + the root and verified to stay inside it; symbolic links resolving | ||
| 263 | + outside the root are rejected. | ||
| 264 | + | ||
| 265 | + ### `new RealFSProvider(rootPath)` | ||
| 266 | + | ||
| 267 | + <!-- YAML | ||
| 268 | + added: REPLACEME | ||
| 269 | + --> | ||
| 270 | + | ||
| 271 | + * `rootPath` {string} The absolute file-system path to use as the root. | ||
| 272 | + Must be a non-empty string. | ||
| 273 | + | ||
| 274 | + ```cjs | ||
| 275 | + const vfs = require('node:vfs'); | ||
| 276 | + | ||
| 277 | + const realVfs = vfs.create(new vfs.RealFSProvider('/tmp/sandbox')); | ||
| 278 | + realVfs.writeFileSync('/file.txt', 'hello'); // writes /tmp/sandbox/file.txt | ||
| 279 | + ``` | ||
| 280 | + | ||
| 281 | + ### `realFSProvider.rootPath` | ||
| 282 | + | ||
| 283 | + <!-- YAML | ||
| 284 | + added: REPLACEME | ||
| 285 | + --> | ||
| 286 | + | ||
| 287 | + * {string} | ||
| 288 | + | ||
| 289 | + The resolved absolute path used as the root. | ||
| 290 | + | ||
| 291 | + ## Implementation details | ||
| 292 | + | ||
| 293 | + ### `Stats` objects | ||
| 294 | + | ||
| 295 | + VFS `Stats` objects are real instances of [`fs.Stats`][] (or | ||
| 296 | + [`fs.BigIntStats`][] when `{ bigint: true }` is requested). Their | ||
| 297 | + fields use synthetic but stable values: | ||
| 298 | + | ||
| 299 | + * `dev` is `4085` (the VFS device id). | ||
| 300 | + * `ino` is monotonically increasing per process. | ||
| 301 | + * `blksize` is `4096`. | ||
| 302 | + * `blocks` is `Math.ceil(size / 512)`. | ||
| 303 | + * Times default to the moment the entry was created/last modified. | ||
| 304 | + | ||
| 305 | + [`MemoryProvider`]: #class-memoryprovider | ||
| 306 | + [`VirtualFileSystem`]: #class-virtualfilesystem | ||
| 307 | + [`VirtualProvider`]: #class-virtualprovider | ||
| 308 | + [`fs.BigIntStats`]: fs.md#class-fsbigintstats | ||
| 309 | + [`fs.Stats`]: fs.md#class-fsstats | ||
| 310 | + [`node:fs`]: fs.md | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -130,9 +130,17 @@ const schemelessBlockList = new SafeSet([ | |||
| 130 | 130 | 'quic', | |
| 131 | 131 | 'test', | |
| 132 | 132 | 'test/reporters', | |
| 133 | + 'vfs', | ||
| 133 | 134 | ]); | |
| 134 | 135 | // Modules that will only be enabled at run time. | |
| 135 | - const experimentalModuleList = new SafeSet(['ffi', 'sqlite', 'quic', 'stream/iter', 'zlib/iter']); | ||
| 136 | + const experimentalModuleList = new SafeSet([ | ||
| 137 | + 'ffi', | ||
| 138 | + 'quic', | ||
| 139 | + 'sqlite', | ||
| 140 | + 'stream/iter', | ||
| 141 | + 'vfs', | ||
| 142 | + 'zlib/iter', | ||
| 143 | + ]); | ||
| 136 | 144 | ||
| 137 | 145 | // Set up process.binding() and process._linkedBinding(). | |
| 138 | 146 | { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -498,6 +498,9 @@ function initializeCJS() { | |||
| 498 | 498 | if (!getOptionValue('--experimental-ffi')) { | |
| 499 | 499 | modules = modules.filter((i) => i !== 'node:ffi'); | |
| 500 | 500 | } | |
| 501 | + if (!getOptionValue('--experimental-vfs')) { | ||
| 502 | + modules = modules.filter((i) => i !== 'node:vfs'); | ||
| 503 | + } | ||
| 501 | 504 | Module.builtinModules = ObjectFreeze(modules); | |
| 502 | 505 | ||
| 503 | 506 | initializeCjsConditions(); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments