1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109 |
x2
x2
x2
x2
x25
x10
x10
x40
x10
x10
x12
x12
x12
x12
x36
x12
x12
x12
x7
x2
x16
x4
x4
x4
x6
x6
x6
x6
x6
x20
x6
x6
x6
x6
x6
x4
x2
x2
x20
x5
|
I
|
import type { Fn, Bytes, Step, Async } from '../index.ts';
import { joinPath, resolvePath, getCwd, tmpdir, mkdtemp,
writeFile, mkdir, rm } from '../deps.ts';
import { Pipe, Name } from '../format/function.ts';
/** A directory. */
export type Dir = {
path: string,
paths?: Record<string, unknown>,
mkdir?: typeof mkdir,
mkdtemp?: typeof mkdtemp,
writeFile?: typeof writeFile,
rimraf?: Fn,
};
/** A filesystem operation. Needs current working directory. */
export type DirEntry<T extends Dir = Dir, U extends unknown[] = unknown[]> =
Fn<[T, ...U]>;
/** Specify a directory.
*
* Form 1 (sync)
* - When called with no arguments, returns `{ cwd }`.
*
* Example:
* const { path } = Dir();
*
* Form 2 (deferred)
* - When called with string, returns async function that creates that path.
* - When called with string and one or more steps, the returned function
* also runs the steps sequentially to populate the created directory.
*
* Example:
* const makeEmptyDir = Dir('foo');
* await makeEmptyDir();
* // created "./foo/"
*
* const makeDirWithFiles = Dir('foo', Txt('hello.txt', 'world'));
* await makeDirWithFiles({ cwd: '/path/to/somewhere/' });
* // created "/path/to/somewhere/foo/hello.txt" and wrote "world'
**/
export function Dir (): Dir
export function Dir <D extends Dir> (..._: (string|DirEntry<D>)[]): Fn<[D], Async<D>>;
export function Dir <D extends Dir> (...opts: unknown[]): unknown {
// Synchronous form.
if (opts.length === 0) return { path: getCwd() };
// Build relative path.
let path = '';
while (typeof opts[0] === 'string') path = joinPath(path, opts.shift() as string);
// Return function that creates and populates it.
const props = { path, contents: opts };
return Name(`Dir(${path}))`, makeDirectory, props) as DirEntry<D>;
async function makeDirectory (dir: string|D, ...args: unknown[]): Promise<D> {
if (typeof dir !== 'object') dir = {} as D;
dir ??= {} as D;
dir.path ??= getCwd();
dir.paths ??= {};
dir.paths[resolvePath(dir.path)] = {};
(dir.mkdir ??= mkdir)(path, { recursive: true });
dir.path = path;
return await Pipe(...args as DirEntry<D>[])(dir, args) as D;
//await runInDir(path, args as DirEntry<D>[], dir, context);
//return dir;
}
}
/** Specify a temporary directory. */
export function Temp <D extends Dir> (prefix: string = '', ...ops: DirEntry<D>[]) {
const props = { prefix, ops };
const fn = Name(`Temp(${prefix})`, inTemporaryDirectory, props);
return fn;
async function inTemporaryDirectory (dir: D = Dir() as D, ...context: unknown[]): Promise<D> {
dir.mkdtemp ??= mkdtemp;
const path = await mkdtemp(resolvePath(tmpdir(), `fadroma`, `${prefix}-`));
dir.paths ??= {};
dir.paths[path] = fn;
dir.path = path;
dir.rimraf = () => rm(path, { recursive: true });
const create = Pipe(...ops as DirEntry<D>[]);
const result = await create(dir, context) as D;
await dir.rimraf();
return result;
}
}
/** Specify a text file. */
export function Txt <T = string|number|object|null> (
path: string, value?: T|T[], ...steps: Array<T|Step<T>>
): DirEntry {
return Name(`Txt(${path})`, async function writeTxt (dir: Dir) {
const full = resolvePath(dir.path, path);
const data = await Pipe(...steps as Fn[])(value||'') || '';
dir.paths ??= {};
dir.paths[full] ??= null;
dir.writeFile ??= writeFile;
await dir.writeFile(full, data as string, 'utf8');
dir.paths[full] = data;
return dir;
}, { path, value, steps });
}
/** Specify a binary data file. */
export function Bin (path: string, value?: number|Bytes, ...steps: Step<Bytes>[]) {
return Name(`Bin(${path})`, async function writeBinaryData (dir: Dir = Dir()) {
value = (typeof value === 'number') ? new Uint8Array(value) : value
const full = resolvePath(dir.path, path);
const data = await Pipe(...steps as Fn[])(value||'') || '';
dir.paths ??= {};
dir.paths[full] ??= null;
dir.writeFile ??= writeFile;
await dir.writeFile(full, data as Bytes);
dir.paths[full] = data;
return dir;
}, { path, value, steps });
}
|