@nevermorelove/ompp 0.2.3 → 0.3.1
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/ompp.js +157 -33
- package/package.json +1 -1
package/ompp.js
CHANGED
|
@@ -19,8 +19,76 @@
|
|
|
19
19
|
const fs = require("fs");
|
|
20
20
|
const path = require("path");
|
|
21
21
|
const { spawn } = require("child_process");
|
|
22
|
-
|
|
23
22
|
const os = require("os");
|
|
23
|
+
const https = require("https");
|
|
24
|
+
// --- update check -----------------------------------------------------------
|
|
25
|
+
// One registry request per run, bounded by a 5s timeout. If the registry
|
|
26
|
+
// has a newer semver, print an upgrade banner.
|
|
27
|
+
function fetchLatestVersion() {
|
|
28
|
+
return new Promise((resolve) => {
|
|
29
|
+
const req = https.get(
|
|
30
|
+
"https://registry.npmjs.org/@nevermorelove%2fompp",
|
|
31
|
+
{ timeout: 5000 },
|
|
32
|
+
(res) => {
|
|
33
|
+
if (res.statusCode !== 200) {
|
|
34
|
+
res.resume();
|
|
35
|
+
return resolve(null);
|
|
36
|
+
}
|
|
37
|
+
let body = "";
|
|
38
|
+
res.on("data", (c) => (body += c));
|
|
39
|
+
res.on("end", () => {
|
|
40
|
+
try {
|
|
41
|
+
resolve(JSON.parse(body)["dist-tags"]?.latest ?? null);
|
|
42
|
+
} catch {
|
|
43
|
+
resolve(null);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
},
|
|
47
|
+
);
|
|
48
|
+
req.on("timeout", () => {
|
|
49
|
+
req.destroy();
|
|
50
|
+
resolve(null);
|
|
51
|
+
});
|
|
52
|
+
req.on("error", () => resolve(null));
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function isNewerVersion(current, latest) {
|
|
57
|
+
const parse = (v) =>
|
|
58
|
+
v.split(/[.-]/).slice(0, 3).map((n) => parseInt(n, 10) || 0);
|
|
59
|
+
const [cmaj, cmin, cpat] = parse(current);
|
|
60
|
+
const [lmaj, lmin, lpat] = parse(latest);
|
|
61
|
+
if (lmaj !== cmaj) return lmaj > cmaj;
|
|
62
|
+
if (lmin !== cmin) return lmin > cmin;
|
|
63
|
+
return lpat > cpat;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function printUpdateBanner(current, latest) {
|
|
67
|
+
const line = `Update available! ${current} → ${latest}`;
|
|
68
|
+
const hint = `Run "npm i -g @nevermorelove/ompp" to update`;
|
|
69
|
+
const width = Math.max(line.length, hint.length) + 4;
|
|
70
|
+
const pad = (s) => " " + s + " ".repeat(width - 2 - s.length) + " ";
|
|
71
|
+
console.error("┌" + "─".repeat(width) + "┐");
|
|
72
|
+
console.error("│" + " ".repeat(width) + "│");
|
|
73
|
+
console.error("│" + pad(line) + "│");
|
|
74
|
+
console.error("│" + pad(hint) + "│");
|
|
75
|
+
console.error("│" + " ".repeat(width) + "│");
|
|
76
|
+
console.error("└" + "─".repeat(width) + "┘");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function checkForUpdate() {
|
|
80
|
+
if (process.env.OMPP_NO_UPDATE_CHECK === "1") return;
|
|
81
|
+
if (process.env.CI) return;
|
|
82
|
+
// Skip dev checkouts: a repo clone updates via git, not npm.
|
|
83
|
+
if (fs.existsSync(path.join(__dirname, ".git"))) return;
|
|
84
|
+
|
|
85
|
+
const current = require("./package.json").version;
|
|
86
|
+
const latest = await fetchLatestVersion();
|
|
87
|
+
if (latest && isNewerVersion(current, latest)) {
|
|
88
|
+
printUpdateBanner(current, latest);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
24
92
|
|
|
25
93
|
// Modes come from two places, in priority order:
|
|
26
94
|
// 1. OMPP_MODES_DIR (explicit override; repo checkout, custom folder)
|
|
@@ -277,16 +345,43 @@ function createMode(name) {
|
|
|
277
345
|
return 0;
|
|
278
346
|
}
|
|
279
347
|
|
|
348
|
+
const CREATE_OPTION = "__create__";
|
|
349
|
+
|
|
350
|
+
function isValidModeName(name) {
|
|
351
|
+
return /^[a-z0-9][a-z0-9-]*$/.test(name);
|
|
352
|
+
}
|
|
353
|
+
|
|
280
354
|
async function pickMode(modeNames) {
|
|
281
|
-
const { select, isCancel, cancel } = require("@clack/prompts");
|
|
355
|
+
const { select, text, isCancel, cancel } = require("@clack/prompts");
|
|
356
|
+
const options = [
|
|
357
|
+
...modeNames.map((name) => ({ value: name, label: name })),
|
|
358
|
+
{ value: CREATE_OPTION, label: "Create a new mode +" },
|
|
359
|
+
];
|
|
282
360
|
const chosen = await select({
|
|
283
361
|
message: "Pick a mode",
|
|
284
|
-
options
|
|
362
|
+
options,
|
|
285
363
|
});
|
|
286
364
|
if (isCancel(chosen)) {
|
|
287
365
|
cancel("Cancelled");
|
|
288
366
|
process.exit(130);
|
|
289
367
|
}
|
|
368
|
+
if (chosen === CREATE_OPTION) {
|
|
369
|
+
const name = await text({
|
|
370
|
+
message: "New mode name",
|
|
371
|
+
validate: (v) => {
|
|
372
|
+
if (!v || !isValidModeName(v)) {
|
|
373
|
+
return "Lowercase letters, digits, and dashes only";
|
|
374
|
+
}
|
|
375
|
+
if (RESERVED_NAMES.includes(v)) return `"${v}" is reserved`;
|
|
376
|
+
return undefined;
|
|
377
|
+
},
|
|
378
|
+
});
|
|
379
|
+
if (isCancel(name)) {
|
|
380
|
+
cancel("Cancelled");
|
|
381
|
+
process.exit(130);
|
|
382
|
+
}
|
|
383
|
+
return { create: name };
|
|
384
|
+
}
|
|
290
385
|
return chosen;
|
|
291
386
|
}
|
|
292
387
|
|
|
@@ -314,7 +409,39 @@ function pickModePiped(modeNames) {
|
|
|
314
409
|
|
|
315
410
|
|
|
316
411
|
|
|
412
|
+
// Spawn omp in the given mode.
|
|
413
|
+
function launchMode(mode, userArgs, dryRun) {
|
|
414
|
+
const modeDir = modeDirOf(mode.name, mode.source);
|
|
415
|
+
const args = buildArgv(modeDir, userArgs);
|
|
416
|
+
const { bin, shell } = resolveBin();
|
|
417
|
+
|
|
418
|
+
if (dryRun) {
|
|
419
|
+
console.error(`[ompp] mode: ${mode.name}`);
|
|
420
|
+
console.error(`[ompp] omp: ${bin}${shell ? " (shell)" : ""}`);
|
|
421
|
+
console.error(`[ompp] argv: ${JSON.stringify(args)}`);
|
|
422
|
+
return 0;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
console.error(`[ompp] mode: ${mode.name}`);
|
|
426
|
+
const child = shell
|
|
427
|
+
? spawn([winQuote(bin), ...args.map(winQuote)].join(" "), {
|
|
428
|
+
stdio: "inherit",
|
|
429
|
+
shell: true,
|
|
430
|
+
})
|
|
431
|
+
: spawn(bin, args, { stdio: "inherit" });
|
|
432
|
+
child.on("error", (err) => {
|
|
433
|
+
console.error(`[ompp] failed to start omp: ${err.message}`);
|
|
434
|
+
process.exitCode = 1;
|
|
435
|
+
});
|
|
436
|
+
child.on("exit", (code, signal) => {
|
|
437
|
+
process.exitCode = code ?? (signal ? 1 : 0);
|
|
438
|
+
});
|
|
439
|
+
return 0;
|
|
440
|
+
}
|
|
441
|
+
|
|
317
442
|
async function main() {
|
|
443
|
+
await checkForUpdate();
|
|
444
|
+
|
|
318
445
|
const argv = process.argv.slice(2);
|
|
319
446
|
|
|
320
447
|
if (argv[0] === "create") {
|
|
@@ -357,13 +484,26 @@ async function main() {
|
|
|
357
484
|
`[ompp] skipped folders without recognized files: ${skipped.join(", ")}`,
|
|
358
485
|
);
|
|
359
486
|
}
|
|
360
|
-
if (!modes.length) {
|
|
487
|
+
if (!modes.length && !process.stdin.isTTY) {
|
|
361
488
|
console.error(
|
|
362
489
|
`You have no modes yet. Create one with "ompp create <name>".`,
|
|
363
490
|
);
|
|
364
491
|
for (const s of SOURCES) console.error(`[ompp] looked in: ${s}`);
|
|
365
492
|
return 1;
|
|
366
493
|
}
|
|
494
|
+
if (!modes.length) {
|
|
495
|
+
// TTY with zero modes: jump straight into creating the first one.
|
|
496
|
+
try {
|
|
497
|
+
const picked = await pickMode([]);
|
|
498
|
+
if (picked && typeof picked === "object" && picked.create) {
|
|
499
|
+
return createMode(picked.create);
|
|
500
|
+
}
|
|
501
|
+
return 1;
|
|
502
|
+
} catch (err) {
|
|
503
|
+
console.error(`[ompp] ${err.message}`);
|
|
504
|
+
return 1;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
367
507
|
|
|
368
508
|
const modeNames = modes.map((m) => m.name);
|
|
369
509
|
let mode = null;
|
|
@@ -379,44 +519,28 @@ async function main() {
|
|
|
379
519
|
console.error(`[ompp] available: ${modeNames.join(", ")}`);
|
|
380
520
|
return 1;
|
|
381
521
|
}
|
|
382
|
-
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
if (!mode) {
|
|
383
525
|
try {
|
|
384
|
-
const
|
|
526
|
+
const picked = process.stdin.isTTY
|
|
385
527
|
? await pickMode(modeNames)
|
|
386
528
|
: await pickModePiped(modeNames);
|
|
387
|
-
|
|
529
|
+
if (picked && typeof picked === "object" && picked.create) {
|
|
530
|
+
// Scaffold and stop: the mode is all placeholders, launching it
|
|
531
|
+
// now would run an unconfigured mode. The user fills it in and
|
|
532
|
+
// launches when ready.
|
|
533
|
+
return createMode(picked.create);
|
|
534
|
+
} else {
|
|
535
|
+
mode = modes.find((m) => m.name === picked);
|
|
536
|
+
}
|
|
388
537
|
} catch (err) {
|
|
389
538
|
console.error(`[ompp] ${err.message}`);
|
|
390
539
|
return 1;
|
|
391
540
|
}
|
|
392
541
|
}
|
|
393
542
|
|
|
394
|
-
|
|
395
|
-
const args = buildArgv(modeDir, userArgs);
|
|
396
|
-
const { bin, shell } = resolveBin();
|
|
397
|
-
|
|
398
|
-
if (dryRun) {
|
|
399
|
-
console.error(`[ompp] mode: ${mode.name}`);
|
|
400
|
-
console.error(`[ompp] omp: ${bin}${shell ? " (shell)" : ""}`);
|
|
401
|
-
console.error(`[ompp] argv: ${JSON.stringify(args)}`);
|
|
402
|
-
return 0;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
console.error(`[ompp] mode: ${mode.name}`);
|
|
406
|
-
const child = shell
|
|
407
|
-
? spawn([winQuote(bin), ...args.map(winQuote)].join(" "), {
|
|
408
|
-
stdio: "inherit",
|
|
409
|
-
shell: true,
|
|
410
|
-
})
|
|
411
|
-
: spawn(bin, args, { stdio: "inherit" });
|
|
412
|
-
child.on("error", (err) => {
|
|
413
|
-
console.error(`[ompp] failed to start omp: ${err.message}`);
|
|
414
|
-
process.exitCode = 1;
|
|
415
|
-
});
|
|
416
|
-
child.on("exit", (code, signal) => {
|
|
417
|
-
process.exitCode = code ?? (signal ? 1 : 0);
|
|
418
|
-
});
|
|
419
|
-
return 0;
|
|
543
|
+
return launchMode(mode, userArgs, dryRun);
|
|
420
544
|
}
|
|
421
545
|
|
|
422
546
|
main()
|