@devdogsuga/devtools 0.1.14 → 0.1.16
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/dist/run/pick.d.ts +30 -0
- package/dist/run/pick.js +109 -4
- package/package.json +3 -3
package/dist/run/pick.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
export interface App {
|
|
2
|
+
name: string;
|
|
3
|
+
script: string;
|
|
4
|
+
}
|
|
1
5
|
/**
|
|
2
6
|
* Whether to ask at all.
|
|
3
7
|
*
|
|
@@ -31,6 +35,32 @@ export declare function extractFilters(args: readonly string[]): {
|
|
|
31
35
|
filters: string[];
|
|
32
36
|
rest: string[];
|
|
33
37
|
};
|
|
38
|
+
export interface DevPlan {
|
|
39
|
+
/** Apps whose dev server is `vinext dev`, started directly, one each. */
|
|
40
|
+
vinext: string[];
|
|
41
|
+
/** The `pnpm` args for every other selected app's `dev`, or null for none. */
|
|
42
|
+
others: string[] | null;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Splits a `dev` run between apps whose dev server is `vinext dev` and the
|
|
46
|
+
* rest.
|
|
47
|
+
*
|
|
48
|
+
* A vinext app's server code runs in workerd, which only sees what
|
|
49
|
+
* `@cloudflare/vite-plugin` hands it: the app dir's own `.dev.vars`/`.env`, or
|
|
50
|
+
* with `CLOUDFLARE_INCLUDE_PROCESS_ENV` the whole `process.env`. The repo's
|
|
51
|
+
* `.env` is at the root, so through the app's own `with-env vinext dev` script
|
|
52
|
+
* the Worker gets nothing and env validation fails. So a vinext app skips its
|
|
53
|
+
* script and runs `pnpm --filter <app> exec vinext dev` with the env
|
|
54
|
+
* `scopedProcessEnv` builds, the same way `workflows run` starts one: the
|
|
55
|
+
* flag set, and every other app's variables left out. Its script's own
|
|
56
|
+
* `with-env` would load the whole `.env` back in, so it is not used.
|
|
57
|
+
*
|
|
58
|
+
* Everything else still runs its own `dev` script under one `pnpm --parallel`.
|
|
59
|
+
* `apps` is every app with a `dev` script; `filters` the selection (empty for
|
|
60
|
+
* all of them). Only apps are named here: no other package has a `dev`
|
|
61
|
+
* script.
|
|
62
|
+
*/
|
|
63
|
+
export declare function planDev(apps: readonly App[], filters: readonly string[], rest: readonly string[]): DevPlan;
|
|
34
64
|
export interface TierArgResult {
|
|
35
65
|
/** The validated tier, or absent when `--tier` was not passed. */
|
|
36
66
|
tier?: string;
|
package/dist/run/pick.js
CHANGED
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
* typecheck/lint/test/dev run has no business doing (see `passthroughApps`'s
|
|
60
60
|
* doc comment for why that is more than just wasted work).
|
|
61
61
|
*/
|
|
62
|
-
import { spawnSync } from "node:child_process";
|
|
62
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
63
63
|
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, } from "node:fs";
|
|
64
64
|
import { dirname, join } from "node:path";
|
|
65
65
|
import { cancel, confirm, isCancel, multiselect } from "@clack/prompts";
|
|
@@ -336,6 +336,111 @@ function passthroughApps(task, filters, rest, extraEnv) {
|
|
|
336
336
|
]);
|
|
337
337
|
passthrough(commands, extraEnv);
|
|
338
338
|
}
|
|
339
|
+
// ── dev on vinext ────────────────────────────────────────────────────────────
|
|
340
|
+
/** A `dev` script that starts `vinext dev` (platform's `with-env vinext dev`). */
|
|
341
|
+
const VINEXT_DEV = /(^|[\s;&|])vinext\s+dev\b/;
|
|
342
|
+
/**
|
|
343
|
+
* Splits a `dev` run between apps whose dev server is `vinext dev` and the
|
|
344
|
+
* rest.
|
|
345
|
+
*
|
|
346
|
+
* A vinext app's server code runs in workerd, which only sees what
|
|
347
|
+
* `@cloudflare/vite-plugin` hands it: the app dir's own `.dev.vars`/`.env`, or
|
|
348
|
+
* with `CLOUDFLARE_INCLUDE_PROCESS_ENV` the whole `process.env`. The repo's
|
|
349
|
+
* `.env` is at the root, so through the app's own `with-env vinext dev` script
|
|
350
|
+
* the Worker gets nothing and env validation fails. So a vinext app skips its
|
|
351
|
+
* script and runs `pnpm --filter <app> exec vinext dev` with the env
|
|
352
|
+
* `scopedProcessEnv` builds, the same way `workflows run` starts one: the
|
|
353
|
+
* flag set, and every other app's variables left out. Its script's own
|
|
354
|
+
* `with-env` would load the whole `.env` back in, so it is not used.
|
|
355
|
+
*
|
|
356
|
+
* Everything else still runs its own `dev` script under one `pnpm --parallel`.
|
|
357
|
+
* `apps` is every app with a `dev` script; `filters` the selection (empty for
|
|
358
|
+
* all of them). Only apps are named here: no other package has a `dev`
|
|
359
|
+
* script.
|
|
360
|
+
*/
|
|
361
|
+
export function planDev(apps, filters, rest) {
|
|
362
|
+
const selected = filters.length > 0
|
|
363
|
+
? apps.filter((app) => filters.includes(app.name))
|
|
364
|
+
: [...apps];
|
|
365
|
+
const vinext = selected
|
|
366
|
+
.filter((app) => VINEXT_DEV.test(app.script))
|
|
367
|
+
.map((app) => app.name);
|
|
368
|
+
const otherFilters = filters.length > 0
|
|
369
|
+
? filters.filter((f) => !vinext.includes(f))
|
|
370
|
+
: apps.map((app) => app.name).filter((name) => !vinext.includes(name));
|
|
371
|
+
const others = otherFilters.length > 0
|
|
372
|
+
? [
|
|
373
|
+
"-r",
|
|
374
|
+
"--if-present",
|
|
375
|
+
"--parallel",
|
|
376
|
+
...otherFilters.flatMap((f) => ["--filter", f]),
|
|
377
|
+
"run",
|
|
378
|
+
"dev",
|
|
379
|
+
...rest,
|
|
380
|
+
]
|
|
381
|
+
: null;
|
|
382
|
+
return { vinext, others };
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* `dev` when at least one selected app runs `vinext dev`: builds every
|
|
386
|
+
* selected app's workspace dependencies, then starts the vinext apps (each
|
|
387
|
+
* with its own scoped env, see `planDev`) and the rest side by side, and
|
|
388
|
+
* exits when the first of them does, stopping the others.
|
|
389
|
+
*
|
|
390
|
+
* The env is the session's: `launch.ts` has already entered the tier into
|
|
391
|
+
* `process.env` (or `--tier` loaded one into `tierEnv`).
|
|
392
|
+
*/
|
|
393
|
+
async function runDev(plan, filters, rest, tierEnv) {
|
|
394
|
+
const depsOfTargets = filters.length > 0 ? filters : allAppNames();
|
|
395
|
+
runOne([
|
|
396
|
+
"-r",
|
|
397
|
+
"--if-present",
|
|
398
|
+
...depsOfTargets.flatMap((f) => ["--filter", `${f}^...`]),
|
|
399
|
+
"run",
|
|
400
|
+
"build",
|
|
401
|
+
], tierEnv, { final: false });
|
|
402
|
+
// Dynamic, like `loadEnvLoad`: the env registry is no business of a plain
|
|
403
|
+
// `pnpm build`.
|
|
404
|
+
const { scopedProcessEnv } = await import("../cf/local-env.js");
|
|
405
|
+
const environment = { ...process.env, ...tierEnv };
|
|
406
|
+
const tier = environment.DEPLOY_ENV ?? "development";
|
|
407
|
+
const children = [
|
|
408
|
+
...(await Promise.all(plan.vinext.map(async (app) => ({
|
|
409
|
+
args: ["--filter", app, "exec", "vinext", "dev", ...rest],
|
|
410
|
+
env: await scopedProcessEnv(app, environment, tier),
|
|
411
|
+
})))),
|
|
412
|
+
...(plan.others ? [{ args: plan.others, env: environment }] : []),
|
|
413
|
+
].map(({ args, env }) => spawn("pnpm", args, {
|
|
414
|
+
cwd: findRepoRoot(),
|
|
415
|
+
stdio: "inherit",
|
|
416
|
+
env: { ...env, DEVDOGS_PICK: "0" },
|
|
417
|
+
}));
|
|
418
|
+
// Ctrl-C reaches every child through the terminal's process group; this
|
|
419
|
+
// process waits for them to finish rather than dying first.
|
|
420
|
+
const ignore = () => { };
|
|
421
|
+
process.on("SIGINT", ignore);
|
|
422
|
+
process.on("SIGTERM", ignore);
|
|
423
|
+
const first = await Promise.race(children.map((child) => new Promise((resolve) => {
|
|
424
|
+
child.once("error", () => resolve(1));
|
|
425
|
+
child.once("exit", (code, signal) => resolve(code ?? (signal ? 130 : 1)));
|
|
426
|
+
})));
|
|
427
|
+
for (const child of children) {
|
|
428
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
429
|
+
child.kill("SIGTERM");
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
process.exit(first);
|
|
433
|
+
}
|
|
434
|
+
/** `passthroughApps`, except that a `dev` selecting a vinext app goes to
|
|
435
|
+
* `runDev`. */
|
|
436
|
+
async function dispatch(task, filters, rest, tierEnv) {
|
|
437
|
+
if (task === "dev") {
|
|
438
|
+
const plan = planDev(appsWith("dev"), filters, rest);
|
|
439
|
+
if (plan.vinext.length > 0)
|
|
440
|
+
return runDev(plan, filters, rest, tierEnv);
|
|
441
|
+
}
|
|
442
|
+
passthroughApps(task, filters, rest, tierEnv);
|
|
443
|
+
}
|
|
339
444
|
// ── Tier ─────────────────────────────────────────────────────────────────────
|
|
340
445
|
/** The tiers a loaded env can select. `preflight` is not a deploy environment
|
|
341
446
|
* (see `@devdogsuga/env`'s target table) and is refused here for the same
|
|
@@ -513,13 +618,13 @@ export async function runTask(argv) {
|
|
|
513
618
|
// to the underlying script rather than treating it as its own flag.
|
|
514
619
|
if (all || !shouldAsk(rest)) {
|
|
515
620
|
const { filters, rest: cleanRest } = extractFilters(rest);
|
|
516
|
-
|
|
621
|
+
return dispatch(task, filters, cleanRest, tierEnv);
|
|
517
622
|
}
|
|
518
623
|
const apps = appsWith(task);
|
|
519
624
|
// Nothing to choose between: one app, or none that define this task (pnpm
|
|
520
625
|
// will say so better than a picker with a single option would).
|
|
521
626
|
if (apps.length < 2)
|
|
522
|
-
|
|
627
|
+
return dispatch(task, [], rest, tierEnv);
|
|
523
628
|
const previous = remembered(task);
|
|
524
629
|
const chosen = await multiselect({
|
|
525
630
|
// The `--all` escape is named in the message rather than offered as an
|
|
@@ -555,5 +660,5 @@ export async function runTask(argv) {
|
|
|
555
660
|
process.exit(0);
|
|
556
661
|
}
|
|
557
662
|
remember(task, chosen);
|
|
558
|
-
|
|
663
|
+
return dispatch(task, chosen, rest, tierEnv);
|
|
559
664
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devdogsuga/devtools",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Contributor CLI for a DevDogsUGA checkout: database, env, images, emails, cron, deploy. Ships built JS; run via `pnpm dlx @devdogsuga/devtools` (or the `devtools`/`devtools-ci` bins once installed) from inside a DevDogsUGA clone.",
|
|
@@ -67,9 +67,9 @@
|
|
|
67
67
|
"react": "^19.2.8",
|
|
68
68
|
"vitest": "^4.1.11",
|
|
69
69
|
"@devdogsuga/brand": "0.1.2",
|
|
70
|
-
"@devdogsuga/env": "0.1.2",
|
|
71
|
-
"@devdogsuga/db": "0.1.1",
|
|
72
70
|
"@devdogsuga/config": "0.1.1",
|
|
71
|
+
"@devdogsuga/db": "0.1.1",
|
|
72
|
+
"@devdogsuga/env": "0.1.2",
|
|
73
73
|
"@devdogsuga/events": "0.1.3"
|
|
74
74
|
},
|
|
75
75
|
"scripts": {
|