UNPKG

with-local-tmp-dir

Version:

Creates a temporary folder inside cwd, cds inside the folder, runs a function, and removes the folder. Especially useful for testing.

74 lines 2.07 kB
import crypto from "node:crypto"; import fsPromises from "node:fs/promises"; import pathLib from "node:path"; import chdir from "@dword-design/chdir"; import fs from "fs-extra"; import pRetry from "p-retry"; const TEMPLATE_PATTERN = /XXXXXX/; const RANDOM_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; const _randomChars = howMany => { const value = []; let rnd = null; try { rnd = crypto.randomBytes(howMany); } catch { rnd = crypto.pseudoRandomBytes(howMany); } for (let i = 0; i < howMany; i++) { value.push(RANDOM_CHARS[rnd[i] % RANDOM_CHARS.length]); } return value.join(""); }; const _generateTmpName = opts => { if (opts.name !== void 0) { return pathLib.join(opts.dir, opts.name); } if (opts.template !== void 0) { return pathLib.join(opts.dir, opts.template).replace(TEMPLATE_PATTERN, _randomChars(6)); } const name = [opts.prefix, "-", process.pid, "-", _randomChars(12), opts.postfix ? "-" + opts.postfix : ""].join(""); return pathLib.join(opts.dir, name); }; export default async (...args) => { let options = args.find(arg => typeof arg === "object"); options = { dir: ".", keep: false, mode: 448, prefix: "tmp", tries: 3, unsafeCleanup: true, ...options }; const callback = args.find(arg => typeof arg === "function"); const path = _generateTmpName(options); await pRetry(async () => { try { await fs.stat(path); } catch { return; } throw new Error(`Temporary directory already exists: ${path}`); }, { retries: options.tries }); await fs.ensureDir(path, options.mode); const removeFunction = options.unsafeCleanup ? fs.remove.bind(fs) : fsPromises.rmdir.bind(fsPromises); if (callback) { try { return await chdir(path, callback); } finally { if (!options.keep) { await removeFunction(path); } } } const back = chdir(path); return () => { back(); if (options.keep) { return Promise.resolve(); } return removeFunction(path); }; };