pax_global_header00006660000000000000000000000064151401463310014510gustar00rootroot0000000000000052 comment=6c0f2ccdfabc37ce002d8c073457bc447c089d20 sindresorhus-tempy-7c1cf53/000077500000000000000000000000001514014633100160115ustar00rootroot00000000000000sindresorhus-tempy-7c1cf53/.editorconfig000066400000000000000000000002571514014633100204720ustar00rootroot00000000000000root = true [*] indent_style = tab end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.yml] indent_style = space indent_size = 2 sindresorhus-tempy-7c1cf53/.gitattributes000066400000000000000000000000231514014633100206770ustar00rootroot00000000000000* text=auto eol=lf sindresorhus-tempy-7c1cf53/.github/000077500000000000000000000000001514014633100173515ustar00rootroot00000000000000sindresorhus-tempy-7c1cf53/.github/security.md000066400000000000000000000002631514014633100215430ustar00rootroot00000000000000# Security Policy To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. sindresorhus-tempy-7c1cf53/.github/workflows/000077500000000000000000000000001514014633100214065ustar00rootroot00000000000000sindresorhus-tempy-7c1cf53/.github/workflows/main.yml000066400000000000000000000006261514014633100230610ustar00rootroot00000000000000name: CI on: - push - pull_request jobs: test: name: Node.js ${{ matrix.node-version }} runs-on: ubuntu-latest strategy: fail-fast: false matrix: node-version: - 16 steps: - uses: actions/checkout@v6 - uses: actions/setup-node@v6 with: node-version: ${{ matrix.node-version }} - run: npm install - run: npm test sindresorhus-tempy-7c1cf53/.gitignore000066400000000000000000000000271514014633100200000ustar00rootroot00000000000000node_modules yarn.lock sindresorhus-tempy-7c1cf53/.npmrc000066400000000000000000000000231514014633100171240ustar00rootroot00000000000000package-lock=false sindresorhus-tempy-7c1cf53/index.d.ts000066400000000000000000000137671514014633100177300ustar00rootroot00000000000000import {type Buffer} from 'node:buffer'; import {type MergeExclusive, type TypedArray} from 'type-fest'; export type BaseOptions = { /** The name of a directory inside the OS temporary directory to create the temporary file or directory in. The directory is created automatically if it doesn't exist. By default, temporary files and directories are created directly inside the OS temporary directory. This option lets you group related temporary files into a subdirectory. Useful for organizing temporary files by app or task, making cleanup and debugging easier. @example ``` import {temporaryFile} from 'tempy'; temporaryFile({parentDirectory: 'my-app'}); //=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/my-app/4f504b9edb5ba0e89451617bf9f971dd' ``` */ readonly parentDirectory?: string; /** An absolute path to use as the base directory for temporary files and directories, instead of the OS temporary directory. _You usually won't need this option. Prefer the `parentDirectory` option instead._ This is useful for niche use-cases like different filesystem mounts or journaling filesystems. The directory is created automatically if it doesn't exist. @example ``` import {temporaryFile} from 'tempy'; temporaryFile({rootDirectory: '/mnt/fast-storage/tmp'}); //=> '/mnt/fast-storage/tmp/4f504b9edb5ba0e89451617bf9f971dd' ``` */ readonly rootDirectory?: string; }; export type FileOptions = MergeExclusive< { /** File extension. Mutually exclusive with the `name` option. _You usually won't need this option. Specify it only when actually needed._ */ readonly extension?: string; }, { /** Filename. Mutually exclusive with the `extension` option. _You usually won't need this option. Specify it only when actually needed._ */ readonly name?: string; } > & BaseOptions; export type DirectoryOptions = { /** Directory prefix. _You usually won't need this option. Specify it only when actually needed._ Useful for testing by making it easier to identify cache directories that are created. */ readonly prefix?: string; } & BaseOptions; /** The temporary path created by the function. Can be asynchronous. */ export type TaskCallback = (temporaryPath: string) => Promise | ReturnValueType; /** Get a temporary file path you can write to. @example ``` import {temporaryFile, temporaryDirectory} from 'tempy'; temporaryFile(); //=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/4f504b9edb5ba0e89451617bf9f971dd' temporaryFile({extension: 'png'}); //=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/a9fb0decd08179eb6cf4691568aa2018.png' temporaryFile({name: 'unicorn.png'}); //=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/f7f62bfd4e2a05f1589947647ed3f9ec/unicorn.png' temporaryDirectory(); //=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/2f3d094aec2cb1b93bb0f4cffce5ebd6' ``` */ export function temporaryFile(options?: FileOptions): string; /** The `callback` resolves with a temporary file path you can write to. The file is automatically cleaned up after the callback is executed. @returns A promise that resolves after the callback is executed and the file is cleaned up. @example ``` import {temporaryFileTask} from 'tempy'; await temporaryFileTask(tempFile => { console.log(tempFile); //=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/4f504b9edb5ba0e89451617bf9f971dd' }); ``` */ export function temporaryFileTask(callback: TaskCallback, options?: FileOptions): Promise ; /** Get a temporary directory path. The directory is created for you. @example ``` import {temporaryDirectory} from 'tempy'; temporaryDirectory(); //=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/2f3d094aec2cb1b93bb0f4cffce5ebd6' temporaryDirectory({prefix: 'name'}); //=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/name_3c085674ad31223b9653c88f725d6b41' ``` */ export function temporaryDirectory(options?: DirectoryOptions): string; /** The `callback` resolves with a temporary directory path you can write to. The directory is automatically cleaned up after the callback is executed. @returns A promise that resolves after the callback is executed and the directory is cleaned up. @example ``` import {temporaryDirectoryTask} from 'tempy'; await temporaryDirectoryTask(tempDirectory => { //=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/2f3d094aec2cb1b93bb0f4cffce5ebd6' }) ``` */ export function temporaryDirectoryTask(callback: TaskCallback, options?: DirectoryOptions): Promise; /** Write data to a random temp file. @example ``` import {temporaryWrite} from 'tempy'; await temporaryWrite('🦄'); //=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/2f3d094aec2cb1b93bb0f4cffce5ebd6' ``` */ export function temporaryWrite(fileContent: string | Buffer | TypedArray | DataView | NodeJS.ReadableStream, options?: FileOptions): Promise; /** Write data to a random temp file. The file is automatically cleaned up after the callback is executed. @returns A promise that resolves after the callback is executed and the file is cleaned up. @example ``` import {temporaryWriteTask} from 'tempy'; await temporaryWriteTask('🦄', tempFile => { //=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/4f504b9edb5ba0e89451617bf9f971dd' }); ``` */ export function temporaryWriteTask(fileContent: string | Buffer | TypedArray | DataView | NodeJS.ReadableStream, callback: TaskCallback, options?: FileOptions): Promise; /** Synchronously write data to a random temp file. @example ``` import {temporaryWriteSync} from 'tempy'; temporaryWriteSync('🦄'); //=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/2f3d094aec2cb1b93bb0f4cffce5ebd6' ``` */ export function temporaryWriteSync(fileContent: string | Buffer | TypedArray | DataView, options?: FileOptions): string; export {default as rootTemporaryDirectory} from 'temp-dir'; sindresorhus-tempy-7c1cf53/index.js000066400000000000000000000073231514014633100174630ustar00rootroot00000000000000import fs from 'node:fs'; import fsPromises from 'node:fs/promises'; import path from 'node:path'; import stream from 'node:stream'; import {promisify} from 'node:util'; import uniqueString from 'unique-string'; import tempDir from 'temp-dir'; import {isStream} from 'is-stream'; const pipeline = promisify(stream.pipeline); // TODO: Use `node:stream/promises` when targeting Node.js 16. // TODO: Use `is-safe-filename` when targeting Node.js 20. function assertSafePathComponent(pathComponent) { if (typeof pathComponent !== 'string') { throw new TypeError(`Expected a string, got ${typeof pathComponent}`); } const trimmed = pathComponent.trim(); const isSafe = trimmed !== '' && trimmed !== '.' && trimmed !== '..' && !pathComponent.includes('/') && !pathComponent.includes('\\') && !pathComponent.includes('\0'); if (!isSafe) { throw new Error(`Unsafe path component: ${JSON.stringify(pathComponent)}`); } } function resolveParentDirectory(parentDirectory, rootDirectory) { assertSafePathComponent(parentDirectory); const base = rootDirectory ?? tempDir; const resolved = path.join(base, parentDirectory); fs.mkdirSync(resolved, {recursive: true}); return resolved; } const getPath = (prefix = '', {parentDirectory, rootDirectory} = {}) => { if (prefix) { assertSafePathComponent(prefix); } if (rootDirectory !== undefined) { if (!path.isAbsolute(rootDirectory)) { throw new Error('The `rootDirectory` option must be an absolute path'); } fs.mkdirSync(rootDirectory, {recursive: true}); } const parent = parentDirectory ? resolveParentDirectory(parentDirectory, rootDirectory) : (rootDirectory ?? tempDir); return path.join(parent, prefix + uniqueString()); }; const writeStream = async (filePath, data) => pipeline(data, fs.createWriteStream(filePath)); async function runTask(temporaryPath, callback) { try { return await callback(temporaryPath); } finally { // Retry to handle EBUSY/EPERM on Windows (antivirus, indexing, CI). await fsPromises.rm(temporaryPath, {recursive: true, force: true, maxRetries: 10, retryDelay: 100}); } } export function temporaryFile({name, extension, parentDirectory, rootDirectory} = {}) { if (name !== undefined && name !== null) { if (extension !== undefined && extension !== null) { throw new Error('The `name` and `extension` options are mutually exclusive'); } assertSafePathComponent(name); return path.join(temporaryDirectory({parentDirectory, rootDirectory}), name); } if (extension !== undefined && extension !== null) { assertSafePathComponent(extension); } return getPath('', {parentDirectory, rootDirectory}) + (extension === undefined || extension === null ? '' : '.' + extension.replace(/^\./, '')); } export const temporaryFileTask = async (callback, options) => runTask(temporaryFile(options), callback); export function temporaryDirectory({prefix = '', parentDirectory, rootDirectory} = {}) { const directory = getPath(prefix, {parentDirectory, rootDirectory}); fs.mkdirSync(directory, {recursive: true}); return directory; } export const temporaryDirectoryTask = async (callback, options) => runTask(temporaryDirectory(options), callback); export async function temporaryWrite(fileContent, options) { const filename = temporaryFile(options); const write = isStream(fileContent) ? writeStream : fsPromises.writeFile; await write(filename, fileContent); return filename; } export const temporaryWriteTask = async (fileContent, callback, options) => runTask(await temporaryWrite(fileContent, options), callback); export function temporaryWriteSync(fileContent, options) { const filename = temporaryFile(options); fs.writeFileSync(filename, fileContent); return filename; } export {default as rootTemporaryDirectory} from 'temp-dir'; sindresorhus-tempy-7c1cf53/index.test-d.ts000066400000000000000000000030101514014633100206610ustar00rootroot00000000000000import process from 'node:process'; import {Buffer} from 'node:buffer'; import {expectType, expectError} from 'tsd'; import { temporaryFile, temporaryFileTask, temporaryDirectory, temporaryDirectoryTask, temporaryWrite, temporaryWriteTask, temporaryWriteSync, rootTemporaryDirectory, type FileOptions, } from './index.js'; const options: FileOptions = {}; expectType(temporaryDirectory()); expectType(temporaryDirectory({prefix: 'name_'})); expectType(temporaryFile()); expectType>(temporaryFileTask(temporaryFile => { expectType(temporaryFile); })); expectType>(temporaryDirectoryTask(temporaryDirectory => { expectType(temporaryDirectory); })); expectType(temporaryFile({extension: 'png'})); expectType(temporaryFile({name: 'afile.txt'})); expectError(temporaryFile({extension: 'png', name: 'afile.txt'})); expectType(rootTemporaryDirectory); expectType>(temporaryWrite('unicorn')); expectType>(temporaryWrite('unicorn', {name: 'pony.png'})); expectType>(temporaryWrite(process.stdin, {name: 'pony.png'})); expectType>(temporaryWrite(Buffer.from('pony'), {name: 'pony.png'})); expectType>(temporaryWriteTask('', temporaryFile => { expectType(temporaryFile); })); expectType(temporaryWriteSync('unicorn')); expectType(temporaryWriteSync(Buffer.from('unicorn'))); expectType(temporaryWriteSync('unicorn', {name: 'pony.png'})); sindresorhus-tempy-7c1cf53/license000066400000000000000000000021351514014633100173570ustar00rootroot00000000000000MIT License Copyright (c) Sindre Sorhus (https://sindresorhus.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. sindresorhus-tempy-7c1cf53/package.json000066400000000000000000000021171514014633100203000ustar00rootroot00000000000000{ "name": "tempy", "version": "3.2.0", "description": "Get a random temporary file or directory path", "license": "MIT", "repository": "sindresorhus/tempy", "funding": "https://github.com/sponsors/sindresorhus", "author": { "name": "Sindre Sorhus", "email": "sindresorhus@gmail.com", "url": "https://sindresorhus.com" }, "type": "module", "exports": "./index.js", "types": "./index.d.ts", "sideEffects": false, "engines": { "node": ">=14.16" }, "scripts": { "test": "xo && ava && tsd" }, "files": [ "index.js", "index.d.ts" ], "keywords": [ "temp", "temporary", "path", "file", "directory", "folder", "tempfile", "tempdir", "tmpdir", "tmpfile", "random", "unique" ], "dependencies": { "is-stream": "^3.0.0", "temp-dir": "^3.0.0", "type-fest": "^2.12.2", "unique-string": "^3.0.0" }, "devDependencies": { "@types/node": "^20.4.1", "ava": "^5.3.1", "path-exists": "^5.0.0", "touch": "^3.1.0", "tsd": "^0.28.1", "xo": "^0.54.2" }, "xo": { "rules": { "@typescript-eslint/no-redundant-type-constituents": "off" } } } sindresorhus-tempy-7c1cf53/readme.md000066400000000000000000000131751514014633100175770ustar00rootroot00000000000000# tempy > Get a random temporary file or directory path ## Install ```sh npm install tempy ``` ## Usage ```js import {temporaryFile, temporaryDirectory} from 'tempy'; temporaryFile(); //=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/4f504b9edb5ba0e89451617bf9f971dd' temporaryFile({extension: 'png'}); //=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/a9fb0decd08179eb6cf4691568aa2018.png' temporaryFile({name: 'unicorn.png'}); //=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/f7f62bfd4e2a05f1589947647ed3f9ec/unicorn.png' temporaryDirectory(); //=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/2f3d094aec2cb1b93bb0f4cffce5ebd6' temporaryDirectory({prefix: 'name'}); //=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/name_3c085674ad31223b9653c88f725d6b41' ``` ## API ### temporaryFile(options?) Get a temporary file path you can write to. ### temporaryFileTask(callback, options?) The `callback` resolves with a temporary file path you can write to. The file is automatically cleaned up after the callback is executed. Returns a promise that resolves with the return value of the callback after it is executed and the file is cleaned up. #### callback Type: `(tempPath: string) => void` A callback that is executed with the temp file path. Can be asynchronous. #### options Type: `object` *You usually won't need either the `extension` or `name` option. Specify them only when actually needed.* ##### extension Type: `string` File extension. ##### name Type: `string` Filename. Mutually exclusive with the `extension` option. ##### parentDirectory Type: `string` The name of a directory inside the OS temporary directory to create the temporary file in. The directory is created automatically if it doesn't exist. By default, the temporary file is created directly inside the OS temporary directory. This option lets you group related temporary files into a subdirectory. Useful for organizing temporary files by app or task, making cleanup and debugging easier. ```js import {temporaryFile} from 'tempy'; temporaryFile({parentDirectory: 'my-app'}); //=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/my-app/4f504b9edb5ba0e89451617bf9f971dd' ``` ##### rootDirectory Type: `string` An absolute path to use as the base directory for temporary files, instead of the OS temporary directory. *You usually won't need this option. Prefer the `parentDirectory` option instead.* Useful for niche use-cases like different filesystem mounts or journaling filesystems. The directory is created automatically if it doesn't exist. ```js import {temporaryFile} from 'tempy'; temporaryFile({rootDirectory: '/mnt/fast-storage/tmp'}); //=> '/mnt/fast-storage/tmp/4f504b9edb5ba0e89451617bf9f971dd' ``` ### temporaryDirectory(options?) Get a temporary directory path. The directory is created for you. ### temporaryDirectoryTask(callback, options?) The `callback` resolves with a temporary directory path you can write to. The directory is automatically cleaned up after the callback is executed. Returns a promise that resolves with the return value of the callback after it is executed and the directory is cleaned up. ##### callback Type: `(tempPath: string) => void` A callback that is executed with the temp directory path. Can be asynchronous. #### options Type: `Object` ##### prefix Type: `string` Directory prefix. Useful for testing by making it easier to identify cache directories that are created. *You usually won't need this option. Specify it only when actually needed.* ##### parentDirectory Type: `string` The name of a directory inside the OS temporary directory to create the temporary directory in. The directory is created automatically if it doesn't exist. By default, the temporary directory is created directly inside the OS temporary directory. This option lets you group related temporary directories into a subdirectory. ```js import {temporaryDirectory} from 'tempy'; temporaryDirectory({parentDirectory: 'my-app'}); //=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/my-app/4f504b9edb5ba0e89451617bf9f971dd' ``` ##### rootDirectory Type: `string` An absolute path to use as the base directory for temporary directories, instead of the OS temporary directory. *You usually won't need this option. Prefer the `parentDirectory` option instead.* Useful for niche use-cases like different filesystem mounts or journaling filesystems. The directory is created automatically if it doesn't exist. ```js import {temporaryDirectory} from 'tempy'; temporaryDirectory({rootDirectory: '/mnt/fast-storage/tmp'}); //=> '/mnt/fast-storage/tmp/2f3d094aec2cb1b93bb0f4cffce5ebd6' ``` ### temporaryWrite(fileContent, options?) Write data to a random temp file. ### temporaryWriteTask(fileContent, callback, options?) Write data to a random temp file. The file is automatically cleaned up after the callback is executed. Returns a promise that resolves with the return value of the callback after it is executed and the file is cleaned up. ##### fileContent Type: `string | Buffer | TypedArray | DataView | stream.Readable` Data to write to the temp file. ##### callback Type: `(tempPath: string) => void` A callback that is executed with the temp file path. Can be asynchronous. ##### options See [options](#options). ### temporaryWriteSync(fileContent, options?) Synchronously write data to a random temp file. ##### fileContent Type: `string | Buffer | TypedArray | DataView` Data to write to the temp file. ##### options See [options](#options). ### rootTemporaryDirectory Get the root temporary directory path. For example: `/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T` sindresorhus-tempy-7c1cf53/test.js000066400000000000000000000224451514014633100173350ustar00rootroot00000000000000import {Buffer} from 'node:buffer'; import path from 'node:path'; import fs from 'node:fs'; import stream from 'node:stream'; import tempDir from 'temp-dir'; import {pathExists} from 'path-exists'; import touch from 'touch'; import test from 'ava'; import { temporaryFile, temporaryFileTask, temporaryDirectory, temporaryDirectoryTask, temporaryWrite, temporaryWriteTask, temporaryWriteSync, rootTemporaryDirectory, } from './index.js'; test('.file()', t => { t.true(temporaryFile().includes(tempDir)); t.false(temporaryFile().endsWith('.')); t.false(temporaryFile({extension: undefined}).endsWith('.')); t.false(temporaryFile({extension: null}).endsWith('.')); t.true(temporaryFile({extension: 'png'}).endsWith('.png')); t.true(temporaryFile({extension: '.png'}).endsWith('.png')); t.false(temporaryFile({extension: '.png'}).endsWith('..png')); t.true(temporaryFile({name: 'custom-name.md'}).endsWith('custom-name.md')); t.throws(() => { temporaryFile({name: 'custom-name.md', extension: '.ext'}); }); t.throws(() => { temporaryFile({name: 'custom-name.md', extension: ''}); }); t.notThrows(() => { temporaryFile({name: 'custom-name.md', extension: undefined}); }); t.notThrows(() => { temporaryFile({name: 'custom-name.md', extension: null}); }); }); test('.file.task()', async t => { let temporaryFilePath; t.is(await temporaryFileTask(async temporaryFile => { await touch(temporaryFile); temporaryFilePath = temporaryFile; return temporaryFile; }), temporaryFilePath); t.false(await pathExists(temporaryFilePath)); }); test('.task() - cleans up even if callback throws', async t => { let temporaryDirectoryPath; await t.throwsAsync(temporaryDirectoryTask(async temporaryDirectory => { temporaryDirectoryPath = temporaryDirectory; throw new Error('Catch me if you can!'); }), { instanceOf: Error, message: 'Catch me if you can!', }); t.false(await pathExists(temporaryDirectoryPath)); }); test('.directory()', t => { const prefix = 'name_'; t.true(temporaryDirectory().includes(tempDir)); t.true(path.basename(temporaryDirectory({prefix})).startsWith(prefix)); }); test('.directory.task()', async t => { let temporaryDirectoryPath; t.is(await temporaryDirectoryTask(async temporaryDirectory => { temporaryDirectoryPath = temporaryDirectory; return temporaryDirectory; }), temporaryDirectoryPath); t.false(await pathExists(temporaryDirectoryPath)); }); test('.write(string)', async t => { const filePath = await temporaryWrite('unicorn', {name: 'test.png'}); t.is(fs.readFileSync(filePath, 'utf8'), 'unicorn'); t.is(path.basename(filePath), 'test.png'); }); test('.write.task(string)', async t => { let temporaryFilePath; t.is(await temporaryWriteTask('', async temporaryFile => { temporaryFilePath = temporaryFile; return temporaryFile; }), temporaryFilePath); t.false(await pathExists(temporaryFilePath)); }); test('.write(buffer)', async t => { const filePath = await temporaryWrite(Buffer.from('unicorn')); t.is(fs.readFileSync(filePath, 'utf8'), 'unicorn'); }); test('.write(stream)', async t => { const readable = new stream.Readable({ read() {}, }); readable.push('unicorn'); readable.push(null); // eslint-disable-line unicorn/no-array-push-push const filePath = await temporaryWrite(readable); t.is(fs.readFileSync(filePath, 'utf8'), 'unicorn'); }); test('.write(stream) failing stream', async t => { const readable = new stream.Readable({ read() {}, }); readable.push('unicorn'); setImmediate(() => { readable.emit('error', new Error('Catch me if you can!')); readable.push(null); }); await t.throwsAsync(temporaryWrite(readable), { instanceOf: Error, message: 'Catch me if you can!', }); }); test('.writeSync()', t => { t.is(fs.readFileSync(temporaryWriteSync('unicorn'), 'utf8'), 'unicorn'); }); test('.root', t => { t.true(rootTemporaryDirectory.length > 0); t.true(path.isAbsolute(rootTemporaryDirectory)); }); test('.file() with parentDirectory', t => { const filePath = temporaryFile({parentDirectory: 'my-app'}); t.true(filePath.includes(path.join(tempDir, 'my-app'))); }); test('.file() with parentDirectory and extension', t => { const filePath = temporaryFile({parentDirectory: 'my-app', extension: 'png'}); t.true(filePath.includes(path.join(tempDir, 'my-app'))); t.true(filePath.endsWith('.png')); }); test('.file() with parentDirectory and name', t => { const filePath = temporaryFile({parentDirectory: 'my-app', name: 'unicorn.png'}); t.true(filePath.includes(path.join(tempDir, 'my-app'))); t.is(path.basename(filePath), 'unicorn.png'); }); test('.directory() with parentDirectory', t => { const directory = temporaryDirectory({parentDirectory: 'my-app'}); t.true(directory.includes(path.join(tempDir, 'my-app'))); t.true(fs.statSync(directory).isDirectory()); }); test('.directory() with parentDirectory and prefix', t => { const directory = temporaryDirectory({parentDirectory: 'my-app', prefix: 'name_'}); t.true(directory.includes(path.join(tempDir, 'my-app'))); t.true(path.basename(directory).startsWith('name_')); }); test('.file() with parentDirectory - auto-creates the directory', t => { const parentDirectory = `auto-create-name-${Date.now()}`; const filePath = temporaryFile({parentDirectory, name: 'test.txt'}); t.true(fs.existsSync(path.dirname(filePath))); }); test('.file() with parentDirectory and extension - auto-creates the directory', t => { const parentDirectory = `auto-create-ext-${Date.now()}`; const filePath = temporaryFile({parentDirectory, extension: 'png'}); t.true(fs.existsSync(path.dirname(filePath))); t.true(filePath.endsWith('.png')); }); test('.write() with parentDirectory', async t => { const parentDirectory = `auto-create-write-${Date.now()}`; const filePath = await temporaryWrite('unicorn', {parentDirectory}); t.is(fs.readFileSync(filePath, 'utf8'), 'unicorn'); t.true(filePath.includes(path.join(tempDir, parentDirectory))); }); test('.file() with parentDirectory - rejects unsafe values', t => { for (const fixture of ['..', '../foo', 'foo/bar', 'foo\\bar', 'foo\0bar']) { t.throws(() => temporaryFile({parentDirectory: fixture}), {message: /Unsafe path component/}); } }); test('.file() with rootDirectory', t => { const rootDirectory = path.join(tempDir, `root-test-${Date.now()}`); const filePath = temporaryFile({rootDirectory}); t.true(filePath.startsWith(rootDirectory)); t.true(fs.existsSync(rootDirectory)); }); test('.file() with rootDirectory and parentDirectory', t => { const rootDirectory = path.join(tempDir, `root-parent-test-${Date.now()}`); const filePath = temporaryFile({rootDirectory, parentDirectory: 'my-app'}); t.true(filePath.startsWith(path.join(rootDirectory, 'my-app'))); }); test('.file() with rootDirectory and extension', t => { const rootDirectory = path.join(tempDir, `root-ext-test-${Date.now()}`); const filePath = temporaryFile({rootDirectory, extension: 'png'}); t.true(filePath.startsWith(rootDirectory)); t.true(filePath.endsWith('.png')); }); test('.file() with rootDirectory and name', t => { const rootDirectory = path.join(tempDir, `root-name-test-${Date.now()}`); const filePath = temporaryFile({rootDirectory, name: 'unicorn.png'}); t.true(filePath.startsWith(rootDirectory)); t.is(path.basename(filePath), 'unicorn.png'); }); test('.directory() with rootDirectory', t => { const rootDirectory = path.join(tempDir, `root-dir-test-${Date.now()}`); const directory = temporaryDirectory({rootDirectory}); t.true(directory.startsWith(rootDirectory)); t.true(fs.statSync(directory).isDirectory()); }); test('.file() with rootDirectory - rejects relative paths', t => { t.throws(() => temporaryFile({rootDirectory: 'relative/path'}), { message: /must be an absolute path/, }); }); test('.write() with rootDirectory', async t => { const rootDirectory = path.join(tempDir, `root-write-test-${Date.now()}`); const filePath = await temporaryWrite('unicorn', {rootDirectory}); t.is(fs.readFileSync(filePath, 'utf8'), 'unicorn'); t.true(filePath.startsWith(rootDirectory)); }); test('.writeSync() with rootDirectory', t => { const rootDirectory = path.join(tempDir, `root-write-sync-test-${Date.now()}`); const filePath = temporaryWriteSync('unicorn', {rootDirectory}); t.is(fs.readFileSync(filePath, 'utf8'), 'unicorn'); t.true(filePath.startsWith(rootDirectory)); }); test('.directory.task() with rootDirectory cleans up', async t => { const rootDirectory = path.join(tempDir, `root-task-test-${Date.now()}`); let temporaryDirectoryPath; await temporaryDirectoryTask(async temporaryDirectory => { temporaryDirectoryPath = temporaryDirectory; t.true(temporaryDirectory.startsWith(rootDirectory)); }, {rootDirectory}); t.false(await pathExists(temporaryDirectoryPath)); }); // TODO: Use `unsafeFilenameFixtures` from `is-safe-filename` when targeting Node.js 20. const unsafeFilenameFixtures = [ '', ' ', '.', '..', ' .', '. ', ' ..', '.. ', '../', '../foo', 'foo/../bar', 'foo/bar', 'foo\\bar', 'foo\0bar', ]; test('rejects unsafe path components', t => { for (const fixture of unsafeFilenameFixtures) { t.throws(() => temporaryFile({name: fixture}), {message: /Unsafe path component/}); t.throws(() => temporaryFile({extension: fixture}), {message: /Unsafe path component/}); // Empty string is valid for prefix (it's the default) if (fixture !== '') { t.throws(() => temporaryDirectory({prefix: fixture}), {message: /Unsafe path component/}); } } });