镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions lib/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -3750,6 +3750,12 @@ function mkdtempSync(prefix, options) {
* @returns {object} A disposable object with a "path" property.
*/
function mkdtempDisposableSync(prefix, options) {
const h = vfsState.handlers;
if (h !== null) {
const result = h.mkdtempDisposableSync(prefix, options);
if (result !== undefined) return result;
}

options = getOptions(options);
if (BufferIsBuffer(prefix)) {
options = { ...options, encoding: 'buffer' };
Expand Down
6 changes: 6 additions & 0 deletions lib/internal/fs/promises.js
Original file line number Diff line number Diff line change
Expand Up @@ -2137,6 +2137,12 @@ async function mkdtemp(prefix, options) {
}

async function mkdtempDisposable(prefix, options) {
const h = vfsState.handlers;
if (h !== null) {
const promise = h.mkdtempDisposable(prefix, options);
if (promise !== undefined) return await promise;
}

options = getOptions(options);
if (BufferIsBuffer(prefix)) {
options = { ...options, encoding: 'buffer' };
Expand Down
29 changes: 29 additions & 0 deletions lib/internal/vfs/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ const {
String,
StringPrototypeEndsWith,
StringPrototypeStartsWith,
SymbolAsyncDispose,
SymbolDispose,
} = primordials;

const { Buffer } = require('buffer');
Expand Down Expand Up @@ -530,6 +532,19 @@ function createVfsHandlers() {
}
return result;
},
mkdtempDisposableSync(prefix, options) {
return vfsOp(prefix, (vfs, n) => {
const dir = vfs.mkdtempSync(n);
const remove = () => vfs.rmSync(dir, { recursive: true, force: true });
return {
path: options?.encoding === 'buffer' ? Buffer.from(dir) : dir,
remove,
[SymbolDispose]() {
remove();
},
};
});
},
opendirSync: (path, options) => vfsOp(path, (vfs, n) => vfs.opendirSync(n, options)),
openAsBlob(path, options) {
const pathStr = toPathStr(path);
Expand Down Expand Up @@ -860,6 +875,20 @@ function createVfsHandlers() {
}
return promise;
},
mkdtempDisposable(prefix, options) {
return vfsOp(prefix, async (vfs, n) => {
const dir = await vfs.promises.mkdtemp(n);
const remove = () => vfs.promises.rm(dir, { recursive: true, force: true });
return {
__proto__: null,
path: options?.encoding === 'buffer' ? Buffer.from(dir) : dir,
remove,
async [SymbolAsyncDispose]() {
await remove();
},
};
});
},
chmod: (path, mode) =>
vfsOp(path, (vfs, n) => vfs.promises.chmod(n, mode).then(() => true)),
utimes: (path, atime, mtime) =>
Expand Down
111 changes: 111 additions & 0 deletions test/parallel/test-vfs-fs-mkdtemp-disposable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Flags: --experimental-vfs
'use strict';

// fs.mkdtempDisposableSync and fs.promises.mkdtempDisposable dispatch to the
// VFS for a mounted prefix, like mkdtempSync and fs.promises.mkdtemp do, and
// their remove() deletes the directory from the VFS. A prefix outside the
// mount still reaches the real file system.

const common = require('../common');
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const vfs = require('node:vfs');
const tmpdir = require('../common/tmpdir');

tmpdir.refresh();

const myVfs = vfs.create();
myVfs.mkdirSync('/work', { recursive: true });
const mountPoint = myVfs.mount();
const prefix = path.join(mountPoint, 'work', 'tmp-');

// Sync: created in the VFS, removed from the VFS, removal is idempotent.
{
const result = fs.mkdtempDisposableSync(prefix);
assert.ok(result.path.startsWith(prefix));
assert.strictEqual(result.path.length, prefix.length + 6);
assert.strictEqual(fs.statSync(result.path).isDirectory(), true);
assert.deepStrictEqual(fs.readdirSync(path.join(mountPoint, 'work')), [path.basename(result.path)]);

fs.writeFileSync(path.join(result.path, 'file.txt'), 'data');
result.remove();
assert.strictEqual(fs.existsSync(result.path), false);
assert.deepStrictEqual(fs.readdirSync(path.join(mountPoint, 'work')), []);
result.remove();
}

// Sync: Symbol.dispose
{
const result = fs.mkdtempDisposableSync(prefix);
assert.strictEqual(fs.existsSync(result.path), true);
result[Symbol.dispose]();
assert.strictEqual(fs.existsSync(result.path), false);
result[Symbol.dispose]();
}

// Sync: buffer encoding
{
const result = fs.mkdtempDisposableSync(prefix, { encoding: 'buffer' });
assert.ok(Buffer.isBuffer(result.path));
assert.ok(result.path.toString().startsWith(prefix));
assert.strictEqual(fs.existsSync(result.path), true);
result.remove();
assert.strictEqual(fs.existsSync(result.path), false);
}

// Sync: a prefix outside the mount uses the real file system.
{
const result = fs.mkdtempDisposableSync(tmpdir.resolve('real-'));
assert.strictEqual(path.dirname(result.path), tmpdir.path);
assert.strictEqual(fs.existsSync(result.path), true);
result.remove();
assert.strictEqual(fs.existsSync(result.path), false);
}

(async () => {
// Promises: created in the VFS, removed from the VFS, removal is idempotent.
{
const result = await fs.promises.mkdtempDisposable(prefix);
assert.ok(result.path.startsWith(prefix));
assert.strictEqual(result.path.length, prefix.length + 6);
assert.strictEqual(fs.statSync(result.path).isDirectory(), true);
assert.deepStrictEqual(fs.readdirSync(path.join(mountPoint, 'work')), [path.basename(result.path)]);

fs.writeFileSync(path.join(result.path, 'file.txt'), 'data');
await result.remove();
assert.strictEqual(fs.existsSync(result.path), false);
assert.deepStrictEqual(fs.readdirSync(path.join(mountPoint, 'work')), []);
await result.remove();
}

// Promises: Symbol.asyncDispose
{
const result = await fs.promises.mkdtempDisposable(prefix);
assert.strictEqual(fs.existsSync(result.path), true);
await result[Symbol.asyncDispose]();
assert.strictEqual(fs.existsSync(result.path), false);
await result[Symbol.asyncDispose]();
}

// Promises: buffer encoding
{
const result = await fs.promises.mkdtempDisposable(prefix, { encoding: 'buffer' });
assert.ok(Buffer.isBuffer(result.path));
assert.ok(result.path.toString().startsWith(prefix));
assert.strictEqual(fs.existsSync(result.path), true);
await result.remove();
assert.strictEqual(fs.existsSync(result.path), false);
}

// Promises: a prefix outside the mount uses the real file system.
{
const result = await fs.promises.mkdtempDisposable(tmpdir.resolve('real-'));
assert.strictEqual(path.dirname(result.path), tmpdir.path);
assert.strictEqual(fs.existsSync(result.path), true);
await result.remove();
assert.strictEqual(fs.existsSync(result.path), false);
}

myVfs.unmount();
})().then(common.mustCall());