Adds the node:vfs builtin module with VirtualFileSystem and provider classes. No integration with fs, modules, or SEA. Assisted-by: Claude-Opus4.7 Signed-off-by: Matteo Collina <hello@matteocollina.com> PR-URL: https://github.com/nodejs/node/pull/63115 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Paolo Insogna <paolo@cowtech.it> Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
154 lines
4.6 KiB
JavaScript
154 lines
4.6 KiB
JavaScript
// Flags: --experimental-vfs
|
|
'use strict';
|
|
|
|
// Exercise the VFS callback-style async API on every method.
|
|
|
|
const common = require('../common');
|
|
const assert = require('assert');
|
|
const vfs = require('node:vfs');
|
|
|
|
const myVfs = vfs.create();
|
|
myVfs.mkdirSync('/dir/sub', { recursive: true });
|
|
myVfs.writeFileSync('/dir/file.txt', 'hello');
|
|
myVfs.writeFileSync('/dir/other.txt', 'other');
|
|
|
|
// readFile (no options)
|
|
myVfs.readFile('/dir/file.txt', common.mustSucceed((data) => {
|
|
assert.ok(Buffer.isBuffer(data));
|
|
}));
|
|
|
|
// writeFile + appendFile (no options) -> readFile
|
|
myVfs.writeFile('/cb-write.txt', 'a', common.mustSucceed(() => {
|
|
myVfs.readFile('/cb-write.txt', 'utf8', common.mustSucceed((data) => {
|
|
assert.strictEqual(data, 'a');
|
|
}));
|
|
}));
|
|
|
|
// stat / lstat (with and without options)
|
|
myVfs.stat('/dir/file.txt', common.mustSucceed((st) => {
|
|
assert.strictEqual(st.size, 5);
|
|
}));
|
|
myVfs.stat('/dir/file.txt', { bigint: true }, common.mustSucceed((st) => {
|
|
assert.strictEqual(typeof st.size, 'bigint');
|
|
}));
|
|
myVfs.lstat('/dir/file.txt', common.mustSucceed((st) => {
|
|
assert.ok(st.isFile());
|
|
}));
|
|
|
|
// readdir
|
|
myVfs.readdir('/dir', common.mustSucceed((names) => {
|
|
assert.ok(names.includes('file.txt'));
|
|
}));
|
|
|
|
// realpath
|
|
myVfs.realpath('/dir/file.txt', common.mustSucceed((p) => {
|
|
assert.strictEqual(p, '/dir/file.txt');
|
|
}));
|
|
|
|
// access (with and without mode)
|
|
myVfs.access('/dir/file.txt', common.mustSucceed());
|
|
myVfs.access('/dir/file.txt', 0, common.mustSucceed());
|
|
myVfs.access('/missing.txt', common.mustCall((err) => {
|
|
assert.strictEqual(err.code, 'ENOENT');
|
|
}));
|
|
|
|
// open / read / write / close cb chain
|
|
myVfs.open('/dir/file.txt', common.mustSucceed((fd) => {
|
|
const buf = Buffer.alloc(5);
|
|
myVfs.read(fd, buf, 0, 5, 0, common.mustSucceed((bytesRead) => {
|
|
assert.strictEqual(bytesRead, 5);
|
|
assert.strictEqual(buf.toString(), 'hello');
|
|
myVfs.close(fd, common.mustSucceed());
|
|
}));
|
|
}));
|
|
|
|
// Open with explicit flags / mode
|
|
myVfs.open('/dir/new1.txt', 'w', common.mustSucceed((fd) => {
|
|
const buf = Buffer.from('xyz');
|
|
myVfs.write(fd, buf, 0, 3, 0, common.mustSucceed((bytesWritten) => {
|
|
assert.strictEqual(bytesWritten, 3);
|
|
myVfs.fstat(fd, common.mustSucceed((st) => {
|
|
assert.strictEqual(st.size, 3);
|
|
myVfs.ftruncate(fd, 1, common.mustSucceed(() => {
|
|
myVfs.close(fd, common.mustCall());
|
|
}));
|
|
}));
|
|
}));
|
|
}));
|
|
|
|
// Open with explicit flags, no mode arg form
|
|
myVfs.open('/dir/new2.txt', 'w', 0o644, common.mustSucceed((fd) => {
|
|
myVfs.close(fd, common.mustCall());
|
|
}));
|
|
|
|
// rm callback (file)
|
|
myVfs.writeFileSync('/cb-rm.txt', 'x');
|
|
myVfs.rm('/cb-rm.txt', common.mustSucceed(() => {
|
|
assert.strictEqual(myVfs.existsSync('/cb-rm.txt'), false);
|
|
}));
|
|
|
|
// Rm callback with options (recursive)
|
|
myVfs.mkdirSync('/cb-rm-dir/sub', { recursive: true });
|
|
myVfs.writeFileSync('/cb-rm-dir/sub/f.txt', 'x');
|
|
myVfs.rm('/cb-rm-dir', { recursive: true }, common.mustSucceed());
|
|
|
|
// Rm callback failure path
|
|
myVfs.rm('/missing-rm', common.mustCall((err) => {
|
|
assert.strictEqual(err.code, 'ENOENT');
|
|
}));
|
|
|
|
// truncate / ftruncate cb
|
|
myVfs.writeFileSync('/cb-tr.txt', 'abcdef');
|
|
myVfs.truncate('/cb-tr.txt', 3, common.mustSucceed(() => {
|
|
assert.strictEqual(myVfs.readFileSync('/cb-tr.txt', 'utf8'), 'abc');
|
|
}));
|
|
myVfs.truncate('/missing-tr.txt', common.mustCall((err) => {
|
|
assert.ok(err);
|
|
}));
|
|
myVfs.ftruncate(0xFFFFFFF /* invalid fd */, common.mustCall((err) => {
|
|
assert.strictEqual(err.code, 'EBADF');
|
|
}));
|
|
|
|
// link cb
|
|
myVfs.writeFileSync('/cb-link-src.txt', 'x');
|
|
myVfs.link('/cb-link-src.txt', '/cb-link-dst.txt', common.mustSucceed());
|
|
myVfs.link('/missing-src.txt', '/cb-bad-link.txt', common.mustCall((err) => {
|
|
assert.ok(err);
|
|
}));
|
|
|
|
// mkdtemp cb
|
|
myVfs.mkdtemp('/tmp-', common.mustSucceed((p) => {
|
|
assert.ok(p.startsWith('/tmp-'));
|
|
}));
|
|
myVfs.mkdtemp('/tmp-', {}, common.mustSucceed((p) => {
|
|
assert.ok(p.startsWith('/tmp-'));
|
|
}));
|
|
|
|
// opendir cb
|
|
myVfs.opendir('/dir', common.mustSucceed((dir) => {
|
|
assert.strictEqual(dir.path, '/dir');
|
|
dir.closeSync();
|
|
}));
|
|
myVfs.opendir('/missing-dir', common.mustCall((err) => {
|
|
assert.ok(err);
|
|
}));
|
|
|
|
// EBADF callback paths
|
|
myVfs.read(0xFFFFFFF, Buffer.alloc(1), 0, 1, 0, common.mustCall((err) => {
|
|
assert.strictEqual(err.code, 'EBADF');
|
|
}));
|
|
myVfs.write(0xFFFFFFF, Buffer.alloc(1), 0, 1, 0, common.mustCall((err) => {
|
|
assert.strictEqual(err.code, 'EBADF');
|
|
}));
|
|
myVfs.close(0xFFFFFFF, common.mustCall((err) => {
|
|
assert.strictEqual(err.code, 'EBADF');
|
|
}));
|
|
myVfs.fstat(0xFFFFFFF, common.mustCall((err) => {
|
|
assert.strictEqual(err.code, 'EBADF');
|
|
}));
|
|
|
|
// readlink cb
|
|
myVfs.symlinkSync('/dir/file.txt', '/cb-link');
|
|
myVfs.readlink('/cb-link', common.mustSucceed((target) => {
|
|
assert.strictEqual(target, '/dir/file.txt');
|
|
}));
|