@jpillora/take 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/take.d.mts +18 -1
- package/take.mjs +157 -5
package/package.json
CHANGED
package/take.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { SpawnOptions as nodeSpawnOptions } from "node:child_process";
|
|
1
|
+
import { type SpawnOptions as nodeSpawnOptions } from "node:child_process";
|
|
2
|
+
export declare function insertHelp(path: string): void;
|
|
2
3
|
export type Flag = {
|
|
3
4
|
initial: number | string | boolean | Date;
|
|
4
5
|
description: string;
|
|
@@ -35,6 +36,22 @@ export type SpawnOptions = {
|
|
|
35
36
|
args?: string[];
|
|
36
37
|
} & nodeSpawnOptions;
|
|
37
38
|
export declare function spawn(options: SpawnOptions): Promise<number>;
|
|
39
|
+
export type RunResult = {
|
|
40
|
+
code: number;
|
|
41
|
+
stdout: string;
|
|
42
|
+
stderr: string;
|
|
43
|
+
combined: string;
|
|
44
|
+
error?: string;
|
|
45
|
+
success: boolean;
|
|
46
|
+
};
|
|
47
|
+
export type ExecResult = {
|
|
48
|
+
pid: number;
|
|
49
|
+
error?: string;
|
|
50
|
+
wait: () => Promise<RunResult>;
|
|
51
|
+
kill: (signal: number) => void;
|
|
52
|
+
};
|
|
53
|
+
export declare function exec(options: SpawnOptions): ExecResult;
|
|
54
|
+
export declare function run(options: SpawnOptions): Promise<RunResult>;
|
|
38
55
|
export declare const $: (script: string) => Promise<void>;
|
|
39
56
|
export declare const timer: () => () => string;
|
|
40
57
|
export declare function Register(...commands: NewCommand<any>[]): Promise<void>;
|
package/take.mjs
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
|
-
// Take is a mini-CLI library for building typescript-based command-line tools
|
|
2
|
-
// Works with Deno, Node.js (with type stripping), and Bun
|
|
1
|
+
// Take is a mini-CLI library for building typescript-based command-line tools.
|
|
2
|
+
// Works with Deno, Node.js (with type stripping), and Bun.
|
|
3
|
+
// IMPORTANT: All code must stay in 1 file.
|
|
3
4
|
// deno-lint-ignore-file no-explicit-any
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
5
|
+
import { Buffer } from "node:buffer";
|
|
6
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
7
|
+
import { basename, dirname, isAbsolute, join } from "node:path";
|
|
6
8
|
import { spawn as nodeSpawn, } from "node:child_process";
|
|
7
9
|
import process from "node:process";
|
|
10
|
+
let _insertHelpPath = null;
|
|
11
|
+
export function insertHelp(path) {
|
|
12
|
+
if (isAbsolute(path)) {
|
|
13
|
+
_insertHelpPath = path;
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
const scriptDir = dirname(process.argv[1] || ".");
|
|
17
|
+
_insertHelpPath = join(scriptDir, path);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
8
20
|
export function newFlags(flags) {
|
|
9
21
|
return flags;
|
|
10
22
|
}
|
|
@@ -51,6 +63,84 @@ export async function spawn(options) {
|
|
|
51
63
|
});
|
|
52
64
|
});
|
|
53
65
|
}
|
|
66
|
+
export function exec(options) {
|
|
67
|
+
let code = -1;
|
|
68
|
+
let error = "";
|
|
69
|
+
const datas = {
|
|
70
|
+
out: [],
|
|
71
|
+
err: [],
|
|
72
|
+
combined: [],
|
|
73
|
+
};
|
|
74
|
+
const result = {
|
|
75
|
+
get code() {
|
|
76
|
+
return code;
|
|
77
|
+
},
|
|
78
|
+
get stdout() {
|
|
79
|
+
return Buffer.concat(datas.out).toString();
|
|
80
|
+
},
|
|
81
|
+
get stderr() {
|
|
82
|
+
return Buffer.concat(datas.err).toString();
|
|
83
|
+
},
|
|
84
|
+
get combined() {
|
|
85
|
+
return Buffer.concat(datas.combined).toString();
|
|
86
|
+
},
|
|
87
|
+
get error() {
|
|
88
|
+
return error || undefined;
|
|
89
|
+
},
|
|
90
|
+
get success() {
|
|
91
|
+
return code === 0;
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
try {
|
|
95
|
+
const child = nodeSpawn(options.program, options.args ?? [], options);
|
|
96
|
+
child.stdout?.on("data", (data) => {
|
|
97
|
+
const buf = Buffer.from(data);
|
|
98
|
+
datas.out.push(buf);
|
|
99
|
+
datas.combined.push(buf);
|
|
100
|
+
});
|
|
101
|
+
child.stderr?.on("data", (data) => {
|
|
102
|
+
const buf = Buffer.from(data);
|
|
103
|
+
datas.err.push(buf);
|
|
104
|
+
datas.combined.push(buf);
|
|
105
|
+
});
|
|
106
|
+
let resolved = false;
|
|
107
|
+
const resultPromise = new Promise((resolve) => {
|
|
108
|
+
const done = (finalCode) => {
|
|
109
|
+
if (resolved) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
code = finalCode;
|
|
113
|
+
resolved = true;
|
|
114
|
+
resolve(result);
|
|
115
|
+
};
|
|
116
|
+
child.on("error", (err) => {
|
|
117
|
+
error = err.message;
|
|
118
|
+
done(1);
|
|
119
|
+
});
|
|
120
|
+
child.on("close", (c) => {
|
|
121
|
+
done(c ?? 1);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
return {
|
|
125
|
+
pid: child.pid ?? 0,
|
|
126
|
+
wait: () => resultPromise,
|
|
127
|
+
kill: (signal) => child.kill(signal),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
code = 1;
|
|
132
|
+
error = err instanceof Error ? err.message : String(err);
|
|
133
|
+
return {
|
|
134
|
+
pid: 0,
|
|
135
|
+
error,
|
|
136
|
+
wait: () => Promise.resolve(result),
|
|
137
|
+
kill: () => { },
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
export function run(options) {
|
|
142
|
+
return exec(options).wait();
|
|
143
|
+
}
|
|
54
144
|
// helper for running bash scripts
|
|
55
145
|
export const $ = async (script) => {
|
|
56
146
|
await spawn({
|
|
@@ -113,6 +203,62 @@ async function loadEnvFile(path) {
|
|
|
113
203
|
return false;
|
|
114
204
|
}
|
|
115
205
|
}
|
|
206
|
+
const MARKER_START = "<!-- take:start -->";
|
|
207
|
+
const MARKER_END = "<!-- take:end -->";
|
|
208
|
+
async function _writeInsertHelp(commands) {
|
|
209
|
+
if (!_insertHelpPath)
|
|
210
|
+
return;
|
|
211
|
+
const path = _insertHelpPath;
|
|
212
|
+
// Build markdown list with flags as sub-bullets
|
|
213
|
+
const lines = [];
|
|
214
|
+
for (const cmd of commands) {
|
|
215
|
+
if (cmd.name === "debug")
|
|
216
|
+
continue;
|
|
217
|
+
const desc = cmd.description ? ` - ${cmd.description}` : "";
|
|
218
|
+
lines.push(`- \`${cmd.name}\`${desc}`);
|
|
219
|
+
// Add flags as sub-bullets
|
|
220
|
+
const shorts = new Set();
|
|
221
|
+
for (const [name, flag] of Object.entries(cmd.flags)) {
|
|
222
|
+
const letter = name[0];
|
|
223
|
+
const shortStr = shorts.has(letter) ? "" : ` \`-${letter}\``;
|
|
224
|
+
shorts.add(letter);
|
|
225
|
+
const typeStr = typeof flag.initial === "boolean"
|
|
226
|
+
? ""
|
|
227
|
+
: ` <${typeof flag.initial}>`;
|
|
228
|
+
const parts = [];
|
|
229
|
+
if (flag.env)
|
|
230
|
+
parts.push(`env=${flag.env}`);
|
|
231
|
+
if (flag.initial)
|
|
232
|
+
parts.push(`default=${flag.initial}`);
|
|
233
|
+
const extras = parts.length ? ` (${parts.join(" ")})` : "";
|
|
234
|
+
lines.push(` - \`--${name}\`${shortStr}${typeStr} - ${flag.description}${extras}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
const content = MARKER_START + "\n" + lines.join("\n") + "\n" + MARKER_END;
|
|
238
|
+
// Read existing file
|
|
239
|
+
let fileContent;
|
|
240
|
+
try {
|
|
241
|
+
fileContent = await readFile(path, "utf-8");
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
return; // file doesn't exist, nothing to insert into
|
|
245
|
+
}
|
|
246
|
+
// Find markers and replace or append
|
|
247
|
+
const startIdx = fileContent.indexOf(MARKER_START);
|
|
248
|
+
const endIdx = fileContent.indexOf(MARKER_END);
|
|
249
|
+
let newContent;
|
|
250
|
+
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
251
|
+
newContent = fileContent.substring(0, startIdx) + content +
|
|
252
|
+
fileContent.substring(endIdx + MARKER_END.length);
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
const sep = fileContent.endsWith("\n") ? "\n" : "\n\n";
|
|
256
|
+
newContent = fileContent + sep + content + "\n";
|
|
257
|
+
}
|
|
258
|
+
if (newContent !== fileContent) {
|
|
259
|
+
await writeFile(path, newContent, "utf-8");
|
|
260
|
+
}
|
|
261
|
+
}
|
|
116
262
|
export async function Register(...commands) {
|
|
117
263
|
// Load .env by default
|
|
118
264
|
await loadEnvFile(".env");
|
|
@@ -158,6 +304,7 @@ export async function Register(...commands) {
|
|
|
158
304
|
}
|
|
159
305
|
}
|
|
160
306
|
commands.sort((a, b) => (a.name < b.name ? -1 : 1));
|
|
307
|
+
await _writeInsertHelp(commands);
|
|
161
308
|
const joinColumns = (table) => {
|
|
162
309
|
const max = table.reduce((m, { left }) => Math.max(m, left.length), 0);
|
|
163
310
|
return table
|
|
@@ -373,7 +520,12 @@ export async function Register(...commands) {
|
|
|
373
520
|
}
|
|
374
521
|
export function Command(command) {
|
|
375
522
|
const flagsInitial = Object.fromEntries(Object.entries(command.flags).map(([k, v]) => [k, v.initial]));
|
|
376
|
-
return {
|
|
523
|
+
return {
|
|
524
|
+
...command,
|
|
525
|
+
flagValues: null,
|
|
526
|
+
input: null,
|
|
527
|
+
flagsInitial,
|
|
528
|
+
};
|
|
377
529
|
}
|
|
378
530
|
// deno-lint-ignore no-constant-condition
|
|
379
531
|
if (42 < 7) {
|