mirror of
https://github.com/nodejs/node.git
synced 2026-10-28 10:15:07 +08:00
Enable node:vfs by default. --no-experimental-vfs disables node:vfs exposure and --vfs-load. Assisted-by: pi Signed-off-by: Matteo Collina <hello@matteocollina.com> PR-URL: https://github.com/nodejs/node/pull/66318 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Paolo Insogna <paolo@cowtech.it>
152 lines
5.0 KiB
JavaScript
152 lines
5.0 KiB
JavaScript
// Flags: --disable-warning=ExperimentalWarning --expose-internals
|
|
'use strict';
|
|
|
|
// MemoryProvider supports dynamic content providers and lazily-populated
|
|
// directories internally. These features have no public construction API,
|
|
// so we drive them directly through MemoryEntry / MemoryProvider.
|
|
|
|
const common = require('../common');
|
|
const assert = require('assert');
|
|
const vfs = require('node:vfs');
|
|
const { MemoryProvider } = require('internal/vfs/providers/memory');
|
|
|
|
function getRoot(provider) {
|
|
const symbols = Object.getOwnPropertySymbols(provider);
|
|
const kRoot = symbols.find((s) => s.description === 'kRoot');
|
|
assert.ok(kRoot, 'kRoot symbol expected on MemoryProvider');
|
|
return provider[kRoot];
|
|
}
|
|
|
|
function makeFileEntry(prototypeFrom, contentProvider) {
|
|
const t = Date.now();
|
|
const fileEntry = { __proto__: Object.getPrototypeOf(prototypeFrom) };
|
|
Object.assign(fileEntry, {
|
|
type: 0, // TYPE_FILE
|
|
mode: 0o644,
|
|
content: Buffer.alloc(0),
|
|
contentProvider,
|
|
children: null,
|
|
target: null,
|
|
populate: null,
|
|
populated: true,
|
|
nlink: 1,
|
|
uid: 0,
|
|
gid: 0,
|
|
atime: t,
|
|
mtime: t,
|
|
ctime: t,
|
|
birthtime: t,
|
|
});
|
|
fileEntry.isFile = prototypeFrom.isFile.bind(fileEntry);
|
|
fileEntry.isDirectory = prototypeFrom.isDirectory.bind(fileEntry);
|
|
fileEntry.isSymbolicLink = prototypeFrom.isSymbolicLink.bind(fileEntry);
|
|
fileEntry.isDynamic = prototypeFrom.isDynamic.bind(fileEntry);
|
|
fileEntry.getContentSync = prototypeFrom.getContentSync.bind(fileEntry);
|
|
fileEntry.getContentAsync = prototypeFrom.getContentAsync.bind(fileEntry);
|
|
return fileEntry;
|
|
}
|
|
|
|
function makeDirEntry(prototypeFrom, populate) {
|
|
const t = Date.now();
|
|
const dirEntry = { __proto__: Object.getPrototypeOf(prototypeFrom) };
|
|
Object.assign(dirEntry, {
|
|
type: 1, // TYPE_DIR
|
|
mode: 0o755,
|
|
children: new Map(),
|
|
populate,
|
|
populated: false,
|
|
nlink: 1,
|
|
uid: 0,
|
|
gid: 0,
|
|
atime: t,
|
|
mtime: t,
|
|
ctime: t,
|
|
birthtime: t,
|
|
});
|
|
dirEntry.isFile = prototypeFrom.isFile.bind(dirEntry);
|
|
dirEntry.isDirectory = prototypeFrom.isDirectory.bind(dirEntry);
|
|
dirEntry.isSymbolicLink = prototypeFrom.isSymbolicLink.bind(dirEntry);
|
|
dirEntry.isDynamic = prototypeFrom.isDynamic.bind(dirEntry);
|
|
dirEntry.getContentSync = prototypeFrom.getContentSync.bind(dirEntry);
|
|
dirEntry.getContentAsync = prototypeFrom.getContentAsync.bind(dirEntry);
|
|
return dirEntry;
|
|
}
|
|
|
|
// ===== Lazy-populated directory =====
|
|
{
|
|
const provider = new MemoryProvider();
|
|
const root = getRoot(provider);
|
|
|
|
const dir = makeDirEntry(root, (scoped) => {
|
|
scoped.addFile('hello.txt', 'lazy hello');
|
|
scoped.addFile('dyn.txt', () => 'dynamic-string');
|
|
scoped.addDirectory('subdir', null);
|
|
scoped.addSymlink('link.txt', '/lazy/hello.txt');
|
|
});
|
|
root.children.set('lazy', dir);
|
|
|
|
const myVfs = vfs.create(provider);
|
|
|
|
// Reading the lazy directory triggers populate
|
|
const entries = myVfs.readdirSync('/lazy');
|
|
assert.deepStrictEqual(entries.sort(),
|
|
['dyn.txt', 'hello.txt', 'link.txt', 'subdir']);
|
|
|
|
// Static file in the lazy directory
|
|
assert.strictEqual(myVfs.readFileSync('/lazy/hello.txt', 'utf8'),
|
|
'lazy hello');
|
|
|
|
// Dynamic content provider returning a string (sync read)
|
|
assert.strictEqual(myVfs.readFileSync('/lazy/dyn.txt', 'utf8'),
|
|
'dynamic-string');
|
|
|
|
// Dynamic content provider via promises.readFile
|
|
myVfs.promises.readFile('/lazy/dyn.txt', 'utf8').then(common.mustCall((s) => {
|
|
assert.strictEqual(s, 'dynamic-string');
|
|
}));
|
|
}
|
|
|
|
// ===== Dynamic content provider returning a Buffer =====
|
|
{
|
|
const provider = new MemoryProvider();
|
|
const root = getRoot(provider);
|
|
root.children.set('buf-dyn.txt',
|
|
makeFileEntry(root, () => Buffer.from('buffer-content')));
|
|
|
|
const myVfs = vfs.create(provider);
|
|
assert.strictEqual(myVfs.readFileSync('/buf-dyn.txt', 'utf8'),
|
|
'buffer-content');
|
|
}
|
|
|
|
// ===== Async-only content provider: sync API throws ERR_INVALID_STATE =====
|
|
{
|
|
const provider = new MemoryProvider();
|
|
const root = getRoot(provider);
|
|
root.children.set('async-only.txt',
|
|
makeFileEntry(root, async () => 'async-only'));
|
|
|
|
const myVfs = vfs.create(provider);
|
|
assert.throws(() => myVfs.readFileSync('/async-only.txt'),
|
|
{ code: 'ERR_INVALID_STATE' });
|
|
|
|
myVfs.promises.readFile('/async-only.txt', 'utf8').then(common.mustCall((s) => {
|
|
assert.strictEqual(s, 'async-only');
|
|
}));
|
|
}
|
|
|
|
// ===== Renaming over a lazy directory does not discard its entries =====
|
|
{
|
|
const provider = new MemoryProvider();
|
|
const root = getRoot(provider);
|
|
root.children.set('lazy', makeDirEntry(root, (scoped) => {
|
|
scoped.addFile('keep.txt', 'keep');
|
|
}));
|
|
|
|
const myVfs = vfs.create(provider);
|
|
myVfs.mkdirSync('/src');
|
|
|
|
assert.throws(() => myVfs.renameSync('/src', '/lazy'), { code: 'ENOTEMPTY' });
|
|
assert.strictEqual(myVfs.existsSync('/src'), true);
|
|
assert.strictEqual(myVfs.readFileSync('/lazy/keep.txt', 'utf8'), 'keep');
|
|
}
|