Skip to content

Commit

Permalink
feat: tmp task creates a temporal directory and can create none or mu…
Browse files Browse the repository at this point in the history
…ltiple temporal files

BREAKING CHANGE: Task tmp signature has changed. Review the latest documentation.
  • Loading branch information
rafamel committed Jul 5, 2024
1 parent 76fdddb commit e3334d5
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 42 deletions.
51 changes: 45 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
"glob": "^10.4.2",
"is-unicode-supported": "^2.0.0",
"merge-strategies": "^0.3.0",
"nanoid": "^5.0.7",
"ora": "^8.0.1",
"pipettes": "^0.1.3",
"prefix-stream": "^1.0.1",
Expand Down
61 changes: 25 additions & 36 deletions src/tasks/filesystem/tmp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import path from 'node:path';
import os from 'node:os';

import type { Empty, MaybePromise, Serial } from 'type-core';
import { shallow } from 'merge-strategies';
import { nanoid } from 'nanoid';
import fs from 'fs-extra';

import type { Context, Task } from '../../definitions';
Expand All @@ -12,57 +12,46 @@ import { isCancelled, onCancel } from '../../utils/cancellation';
import { create } from '../creation/create';
import { series } from '../aggregate/series';
import { finalize } from '../exception/finalize';
import { log } from '../stdio/log';
import { write } from './write';
import { mkdir } from './mkdir';

export interface TmpOptions {
/** Extension to use for the temporal file */
ext?: string | null;
export interface TmpFile {
name: string;
content: Buffer | Serial.Type;
}

/**
* Creates a temporal file with the result of `content`;
* when it is an object, it will be stringified as JSON.
* Passes the temporal file path to a `callback`, which
* can return a `Task`.
* Creates a temporal `directory` and places there
* all `TmpFile`s returned by the `files` callback.
* When `TmpFile.content` is an object, it will be
* stringified as JSON.
* Passes the temporal `directory` to a `callback`,
* which can return a `Task`.
* @returns Task
*/
export function tmp(
content: (context: Context) => MaybePromise<Buffer | Serial.Type>,
callback: (path: string) => MaybePromise<Task | Empty>,
options?: TmpOptions
files: (context: Context) => MaybePromise<null | TmpFile | TmpFile[]>,
callback: (directory: string) => MaybePromise<Task | Empty>
): Task.Async {
return create(async (ctx) => {
const opts = shallow({ ext: null as string | null }, options || undefined);

const buffer = await content(ctx);
if (isCancelled(ctx)) return;

const tmpdir = path.resolve(os.tmpdir(), constants.name);
await fs.ensureDir(tmpdir);
const response = await files(ctx);
if (isCancelled(ctx)) return;

const filename =
'tmp-' +
String(Math.random()).substring(2) +
(opts.ext ? '.' + opts.ext.replace(/^\./, '') : '');
const filepath = path.resolve(tmpdir, filename);

const teardown = (): void => {
try {
fs.unlinkSync(filepath);
} catch (err: any) {
if (err.code !== 'ENOENT') throw err;
}
};

const tmpdir = path.join(os.tmpdir(), constants.name, 'tmp-' + nanoid());
const teardown = (): void => fs.removeSync(tmpdir);
const cleanup = onCancel(ctx, () => teardown());

return finalize(
series(
log('debug', 'Tmp:', filename),
write(filepath, buffer, { exists: 'error' }),
create(() => callback(filepath))
mkdir(tmpdir, { ensure: true }),
...(Array.isArray(response) ? response : [response])
.filter((file): file is TmpFile => Boolean(file))
.map((file) => {
return write(path.join(tmpdir, file.name), file.content, {
exists: 'error'
});
}),
create(() => callback(tmpdir))
),
() => {
cleanup();
Expand Down

0 comments on commit e3334d5

Please sign in to comment.