Skip to content

Commit

Permalink
Add cleanup methods (#25)
Browse files Browse the repository at this point in the history
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
  • Loading branch information
Richienb and sindresorhus authored Sep 13, 2020
1 parent c6e32f1 commit 96592fe
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 45 deletions.
136 changes: 98 additions & 38 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,59 +35,119 @@ declare namespace tempy {
*/
readonly prefix?: string;
};

/**
The temporary path created by the function. Can be asynchronous.
*/
type TaskCallback = (tempPath: string) => Promise<void> | void;
}

declare const tempy: {
/**
Get a temporary file path you can write to.
file: {
/**
The `callback` resolves with a temporary file path you can write to. The file is automatically cleaned up after the callback is executed.
@example
```
import tempy = require('tempy');
@returns A promise that resolves after the callback is executed and the file is cleaned up.
tempy.file();
//=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/4f504b9edb5ba0e89451617bf9f971dd'
@example
```
import tempy = require('tempy');
tempy.file({extension: 'png'});
//=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/a9fb0decd08179eb6cf4691568aa2018.png'
await tempy.file.task(tempFile => {
console.log(tempFile);
//=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/4f504b9edb5ba0e89451617bf9f971dd'
});
```
*/
task: (callback: tempy.TaskCallback, options?: tempy.FileOptions) => Promise<void>;

tempy.file({name: 'unicorn.png'});
//=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/f7f62bfd4e2a05f1589947647ed3f9ec/unicorn.png'
/**
Get a temporary file path you can write to.
tempy.directory();
//=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/2f3d094aec2cb1b93bb0f4cffce5ebd6'
```
*/
file: (options?: tempy.FileOptions) => string;
@example
```
import tempy = require('tempy');
/**
Get a temporary directory path. The directory is created for you.
tempy.file();
//=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/4f504b9edb5ba0e89451617bf9f971dd'
@example
```
import tempy = require('tempy');
tempy.file({extension: 'png'});
//=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/a9fb0decd08179eb6cf4691568aa2018.png'
tempy.directory();
//=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/2f3d094aec2cb1b93bb0f4cffce5ebd6'
tempy.file({name: 'unicorn.png'});
//=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/f7f62bfd4e2a05f1589947647ed3f9ec/unicorn.png'
tempy.directory({prefix: 'a'});
//=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/name_3c085674ad31223b9653c88f725d6b41'
```
*/
directory: (options?: tempy.DirectoryOptions) => string;
tempy.directory();
//=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/2f3d094aec2cb1b93bb0f4cffce5ebd6'
```
*/
(options?: tempy.FileOptions): string;
};

/**
Write data to a random temp file.
directory: {
/**
The `callback` resolves with a temporary directory path you can write to. The directory is automatically cleaned up after the callback is executed.
@example
```
import tempy = require('tempy');
@returns A promise that resolves after the callback is executed and the directory is cleaned up.
await tempy.write('🦄');
//=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/2f3d094aec2cb1b93bb0f4cffce5ebd6'
```
*/
write: (fileContent: string | Buffer | TypedArray | DataView | NodeJS.ReadableStream, options?: tempy.FileOptions) => Promise<string>;
@example
```
import tempy = require('tempy');
await tempy.directory.task(tempDirectory => {
//=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/2f3d094aec2cb1b93bb0f4cffce5ebd6'
})
```
*/
task: (callback: tempy.TaskCallback, options?: tempy.DirectoryOptions) => Promise<void>;

/**
Get a temporary directory path. The directory is created for you.
@example
```
import tempy = require('tempy');
tempy.directory();
//=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/2f3d094aec2cb1b93bb0f4cffce5ebd6'
tempy.directory({prefix: 'a'});
//=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/name_3c085674ad31223b9653c88f725d6b41'
```
*/
(options?: tempy.DirectoryOptions): string;
};

write: {
/**
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 tempy = require('tempy');
await tempy.write.task('🦄', tempFile => {
//=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/4f504b9edb5ba0e89451617bf9f971dd'
});
```
*/
task: (fileContent: string | Buffer | TypedArray | DataView | NodeJS.ReadableStream, callback: tempy.TaskCallback, options?: tempy.FileOptions) => Promise<void>;

/**
Write data to a random temp file.
@example
```
import tempy = require('tempy');
await tempy.write('🦄');
//=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/2f3d094aec2cb1b93bb0f4cffce5ebd6'
```
*/
(fileContent: string | Buffer | TypedArray | DataView | NodeJS.ReadableStream, options?: tempy.FileOptions): Promise<string>;
};

/**
Synchronously write data to a random temp file.
Expand Down
14 changes: 14 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const path = require('path');
const uniqueString = require('unique-string');
const tempDir = require('temp-dir');
const isStream = require('is-stream');
const del = require('del');
const stream = require('stream');
const {promisify} = require('util');

Expand All @@ -14,6 +15,13 @@ const getPath = (prefix = '') => path.join(tempDir, prefix + uniqueString());

const writeStream = async (filePath, data) => pipeline(data, fs.createWriteStream(filePath));

const createTask = (tempyFunction, {extraArguments = 0} = {}) => async (...arguments_) => {
const [callback, options] = arguments_.slice(extraArguments);
const result = await tempyFunction(...arguments_.slice(0, extraArguments), options);
await callback(result);
await del(result, {force: true});
};

module.exports.file = options => {
options = {
...options
Expand All @@ -30,19 +38,25 @@ module.exports.file = options => {
return getPath() + (options.extension === undefined || options.extension === null ? '' : '.' + options.extension.replace(/^\./, ''));
};

module.exports.file.task = createTask(module.exports.file);

module.exports.directory = ({prefix = ''} = {}) => {
const directory = getPath(prefix);
fs.mkdirSync(directory);
return directory;
};

module.exports.directory.task = createTask(module.exports.directory);

module.exports.write = async (data, options) => {
const filename = module.exports.file(options);
const write = isStream(data) ? writeStream : writeFile;
await write(filename, data);
return filename;
};

module.exports.write.task = createTask(module.exports.write, {extraArguments: 1});

module.exports.writeSync = (data, options) => {
const filename = module.exports.file(options);
fs.writeFileSync(filename, data);
Expand Down
9 changes: 9 additions & 0 deletions index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ const options: tempy.FileOptions = {}; // eslint-disable-line @typescript-eslint
expectType<string>(tempy.directory());
expectType<string>(tempy.directory({prefix: 'name_'}));
expectType<string>(tempy.file());
expectType<Promise<void>>(tempy.file.task(temporaryFile => {
expectType<string>(temporaryFile);
}));
expectType<Promise<void>>(tempy.directory.task(temporaryDirectory => {
expectType<string>(temporaryDirectory);
}));
expectType<string>(tempy.file({extension: 'png'}));
expectType<string>(tempy.file({name: 'afile.txt'}));
expectError(tempy.file({extension: 'png', name: 'afile.txt'}));
Expand All @@ -14,6 +20,9 @@ expectType<Promise<string>>(tempy.write('unicorn'));
expectType<Promise<string>>(tempy.write('unicorn', {name: 'pony.png'}));
expectType<Promise<string>>(tempy.write(process.stdin, {name: 'pony.png'}));
expectType<Promise<string>>(tempy.write(Buffer.from('pony'), {name: 'pony.png'}));
expectType<Promise<void>>(tempy.write.task('', temporaryFile => {
expectType<string>(temporaryFile);
}));

expectType<string>(tempy.writeSync('unicorn'));
expectType<string>(tempy.writeSync(Buffer.from('unicorn')));
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,16 @@
"unique"
],
"dependencies": {
"del": "^5.1.0",
"is-stream": "^2.0.0",
"temp-dir": "^2.0.0",
"type-fest": "^0.16.0",
"unique-string": "^2.0.0"
},
"devDependencies": {
"ava": "^2.4.0",
"path-exists": "^4.0.0",
"touch": "^3.1.0",
"tsd": "^0.13.1",
"xo": "^0.32.1"
},
Expand Down
37 changes: 30 additions & 7 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ tempy.directory({prefix: 'name'});

Get a temporary file path you can write to.

### tempy.file.task(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 after the callback 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`
Expand All @@ -57,13 +67,22 @@ Filename. Mutually exclusive with the `extension` option.

Get a temporary directory path. The directory is created for you.

### tempy.directory.task(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 after the callback 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.
Expand All @@ -76,12 +95,22 @@ Useful for testing by making it easier to identify cache directories that are cr

Write data to a random temp file.

### tempy.write.task(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 after the callback 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).
Expand All @@ -103,9 +132,3 @@ See [options](#options).
### tempy.root

Get the root temporary directory path. For example: `/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T`

## FAQ

#### Why doesn't it have a cleanup method?

Temp files will be periodically cleaned up on macOS. Most Linux distros will clean up on reboot. If you're generating a lot of temp files, it's recommended to use a complementary module like [`del`](https://github.com/sindresorhus/del) for cleanup.
27 changes: 27 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import path from 'path';
import tempDir from 'temp-dir';
import pathExists from 'path-exists';
import touch from 'touch';
import fs from 'fs';
import stream from 'stream';
import test from 'ava';
Expand Down Expand Up @@ -32,19 +34,44 @@ test('.file()', t => {
});
});

test('.file.task()', async t => {
let temporaryFilePath;
await tempy.file.task(async temporaryFile => {
await touch(temporaryFile);
temporaryFilePath = temporaryFile;
});
t.false(await pathExists(temporaryFilePath));
});

test('.directory()', t => {
const prefix = 'name_';

t.true(tempy.directory().includes(tempDir));
t.true(path.basename(tempy.directory({prefix})).startsWith(prefix));
});

test('.directory.task()', async t => {
let temporaryDirectoryPath;
await tempy.directory.task(async temporaryDirectory => {
temporaryDirectoryPath = temporaryDirectory;
});
t.false(await pathExists(temporaryDirectoryPath));
});

test('.write(string)', async t => {
const filePath = await tempy.write('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;
await tempy.write.task('', async temporaryFile => {
temporaryFilePath = temporaryFile;
});
t.false(await pathExists(temporaryFilePath));
});

test('.write(buffer)', async t => {
const filePath = await tempy.write(Buffer.from('unicorn'));
t.is(fs.readFileSync(filePath, 'utf8'), 'unicorn');
Expand Down

0 comments on commit 96592fe

Please sign in to comment.