@moku-labs/web 1.3.1 → 1.4.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/dist/index.cjs +90 -29
- package/dist/index.d.cts +11 -1
- package/dist/index.d.mts +11 -1
- package/dist/index.mjs +90 -29
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3842,7 +3842,7 @@ const DEFAULT_BODY = "<h1>404</h1><p>The page you requested could not be found.<
|
|
|
3842
3842
|
* ```
|
|
3843
3843
|
*/
|
|
3844
3844
|
function wrap(body) {
|
|
3845
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>404 — Not Found</title></head><body>${body}</body></html>`;
|
|
3845
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>404 — Not Found</title></head><body>${body}</body></html>`;
|
|
3846
3846
|
}
|
|
3847
3847
|
/**
|
|
3848
3848
|
* Emits `outDir/404.html`. When `config.notFound` is `true`, writes the built-in
|
|
@@ -4526,6 +4526,8 @@ const HEAD_PLACEHOLDER = "<!--moku:head-->";
|
|
|
4526
4526
|
const BODY_PLACEHOLDER = "<!--moku:body-->";
|
|
4527
4527
|
/** Template placeholder for the injected asset `<link>`/`<script>` tags. */
|
|
4528
4528
|
const ASSETS_PLACEHOLDER = "<!--moku:assets-->";
|
|
4529
|
+
/** Template placeholder for the page's locale (`<html lang>`). */
|
|
4530
|
+
const LANG_PLACEHOLDER = "<!--moku:lang-->";
|
|
4529
4531
|
/**
|
|
4530
4532
|
* Read the bundle phase's hashed asset manifest for one kind from `state.buildCache`
|
|
4531
4533
|
* as a typed {@link BuildCacheEntry} (no `Map<string, unknown>` reads).
|
|
@@ -4573,14 +4575,16 @@ function buildAssetTags(ctx) {
|
|
|
4573
4575
|
* ```
|
|
4574
4576
|
*/
|
|
4575
4577
|
function renderDocument(parts) {
|
|
4576
|
-
return `<!DOCTYPE html><html lang="${parts.locale}"><head>${parts.head}${parts.assets}</head><body>${parts.body}</body></html>`;
|
|
4578
|
+
return `<!DOCTYPE html><html lang="${parts.locale}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">${parts.head}${parts.assets}</head><body>${parts.body}</body></html>`;
|
|
4577
4579
|
}
|
|
4578
4580
|
/**
|
|
4579
|
-
* Fill a shell template's `<!--moku:
|
|
4580
|
-
* `<!--moku:assets-->` placeholders deterministically at build
|
|
4581
|
+
* Fill a shell template's `<!--moku:lang-->` / `<!--moku:head-->` /
|
|
4582
|
+
* `<!--moku:body-->` / `<!--moku:assets-->` placeholders deterministically at build
|
|
4583
|
+
* time. `<!--moku:lang-->` carries the page locale (for `<html lang>`), so a single
|
|
4584
|
+
* shared template stays locale-correct across every locale.
|
|
4581
4585
|
*
|
|
4582
4586
|
* @param template - The raw shell template HTML.
|
|
4583
|
-
* @param parts - The composed head/body/assets pieces.
|
|
4587
|
+
* @param parts - The composed head/body/assets/locale pieces.
|
|
4584
4588
|
* @returns The filled document string.
|
|
4585
4589
|
* @example
|
|
4586
4590
|
* ```ts
|
|
@@ -4588,7 +4592,7 @@ function renderDocument(parts) {
|
|
|
4588
4592
|
* ```
|
|
4589
4593
|
*/
|
|
4590
4594
|
function fillTemplate(template, parts) {
|
|
4591
|
-
return template.replaceAll(HEAD_PLACEHOLDER, parts.head).replaceAll(BODY_PLACEHOLDER, parts.body).replaceAll(ASSETS_PLACEHOLDER, parts.assets);
|
|
4595
|
+
return template.replaceAll(LANG_PLACEHOLDER, parts.locale).replaceAll(HEAD_PLACEHOLDER, parts.head).replaceAll(BODY_PLACEHOLDER, parts.body).replaceAll(ASSETS_PLACEHOLDER, parts.assets);
|
|
4592
4596
|
}
|
|
4593
4597
|
/**
|
|
4594
4598
|
* Resolve the compiled entry for a manifest definition, asserting the router
|
|
@@ -5840,7 +5844,8 @@ function buildWranglerArgs(input) {
|
|
|
5840
5844
|
"--project-name",
|
|
5841
5845
|
input.slug,
|
|
5842
5846
|
"--branch",
|
|
5843
|
-
branch
|
|
5847
|
+
branch,
|
|
5848
|
+
"--commit-dirty=true"
|
|
5844
5849
|
];
|
|
5845
5850
|
}
|
|
5846
5851
|
/**
|
|
@@ -6449,17 +6454,18 @@ function validateConfig$1(ctx) {
|
|
|
6449
6454
|
ctx.require(sitePlugin);
|
|
6450
6455
|
}
|
|
6451
6456
|
/**
|
|
6452
|
-
* Run wrangler for the prepared argv and
|
|
6453
|
-
*
|
|
6454
|
-
*
|
|
6455
|
-
*
|
|
6457
|
+
* Run wrangler for the prepared argv and return its stdout, translating a non-zero
|
|
6458
|
+
* exit into the classified deploy error. The API token is read from env here so it
|
|
6459
|
+
* never crosses a logging boundary; the scrubbed stderr is used only to classify a
|
|
6460
|
+
* failure — it is never logged (that was console noise), so nothing leaks. Shared by
|
|
6461
|
+
* `run()` (deploy) and `createProject()` (project create).
|
|
6456
6462
|
*
|
|
6457
6463
|
* @param ctx - Plugin context (provides `state.spawn`, `config`, `env`).
|
|
6458
6464
|
* @param args - The fully-built, pre-validated wrangler argv.
|
|
6459
|
-
* @returns The wrangler `stdout`
|
|
6465
|
+
* @returns The wrangler `stdout` (for URL/id parsing on a deploy).
|
|
6460
6466
|
* @throws {Error} With a `code` from the deploy error taxonomy on a non-zero exit.
|
|
6461
6467
|
* @example
|
|
6462
|
-
* const
|
|
6468
|
+
* const stdout = await executeWrangler(ctx, args);
|
|
6463
6469
|
*/
|
|
6464
6470
|
async function executeWrangler(ctx, args) {
|
|
6465
6471
|
const token = ctx.env.require("CLOUDFLARE_API_TOKEN");
|
|
@@ -6473,10 +6479,7 @@ async function executeWrangler(ctx, args) {
|
|
|
6473
6479
|
const { code, message } = classifyWranglerError(exitCode, scrubbedStderr);
|
|
6474
6480
|
throw deployError(code, message);
|
|
6475
6481
|
}
|
|
6476
|
-
return
|
|
6477
|
-
stdout,
|
|
6478
|
-
scrubbedStderr
|
|
6479
|
-
};
|
|
6482
|
+
return stdout;
|
|
6480
6483
|
}
|
|
6481
6484
|
/**
|
|
6482
6485
|
* Assemble the public {@link DeployResult} from wrangler's stdout, parsing the
|
|
@@ -6533,9 +6536,7 @@ function createApi$2(ctx) {
|
|
|
6533
6536
|
root
|
|
6534
6537
|
});
|
|
6535
6538
|
const start = Date.now();
|
|
6536
|
-
const
|
|
6537
|
-
ctx.log.info(scrubbedStderr);
|
|
6538
|
-
const result = buildDeployResult(stdout, branch, start);
|
|
6539
|
+
const result = buildDeployResult(await executeWrangler(ctx, args), branch, start);
|
|
6539
6540
|
ctx.state.lastDeployment = result;
|
|
6540
6541
|
ctx.emit("deploy:complete", {
|
|
6541
6542
|
url: result.url,
|
|
@@ -6595,11 +6596,10 @@ function createApi$2(ctx) {
|
|
|
6595
6596
|
async createProject() {
|
|
6596
6597
|
const name = toSlug(ctx.require(sitePlugin).name());
|
|
6597
6598
|
const branch = ctx.config.productionBranch ?? "main";
|
|
6598
|
-
|
|
6599
|
+
await executeWrangler(ctx, buildProjectCreateArgs({
|
|
6599
6600
|
slug: name,
|
|
6600
6601
|
branch
|
|
6601
6602
|
}));
|
|
6602
|
-
ctx.log.info(scrubbedStderr);
|
|
6603
6603
|
return {
|
|
6604
6604
|
name,
|
|
6605
6605
|
branch
|
|
@@ -8766,16 +8766,24 @@ function createPanelRenderer(options = {}) {
|
|
|
8766
8766
|
write(line);
|
|
8767
8767
|
},
|
|
8768
8768
|
/**
|
|
8769
|
-
* Render the deploy result from a `deploy:complete` event
|
|
8770
|
-
*
|
|
8769
|
+
* Render the deploy result from a `deploy:complete` event as a full-width box (matching
|
|
8770
|
+
* the BUILD panel): a `✓ DEPLOYED · branch` header with the elapsed time right-aligned,
|
|
8771
|
+
* then a `→ url · id` row. The url/id row is omitted entirely when wrangler returned no
|
|
8772
|
+
* URL, so a first-deploy with nothing to parse never renders a dangling `→` or `· ·`.
|
|
8771
8773
|
*
|
|
8772
8774
|
* @param result - The `deploy:complete` payload.
|
|
8773
8775
|
* @example
|
|
8774
8776
|
* render.deployed({ url: "https://x.pages.dev", deploymentId: "id", branch: "main", durationMs: 1200 });
|
|
8775
8777
|
*/
|
|
8776
8778
|
deployed(result) {
|
|
8777
|
-
const
|
|
8778
|
-
|
|
8779
|
+
const dot = palette.dim("·");
|
|
8780
|
+
const time = result.durationMs >= 1e3 ? `${(result.durationMs / 1e3).toFixed(1)}s` : `${result.durationMs}ms`;
|
|
8781
|
+
const lines = [railLine(`${palette.green("✓")} ${palette.bold("DEPLOYED")} ${dot} ${result.branch}`, palette.dim(time), BOX_INNER)];
|
|
8782
|
+
if (result.url) {
|
|
8783
|
+
const id = result.deploymentId ? ` ${dot} ${palette.dim(result.deploymentId)}` : "";
|
|
8784
|
+
lines.push(`${palette.dim("→")} ${palette.cyan(result.url)}${id}`);
|
|
8785
|
+
} else if (result.deploymentId) lines.push(palette.dim(`id ${result.deploymentId}`));
|
|
8786
|
+
writeBlock(box(lines, color, BOX_INNER));
|
|
8779
8787
|
},
|
|
8780
8788
|
/**
|
|
8781
8789
|
* Render a neutral informational line.
|
|
@@ -8870,6 +8878,29 @@ function createPanelRenderer(options = {}) {
|
|
|
8870
8878
|
*/
|
|
8871
8879
|
/** Matches an explicit affirmative answer (`y`/`yes`, case-insensitive). */
|
|
8872
8880
|
const YES_PATTERN = /^y(es)?$/i;
|
|
8881
|
+
/** Prompt rail width — matches the renderer's `RAIL_WIDTH` so the hint aligns with other rows. */
|
|
8882
|
+
const PROMPT_WIDTH = 66;
|
|
8883
|
+
/** Whether the interactive prompts render with the MOKU marker styling (color/TTY only). */
|
|
8884
|
+
const PROMPT_COLOR = supportsColor();
|
|
8885
|
+
/** Shared palette for the interactive prompts (same brand colors as the Panel renderer). */
|
|
8886
|
+
const PROMPT_PALETTE = makePalette(PROMPT_COLOR, PROMPT_COLOR && supportsTruecolor());
|
|
8887
|
+
/**
|
|
8888
|
+
* Build the styled y/N confirm prompt: a brand `◆` marker + the question on the left,
|
|
8889
|
+
* a dim `y / N` hint + cyan `›` caret right-aligned to {@link PROMPT_WIDTH}. Falls back
|
|
8890
|
+
* to the plain `question [y/N] ` form off a color TTY (CI/pipes), where prompts rarely run.
|
|
8891
|
+
*
|
|
8892
|
+
* @param question - The yes/no question to display.
|
|
8893
|
+
* @returns The readline prompt string (the typed answer follows the caret).
|
|
8894
|
+
* @example
|
|
8895
|
+
* confirmPrompt("Deploy dist/ to Cloudflare Pages?");
|
|
8896
|
+
*/
|
|
8897
|
+
function confirmPrompt(question) {
|
|
8898
|
+
if (!PROMPT_COLOR) return `${question} [y/N] `;
|
|
8899
|
+
const left = ` ${PROMPT_PALETTE.pink("◆")} ${question}`;
|
|
8900
|
+
const right = `${PROMPT_PALETTE.dim("y / N")} ${PROMPT_PALETTE.cyan("›")} `;
|
|
8901
|
+
const gap = Math.max(1, PROMPT_WIDTH - visibleWidth(left) - visibleWidth(right));
|
|
8902
|
+
return `${left}${" ".repeat(gap)}${right}`;
|
|
8903
|
+
}
|
|
8873
8904
|
/**
|
|
8874
8905
|
* Resolve the `Bun` runtime global, or `undefined` when not running under Bun.
|
|
8875
8906
|
*
|
|
@@ -8927,7 +8958,7 @@ function defaultConfirm(question) {
|
|
|
8927
8958
|
input: process.stdin,
|
|
8928
8959
|
output: process.stdout
|
|
8929
8960
|
});
|
|
8930
|
-
readline.question(
|
|
8961
|
+
readline.question(confirmPrompt(question), (answer) => {
|
|
8931
8962
|
readline.close();
|
|
8932
8963
|
resolve(YES_PATTERN.test(answer.trim()));
|
|
8933
8964
|
});
|
|
@@ -8950,8 +8981,8 @@ function defaultSelect(question, choices) {
|
|
|
8950
8981
|
input: process.stdin,
|
|
8951
8982
|
output: process.stdout
|
|
8952
8983
|
});
|
|
8953
|
-
|
|
8954
|
-
readline.question(
|
|
8984
|
+
console.log(selectChoicesBlock(question, choices));
|
|
8985
|
+
readline.question(selectPrompt(question, choices.length), (answer) => {
|
|
8955
8986
|
readline.close();
|
|
8956
8987
|
const picked = Number.parseInt(answer.trim(), 10);
|
|
8957
8988
|
resolve(Number.isInteger(picked) && picked >= 1 && picked <= choices.length ? picked - 1 : 0);
|
|
@@ -8959,6 +8990,36 @@ function defaultSelect(question, choices) {
|
|
|
8959
8990
|
});
|
|
8960
8991
|
}
|
|
8961
8992
|
/**
|
|
8993
|
+
* Render the select block: a brand `◆` marker + the question, then each choice as an
|
|
8994
|
+
* indented dim number + label. Off a color TTY, falls back to the plain ` N) label`
|
|
8995
|
+
* list (the question rides the prompt instead).
|
|
8996
|
+
*
|
|
8997
|
+
* @param question - The prompt shown above the choices (styled mode only).
|
|
8998
|
+
* @param choices - The selectable option labels.
|
|
8999
|
+
* @returns The multi-line choices block.
|
|
9000
|
+
* @example
|
|
9001
|
+
* selectChoicesBlock("Set up a workflow?", ["Auto", "Manual", "Skip"]);
|
|
9002
|
+
*/
|
|
9003
|
+
function selectChoicesBlock(question, choices) {
|
|
9004
|
+
if (!PROMPT_COLOR) return choices.map((choice, index) => ` ${index + 1}) ${choice}`).join("\n");
|
|
9005
|
+
return [` ${PROMPT_PALETTE.pink("◆")} ${question}`, ...choices.map((choice, index) => ` ${PROMPT_PALETTE.dim(String(index + 1))} ${choice}`)].join("\n");
|
|
9006
|
+
}
|
|
9007
|
+
/**
|
|
9008
|
+
* Build the select input prompt: a dim `pick 1–N` hint + cyan `›` caret in styled mode,
|
|
9009
|
+
* or the plain `question [1-N] ` form off a color TTY (where the question is not printed
|
|
9010
|
+
* separately).
|
|
9011
|
+
*
|
|
9012
|
+
* @param question - The prompt (used only by the plain fallback).
|
|
9013
|
+
* @param count - The number of choices.
|
|
9014
|
+
* @returns The readline prompt string.
|
|
9015
|
+
* @example
|
|
9016
|
+
* selectPrompt("Set up a workflow?", 3);
|
|
9017
|
+
*/
|
|
9018
|
+
function selectPrompt(question, count) {
|
|
9019
|
+
if (!PROMPT_COLOR) return `${question} [1-${count}] `;
|
|
9020
|
+
return ` ${PROMPT_PALETTE.dim(`pick 1–${count}`)} ${PROMPT_PALETTE.cyan("›")} `;
|
|
9021
|
+
}
|
|
9022
|
+
/**
|
|
8962
9023
|
* Default recursive directory watcher — wraps `node:fs.watch` with `{ recursive: true }`
|
|
8963
9024
|
* and adapts its handle to {@link WatchHandle}. Tests inject a fake emitter so no real
|
|
8964
9025
|
* FS watch is registered.
|
package/dist/index.d.cts
CHANGED
|
@@ -1615,7 +1615,17 @@ type Config$3 = {
|
|
|
1615
1615
|
body?: string;
|
|
1616
1616
|
}; /** Emit per-path i18n bare-path redirect HTML pages. Default `false`. */
|
|
1617
1617
|
localeRedirects?: boolean; /** Authoritative client bundle entry path (overrides the conventional scan). */
|
|
1618
|
-
clientEntry?: string;
|
|
1618
|
+
clientEntry?: string;
|
|
1619
|
+
/**
|
|
1620
|
+
* Path to a custom HTML document shell, giving the app full control over the
|
|
1621
|
+
* scaffold (charset, viewport, `<html lang>`, body attributes, wrapper markup).
|
|
1622
|
+
* Placeholders, substituted per page at build time:
|
|
1623
|
+
* `<!--moku:lang-->` (page locale for `<html lang>`),
|
|
1624
|
+
* `<!--moku:head-->` (composed `<head>` inner HTML),
|
|
1625
|
+
* `<!--moku:assets-->` (injected `<link>`/`<script>` tags),
|
|
1626
|
+
* `<!--moku:body-->` (SSR body HTML).
|
|
1627
|
+
* When unset, the built-in shell is used (it emits charset + viewport by default).
|
|
1628
|
+
*/
|
|
1619
1629
|
template?: string;
|
|
1620
1630
|
};
|
|
1621
1631
|
/**
|
package/dist/index.d.mts
CHANGED
|
@@ -1615,7 +1615,17 @@ type Config$3 = {
|
|
|
1615
1615
|
body?: string;
|
|
1616
1616
|
}; /** Emit per-path i18n bare-path redirect HTML pages. Default `false`. */
|
|
1617
1617
|
localeRedirects?: boolean; /** Authoritative client bundle entry path (overrides the conventional scan). */
|
|
1618
|
-
clientEntry?: string;
|
|
1618
|
+
clientEntry?: string;
|
|
1619
|
+
/**
|
|
1620
|
+
* Path to a custom HTML document shell, giving the app full control over the
|
|
1621
|
+
* scaffold (charset, viewport, `<html lang>`, body attributes, wrapper markup).
|
|
1622
|
+
* Placeholders, substituted per page at build time:
|
|
1623
|
+
* `<!--moku:lang-->` (page locale for `<html lang>`),
|
|
1624
|
+
* `<!--moku:head-->` (composed `<head>` inner HTML),
|
|
1625
|
+
* `<!--moku:assets-->` (injected `<link>`/`<script>` tags),
|
|
1626
|
+
* `<!--moku:body-->` (SSR body HTML).
|
|
1627
|
+
* When unset, the built-in shell is used (it emits charset + viewport by default).
|
|
1628
|
+
*/
|
|
1619
1629
|
template?: string;
|
|
1620
1630
|
};
|
|
1621
1631
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -3829,7 +3829,7 @@ const DEFAULT_BODY = "<h1>404</h1><p>The page you requested could not be found.<
|
|
|
3829
3829
|
* ```
|
|
3830
3830
|
*/
|
|
3831
3831
|
function wrap(body) {
|
|
3832
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>404 — Not Found</title></head><body>${body}</body></html>`;
|
|
3832
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>404 — Not Found</title></head><body>${body}</body></html>`;
|
|
3833
3833
|
}
|
|
3834
3834
|
/**
|
|
3835
3835
|
* Emits `outDir/404.html`. When `config.notFound` is `true`, writes the built-in
|
|
@@ -4513,6 +4513,8 @@ const HEAD_PLACEHOLDER = "<!--moku:head-->";
|
|
|
4513
4513
|
const BODY_PLACEHOLDER = "<!--moku:body-->";
|
|
4514
4514
|
/** Template placeholder for the injected asset `<link>`/`<script>` tags. */
|
|
4515
4515
|
const ASSETS_PLACEHOLDER = "<!--moku:assets-->";
|
|
4516
|
+
/** Template placeholder for the page's locale (`<html lang>`). */
|
|
4517
|
+
const LANG_PLACEHOLDER = "<!--moku:lang-->";
|
|
4516
4518
|
/**
|
|
4517
4519
|
* Read the bundle phase's hashed asset manifest for one kind from `state.buildCache`
|
|
4518
4520
|
* as a typed {@link BuildCacheEntry} (no `Map<string, unknown>` reads).
|
|
@@ -4560,14 +4562,16 @@ function buildAssetTags(ctx) {
|
|
|
4560
4562
|
* ```
|
|
4561
4563
|
*/
|
|
4562
4564
|
function renderDocument(parts) {
|
|
4563
|
-
return `<!DOCTYPE html><html lang="${parts.locale}"><head>${parts.head}${parts.assets}</head><body>${parts.body}</body></html>`;
|
|
4565
|
+
return `<!DOCTYPE html><html lang="${parts.locale}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">${parts.head}${parts.assets}</head><body>${parts.body}</body></html>`;
|
|
4564
4566
|
}
|
|
4565
4567
|
/**
|
|
4566
|
-
* Fill a shell template's `<!--moku:
|
|
4567
|
-
* `<!--moku:assets-->` placeholders deterministically at build
|
|
4568
|
+
* Fill a shell template's `<!--moku:lang-->` / `<!--moku:head-->` /
|
|
4569
|
+
* `<!--moku:body-->` / `<!--moku:assets-->` placeholders deterministically at build
|
|
4570
|
+
* time. `<!--moku:lang-->` carries the page locale (for `<html lang>`), so a single
|
|
4571
|
+
* shared template stays locale-correct across every locale.
|
|
4568
4572
|
*
|
|
4569
4573
|
* @param template - The raw shell template HTML.
|
|
4570
|
-
* @param parts - The composed head/body/assets pieces.
|
|
4574
|
+
* @param parts - The composed head/body/assets/locale pieces.
|
|
4571
4575
|
* @returns The filled document string.
|
|
4572
4576
|
* @example
|
|
4573
4577
|
* ```ts
|
|
@@ -4575,7 +4579,7 @@ function renderDocument(parts) {
|
|
|
4575
4579
|
* ```
|
|
4576
4580
|
*/
|
|
4577
4581
|
function fillTemplate(template, parts) {
|
|
4578
|
-
return template.replaceAll(HEAD_PLACEHOLDER, parts.head).replaceAll(BODY_PLACEHOLDER, parts.body).replaceAll(ASSETS_PLACEHOLDER, parts.assets);
|
|
4582
|
+
return template.replaceAll(LANG_PLACEHOLDER, parts.locale).replaceAll(HEAD_PLACEHOLDER, parts.head).replaceAll(BODY_PLACEHOLDER, parts.body).replaceAll(ASSETS_PLACEHOLDER, parts.assets);
|
|
4579
4583
|
}
|
|
4580
4584
|
/**
|
|
4581
4585
|
* Resolve the compiled entry for a manifest definition, asserting the router
|
|
@@ -5827,7 +5831,8 @@ function buildWranglerArgs(input) {
|
|
|
5827
5831
|
"--project-name",
|
|
5828
5832
|
input.slug,
|
|
5829
5833
|
"--branch",
|
|
5830
|
-
branch
|
|
5834
|
+
branch,
|
|
5835
|
+
"--commit-dirty=true"
|
|
5831
5836
|
];
|
|
5832
5837
|
}
|
|
5833
5838
|
/**
|
|
@@ -6436,17 +6441,18 @@ function validateConfig$1(ctx) {
|
|
|
6436
6441
|
ctx.require(sitePlugin);
|
|
6437
6442
|
}
|
|
6438
6443
|
/**
|
|
6439
|
-
* Run wrangler for the prepared argv and
|
|
6440
|
-
*
|
|
6441
|
-
*
|
|
6442
|
-
*
|
|
6444
|
+
* Run wrangler for the prepared argv and return its stdout, translating a non-zero
|
|
6445
|
+
* exit into the classified deploy error. The API token is read from env here so it
|
|
6446
|
+
* never crosses a logging boundary; the scrubbed stderr is used only to classify a
|
|
6447
|
+
* failure — it is never logged (that was console noise), so nothing leaks. Shared by
|
|
6448
|
+
* `run()` (deploy) and `createProject()` (project create).
|
|
6443
6449
|
*
|
|
6444
6450
|
* @param ctx - Plugin context (provides `state.spawn`, `config`, `env`).
|
|
6445
6451
|
* @param args - The fully-built, pre-validated wrangler argv.
|
|
6446
|
-
* @returns The wrangler `stdout`
|
|
6452
|
+
* @returns The wrangler `stdout` (for URL/id parsing on a deploy).
|
|
6447
6453
|
* @throws {Error} With a `code` from the deploy error taxonomy on a non-zero exit.
|
|
6448
6454
|
* @example
|
|
6449
|
-
* const
|
|
6455
|
+
* const stdout = await executeWrangler(ctx, args);
|
|
6450
6456
|
*/
|
|
6451
6457
|
async function executeWrangler(ctx, args) {
|
|
6452
6458
|
const token = ctx.env.require("CLOUDFLARE_API_TOKEN");
|
|
@@ -6460,10 +6466,7 @@ async function executeWrangler(ctx, args) {
|
|
|
6460
6466
|
const { code, message } = classifyWranglerError(exitCode, scrubbedStderr);
|
|
6461
6467
|
throw deployError(code, message);
|
|
6462
6468
|
}
|
|
6463
|
-
return
|
|
6464
|
-
stdout,
|
|
6465
|
-
scrubbedStderr
|
|
6466
|
-
};
|
|
6469
|
+
return stdout;
|
|
6467
6470
|
}
|
|
6468
6471
|
/**
|
|
6469
6472
|
* Assemble the public {@link DeployResult} from wrangler's stdout, parsing the
|
|
@@ -6520,9 +6523,7 @@ function createApi$2(ctx) {
|
|
|
6520
6523
|
root
|
|
6521
6524
|
});
|
|
6522
6525
|
const start = Date.now();
|
|
6523
|
-
const
|
|
6524
|
-
ctx.log.info(scrubbedStderr);
|
|
6525
|
-
const result = buildDeployResult(stdout, branch, start);
|
|
6526
|
+
const result = buildDeployResult(await executeWrangler(ctx, args), branch, start);
|
|
6526
6527
|
ctx.state.lastDeployment = result;
|
|
6527
6528
|
ctx.emit("deploy:complete", {
|
|
6528
6529
|
url: result.url,
|
|
@@ -6582,11 +6583,10 @@ function createApi$2(ctx) {
|
|
|
6582
6583
|
async createProject() {
|
|
6583
6584
|
const name = toSlug(ctx.require(sitePlugin).name());
|
|
6584
6585
|
const branch = ctx.config.productionBranch ?? "main";
|
|
6585
|
-
|
|
6586
|
+
await executeWrangler(ctx, buildProjectCreateArgs({
|
|
6586
6587
|
slug: name,
|
|
6587
6588
|
branch
|
|
6588
6589
|
}));
|
|
6589
|
-
ctx.log.info(scrubbedStderr);
|
|
6590
6590
|
return {
|
|
6591
6591
|
name,
|
|
6592
6592
|
branch
|
|
@@ -8753,16 +8753,24 @@ function createPanelRenderer(options = {}) {
|
|
|
8753
8753
|
write(line);
|
|
8754
8754
|
},
|
|
8755
8755
|
/**
|
|
8756
|
-
* Render the deploy result from a `deploy:complete` event
|
|
8757
|
-
*
|
|
8756
|
+
* Render the deploy result from a `deploy:complete` event as a full-width box (matching
|
|
8757
|
+
* the BUILD panel): a `✓ DEPLOYED · branch` header with the elapsed time right-aligned,
|
|
8758
|
+
* then a `→ url · id` row. The url/id row is omitted entirely when wrangler returned no
|
|
8759
|
+
* URL, so a first-deploy with nothing to parse never renders a dangling `→` or `· ·`.
|
|
8758
8760
|
*
|
|
8759
8761
|
* @param result - The `deploy:complete` payload.
|
|
8760
8762
|
* @example
|
|
8761
8763
|
* render.deployed({ url: "https://x.pages.dev", deploymentId: "id", branch: "main", durationMs: 1200 });
|
|
8762
8764
|
*/
|
|
8763
8765
|
deployed(result) {
|
|
8764
|
-
const
|
|
8765
|
-
|
|
8766
|
+
const dot = palette.dim("·");
|
|
8767
|
+
const time = result.durationMs >= 1e3 ? `${(result.durationMs / 1e3).toFixed(1)}s` : `${result.durationMs}ms`;
|
|
8768
|
+
const lines = [railLine(`${palette.green("✓")} ${palette.bold("DEPLOYED")} ${dot} ${result.branch}`, palette.dim(time), BOX_INNER)];
|
|
8769
|
+
if (result.url) {
|
|
8770
|
+
const id = result.deploymentId ? ` ${dot} ${palette.dim(result.deploymentId)}` : "";
|
|
8771
|
+
lines.push(`${palette.dim("→")} ${palette.cyan(result.url)}${id}`);
|
|
8772
|
+
} else if (result.deploymentId) lines.push(palette.dim(`id ${result.deploymentId}`));
|
|
8773
|
+
writeBlock(box(lines, color, BOX_INNER));
|
|
8766
8774
|
},
|
|
8767
8775
|
/**
|
|
8768
8776
|
* Render a neutral informational line.
|
|
@@ -8857,6 +8865,29 @@ function createPanelRenderer(options = {}) {
|
|
|
8857
8865
|
*/
|
|
8858
8866
|
/** Matches an explicit affirmative answer (`y`/`yes`, case-insensitive). */
|
|
8859
8867
|
const YES_PATTERN = /^y(es)?$/i;
|
|
8868
|
+
/** Prompt rail width — matches the renderer's `RAIL_WIDTH` so the hint aligns with other rows. */
|
|
8869
|
+
const PROMPT_WIDTH = 66;
|
|
8870
|
+
/** Whether the interactive prompts render with the MOKU marker styling (color/TTY only). */
|
|
8871
|
+
const PROMPT_COLOR = supportsColor();
|
|
8872
|
+
/** Shared palette for the interactive prompts (same brand colors as the Panel renderer). */
|
|
8873
|
+
const PROMPT_PALETTE = makePalette(PROMPT_COLOR, PROMPT_COLOR && supportsTruecolor());
|
|
8874
|
+
/**
|
|
8875
|
+
* Build the styled y/N confirm prompt: a brand `◆` marker + the question on the left,
|
|
8876
|
+
* a dim `y / N` hint + cyan `›` caret right-aligned to {@link PROMPT_WIDTH}. Falls back
|
|
8877
|
+
* to the plain `question [y/N] ` form off a color TTY (CI/pipes), where prompts rarely run.
|
|
8878
|
+
*
|
|
8879
|
+
* @param question - The yes/no question to display.
|
|
8880
|
+
* @returns The readline prompt string (the typed answer follows the caret).
|
|
8881
|
+
* @example
|
|
8882
|
+
* confirmPrompt("Deploy dist/ to Cloudflare Pages?");
|
|
8883
|
+
*/
|
|
8884
|
+
function confirmPrompt(question) {
|
|
8885
|
+
if (!PROMPT_COLOR) return `${question} [y/N] `;
|
|
8886
|
+
const left = ` ${PROMPT_PALETTE.pink("◆")} ${question}`;
|
|
8887
|
+
const right = `${PROMPT_PALETTE.dim("y / N")} ${PROMPT_PALETTE.cyan("›")} `;
|
|
8888
|
+
const gap = Math.max(1, PROMPT_WIDTH - visibleWidth(left) - visibleWidth(right));
|
|
8889
|
+
return `${left}${" ".repeat(gap)}${right}`;
|
|
8890
|
+
}
|
|
8860
8891
|
/**
|
|
8861
8892
|
* Resolve the `Bun` runtime global, or `undefined` when not running under Bun.
|
|
8862
8893
|
*
|
|
@@ -8914,7 +8945,7 @@ function defaultConfirm(question) {
|
|
|
8914
8945
|
input: process.stdin,
|
|
8915
8946
|
output: process.stdout
|
|
8916
8947
|
});
|
|
8917
|
-
readline.question(
|
|
8948
|
+
readline.question(confirmPrompt(question), (answer) => {
|
|
8918
8949
|
readline.close();
|
|
8919
8950
|
resolve(YES_PATTERN.test(answer.trim()));
|
|
8920
8951
|
});
|
|
@@ -8937,8 +8968,8 @@ function defaultSelect(question, choices) {
|
|
|
8937
8968
|
input: process.stdin,
|
|
8938
8969
|
output: process.stdout
|
|
8939
8970
|
});
|
|
8940
|
-
|
|
8941
|
-
readline.question(
|
|
8971
|
+
console.log(selectChoicesBlock(question, choices));
|
|
8972
|
+
readline.question(selectPrompt(question, choices.length), (answer) => {
|
|
8942
8973
|
readline.close();
|
|
8943
8974
|
const picked = Number.parseInt(answer.trim(), 10);
|
|
8944
8975
|
resolve(Number.isInteger(picked) && picked >= 1 && picked <= choices.length ? picked - 1 : 0);
|
|
@@ -8946,6 +8977,36 @@ function defaultSelect(question, choices) {
|
|
|
8946
8977
|
});
|
|
8947
8978
|
}
|
|
8948
8979
|
/**
|
|
8980
|
+
* Render the select block: a brand `◆` marker + the question, then each choice as an
|
|
8981
|
+
* indented dim number + label. Off a color TTY, falls back to the plain ` N) label`
|
|
8982
|
+
* list (the question rides the prompt instead).
|
|
8983
|
+
*
|
|
8984
|
+
* @param question - The prompt shown above the choices (styled mode only).
|
|
8985
|
+
* @param choices - The selectable option labels.
|
|
8986
|
+
* @returns The multi-line choices block.
|
|
8987
|
+
* @example
|
|
8988
|
+
* selectChoicesBlock("Set up a workflow?", ["Auto", "Manual", "Skip"]);
|
|
8989
|
+
*/
|
|
8990
|
+
function selectChoicesBlock(question, choices) {
|
|
8991
|
+
if (!PROMPT_COLOR) return choices.map((choice, index) => ` ${index + 1}) ${choice}`).join("\n");
|
|
8992
|
+
return [` ${PROMPT_PALETTE.pink("◆")} ${question}`, ...choices.map((choice, index) => ` ${PROMPT_PALETTE.dim(String(index + 1))} ${choice}`)].join("\n");
|
|
8993
|
+
}
|
|
8994
|
+
/**
|
|
8995
|
+
* Build the select input prompt: a dim `pick 1–N` hint + cyan `›` caret in styled mode,
|
|
8996
|
+
* or the plain `question [1-N] ` form off a color TTY (where the question is not printed
|
|
8997
|
+
* separately).
|
|
8998
|
+
*
|
|
8999
|
+
* @param question - The prompt (used only by the plain fallback).
|
|
9000
|
+
* @param count - The number of choices.
|
|
9001
|
+
* @returns The readline prompt string.
|
|
9002
|
+
* @example
|
|
9003
|
+
* selectPrompt("Set up a workflow?", 3);
|
|
9004
|
+
*/
|
|
9005
|
+
function selectPrompt(question, count) {
|
|
9006
|
+
if (!PROMPT_COLOR) return `${question} [1-${count}] `;
|
|
9007
|
+
return ` ${PROMPT_PALETTE.dim(`pick 1–${count}`)} ${PROMPT_PALETTE.cyan("›")} `;
|
|
9008
|
+
}
|
|
9009
|
+
/**
|
|
8949
9010
|
* Default recursive directory watcher — wraps `node:fs.watch` with `{ recursive: true }`
|
|
8950
9011
|
* and adapts its handle to {@link WatchHandle}. Tests inject a fake emitter so no real
|
|
8951
9012
|
* FS watch is registered.
|
package/package.json
CHANGED