@moku-labs/web 0.2.0 → 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/dist/chunk-D7D4PA-g.mjs +13 -0
- package/dist/index.cjs +940 -266
- package/dist/index.d.cts +315 -121
- package/dist/index.d.mts +314 -120
- package/dist/index.mjs +857 -175
- package/package.json +8 -8
- package/dist/chunk-DQk6qfdC.mjs +0 -18
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as __exportAll } from "./chunk-
|
|
1
|
+
import { t as __exportAll } from "./chunk-D7D4PA-g.mjs";
|
|
2
2
|
import { createCoreConfig, createCorePlugin } from "@moku-labs/core";
|
|
3
3
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
4
4
|
import { cp, mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
@@ -23,7 +23,6 @@ import { Resvg } from "@resvg/resvg-js";
|
|
|
23
23
|
import satori from "satori";
|
|
24
24
|
import { jsx } from "preact/jsx-runtime";
|
|
25
25
|
import { renderToString } from "preact-render-to-string";
|
|
26
|
-
|
|
27
26
|
//#region src/plugins/env/api.ts
|
|
28
27
|
/** Error prefix for all env API failures. */
|
|
29
28
|
const ERROR_PREFIX$13 = "[web]";
|
|
@@ -44,26 +43,75 @@ const ERROR_PREFIX$13 = "[web]";
|
|
|
44
43
|
function createEnvApi(ctx) {
|
|
45
44
|
const { resolved, publicMap } = ctx.state;
|
|
46
45
|
return {
|
|
46
|
+
/**
|
|
47
|
+
* Reads a resolved variable.
|
|
48
|
+
*
|
|
49
|
+
* @param key - Variable name.
|
|
50
|
+
* @returns The value, or `undefined` if not present.
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* api.get("PUBLIC_API_URL");
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
47
56
|
get(key) {
|
|
48
57
|
return resolved.get(key);
|
|
49
58
|
},
|
|
59
|
+
/**
|
|
60
|
+
* Reads a variable that must exist.
|
|
61
|
+
*
|
|
62
|
+
* @param key - Variable name.
|
|
63
|
+
* @returns The value.
|
|
64
|
+
* @throws {Error} If the variable is undefined.
|
|
65
|
+
* @example
|
|
66
|
+
* ```ts
|
|
67
|
+
* api.require("DEPLOY_TOKEN");
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
50
70
|
require(key) {
|
|
51
71
|
const value = resolved.get(key);
|
|
52
72
|
if (value === void 0) throw new Error(`${ERROR_PREFIX$13} env: required variable "${key}" is not defined.`);
|
|
53
73
|
return value;
|
|
54
74
|
},
|
|
75
|
+
/**
|
|
76
|
+
* Tests presence of a resolved variable.
|
|
77
|
+
*
|
|
78
|
+
* @param key - Variable name.
|
|
79
|
+
* @returns `true` if a value is present.
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* api.has("PUBLIC_API_URL");
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
55
85
|
has(key) {
|
|
56
86
|
return resolved.has(key);
|
|
57
87
|
},
|
|
88
|
+
/**
|
|
89
|
+
* Returns all public variables as a frozen plain object — a fresh copy,
|
|
90
|
+
* never the raw state map.
|
|
91
|
+
*
|
|
92
|
+
* @returns A frozen `Record` of public variable names to values.
|
|
93
|
+
* @example
|
|
94
|
+
* ```ts
|
|
95
|
+
* const payload = { ...api.getPublic() };
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
58
98
|
getPublic() {
|
|
59
99
|
return Object.freeze(Object.fromEntries(publicMap));
|
|
60
100
|
},
|
|
101
|
+
/**
|
|
102
|
+
* Returns the already-frozen map of public variables.
|
|
103
|
+
*
|
|
104
|
+
* @returns The frozen public map.
|
|
105
|
+
* @example
|
|
106
|
+
* ```ts
|
|
107
|
+
* [...api.getPublicMap()];
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
61
110
|
getPublicMap() {
|
|
62
111
|
return publicMap;
|
|
63
112
|
}
|
|
64
113
|
};
|
|
65
114
|
}
|
|
66
|
-
|
|
67
115
|
//#endregion
|
|
68
116
|
//#region src/plugins/env/state.ts
|
|
69
117
|
/**
|
|
@@ -83,7 +131,6 @@ function createEnvState() {
|
|
|
83
131
|
publicMap: /* @__PURE__ */ new Map()
|
|
84
132
|
};
|
|
85
133
|
}
|
|
86
|
-
|
|
87
134
|
//#endregion
|
|
88
135
|
//#region src/plugins/env/validate.ts
|
|
89
136
|
/** Error message thrown by every frozen-map mutator. */
|
|
@@ -202,7 +249,6 @@ function validateSchema(ctx) {
|
|
|
202
249
|
freezeMap(state.resolved);
|
|
203
250
|
freezeMap(state.publicMap);
|
|
204
251
|
}
|
|
205
|
-
|
|
206
252
|
//#endregion
|
|
207
253
|
//#region src/plugins/env/providers.ts
|
|
208
254
|
/**
|
|
@@ -271,6 +317,15 @@ function parseDotenv(text) {
|
|
|
271
317
|
function dotenv(path = DEFAULT_DOTENV_PATH) {
|
|
272
318
|
return {
|
|
273
319
|
name: `dotenv:${path}`,
|
|
320
|
+
/**
|
|
321
|
+
* Reads and parses the dotenv file fresh from disk; `{}` if it is missing.
|
|
322
|
+
*
|
|
323
|
+
* @returns The parsed environment record, or `{}` when the file is absent.
|
|
324
|
+
* @example
|
|
325
|
+
* ```ts
|
|
326
|
+
* dotenv(".env.local").load();
|
|
327
|
+
* ```
|
|
328
|
+
*/
|
|
274
329
|
load() {
|
|
275
330
|
if (!existsSync(path)) return {};
|
|
276
331
|
return parseDotenv(readFileSync(path, "utf8"));
|
|
@@ -290,24 +345,20 @@ function dotenv(path = DEFAULT_DOTENV_PATH) {
|
|
|
290
345
|
function processEnv() {
|
|
291
346
|
return {
|
|
292
347
|
name: "process-env",
|
|
348
|
+
/**
|
|
349
|
+
* Returns a shallow copy of `process.env` at call time.
|
|
350
|
+
*
|
|
351
|
+
* @returns A fresh shallow copy of `process.env`.
|
|
352
|
+
* @example
|
|
353
|
+
* ```ts
|
|
354
|
+
* processEnv().load();
|
|
355
|
+
* ```
|
|
356
|
+
*/
|
|
293
357
|
load() {
|
|
294
358
|
return { ...process.env };
|
|
295
359
|
}
|
|
296
360
|
};
|
|
297
361
|
}
|
|
298
|
-
|
|
299
|
-
//#endregion
|
|
300
|
-
//#region src/plugins/env/index.ts
|
|
301
|
-
/**
|
|
302
|
-
* @file Core plugin: universal env injection — schema + providers + PUBLIC_ cross-validation at onInit.
|
|
303
|
-
* @see README.md
|
|
304
|
-
*/
|
|
305
|
-
/** Plugin config defaults (R6 typed const). `providers: []` — framework sets `[dotenv(), processEnv()]` via the 4-level cascade. */
|
|
306
|
-
const defaultEnvConfig = {
|
|
307
|
-
schema: {},
|
|
308
|
-
providers: [],
|
|
309
|
-
publicPrefix: "PUBLIC_"
|
|
310
|
-
};
|
|
311
362
|
/**
|
|
312
363
|
* Core plugin that resolves, validates, and freezes the environment at `onInit`,
|
|
313
364
|
* exposing a read-only accessor at `ctx.env`. No `onStart`/`onStop` — holds no resource.
|
|
@@ -318,12 +369,15 @@ const defaultEnvConfig = {
|
|
|
318
369
|
* ```
|
|
319
370
|
*/
|
|
320
371
|
const envPlugin = createCorePlugin("env", {
|
|
321
|
-
config:
|
|
372
|
+
config: {
|
|
373
|
+
schema: {},
|
|
374
|
+
providers: [],
|
|
375
|
+
publicPrefix: "PUBLIC_"
|
|
376
|
+
},
|
|
322
377
|
createState: createEnvState,
|
|
323
378
|
api: createEnvApi,
|
|
324
379
|
onInit: validateSchema
|
|
325
380
|
});
|
|
326
|
-
|
|
327
381
|
//#endregion
|
|
328
382
|
//#region src/plugins/log/expect.ts
|
|
329
383
|
/**
|
|
@@ -434,10 +488,33 @@ function describePartial(partial) {
|
|
|
434
488
|
*/
|
|
435
489
|
function createExpectChain(entries) {
|
|
436
490
|
const chain = {
|
|
491
|
+
/**
|
|
492
|
+
* Assert at least one entry has `event`, optionally matching `partial`.
|
|
493
|
+
*
|
|
494
|
+
* @param event - Event name to find.
|
|
495
|
+
* @param partial - Optional partial data shape (subset-matched).
|
|
496
|
+
* @returns The same chain for chaining.
|
|
497
|
+
* @throws {LogExpectAssertionError} When no matching entry exists.
|
|
498
|
+
* @example
|
|
499
|
+
* ```ts
|
|
500
|
+
* chain.toHaveEvent("build:phase", { status: "start" });
|
|
501
|
+
* ```
|
|
502
|
+
*/
|
|
437
503
|
toHaveEvent(event, partial) {
|
|
438
504
|
if (!entries.some((entry) => entryMatches(entry, event, partial))) throw new LogExpectAssertionError(`Expected trace to contain event "${event}"${describePartial(partial)}, but none was found.`);
|
|
439
505
|
return chain;
|
|
440
506
|
},
|
|
507
|
+
/**
|
|
508
|
+
* Assert all of `events` appear in the trace in the given relative order.
|
|
509
|
+
*
|
|
510
|
+
* @param events - Ordered list of event names (gaps allowed).
|
|
511
|
+
* @returns The same chain for chaining.
|
|
512
|
+
* @throws {LogExpectAssertionError} When the ordering cannot be satisfied.
|
|
513
|
+
* @example
|
|
514
|
+
* ```ts
|
|
515
|
+
* chain.toHaveEventInOrder(["build:phase", "build:complete"]);
|
|
516
|
+
* ```
|
|
517
|
+
*/
|
|
441
518
|
toHaveEventInOrder(events) {
|
|
442
519
|
let cursor = 0;
|
|
443
520
|
for (const [position, event] of events.entries()) {
|
|
@@ -451,6 +528,18 @@ function createExpectChain(entries) {
|
|
|
451
528
|
}
|
|
452
529
|
return chain;
|
|
453
530
|
},
|
|
531
|
+
/**
|
|
532
|
+
* Assert NO entry has `event` (optionally narrowed by `partial`).
|
|
533
|
+
*
|
|
534
|
+
* @param event - Event name that must be absent.
|
|
535
|
+
* @param partial - Optional partial data shape; only matching entries violate.
|
|
536
|
+
* @returns The same chain for chaining.
|
|
537
|
+
* @throws {LogExpectAssertionError} When a matching entry exists.
|
|
538
|
+
* @example
|
|
539
|
+
* ```ts
|
|
540
|
+
* chain.toNotHaveEvent("deploy:failed");
|
|
541
|
+
* ```
|
|
542
|
+
*/
|
|
454
543
|
toNotHaveEvent(event, partial) {
|
|
455
544
|
const offending = entries.findIndex((entry) => entryMatches(entry, event, partial));
|
|
456
545
|
if (offending !== -1) throw new LogExpectAssertionError(`Expected trace to NOT contain event "${event}"${describePartial(partial)}, but found one at index ${offending}.`);
|
|
@@ -459,7 +548,6 @@ function createExpectChain(entries) {
|
|
|
459
548
|
};
|
|
460
549
|
return chain;
|
|
461
550
|
}
|
|
462
|
-
|
|
463
551
|
//#endregion
|
|
464
552
|
//#region src/plugins/log/api.ts
|
|
465
553
|
/**
|
|
@@ -528,33 +616,110 @@ function mergeError(data, error) {
|
|
|
528
616
|
function createLogApi(ctx) {
|
|
529
617
|
const { state } = ctx;
|
|
530
618
|
return {
|
|
619
|
+
/**
|
|
620
|
+
* Append an `info` entry and fan it out to every sink.
|
|
621
|
+
*
|
|
622
|
+
* @param event - Event identifier (convention: `domain:action`).
|
|
623
|
+
* @param data - Optional structured payload.
|
|
624
|
+
* @example
|
|
625
|
+
* ```ts
|
|
626
|
+
* log.info("content:ready", { count: 12 });
|
|
627
|
+
* ```
|
|
628
|
+
*/
|
|
531
629
|
info(event, data) {
|
|
532
630
|
append(state, "info", event, data);
|
|
533
631
|
},
|
|
632
|
+
/**
|
|
633
|
+
* Append a `debug` entry and fan it out to every sink.
|
|
634
|
+
*
|
|
635
|
+
* @param event - Event identifier (convention: `domain:action`).
|
|
636
|
+
* @param data - Optional structured payload.
|
|
637
|
+
* @example
|
|
638
|
+
* ```ts
|
|
639
|
+
* log.debug("router:match", { path: "/blog/" });
|
|
640
|
+
* ```
|
|
641
|
+
*/
|
|
534
642
|
debug(event, data) {
|
|
535
643
|
append(state, "debug", event, data);
|
|
536
644
|
},
|
|
645
|
+
/**
|
|
646
|
+
* Append a `warn` entry and fan it out to every sink.
|
|
647
|
+
*
|
|
648
|
+
* @param event - Event identifier (convention: `domain:action`).
|
|
649
|
+
* @param data - Optional structured payload.
|
|
650
|
+
* @example
|
|
651
|
+
* ```ts
|
|
652
|
+
* log.warn("build:skip", { reason: "no sitemap" });
|
|
653
|
+
* ```
|
|
654
|
+
*/
|
|
537
655
|
warn(event, data) {
|
|
538
656
|
append(state, "warn", event, data);
|
|
539
657
|
},
|
|
658
|
+
/**
|
|
659
|
+
* Append an `error` entry. When `error` is provided, its `message`/`stack`
|
|
660
|
+
* are merged into `data` under an `error` key (existing keys preserved);
|
|
661
|
+
* otherwise `data` is recorded as-is.
|
|
662
|
+
*
|
|
663
|
+
* @param event - Event identifier (convention: `domain:action`).
|
|
664
|
+
* @param data - Optional structured payload.
|
|
665
|
+
* @param error - Optional originating Error to merge into `data`.
|
|
666
|
+
* @example
|
|
667
|
+
* ```ts
|
|
668
|
+
* log.error("deploy:failed", { target: "cf" }, err);
|
|
669
|
+
* ```
|
|
670
|
+
*/
|
|
540
671
|
error(event, data, error) {
|
|
541
672
|
append(state, "error", event, error === void 0 ? data : mergeError(data, error));
|
|
542
673
|
},
|
|
674
|
+
/**
|
|
675
|
+
* Return a frozen snapshot (fresh copy) of the entries recorded so far.
|
|
676
|
+
*
|
|
677
|
+
* @returns A readonly, frozen copy of the recorded entries.
|
|
678
|
+
* @example
|
|
679
|
+
* ```ts
|
|
680
|
+
* const entries = log.trace();
|
|
681
|
+
* ```
|
|
682
|
+
*/
|
|
543
683
|
trace() {
|
|
544
684
|
return Object.freeze([...state.entries]);
|
|
545
685
|
},
|
|
686
|
+
/**
|
|
687
|
+
* Return a fluent assertion chain bound to the live entries array.
|
|
688
|
+
*
|
|
689
|
+
* @returns A fresh {@link ExpectChain} reading `state.entries` live.
|
|
690
|
+
* @example
|
|
691
|
+
* ```ts
|
|
692
|
+
* log.expect().toHaveEvent("build:complete");
|
|
693
|
+
* ```
|
|
694
|
+
*/
|
|
546
695
|
expect() {
|
|
547
696
|
return createExpectChain(state.entries);
|
|
548
697
|
},
|
|
698
|
+
/**
|
|
699
|
+
* Register an additional output sink at runtime.
|
|
700
|
+
*
|
|
701
|
+
* @param sink - The sink to add to the fan-out list.
|
|
702
|
+
* @example
|
|
703
|
+
* ```ts
|
|
704
|
+
* log.addSink({ write: (e) => stream.write(JSON.stringify(e)) });
|
|
705
|
+
* ```
|
|
706
|
+
*/
|
|
549
707
|
addSink(sink) {
|
|
550
708
|
state.sinks.push(sink);
|
|
551
709
|
},
|
|
710
|
+
/**
|
|
711
|
+
* Clear all recorded entries while keeping registered sinks.
|
|
712
|
+
*
|
|
713
|
+
* @example
|
|
714
|
+
* ```ts
|
|
715
|
+
* log.reset();
|
|
716
|
+
* ```
|
|
717
|
+
*/
|
|
552
718
|
reset() {
|
|
553
719
|
state.entries.length = 0;
|
|
554
720
|
}
|
|
555
721
|
};
|
|
556
722
|
}
|
|
557
|
-
|
|
558
723
|
//#endregion
|
|
559
724
|
//#region src/plugins/log/sinks.ts
|
|
560
725
|
/**
|
|
@@ -569,7 +734,17 @@ function createLogApi(ctx) {
|
|
|
569
734
|
* ```
|
|
570
735
|
*/
|
|
571
736
|
function consoleSink() {
|
|
572
|
-
return {
|
|
737
|
+
return {
|
|
738
|
+
/**
|
|
739
|
+
* Route a single entry to the console channel matching its level.
|
|
740
|
+
*
|
|
741
|
+
* @param entry - The entry to emit.
|
|
742
|
+
* @example
|
|
743
|
+
* ```ts
|
|
744
|
+
* sink.write({ level: "warn", event: "build:skip", ts: Date.now() });
|
|
745
|
+
* ```
|
|
746
|
+
*/
|
|
747
|
+
write(entry) {
|
|
573
748
|
if (entry.level === "error") console.error(entry);
|
|
574
749
|
else if (entry.level === "warn") console.warn(entry);
|
|
575
750
|
else console.log(entry);
|
|
@@ -590,7 +765,6 @@ function consoleSink() {
|
|
|
590
765
|
function installDefaultSinks(ctx) {
|
|
591
766
|
if (ctx.config.mode === "dev" || ctx.config.mode === "production") ctx.state.sinks.push(consoleSink());
|
|
592
767
|
}
|
|
593
|
-
|
|
594
768
|
//#endregion
|
|
595
769
|
//#region src/plugins/log/state.ts
|
|
596
770
|
/**
|
|
@@ -611,15 +785,6 @@ function createLogState(_ctx) {
|
|
|
611
785
|
sinks: []
|
|
612
786
|
};
|
|
613
787
|
}
|
|
614
|
-
|
|
615
|
-
//#endregion
|
|
616
|
-
//#region src/plugins/log/index.ts
|
|
617
|
-
/**
|
|
618
|
-
* @file log — Core Plugin (Standard tier): in-memory trace + expect() DSL.
|
|
619
|
-
* @see README.md
|
|
620
|
-
*/
|
|
621
|
-
/** Default config; overridden via the 4-level pluginConfigs.log merge. */
|
|
622
|
-
const defaultLogConfig = { mode: "production" };
|
|
623
788
|
/**
|
|
624
789
|
* Core logging plugin — always-on in-memory trace + `expect()` event-trace DSL.
|
|
625
790
|
* API injected as `ctx.log` on every regular plugin and surfaced as `app.log`.
|
|
@@ -628,29 +793,40 @@ const defaultLogConfig = { mode: "production" };
|
|
|
628
793
|
* @see README.md
|
|
629
794
|
*/
|
|
630
795
|
const logPlugin = createCorePlugin("log", {
|
|
631
|
-
config:
|
|
796
|
+
config: { mode: "production" },
|
|
632
797
|
createState: createLogState,
|
|
633
798
|
api: createLogApi,
|
|
634
799
|
onInit: installDefaultSinks
|
|
635
800
|
});
|
|
636
|
-
|
|
637
|
-
//#endregion
|
|
638
|
-
//#region src/config.ts
|
|
639
|
-
/**
|
|
640
|
-
* @file Framework configuration — Config + Events types, core plugin registration.
|
|
641
|
-
* @see README.md
|
|
642
|
-
*/
|
|
643
|
-
const defaultConfig$6 = { mode: "production" };
|
|
644
801
|
const coreConfig = createCoreConfig("web", {
|
|
645
|
-
config:
|
|
802
|
+
config: { mode: "production" },
|
|
646
803
|
plugins: [logPlugin, envPlugin],
|
|
647
804
|
pluginConfigs: {
|
|
648
805
|
log: { mode: "production" },
|
|
649
806
|
env: { providers: [dotenv(), processEnv()] }
|
|
650
807
|
}
|
|
651
808
|
});
|
|
652
|
-
|
|
653
|
-
|
|
809
|
+
/**
|
|
810
|
+
* Create a custom plugin bound to this framework's `Config`/`Events` and the core
|
|
811
|
+
* plugin APIs (`log`, `env`). Plugin types are fully inferred from the spec
|
|
812
|
+
* object — never write them explicitly. This is the binding every built-in
|
|
813
|
+
* plugin is wired with, and the one consumer plugins should use too.
|
|
814
|
+
*
|
|
815
|
+
* @example
|
|
816
|
+
* ```ts
|
|
817
|
+
* const analytics = createPlugin("analytics", {
|
|
818
|
+
* config: { writeKey: "" },
|
|
819
|
+
* api: (ctx) => ({ track: (event: string) => ctx.log.info("analytics:track", { event }) })
|
|
820
|
+
* });
|
|
821
|
+
* ```
|
|
822
|
+
*/
|
|
823
|
+
const createPlugin$1 = coreConfig.createPlugin;
|
|
824
|
+
/**
|
|
825
|
+
* Step 2 of the factory chain — captures the framework's default plugin set and
|
|
826
|
+
* returns the consumer entry points ({@link createApp} + a re-exported
|
|
827
|
+
* `createPlugin`). Wired once in `src/index.ts`; consumers don't call it directly.
|
|
828
|
+
*/
|
|
829
|
+
const createCore = coreConfig.createCore;
|
|
654
830
|
//#endregion
|
|
655
831
|
//#region src/plugins/i18n/api.ts
|
|
656
832
|
/** Error prefix for all i18n lifecycle failures. */
|
|
@@ -690,21 +866,83 @@ function validateI18nConfig(ctx) {
|
|
|
690
866
|
function createI18nApi(ctx) {
|
|
691
867
|
const { config } = ctx;
|
|
692
868
|
return {
|
|
869
|
+
/**
|
|
870
|
+
* Returns the configured supported locales in declared order.
|
|
871
|
+
*
|
|
872
|
+
* @returns The configured `locales` list (priority/display order).
|
|
873
|
+
* @example
|
|
874
|
+
* ```ts
|
|
875
|
+
* api.locales(); // ["en", "uk"]
|
|
876
|
+
* ```
|
|
877
|
+
*/
|
|
693
878
|
locales() {
|
|
694
879
|
return config.locales;
|
|
695
880
|
},
|
|
881
|
+
/**
|
|
882
|
+
* Returns the fallback locale used when a requested locale is absent.
|
|
883
|
+
*
|
|
884
|
+
* @returns The configured `defaultLocale`.
|
|
885
|
+
* @example
|
|
886
|
+
* ```ts
|
|
887
|
+
* api.defaultLocale(); // "en"
|
|
888
|
+
* ```
|
|
889
|
+
*/
|
|
696
890
|
defaultLocale() {
|
|
697
891
|
return config.defaultLocale;
|
|
698
892
|
},
|
|
893
|
+
/**
|
|
894
|
+
* Membership guard: whether `x` is one of the supported locales
|
|
895
|
+
* (case-sensitive).
|
|
896
|
+
*
|
|
897
|
+
* @param x - Candidate locale code.
|
|
898
|
+
* @returns `true` if `x ∈ locales`, else `false`.
|
|
899
|
+
* @example
|
|
900
|
+
* ```ts
|
|
901
|
+
* api.isLocale("uk"); // true
|
|
902
|
+
* ```
|
|
903
|
+
*/
|
|
699
904
|
isLocale(x) {
|
|
700
905
|
return config.locales.includes(x);
|
|
701
906
|
},
|
|
907
|
+
/**
|
|
908
|
+
* Human-readable display name for a locale.
|
|
909
|
+
*
|
|
910
|
+
* @param locale - Locale code to look up.
|
|
911
|
+
* @returns The display name, or `undefined` if unmapped.
|
|
912
|
+
* @example
|
|
913
|
+
* ```ts
|
|
914
|
+
* api.localeName("uk"); // "Українська"
|
|
915
|
+
* ```
|
|
916
|
+
*/
|
|
702
917
|
localeName(locale) {
|
|
703
918
|
return config.localeNames?.[locale];
|
|
704
919
|
},
|
|
920
|
+
/**
|
|
921
|
+
* Open Graph `og:locale` value for a locale.
|
|
922
|
+
*
|
|
923
|
+
* @param locale - Locale code to look up.
|
|
924
|
+
* @returns The `og:locale` value (e.g. `"en_US"`), or `undefined` if unmapped.
|
|
925
|
+
* @example
|
|
926
|
+
* ```ts
|
|
927
|
+
* api.ogLocale("en"); // "en_US"
|
|
928
|
+
* ```
|
|
929
|
+
*/
|
|
705
930
|
ogLocale(locale) {
|
|
706
931
|
return config.ogLocaleMap?.[locale];
|
|
707
932
|
},
|
|
933
|
+
/**
|
|
934
|
+
* Translate `key` for `locale` with a deterministic fallback chain
|
|
935
|
+
* (requested locale → default locale → the key itself). The default-locale
|
|
936
|
+
* lookup is skipped when `locale === defaultLocale`.
|
|
937
|
+
*
|
|
938
|
+
* @param locale - Requested locale code.
|
|
939
|
+
* @param key - Translation key (e.g. `"nav.home"`).
|
|
940
|
+
* @returns The translated value, the default-locale value, or `key`.
|
|
941
|
+
* @example
|
|
942
|
+
* ```ts
|
|
943
|
+
* api.t("uk", "nav.home"); // "Головна"
|
|
944
|
+
* ```
|
|
945
|
+
*/
|
|
708
946
|
t(locale, key) {
|
|
709
947
|
const exact = config.translations?.[locale]?.[key];
|
|
710
948
|
if (exact !== void 0) return exact;
|
|
@@ -716,34 +954,36 @@ function createI18nApi(ctx) {
|
|
|
716
954
|
}
|
|
717
955
|
};
|
|
718
956
|
}
|
|
719
|
-
|
|
720
|
-
//#endregion
|
|
721
|
-
//#region src/plugins/i18n/index.ts
|
|
722
957
|
/**
|
|
723
|
-
*
|
|
724
|
-
*
|
|
725
|
-
*
|
|
726
|
-
* Locale registry + flat translation helper with default-locale fallback.
|
|
727
|
-
* Pure config-as-data: no state, no events, no lifecycle resources.
|
|
728
|
-
* Consumed read-only by content/router/head/build via `ctx.require(i18nPlugin)`.
|
|
958
|
+
* Internationalization plugin — locale registry plus a flat translation helper
|
|
959
|
+
* with default-locale fallback. Pure config-as-data (no state or events);
|
|
960
|
+
* consumed read-only by content, router, head, and build.
|
|
729
961
|
*
|
|
730
|
-
* @
|
|
731
|
-
*
|
|
962
|
+
* @example Register locales and translations
|
|
963
|
+
* ```ts
|
|
964
|
+
* const app = createApp({
|
|
965
|
+
* pluginConfigs: {
|
|
966
|
+
* i18n: {
|
|
967
|
+
* locales: ["en", "uk"],
|
|
968
|
+
* defaultLocale: "en",
|
|
969
|
+
* localeNames: { en: "English", uk: "Українська" },
|
|
970
|
+
* translations: { uk: { "nav.home": "Головна" } }
|
|
971
|
+
* }
|
|
972
|
+
* }
|
|
973
|
+
* });
|
|
974
|
+
* ```
|
|
732
975
|
*/
|
|
733
|
-
/** Typed default config (R6: no inline `as`). Optional maps default to `{}` so every lookup is total. */
|
|
734
|
-
const defaultConfig$5 = {
|
|
735
|
-
locales: ["en"],
|
|
736
|
-
defaultLocale: "en",
|
|
737
|
-
localeNames: {},
|
|
738
|
-
ogLocaleMap: {},
|
|
739
|
-
translations: {}
|
|
740
|
-
};
|
|
741
976
|
const i18nPlugin = createPlugin$1("i18n", {
|
|
742
|
-
config:
|
|
977
|
+
config: {
|
|
978
|
+
locales: ["en"],
|
|
979
|
+
defaultLocale: "en",
|
|
980
|
+
localeNames: {},
|
|
981
|
+
ogLocaleMap: {},
|
|
982
|
+
translations: {}
|
|
983
|
+
},
|
|
743
984
|
onInit: validateI18nConfig,
|
|
744
985
|
api: createI18nApi
|
|
745
986
|
});
|
|
746
|
-
|
|
747
987
|
//#endregion
|
|
748
988
|
//#region src/plugins/content/pipeline/frontmatter.ts
|
|
749
989
|
/**
|
|
@@ -789,7 +1029,6 @@ function parseFrontmatter(raw, config) {
|
|
|
789
1029
|
body: parsed.content
|
|
790
1030
|
};
|
|
791
1031
|
}
|
|
792
|
-
|
|
793
1032
|
//#endregion
|
|
794
1033
|
//#region src/plugins/content/pipeline/plugins.ts
|
|
795
1034
|
/**
|
|
@@ -946,7 +1185,6 @@ function defaultRehypePlugins() {
|
|
|
946
1185
|
sectionDividerPlugin
|
|
947
1186
|
];
|
|
948
1187
|
}
|
|
949
|
-
|
|
950
1188
|
//#endregion
|
|
951
1189
|
//#region src/plugins/content/pipeline/sanitize.ts
|
|
952
1190
|
/**
|
|
@@ -1033,7 +1271,6 @@ function buildSanitizeSchema() {
|
|
|
1033
1271
|
}
|
|
1034
1272
|
};
|
|
1035
1273
|
}
|
|
1036
|
-
|
|
1037
1274
|
//#endregion
|
|
1038
1275
|
//#region src/plugins/content/pipeline/markdown.ts
|
|
1039
1276
|
/**
|
|
@@ -1091,7 +1328,6 @@ function applyPluggable(processor, plugin) {
|
|
|
1091
1328
|
}
|
|
1092
1329
|
processor.use(plugin);
|
|
1093
1330
|
}
|
|
1094
|
-
|
|
1095
1331
|
//#endregion
|
|
1096
1332
|
//#region src/plugins/content/pipeline/reading-time.ts
|
|
1097
1333
|
/**
|
|
@@ -1117,7 +1353,6 @@ function calculateReadingTime(text) {
|
|
|
1117
1353
|
wordCount: stats.words
|
|
1118
1354
|
};
|
|
1119
1355
|
}
|
|
1120
|
-
|
|
1121
1356
|
//#endregion
|
|
1122
1357
|
//#region src/plugins/content/api.ts
|
|
1123
1358
|
/**
|
|
@@ -1329,6 +1564,18 @@ function toCard(article) {
|
|
|
1329
1564
|
*/
|
|
1330
1565
|
function createContentApi(ctx) {
|
|
1331
1566
|
return {
|
|
1567
|
+
/**
|
|
1568
|
+
* Load every article across every active locale, returning a locale-keyed
|
|
1569
|
+
* map of date-descending Article arrays. Lazily builds the processor and
|
|
1570
|
+
* discovers slugs, applies locale fallback, excludes drafts in production,
|
|
1571
|
+
* assigns `contentId` after sorting, then emits `content:ready`.
|
|
1572
|
+
*
|
|
1573
|
+
* @returns A locale-keyed map of date-descending articles.
|
|
1574
|
+
* @example
|
|
1575
|
+
* ```ts
|
|
1576
|
+
* const byLocale = await api.loadAll();
|
|
1577
|
+
* ```
|
|
1578
|
+
*/
|
|
1332
1579
|
async loadAll() {
|
|
1333
1580
|
const slugs = ctx.state.slugs ?? await discoverSlugs(ctx.config.contentDir);
|
|
1334
1581
|
ctx.state.slugs = slugs;
|
|
@@ -1356,6 +1603,19 @@ function createContentApi(ctx) {
|
|
|
1356
1603
|
});
|
|
1357
1604
|
return result;
|
|
1358
1605
|
},
|
|
1606
|
+
/**
|
|
1607
|
+
* Resolve and render a single article for one locale with locale fallback.
|
|
1608
|
+
* Throws a `[web] content` error when neither the requested nor the
|
|
1609
|
+
* default-locale file exists.
|
|
1610
|
+
*
|
|
1611
|
+
* @param slug - Article directory name.
|
|
1612
|
+
* @param locale - Requested locale code.
|
|
1613
|
+
* @returns The resolved Article.
|
|
1614
|
+
* @example
|
|
1615
|
+
* ```ts
|
|
1616
|
+
* const article = await api.load("intro", "uk");
|
|
1617
|
+
* ```
|
|
1618
|
+
*/
|
|
1359
1619
|
async load(slug, locale) {
|
|
1360
1620
|
const article = await resolveArticle(ctx, slug, locale);
|
|
1361
1621
|
if (article === null) throw new Error(`[web] content article "${slug}" not found for locale "${locale}".\n Looked for ${slug}/${locale}.md and the default-locale fallback.`);
|
|
@@ -1364,10 +1624,33 @@ function createContentApi(ctx) {
|
|
|
1364
1624
|
ctx.state.articles.set(locale, cache);
|
|
1365
1625
|
return article;
|
|
1366
1626
|
},
|
|
1627
|
+
/**
|
|
1628
|
+
* Render a raw Markdown string to HTML through the full pipeline (sanitize
|
|
1629
|
+
* last when `trustedContent` is false). Lazily builds the processor.
|
|
1630
|
+
*
|
|
1631
|
+
* @param md - Raw Markdown source.
|
|
1632
|
+
* @returns The rendered HTML string.
|
|
1633
|
+
* @example
|
|
1634
|
+
* ```ts
|
|
1635
|
+
* const html = await api.renderMarkdown("# Hi");
|
|
1636
|
+
* ```
|
|
1637
|
+
*/
|
|
1367
1638
|
async renderMarkdown(md) {
|
|
1368
1639
|
const processor = ensureProcessor(ctx.state, ctx.config);
|
|
1369
1640
|
return String(await processor.process(md));
|
|
1370
1641
|
},
|
|
1642
|
+
/**
|
|
1643
|
+
* Mark file paths stale for incremental dev rebuilds. Each non-blank path is
|
|
1644
|
+
* added to `dirtyPaths` and its derived slug cache entry is dropped so the
|
|
1645
|
+
* next `loadAll()` re-reads only those files. Empty/whitespace paths are
|
|
1646
|
+
* ignored. Emits `content:invalidated` with the accepted paths.
|
|
1647
|
+
*
|
|
1648
|
+
* @param paths - File paths to invalidate.
|
|
1649
|
+
* @example
|
|
1650
|
+
* ```ts
|
|
1651
|
+
* api.invalidate(["src/content/intro/en.md"]);
|
|
1652
|
+
* ```
|
|
1653
|
+
*/
|
|
1371
1654
|
invalidate(paths) {
|
|
1372
1655
|
const accepted = [];
|
|
1373
1656
|
for (const path of paths) {
|
|
@@ -1380,12 +1663,22 @@ function createContentApi(ctx) {
|
|
|
1380
1663
|
ctx.state.slugs = null;
|
|
1381
1664
|
ctx.emit("content:invalidated", { paths: accepted });
|
|
1382
1665
|
},
|
|
1666
|
+
/**
|
|
1667
|
+
* Project a full Article to a lightweight ArticleCard for list/grid
|
|
1668
|
+
* rendering without shipping rendered HTML.
|
|
1669
|
+
*
|
|
1670
|
+
* @param article - The source article.
|
|
1671
|
+
* @returns The card projection.
|
|
1672
|
+
* @example
|
|
1673
|
+
* ```ts
|
|
1674
|
+
* const card = api.articleToCard(article);
|
|
1675
|
+
* ```
|
|
1676
|
+
*/
|
|
1383
1677
|
articleToCard(article) {
|
|
1384
1678
|
return toCard(article);
|
|
1385
1679
|
}
|
|
1386
1680
|
};
|
|
1387
1681
|
}
|
|
1388
|
-
|
|
1389
1682
|
//#endregion
|
|
1390
1683
|
//#region src/plugins/content/config.ts
|
|
1391
1684
|
/**
|
|
@@ -1406,7 +1699,6 @@ const defaultContentConfig = {
|
|
|
1406
1699
|
extraRehypePlugins: [],
|
|
1407
1700
|
shikiTheme: "github-dark"
|
|
1408
1701
|
};
|
|
1409
|
-
|
|
1410
1702
|
//#endregion
|
|
1411
1703
|
//#region src/plugins/content/events.ts
|
|
1412
1704
|
/**
|
|
@@ -1425,7 +1717,6 @@ const contentEvents = (register) => ({
|
|
|
1425
1717
|
"content:ready": register("All articles loaded across locales"),
|
|
1426
1718
|
"content:invalidated": register("Article paths marked stale for dev rebuild")
|
|
1427
1719
|
});
|
|
1428
|
-
|
|
1429
1720
|
//#endregion
|
|
1430
1721
|
//#region src/plugins/content/state.ts
|
|
1431
1722
|
/**
|
|
@@ -1449,7 +1740,6 @@ function createContentState(_ctx) {
|
|
|
1449
1740
|
dirtyPaths: /* @__PURE__ */ new Set()
|
|
1450
1741
|
};
|
|
1451
1742
|
}
|
|
1452
|
-
|
|
1453
1743
|
//#endregion
|
|
1454
1744
|
//#region src/plugins/content/validate.ts
|
|
1455
1745
|
/**
|
|
@@ -1468,7 +1758,6 @@ function validateContentConfig(config) {
|
|
|
1468
1758
|
if (typeof config.contentDir !== "string" || config.contentDir.trim() === "") throw new Error("[web] content.contentDir is required.\n Set pluginConfigs.content.contentDir to your content directory.");
|
|
1469
1759
|
if (typeof config.trustedContent !== "boolean") throw new TypeError("[web] content.trustedContent must be a boolean.");
|
|
1470
1760
|
}
|
|
1471
|
-
|
|
1472
1761
|
//#endregion
|
|
1473
1762
|
//#region src/plugins/content/index.ts
|
|
1474
1763
|
/**
|
|
@@ -1479,6 +1768,26 @@ function validateContentConfig(config) {
|
|
|
1479
1768
|
* and `content:invalidated`.
|
|
1480
1769
|
* @see README.md
|
|
1481
1770
|
*/
|
|
1771
|
+
/**
|
|
1772
|
+
* Content plugin — Markdown pipeline: discovers files, parses frontmatter, renders
|
|
1773
|
+
* to sanitized HTML (rehype-sanitize unless `trustedContent`), and exposes a
|
|
1774
|
+
* locale-keyed Article model. Depends on i18n; emits `content:ready` and
|
|
1775
|
+
* `content:invalidated`.
|
|
1776
|
+
*
|
|
1777
|
+
* @example Point at a content directory and pick a Shiki theme
|
|
1778
|
+
* ```ts
|
|
1779
|
+
* const app = createApp({
|
|
1780
|
+
* pluginConfigs: {
|
|
1781
|
+
* content: {
|
|
1782
|
+
* contentDir: "./content",
|
|
1783
|
+
* shikiTheme: "github-dark",
|
|
1784
|
+
* defaultAuthor: "Ada Lovelace"
|
|
1785
|
+
* // trustedContent: true // ONLY for fully author-controlled Markdown — disables sanitize
|
|
1786
|
+
* }
|
|
1787
|
+
* }
|
|
1788
|
+
* });
|
|
1789
|
+
* ```
|
|
1790
|
+
*/
|
|
1482
1791
|
const contentPlugin = createPlugin$1("content", {
|
|
1483
1792
|
depends: [i18nPlugin],
|
|
1484
1793
|
events: contentEvents,
|
|
@@ -1487,7 +1796,6 @@ const contentPlugin = createPlugin$1("content", {
|
|
|
1487
1796
|
onInit: (ctx) => validateContentConfig(ctx.config),
|
|
1488
1797
|
api: contentApi
|
|
1489
1798
|
});
|
|
1490
|
-
|
|
1491
1799
|
//#endregion
|
|
1492
1800
|
//#region src/plugins/site/api.ts
|
|
1493
1801
|
/** Error prefix for all site lifecycle/validation failures. */
|
|
@@ -1579,50 +1887,99 @@ function validateSiteConfig(ctx) {
|
|
|
1579
1887
|
function createSiteApi(ctx) {
|
|
1580
1888
|
const { config } = ctx;
|
|
1581
1889
|
return {
|
|
1890
|
+
/**
|
|
1891
|
+
* Returns the configured site name.
|
|
1892
|
+
*
|
|
1893
|
+
* @returns The human-readable site name from `config.name`.
|
|
1894
|
+
* @example
|
|
1895
|
+
* ```ts
|
|
1896
|
+
* api.name(); // "My Blog"
|
|
1897
|
+
* ```
|
|
1898
|
+
*/
|
|
1582
1899
|
name() {
|
|
1583
1900
|
return config.name;
|
|
1584
1901
|
},
|
|
1902
|
+
/**
|
|
1903
|
+
* Returns the configured absolute base URL of the site.
|
|
1904
|
+
*
|
|
1905
|
+
* @returns The base URL from `config.url`.
|
|
1906
|
+
* @example
|
|
1907
|
+
* ```ts
|
|
1908
|
+
* api.url(); // "https://blog.dev"
|
|
1909
|
+
* ```
|
|
1910
|
+
*/
|
|
1585
1911
|
url() {
|
|
1586
1912
|
return config.url;
|
|
1587
1913
|
},
|
|
1914
|
+
/**
|
|
1915
|
+
* Returns the configured site author/byline.
|
|
1916
|
+
*
|
|
1917
|
+
* @returns The author from `config.author`.
|
|
1918
|
+
* @example
|
|
1919
|
+
* ```ts
|
|
1920
|
+
* api.author(); // "Alex"
|
|
1921
|
+
* ```
|
|
1922
|
+
*/
|
|
1588
1923
|
author() {
|
|
1589
1924
|
return config.author;
|
|
1590
1925
|
},
|
|
1926
|
+
/**
|
|
1927
|
+
* Returns the configured site description.
|
|
1928
|
+
*
|
|
1929
|
+
* @returns The description from `config.description`.
|
|
1930
|
+
* @example
|
|
1931
|
+
* ```ts
|
|
1932
|
+
* api.description(); // "A personal blog about web frameworks."
|
|
1933
|
+
* ```
|
|
1934
|
+
*/
|
|
1591
1935
|
description() {
|
|
1592
1936
|
return config.description;
|
|
1593
1937
|
},
|
|
1938
|
+
/**
|
|
1939
|
+
* Joins a path against the configured base `url` to produce an absolute
|
|
1940
|
+
* canonical URL. An empty path (or "/") returns the base URL unchanged.
|
|
1941
|
+
*
|
|
1942
|
+
* @param path - Relative path for the page, e.g. "/about/".
|
|
1943
|
+
* @returns The absolute canonical URL.
|
|
1944
|
+
* @example
|
|
1945
|
+
* ```ts
|
|
1946
|
+
* api.canonical("/about/"); // "https://blog.dev/about/"
|
|
1947
|
+
* ```
|
|
1948
|
+
*/
|
|
1594
1949
|
canonical(path) {
|
|
1595
1950
|
return joinCanonical(config.url, path);
|
|
1596
1951
|
}
|
|
1597
1952
|
};
|
|
1598
1953
|
}
|
|
1599
|
-
|
|
1600
|
-
//#endregion
|
|
1601
|
-
//#region src/plugins/site/index.ts
|
|
1602
1954
|
/**
|
|
1603
|
-
*
|
|
1604
|
-
*
|
|
1605
|
-
*
|
|
1606
|
-
* Holds global, frozen site metadata (name, url, author, description) and
|
|
1607
|
-
* constructs canonical URLs. Consumed by router/head/build via
|
|
1608
|
-
* `ctx.require(sitePlugin)`. No events, no dependencies, no state.
|
|
1955
|
+
* Site plugin — holds global, frozen site metadata (name, url, author,
|
|
1956
|
+
* description) and builds canonical URLs. Consumed by router, head, and build.
|
|
1957
|
+
* `name` and `url` must be non-empty (validated at `onInit`).
|
|
1609
1958
|
*
|
|
1610
|
-
* @
|
|
1611
|
-
*
|
|
1959
|
+
* @example Set your site identity
|
|
1960
|
+
* ```ts
|
|
1961
|
+
* const app = createApp({
|
|
1962
|
+
* pluginConfigs: {
|
|
1963
|
+
* site: {
|
|
1964
|
+
* name: "My Blog",
|
|
1965
|
+
* url: "https://blog.dev",
|
|
1966
|
+
* author: "Ada Lovelace",
|
|
1967
|
+
* description: "Notes on computing"
|
|
1968
|
+
* }
|
|
1969
|
+
* }
|
|
1970
|
+
* });
|
|
1971
|
+
* ```
|
|
1612
1972
|
*/
|
|
1613
|
-
/** Typed default config (R6: no inline `as`). Consumers override via `pluginConfigs.site`. */
|
|
1614
|
-
const defaultConfig$4 = {
|
|
1615
|
-
name: "",
|
|
1616
|
-
url: "",
|
|
1617
|
-
author: "",
|
|
1618
|
-
description: ""
|
|
1619
|
-
};
|
|
1620
1973
|
const sitePlugin = createPlugin$1("site", {
|
|
1621
|
-
config:
|
|
1974
|
+
config: {
|
|
1975
|
+
name: "",
|
|
1976
|
+
url: "",
|
|
1977
|
+
author: "",
|
|
1978
|
+
description: ""
|
|
1979
|
+
},
|
|
1622
1980
|
onInit: validateSiteConfig,
|
|
1623
1981
|
api: createSiteApi
|
|
1624
1982
|
});
|
|
1625
|
-
|
|
1626
1983
|
//#endregion
|
|
1627
1984
|
//#region src/plugins/router/builders/match.ts
|
|
1628
1985
|
/**
|
|
@@ -1692,7 +2049,6 @@ function matchRoute(compiled, pathname) {
|
|
|
1692
2049
|
}
|
|
1693
2050
|
return null;
|
|
1694
2051
|
}
|
|
1695
|
-
|
|
1696
2052
|
//#endregion
|
|
1697
2053
|
//#region src/plugins/router/api.ts
|
|
1698
2054
|
/**
|
|
@@ -1755,23 +2111,63 @@ function toTypedRoute(entry) {
|
|
|
1755
2111
|
function createApi$4(ctx) {
|
|
1756
2112
|
const { state } = ctx;
|
|
1757
2113
|
return {
|
|
2114
|
+
/**
|
|
2115
|
+
* Match a pathname against the compiled route table (specificity-sorted).
|
|
2116
|
+
*
|
|
2117
|
+
* @param pathname - URL pathname, e.g. `/en/hello/`.
|
|
2118
|
+
* @returns `{ params, route }` for the most specific match, or `null`.
|
|
2119
|
+
* @example
|
|
2120
|
+
* ```ts
|
|
2121
|
+
* api.match("/en/hello/");
|
|
2122
|
+
* ```
|
|
2123
|
+
*/
|
|
1758
2124
|
match(pathname) {
|
|
1759
2125
|
return matchRoute(readTable(state).compiled, pathname);
|
|
1760
2126
|
},
|
|
2127
|
+
/**
|
|
2128
|
+
* Build a URL for a named route from params.
|
|
2129
|
+
*
|
|
2130
|
+
* @param routeName - Route name key from the route map.
|
|
2131
|
+
* @param params - Param values to substitute into the pattern.
|
|
2132
|
+
* @returns The resolved URL string (e.g. `/en/hello/`).
|
|
2133
|
+
* @throws {Error} If `routeName` is unknown.
|
|
2134
|
+
* @example
|
|
2135
|
+
* ```ts
|
|
2136
|
+
* api.toUrl("article", { lang: "en", slug: "hello" });
|
|
2137
|
+
* ```
|
|
2138
|
+
*/
|
|
1761
2139
|
toUrl(routeName, params) {
|
|
1762
2140
|
const entry = readTable(state).byName.get(routeName);
|
|
1763
2141
|
if (!entry) throw new Error(`${ERROR_PREFIX$9}: unknown route name "${routeName}".`);
|
|
1764
2142
|
return entry.toUrl(params);
|
|
1765
2143
|
},
|
|
2144
|
+
/**
|
|
2145
|
+
* All resolved routes as typed URL utilities, in specificity order.
|
|
2146
|
+
*
|
|
2147
|
+
* @returns A fresh read-only array of resolved typed routes.
|
|
2148
|
+
* @example
|
|
2149
|
+
* ```ts
|
|
2150
|
+
* for (const r of api.entries()) r.toUrl({ slug: "x" });
|
|
2151
|
+
* ```
|
|
2152
|
+
*/
|
|
1766
2153
|
entries() {
|
|
1767
2154
|
return readTable(state).compiled.map((entry) => toTypedRoute(entry));
|
|
1768
2155
|
},
|
|
2156
|
+
/**
|
|
2157
|
+
* The typed route set for build-time consumption (declaration order). An API
|
|
2158
|
+
* return, NOT a config readback — preserves per-route types despite erasure.
|
|
2159
|
+
*
|
|
2160
|
+
* @returns A fresh read-only array of the typed route definitions.
|
|
2161
|
+
* @example
|
|
2162
|
+
* ```ts
|
|
2163
|
+
* for (const def of api.manifest()) def._handlers.load?.({}, "en");
|
|
2164
|
+
* ```
|
|
2165
|
+
*/
|
|
1769
2166
|
manifest() {
|
|
1770
2167
|
return [...readTable(state).byName.values()].map((entry) => entry.definition);
|
|
1771
2168
|
}
|
|
1772
2169
|
};
|
|
1773
2170
|
}
|
|
1774
|
-
|
|
1775
2171
|
//#endregion
|
|
1776
2172
|
//#region src/plugins/router/builders/compile.ts
|
|
1777
2173
|
/** Shared `[web]` error prefix for router validation failures. */
|
|
@@ -1933,9 +2329,31 @@ function compileRoute(name, definition, input) {
|
|
|
1933
2329
|
dynamicSegmentCount: countDynamicSegments(pattern),
|
|
1934
2330
|
matchers,
|
|
1935
2331
|
matchFn: createMatchFunction(matchers, input.defaultLocale),
|
|
2332
|
+
/**
|
|
2333
|
+
* Build a URL for this route from params.
|
|
2334
|
+
*
|
|
2335
|
+
* @param params - Param values to substitute.
|
|
2336
|
+
* @returns The resolved relative URL.
|
|
2337
|
+
* @example
|
|
2338
|
+
* ```ts
|
|
2339
|
+
* entry.toUrl({ slug: "x" });
|
|
2340
|
+
* ```
|
|
2341
|
+
*/
|
|
1936
2342
|
toUrl(params) {
|
|
1937
2343
|
return buildUrl(pattern, params, input.baseUrl);
|
|
1938
2344
|
},
|
|
2345
|
+
/**
|
|
2346
|
+
* Build the output file path for this route from params. Honors a custom
|
|
2347
|
+
* `.toFile()` override (captured in `_handlers.toFile`) when present, falling
|
|
2348
|
+
* back to the pattern-derived `…/index.html` path otherwise.
|
|
2349
|
+
*
|
|
2350
|
+
* @param params - Param values to substitute.
|
|
2351
|
+
* @returns The output file path.
|
|
2352
|
+
* @example
|
|
2353
|
+
* ```ts
|
|
2354
|
+
* entry.toFile({ slug: "x" });
|
|
2355
|
+
* ```
|
|
2356
|
+
*/
|
|
1939
2357
|
toFile(params) {
|
|
1940
2358
|
return definition._handlers.toFile?.(params) ?? buildFilePath(pattern, params);
|
|
1941
2359
|
},
|
|
@@ -1996,7 +2414,6 @@ function buildRouterTable(config, baseUrl, locales, defaultLocale) {
|
|
|
1996
2414
|
defaultLocale
|
|
1997
2415
|
});
|
|
1998
2416
|
}
|
|
1999
|
-
|
|
2000
2417
|
//#endregion
|
|
2001
2418
|
//#region src/plugins/router/builders/route-builder.ts
|
|
2002
2419
|
/**
|
|
@@ -2042,28 +2459,108 @@ function route(pattern) {
|
|
|
2042
2459
|
pattern: carrier.pattern,
|
|
2043
2460
|
_meta: carrier._meta,
|
|
2044
2461
|
_handlers: carrier._handlers,
|
|
2462
|
+
/**
|
|
2463
|
+
* Attach a data loader; widens the data generic for downstream handlers.
|
|
2464
|
+
*
|
|
2465
|
+
* @param loader - The loader producing this route's data.
|
|
2466
|
+
* @returns The same builder, with the data generic widened.
|
|
2467
|
+
* @example
|
|
2468
|
+
* ```ts
|
|
2469
|
+
* route("/{slug}/").load(({ slug }) => ({ slug }));
|
|
2470
|
+
* ```
|
|
2471
|
+
*/
|
|
2045
2472
|
load(loader) {
|
|
2046
2473
|
return set("load", loader);
|
|
2047
2474
|
},
|
|
2475
|
+
/**
|
|
2476
|
+
* Attach a layout wrapper component.
|
|
2477
|
+
*
|
|
2478
|
+
* @param component - The layout component.
|
|
2479
|
+
* @returns The same builder for chaining.
|
|
2480
|
+
* @example
|
|
2481
|
+
* ```ts
|
|
2482
|
+
* route("/").layout((children) => children);
|
|
2483
|
+
* ```
|
|
2484
|
+
*/
|
|
2048
2485
|
layout(component) {
|
|
2049
2486
|
return set("layout", component);
|
|
2050
2487
|
},
|
|
2488
|
+
/**
|
|
2489
|
+
* Attach the page render handler.
|
|
2490
|
+
*
|
|
2491
|
+
* @param handler - The render handler.
|
|
2492
|
+
* @returns The same builder for chaining.
|
|
2493
|
+
* @example
|
|
2494
|
+
* ```ts
|
|
2495
|
+
* route("/").render(() => null);
|
|
2496
|
+
* ```
|
|
2497
|
+
*/
|
|
2051
2498
|
render(handler) {
|
|
2052
2499
|
return set("render", handler);
|
|
2053
2500
|
},
|
|
2501
|
+
/**
|
|
2502
|
+
* Attach the head/SEO handler.
|
|
2503
|
+
*
|
|
2504
|
+
* @param handler - The head handler.
|
|
2505
|
+
* @returns The same builder for chaining.
|
|
2506
|
+
* @example
|
|
2507
|
+
* ```ts
|
|
2508
|
+
* route("/").head(() => ({ title: "Home" }));
|
|
2509
|
+
* ```
|
|
2510
|
+
*/
|
|
2054
2511
|
head(handler) {
|
|
2055
2512
|
return set("head", handler);
|
|
2056
2513
|
},
|
|
2514
|
+
/**
|
|
2515
|
+
* Attach a static-generation param producer.
|
|
2516
|
+
*
|
|
2517
|
+
* @param handler - The param producer.
|
|
2518
|
+
* @returns The same builder for chaining.
|
|
2519
|
+
* @example
|
|
2520
|
+
* ```ts
|
|
2521
|
+
* route("/{slug}/").generate(() => [{ slug: "x" }]);
|
|
2522
|
+
* ```
|
|
2523
|
+
*/
|
|
2057
2524
|
generate(handler) {
|
|
2058
2525
|
return set("generate", handler);
|
|
2059
2526
|
},
|
|
2527
|
+
/**
|
|
2528
|
+
* Merge an arbitrary metadata bag into the route's `_meta`.
|
|
2529
|
+
*
|
|
2530
|
+
* @param meta - Metadata to merge.
|
|
2531
|
+
* @returns The same builder for chaining.
|
|
2532
|
+
* @example
|
|
2533
|
+
* ```ts
|
|
2534
|
+
* route("/").meta({ activeTab: "home" });
|
|
2535
|
+
* ```
|
|
2536
|
+
*/
|
|
2060
2537
|
meta(meta) {
|
|
2061
2538
|
Object.assign(carrier._meta, meta);
|
|
2062
2539
|
return builder;
|
|
2063
2540
|
},
|
|
2541
|
+
/**
|
|
2542
|
+
* Attach a JSON serializer for the route's data.
|
|
2543
|
+
*
|
|
2544
|
+
* @param handler - The JSON serializer.
|
|
2545
|
+
* @returns The same builder for chaining.
|
|
2546
|
+
* @example
|
|
2547
|
+
* ```ts
|
|
2548
|
+
* route("/api/").toJson(() => ({ ok: true }));
|
|
2549
|
+
* ```
|
|
2550
|
+
*/
|
|
2064
2551
|
toJson(handler) {
|
|
2065
2552
|
return set("toJson", handler);
|
|
2066
2553
|
},
|
|
2554
|
+
/**
|
|
2555
|
+
* Override the output file-path producer.
|
|
2556
|
+
*
|
|
2557
|
+
* @param handler - The file-path producer.
|
|
2558
|
+
* @returns The same builder for chaining.
|
|
2559
|
+
* @example
|
|
2560
|
+
* ```ts
|
|
2561
|
+
* route("/feed/").toFile(() => "feed.xml");
|
|
2562
|
+
* ```
|
|
2563
|
+
*/
|
|
2067
2564
|
toFile(handler) {
|
|
2068
2565
|
return set("toFile", handler);
|
|
2069
2566
|
}
|
|
@@ -2084,7 +2581,6 @@ function route(pattern) {
|
|
|
2084
2581
|
function defineRoutes(routes) {
|
|
2085
2582
|
return routes;
|
|
2086
2583
|
}
|
|
2087
|
-
|
|
2088
2584
|
//#endregion
|
|
2089
2585
|
//#region src/plugins/router/state.ts
|
|
2090
2586
|
/**
|
|
@@ -2103,25 +2599,36 @@ function defineRoutes(routes) {
|
|
|
2103
2599
|
function createState$4(_ctx) {
|
|
2104
2600
|
return { table: null };
|
|
2105
2601
|
}
|
|
2106
|
-
|
|
2107
|
-
//#endregion
|
|
2108
|
-
//#region src/plugins/router/index.ts
|
|
2109
2602
|
/**
|
|
2110
|
-
*
|
|
2111
|
-
* @
|
|
2603
|
+
* Router plugin — typed, named route definitions with locale-aware URL generation
|
|
2604
|
+
* and matching. Author routes with {@link route} + {@link defineRoutes}. Depends
|
|
2605
|
+
* on site (base URL) and i18n (locales).
|
|
2606
|
+
*
|
|
2607
|
+
* @example Define routes and choose a render mode
|
|
2608
|
+
* ```ts
|
|
2609
|
+
* const app = createApp({
|
|
2610
|
+
* pluginConfigs: {
|
|
2611
|
+
* router: {
|
|
2612
|
+
* routes: defineRoutes({
|
|
2613
|
+
* home: route("/"),
|
|
2614
|
+
* article: route("/blog/{slug}/")
|
|
2615
|
+
* }),
|
|
2616
|
+
* mode: "hybrid" // "ssg" | "spa" | "hybrid" (default)
|
|
2617
|
+
* }
|
|
2618
|
+
* }
|
|
2619
|
+
* });
|
|
2620
|
+
* ```
|
|
2112
2621
|
*/
|
|
2113
|
-
/** Default router config: empty route map (validated in onInit), hybrid mode. */
|
|
2114
|
-
const defaultConfig$3 = {
|
|
2115
|
-
routes: {},
|
|
2116
|
-
mode: "hybrid"
|
|
2117
|
-
};
|
|
2118
2622
|
const routerPlugin = createPlugin$1("router", {
|
|
2119
2623
|
depends: [sitePlugin, i18nPlugin],
|
|
2120
2624
|
helpers: {
|
|
2121
2625
|
route,
|
|
2122
2626
|
defineRoutes
|
|
2123
2627
|
},
|
|
2124
|
-
config:
|
|
2628
|
+
config: {
|
|
2629
|
+
routes: {},
|
|
2630
|
+
mode: "hybrid"
|
|
2631
|
+
},
|
|
2125
2632
|
createState: createState$4,
|
|
2126
2633
|
api: createApi$4,
|
|
2127
2634
|
onInit(ctx) {
|
|
@@ -2130,7 +2637,6 @@ const routerPlugin = createPlugin$1("router", {
|
|
|
2130
2637
|
ctx.state.table = buildRouterTable(ctx.config, baseUrl, i18n.locales(), i18n.defaultLocale());
|
|
2131
2638
|
}
|
|
2132
2639
|
});
|
|
2133
|
-
|
|
2134
2640
|
//#endregion
|
|
2135
2641
|
//#region src/plugins/head/primitives.ts
|
|
2136
2642
|
/** OG/Twitter article-meta property prefixes (factored to satisfy no-duplicate-string). */
|
|
@@ -2294,7 +2800,6 @@ function buildArticleHead(articleMeta, canonicalUrl) {
|
|
|
2294
2800
|
elements.push(jsonLd(ld));
|
|
2295
2801
|
return elements;
|
|
2296
2802
|
}
|
|
2297
|
-
|
|
2298
2803
|
//#endregion
|
|
2299
2804
|
//#region src/plugins/head/compose.ts
|
|
2300
2805
|
/**
|
|
@@ -2455,7 +2960,6 @@ function serializeElement(element) {
|
|
|
2455
2960
|
function serializeHead(elements) {
|
|
2456
2961
|
return elements.map((element) => serializeElement(element)).join("");
|
|
2457
2962
|
}
|
|
2458
|
-
|
|
2459
2963
|
//#endregion
|
|
2460
2964
|
//#region src/plugins/head/api.ts
|
|
2461
2965
|
/**
|
|
@@ -2497,7 +3001,19 @@ function readDefaults(state) {
|
|
|
2497
3001
|
* ```
|
|
2498
3002
|
*/
|
|
2499
3003
|
function createApi$3(ctx) {
|
|
2500
|
-
return {
|
|
3004
|
+
return {
|
|
3005
|
+
/**
|
|
3006
|
+
* Compose the final `<head>` inner HTML for a route (pulled by `build`).
|
|
3007
|
+
*
|
|
3008
|
+
* @param route - The resolved route descriptor (incl. its `.head()` HeadConfig).
|
|
3009
|
+
* @param data - The page data object passed to the route's loader/render.
|
|
3010
|
+
* @returns The serialized inner HTML of `<head>`.
|
|
3011
|
+
* @example
|
|
3012
|
+
* ```ts
|
|
3013
|
+
* api.render(route, { title: "Post" });
|
|
3014
|
+
* ```
|
|
3015
|
+
*/
|
|
3016
|
+
render(route, data) {
|
|
2501
3017
|
return serializeHead(composeHead({
|
|
2502
3018
|
route,
|
|
2503
3019
|
data,
|
|
@@ -2508,7 +3024,6 @@ function createApi$3(ctx) {
|
|
|
2508
3024
|
}));
|
|
2509
3025
|
} };
|
|
2510
3026
|
}
|
|
2511
|
-
|
|
2512
3027
|
//#endregion
|
|
2513
3028
|
//#region src/plugins/head/config.ts
|
|
2514
3029
|
/** Error prefix for all head config-validation failures. */
|
|
@@ -2563,7 +3078,6 @@ function normalizeHeadConfig(config) {
|
|
|
2563
3078
|
if (config.twitterHandle !== void 0) defaults.twitterHandle = config.twitterHandle;
|
|
2564
3079
|
return Object.freeze(defaults);
|
|
2565
3080
|
}
|
|
2566
|
-
|
|
2567
3081
|
//#endregion
|
|
2568
3082
|
//#region src/plugins/head/helpers.ts
|
|
2569
3083
|
/**
|
|
@@ -2592,7 +3106,6 @@ const headHelpers = {
|
|
|
2592
3106
|
feedLink,
|
|
2593
3107
|
buildArticleHead
|
|
2594
3108
|
};
|
|
2595
|
-
|
|
2596
3109
|
//#endregion
|
|
2597
3110
|
//#region src/plugins/head/state.ts
|
|
2598
3111
|
/**
|
|
@@ -2613,13 +3126,31 @@ const headHelpers = {
|
|
|
2613
3126
|
function createState$3(_ctx) {
|
|
2614
3127
|
return { defaults: null };
|
|
2615
3128
|
}
|
|
2616
|
-
|
|
2617
3129
|
//#endregion
|
|
2618
3130
|
//#region src/plugins/head/index.ts
|
|
2619
3131
|
/**
|
|
2620
3132
|
* @file head — Standard Plugin wiring harness (logic in primitives/compose/api/config).
|
|
2621
3133
|
* @see README.md
|
|
2622
3134
|
*/
|
|
3135
|
+
/**
|
|
3136
|
+
* Head plugin — composes per-route `<head>` metadata (title template, Open Graph,
|
|
3137
|
+
* Twitter cards, canonical, hreflang). Use the re-exported SEO primitives
|
|
3138
|
+
* ({@link meta}, {@link og}, {@link twitter}, …) inside a route's `.head()`.
|
|
3139
|
+
* Depends on site, i18n, and router.
|
|
3140
|
+
*
|
|
3141
|
+
* @example Set global head defaults
|
|
3142
|
+
* ```ts
|
|
3143
|
+
* const app = createApp({
|
|
3144
|
+
* pluginConfigs: {
|
|
3145
|
+
* head: {
|
|
3146
|
+
* titleTemplate: "%s — My Blog",
|
|
3147
|
+
* twitterCard: "summary_large_image",
|
|
3148
|
+
* twitterHandle: "@moku_labs"
|
|
3149
|
+
* }
|
|
3150
|
+
* }
|
|
3151
|
+
* });
|
|
3152
|
+
* ```
|
|
3153
|
+
*/
|
|
2623
3154
|
const headPlugin = createPlugin$1("head", {
|
|
2624
3155
|
depends: [
|
|
2625
3156
|
sitePlugin,
|
|
@@ -2634,7 +3165,6 @@ const headPlugin = createPlugin$1("head", {
|
|
|
2634
3165
|
ctx.state.defaults = normalizeHeadConfig(ctx.config);
|
|
2635
3166
|
}
|
|
2636
3167
|
});
|
|
2637
|
-
|
|
2638
3168
|
//#endregion
|
|
2639
3169
|
//#region src/plugins/build/phases/bundle.ts
|
|
2640
3170
|
/**
|
|
@@ -2736,7 +3266,6 @@ async function bundle(ctx, options = {}) {
|
|
|
2736
3266
|
await runOne(ctx, runner, "css", cssEntrypoints, path.join(outDir, "assets"), minify);
|
|
2737
3267
|
await runOne(ctx, runner, "js", jsEntrypoints, path.join(outDir, "assets"), minify);
|
|
2738
3268
|
}
|
|
2739
|
-
|
|
2740
3269
|
//#endregion
|
|
2741
3270
|
//#region src/plugins/build/phases/content.ts
|
|
2742
3271
|
/**
|
|
@@ -2779,7 +3308,6 @@ function readCachedContent(ctx) {
|
|
|
2779
3308
|
const cached = ctx.state.buildCache.get(CONTENT_CACHE_KEY);
|
|
2780
3309
|
return cached instanceof Map ? cached : /* @__PURE__ */ new Map();
|
|
2781
3310
|
}
|
|
2782
|
-
|
|
2783
3311
|
//#endregion
|
|
2784
3312
|
//#region src/plugins/build/phases/feeds.ts
|
|
2785
3313
|
/**
|
|
@@ -2861,7 +3389,6 @@ async function generateFeeds(ctx) {
|
|
|
2861
3389
|
ctx.log.debug("build:feeds", { items: guids.length });
|
|
2862
3390
|
return result;
|
|
2863
3391
|
}
|
|
2864
|
-
|
|
2865
3392
|
//#endregion
|
|
2866
3393
|
//#region src/plugins/build/phases/images.ts
|
|
2867
3394
|
/**
|
|
@@ -2901,7 +3428,6 @@ async function processImages(ctx, options = {}) {
|
|
|
2901
3428
|
ctx.log.debug("build:images", { copied });
|
|
2902
3429
|
return copied;
|
|
2903
3430
|
}
|
|
2904
|
-
|
|
2905
3431
|
//#endregion
|
|
2906
3432
|
//#region src/plugins/build/phases/og-images.tsx
|
|
2907
3433
|
/**
|
|
@@ -2915,8 +3441,6 @@ const DEFAULT_SIZE = {
|
|
|
2915
3441
|
width: 1200,
|
|
2916
3442
|
height: 630
|
|
2917
3443
|
};
|
|
2918
|
-
/** The fixed concurrency bound for the OG render pool. */
|
|
2919
|
-
const OG_CONCURRENCY = 4;
|
|
2920
3444
|
/** Recognized font file extensions. */
|
|
2921
3445
|
const FONT_EXTENSIONS$1 = [
|
|
2922
3446
|
".ttf",
|
|
@@ -3037,7 +3561,7 @@ async function generateOgImages(ctx, options = {}) {
|
|
|
3037
3561
|
const articles = selectArticles(readCachedContent(ctx));
|
|
3038
3562
|
const cache = ctx.state.ogImageHashCache;
|
|
3039
3563
|
await loadDiskCache(ctx.config.outDir, cache);
|
|
3040
|
-
const limit = pLimit(
|
|
3564
|
+
const limit = pLimit(4);
|
|
3041
3565
|
let active = 0;
|
|
3042
3566
|
let peakConcurrency = 0;
|
|
3043
3567
|
let rendered = 0;
|
|
@@ -3110,7 +3634,6 @@ async function persistDiskCache(outDir, cache) {
|
|
|
3110
3634
|
await mkdir(dir, { recursive: true });
|
|
3111
3635
|
await writeFile(path.join(dir, "og-images.json"), JSON.stringify(Object.fromEntries(cache)), "utf8");
|
|
3112
3636
|
}
|
|
3113
|
-
|
|
3114
3637
|
//#endregion
|
|
3115
3638
|
//#region src/plugins/build/phases/pages.tsx
|
|
3116
3639
|
/**
|
|
@@ -3278,7 +3801,6 @@ async function renderPages(ctx) {
|
|
|
3278
3801
|
rootHtml: root?.html ?? null
|
|
3279
3802
|
};
|
|
3280
3803
|
}
|
|
3281
|
-
|
|
3282
3804
|
//#endregion
|
|
3283
3805
|
//#region src/plugins/build/phases/sitemap.ts
|
|
3284
3806
|
/**
|
|
@@ -3360,7 +3882,6 @@ async function generateSitemap(ctx) {
|
|
|
3360
3882
|
robots
|
|
3361
3883
|
};
|
|
3362
3884
|
}
|
|
3363
|
-
|
|
3364
3885
|
//#endregion
|
|
3365
3886
|
//#region src/plugins/build/pipeline.ts
|
|
3366
3887
|
/**
|
|
@@ -3491,7 +4012,6 @@ async function runPipeline(ctx, options) {
|
|
|
3491
4012
|
phaseContext.emit("build:complete", result);
|
|
3492
4013
|
return result;
|
|
3493
4014
|
}
|
|
3494
|
-
|
|
3495
4015
|
//#endregion
|
|
3496
4016
|
//#region src/plugins/build/api.ts
|
|
3497
4017
|
/**
|
|
@@ -3530,9 +4050,29 @@ const defaultConfig$1 = {
|
|
|
3530
4050
|
*/
|
|
3531
4051
|
function createApi$2(ctx) {
|
|
3532
4052
|
return {
|
|
4053
|
+
/**
|
|
4054
|
+
* Run the full SSG pipeline and write the site to disk.
|
|
4055
|
+
*
|
|
4056
|
+
* @param options - Optional run overrides.
|
|
4057
|
+
* @param options.outDir - Override the configured output directory for this run.
|
|
4058
|
+
* @returns The build result (outDir, pageCount, durationMs).
|
|
4059
|
+
* @example
|
|
4060
|
+
* ```ts
|
|
4061
|
+
* await api.run({ outDir: "./preview" });
|
|
4062
|
+
* ```
|
|
4063
|
+
*/
|
|
3533
4064
|
run(options) {
|
|
3534
4065
|
return runPipeline(ctx, options);
|
|
3535
4066
|
},
|
|
4067
|
+
/**
|
|
4068
|
+
* List the phases in execution order (introspection / tooling).
|
|
4069
|
+
*
|
|
4070
|
+
* @returns A fresh array of the static ordered phase names.
|
|
4071
|
+
* @example
|
|
4072
|
+
* ```ts
|
|
4073
|
+
* api.phases();
|
|
4074
|
+
* ```
|
|
4075
|
+
*/
|
|
3536
4076
|
phases() {
|
|
3537
4077
|
return [...PHASE_ORDER];
|
|
3538
4078
|
}
|
|
@@ -3567,7 +4107,6 @@ function validateConfig$1(config) {
|
|
|
3567
4107
|
if (typeof config.outDir !== "string" || config.outDir.trim().length === 0) throw new Error(`${ERROR_PREFIX$5}.outDir: must be a non-empty string.`);
|
|
3568
4108
|
if (config.ogImage) validateFonts(config.ogImage);
|
|
3569
4109
|
}
|
|
3570
|
-
|
|
3571
4110
|
//#endregion
|
|
3572
4111
|
//#region src/plugins/build/events.ts
|
|
3573
4112
|
/**
|
|
@@ -3587,7 +4126,6 @@ function createEvents(register) {
|
|
|
3587
4126
|
"build:complete": register("Emitted once after a successful build run")
|
|
3588
4127
|
};
|
|
3589
4128
|
}
|
|
3590
|
-
|
|
3591
4129
|
//#endregion
|
|
3592
4130
|
//#region src/plugins/build/state.ts
|
|
3593
4131
|
/**
|
|
@@ -3614,13 +4152,33 @@ function createState$2(ctx) {
|
|
|
3614
4152
|
ogImageHashCache: /* @__PURE__ */ new Map()
|
|
3615
4153
|
};
|
|
3616
4154
|
}
|
|
3617
|
-
|
|
3618
4155
|
//#endregion
|
|
3619
4156
|
//#region src/plugins/build/index.ts
|
|
3620
4157
|
/**
|
|
3621
4158
|
* @file build — Complex plugin: SSG orchestrator (wiring harness only).
|
|
3622
4159
|
* @see README.md
|
|
3623
4160
|
*/
|
|
4161
|
+
/**
|
|
4162
|
+
* Build plugin — the static-site-generation orchestrator. Renders every route to
|
|
4163
|
+
* `outDir`, and optionally emits feeds, a sitemap, optimized images, and OG
|
|
4164
|
+
* images. Depends on site, i18n, content, router, and head; emits `build:phase`.
|
|
4165
|
+
*
|
|
4166
|
+
* @example Configure the production build
|
|
4167
|
+
* ```ts
|
|
4168
|
+
* const app = createApp({
|
|
4169
|
+
* pluginConfigs: {
|
|
4170
|
+
* build: {
|
|
4171
|
+
* outDir: "dist",
|
|
4172
|
+
* minify: true,
|
|
4173
|
+
* feeds: true,
|
|
4174
|
+
* sitemap: true,
|
|
4175
|
+
* images: true,
|
|
4176
|
+
* ogImage: false // or an object to enable + configure OG-image generation
|
|
4177
|
+
* }
|
|
4178
|
+
* }
|
|
4179
|
+
* });
|
|
4180
|
+
* ```
|
|
4181
|
+
*/
|
|
3624
4182
|
const buildPlugin = createPlugin$1("build", {
|
|
3625
4183
|
depends: [
|
|
3626
4184
|
sitePlugin,
|
|
@@ -3635,7 +4193,6 @@ const buildPlugin = createPlugin$1("build", {
|
|
|
3635
4193
|
api: createApi$2,
|
|
3636
4194
|
onInit: (ctx) => validateConfig$1(ctx.config)
|
|
3637
4195
|
});
|
|
3638
|
-
|
|
3639
4196
|
//#endregion
|
|
3640
4197
|
//#region src/plugins/deploy/wrangler.ts
|
|
3641
4198
|
/**
|
|
@@ -3813,8 +4370,6 @@ const ERROR_SIGNATURES = [
|
|
|
3813
4370
|
advice: "A network failure occurred. Check connectivity and retry."
|
|
3814
4371
|
}
|
|
3815
4372
|
];
|
|
3816
|
-
/** Number of trailing characters of scrubbed stderr to surface on an unknown failure. */
|
|
3817
|
-
const STDERR_TAIL_LENGTH = 500;
|
|
3818
4373
|
/**
|
|
3819
4374
|
* Map a non-zero wrangler exit and scrubbed stderr to an actionable error
|
|
3820
4375
|
* `code` + message. Matching is case-insensitive against the scrubbed stderr;
|
|
@@ -3835,7 +4390,7 @@ function classifyWranglerError(exitCode, scrubbedStderr) {
|
|
|
3835
4390
|
};
|
|
3836
4391
|
return {
|
|
3837
4392
|
code: "ERR_DEPLOY_WRANGLER_FAILED",
|
|
3838
|
-
message: `${ERROR_PREFIX$4}: wrangler failed (exit ${exitCode}).\n ${scrubbedStderr.trim().slice(-
|
|
4393
|
+
message: `${ERROR_PREFIX$4}: wrangler failed (exit ${exitCode}).\n ${scrubbedStderr.trim().slice(-500)}`
|
|
3839
4394
|
};
|
|
3840
4395
|
}
|
|
3841
4396
|
/**
|
|
@@ -3894,7 +4449,6 @@ async function runWrangler(input) {
|
|
|
3894
4449
|
exitCode
|
|
3895
4450
|
};
|
|
3896
4451
|
}
|
|
3897
|
-
|
|
3898
4452
|
//#endregion
|
|
3899
4453
|
//#region src/plugins/deploy/generators/github-workflow.ts
|
|
3900
4454
|
/**
|
|
@@ -3949,7 +4503,6 @@ jobs:
|
|
|
3949
4503
|
command: pages deploy dist --project-name ${input.slug}
|
|
3950
4504
|
`;
|
|
3951
4505
|
}
|
|
3952
|
-
|
|
3953
4506
|
//#endregion
|
|
3954
4507
|
//#region src/plugins/deploy/generators/wrangler-config.ts
|
|
3955
4508
|
/**
|
|
@@ -3993,7 +4546,6 @@ async function readWranglerConfig(cwd) {
|
|
|
3993
4546
|
return null;
|
|
3994
4547
|
}
|
|
3995
4548
|
}
|
|
3996
|
-
|
|
3997
4549
|
//#endregion
|
|
3998
4550
|
//#region src/plugins/deploy/init.ts
|
|
3999
4551
|
/**
|
|
@@ -4097,7 +4649,6 @@ async function reconcile(input) {
|
|
|
4097
4649
|
await writeFile(path.join(cwd, relativePath), expected, "utf8");
|
|
4098
4650
|
result.written.push(relativePath);
|
|
4099
4651
|
}
|
|
4100
|
-
|
|
4101
4652
|
//#endregion
|
|
4102
4653
|
//#region src/plugins/deploy/preflight.ts
|
|
4103
4654
|
/**
|
|
@@ -4191,7 +4742,6 @@ async function runPreflight(config, root, env = process.env) {
|
|
|
4191
4742
|
if (stats.fileCount > limit) throw deployError("ERR_DEPLOY_TOO_MANY_FILES", `${ERROR_PREFIX$3}: outDir contains ${stats.fileCount} files; the limit is ${limit}.\n Raise it with ${MAX_FILES_ENV} (paid tier) or reduce the output.`);
|
|
4192
4743
|
if (stats.oversizePath !== null) throw deployError("ERR_DEPLOY_FILE_TOO_LARGE", `${ERROR_PREFIX$3}: file ${JSON.stringify(stats.oversizePath)} exceeds the 25 MiB per-file limit.`);
|
|
4193
4744
|
}
|
|
4194
|
-
|
|
4195
4745
|
//#endregion
|
|
4196
4746
|
//#region src/plugins/deploy/slug.ts
|
|
4197
4747
|
/**
|
|
@@ -4232,7 +4782,6 @@ function toSlug(name) {
|
|
|
4232
4782
|
slug = slug.slice(0, end);
|
|
4233
4783
|
return slug.length > 0 ? slug : FALLBACK_SLUG;
|
|
4234
4784
|
}
|
|
4235
|
-
|
|
4236
4785
|
//#endregion
|
|
4237
4786
|
//#region src/plugins/deploy/api.ts
|
|
4238
4787
|
/** Error prefix for deploy config/validation failures (spec/11 Part-3). */
|
|
@@ -4272,6 +4821,15 @@ function validateConfig(ctx) {
|
|
|
4272
4821
|
*/
|
|
4273
4822
|
function createApi$1(ctx) {
|
|
4274
4823
|
return {
|
|
4824
|
+
/**
|
|
4825
|
+
* Deploy the built outDir to Cloudflare Pages via the wrangler subprocess.
|
|
4826
|
+
*
|
|
4827
|
+
* @param options - Optional branch override and build toggle.
|
|
4828
|
+
* @returns The deploy result (url, deploymentId, branch, durationMs).
|
|
4829
|
+
* @throws {Error} With a `code` from the deploy error taxonomy on any failure.
|
|
4830
|
+
* @example
|
|
4831
|
+
* await api.run();
|
|
4832
|
+
*/
|
|
4275
4833
|
async run(options = {}) {
|
|
4276
4834
|
const root = process.cwd();
|
|
4277
4835
|
const slug = toSlug(ctx.require(sitePlugin).name());
|
|
@@ -4311,10 +4869,25 @@ function createApi$1(ctx) {
|
|
|
4311
4869
|
});
|
|
4312
4870
|
return result;
|
|
4313
4871
|
},
|
|
4872
|
+
/**
|
|
4873
|
+
* Return the most recent successful deploy result, or null if none occurred.
|
|
4874
|
+
*
|
|
4875
|
+
* @returns A frozen snapshot of the last DeployResult, or null.
|
|
4876
|
+
* @example
|
|
4877
|
+
* const last = api.getLastDeployment();
|
|
4878
|
+
*/
|
|
4314
4879
|
getLastDeployment() {
|
|
4315
4880
|
const last = ctx.state.lastDeployment;
|
|
4316
4881
|
return last ? Object.freeze({ ...last }) : null;
|
|
4317
4882
|
},
|
|
4883
|
+
/**
|
|
4884
|
+
* Generate deploy scaffolding (wrangler.jsonc + optional GitHub workflow).
|
|
4885
|
+
*
|
|
4886
|
+
* @param options - Optional ci toggle and check (drift-only) mode.
|
|
4887
|
+
* @returns Which files were written, skipped, or would drift.
|
|
4888
|
+
* @example
|
|
4889
|
+
* await api.init({ ci: true });
|
|
4890
|
+
*/
|
|
4318
4891
|
async init(options = {}) {
|
|
4319
4892
|
const slug = toSlug(ctx.require(sitePlugin).name());
|
|
4320
4893
|
return writeScaffolding({
|
|
@@ -4326,7 +4899,6 @@ function createApi$1(ctx) {
|
|
|
4326
4899
|
}
|
|
4327
4900
|
};
|
|
4328
4901
|
}
|
|
4329
|
-
|
|
4330
4902
|
//#endregion
|
|
4331
4903
|
//#region src/plugins/deploy/defaults.ts
|
|
4332
4904
|
/**
|
|
@@ -4346,7 +4918,6 @@ const defaultConfig = {
|
|
|
4346
4918
|
compatibilityDate: "2024-01-01",
|
|
4347
4919
|
ci: false
|
|
4348
4920
|
};
|
|
4349
|
-
|
|
4350
4921
|
//#endregion
|
|
4351
4922
|
//#region src/plugins/deploy/events.ts
|
|
4352
4923
|
/**
|
|
@@ -4361,7 +4932,6 @@ const defaultConfig = {
|
|
|
4361
4932
|
* ```
|
|
4362
4933
|
*/
|
|
4363
4934
|
const deployEvents = (register) => ({ "deploy:complete": register("Deployment completed successfully") });
|
|
4364
|
-
|
|
4365
4935
|
//#endregion
|
|
4366
4936
|
//#region src/plugins/deploy/state.ts
|
|
4367
4937
|
/**
|
|
@@ -4399,7 +4969,6 @@ function createState$1(_ctx) {
|
|
|
4399
4969
|
spawn: defaultSpawn
|
|
4400
4970
|
};
|
|
4401
4971
|
}
|
|
4402
|
-
|
|
4403
4972
|
//#endregion
|
|
4404
4973
|
//#region src/plugins/deploy/index.ts
|
|
4405
4974
|
/**
|
|
@@ -4409,6 +4978,24 @@ function createState$1(_ctx) {
|
|
|
4409
4978
|
* Depends: site. Emits: deploy:complete.
|
|
4410
4979
|
* @see README.md
|
|
4411
4980
|
*/
|
|
4981
|
+
/**
|
|
4982
|
+
* Deploy plugin — ships the built `outDir` to Cloudflare Pages via the injectable
|
|
4983
|
+
* wrangler subprocess, with entropy-gated secret scrubbing of logged output.
|
|
4984
|
+
* Depends on site; emits `deploy:complete`.
|
|
4985
|
+
*
|
|
4986
|
+
* @example Configure the deploy target
|
|
4987
|
+
* ```ts
|
|
4988
|
+
* const app = createApp({
|
|
4989
|
+
* pluginConfigs: {
|
|
4990
|
+
* deploy: {
|
|
4991
|
+
* target: "cloudflare-pages",
|
|
4992
|
+
* outDir: "dist",
|
|
4993
|
+
* productionBranch: "main"
|
|
4994
|
+
* }
|
|
4995
|
+
* }
|
|
4996
|
+
* });
|
|
4997
|
+
* ```
|
|
4998
|
+
*/
|
|
4412
4999
|
const deployPlugin = createPlugin$1("deploy", {
|
|
4413
5000
|
config: defaultConfig,
|
|
4414
5001
|
depends: [sitePlugin],
|
|
@@ -4417,7 +5004,6 @@ const deployPlugin = createPlugin$1("deploy", {
|
|
|
4417
5004
|
onInit: validateConfig,
|
|
4418
5005
|
api: createApi$1
|
|
4419
5006
|
});
|
|
4420
|
-
|
|
4421
5007
|
//#endregion
|
|
4422
5008
|
//#region src/plugins/spa/api.ts
|
|
4423
5009
|
/**
|
|
@@ -4432,19 +5018,39 @@ const deployPlugin = createPlugin$1("deploy", {
|
|
|
4432
5018
|
*/
|
|
4433
5019
|
function createApi(ctx) {
|
|
4434
5020
|
return {
|
|
5021
|
+
/**
|
|
5022
|
+
* Register a component definition (last-registered-wins); warns on collision.
|
|
5023
|
+
*
|
|
5024
|
+
* @param component - The component definition created via `createComponent`.
|
|
5025
|
+
* @example
|
|
5026
|
+
* app.spa.register(counter);
|
|
5027
|
+
*/
|
|
4435
5028
|
register(component) {
|
|
4436
5029
|
if (ctx.state.registeredComponents.has(component.name)) ctx.log.warn("spa:component-collision", { name: component.name });
|
|
4437
5030
|
ctx.state.kernel?.register(component);
|
|
4438
5031
|
},
|
|
5032
|
+
/**
|
|
5033
|
+
* Programmatically navigate to a path (client runtime; no-op without a DOM).
|
|
5034
|
+
*
|
|
5035
|
+
* @param path - Target path (pathname, optionally with search/hash).
|
|
5036
|
+
* @example
|
|
5037
|
+
* app.spa.navigate("/about");
|
|
5038
|
+
*/
|
|
4439
5039
|
navigate(path) {
|
|
4440
5040
|
ctx.state.kernel?.processNav(path);
|
|
4441
5041
|
},
|
|
5042
|
+
/**
|
|
5043
|
+
* Read the current resolved URL.
|
|
5044
|
+
*
|
|
5045
|
+
* @returns The current pathname + search.
|
|
5046
|
+
* @example
|
|
5047
|
+
* app.spa.current();
|
|
5048
|
+
*/
|
|
4442
5049
|
current() {
|
|
4443
5050
|
return ctx.state.currentUrl;
|
|
4444
5051
|
}
|
|
4445
5052
|
};
|
|
4446
5053
|
}
|
|
4447
|
-
|
|
4448
5054
|
//#endregion
|
|
4449
5055
|
//#region src/plugins/spa/events.ts
|
|
4450
5056
|
/**
|
|
@@ -4464,7 +5070,6 @@ function spaEvents(register) {
|
|
|
4464
5070
|
"spa:component-unmount": register("A component instance detached from an element.")
|
|
4465
5071
|
};
|
|
4466
5072
|
}
|
|
4467
|
-
|
|
4468
5073
|
//#endregion
|
|
4469
5074
|
//#region src/plugins/spa/types.ts
|
|
4470
5075
|
var types_exports$7 = /* @__PURE__ */ __exportAll({ COMPONENT_HOOK_NAMES: () => COMPONENT_HOOK_NAMES });
|
|
@@ -4477,11 +5082,7 @@ const COMPONENT_HOOK_NAMES = [
|
|
|
4477
5082
|
"onUnMount",
|
|
4478
5083
|
"onDestroy"
|
|
4479
5084
|
];
|
|
4480
|
-
|
|
4481
|
-
//#endregion
|
|
4482
|
-
//#region src/plugins/spa/components.ts
|
|
4483
|
-
/** The set of legal hook names, frozen for O(1) membership checks. */
|
|
4484
|
-
const HOOK_NAME_SET = new Set(COMPONENT_HOOK_NAMES);
|
|
5085
|
+
new Set(COMPONENT_HOOK_NAMES);
|
|
4485
5086
|
/**
|
|
4486
5087
|
* Extracts the page data payload from the inline `script#__DATA__` element.
|
|
4487
5088
|
* Returns an empty object when the script is absent, empty, or invalid JSON.
|
|
@@ -4649,7 +5250,6 @@ function notifyNavEnd(state) {
|
|
|
4649
5250
|
const data = typeof document === "undefined" ? {} : extractPageData(document);
|
|
4650
5251
|
for (const [element, instance] of state.instances) if (instance.persistent) runHook(instance, "onNavEnd", makeContext(element, data));
|
|
4651
5252
|
}
|
|
4652
|
-
|
|
4653
5253
|
//#endregion
|
|
4654
5254
|
//#region src/plugins/spa/head.ts
|
|
4655
5255
|
/** Single-element head selectors synced by replace/append/remove on navigation. */
|
|
@@ -4724,7 +5324,6 @@ function syncHead(_head, doc) {
|
|
|
4724
5324
|
for (const selector of META_SELECTORS) syncElement(selector, doc);
|
|
4725
5325
|
for (const selector of REPLACE_ALL_SELECTORS) replaceAllBySelector(selector, doc);
|
|
4726
5326
|
}
|
|
4727
|
-
|
|
4728
5327
|
//#endregion
|
|
4729
5328
|
//#region src/plugins/spa/progress.ts
|
|
4730
5329
|
/** Delay before the bar appears, so fast navigations show no indicator. */
|
|
@@ -4808,7 +5407,6 @@ function createProgressBar(enabled) {
|
|
|
4808
5407
|
done
|
|
4809
5408
|
};
|
|
4810
5409
|
}
|
|
4811
|
-
|
|
4812
5410
|
//#endregion
|
|
4813
5411
|
//#region src/plugins/spa/router.ts
|
|
4814
5412
|
/**
|
|
@@ -5043,7 +5641,6 @@ function attachRouter(handlers) {
|
|
|
5043
5641
|
const navigation = getNavigation();
|
|
5044
5642
|
return navigation ? attachNavigationApi(navigation, handlers) : attachHistoryFallback(handlers);
|
|
5045
5643
|
}
|
|
5046
|
-
|
|
5047
5644
|
//#endregion
|
|
5048
5645
|
//#region src/plugins/spa/state.ts
|
|
5049
5646
|
/** Error prefix for spa config-validation failures (spec/11 Part-3). */
|
|
@@ -5117,7 +5714,6 @@ function createState(_ctx) {
|
|
|
5117
5714
|
kernel: null
|
|
5118
5715
|
};
|
|
5119
5716
|
}
|
|
5120
|
-
|
|
5121
5717
|
//#endregion
|
|
5122
5718
|
//#region src/plugins/spa/kernel.ts
|
|
5123
5719
|
/**
|
|
@@ -5226,10 +5822,22 @@ function createSpaKernel(state, config, emit, deps) {
|
|
|
5226
5822
|
onError: handleError
|
|
5227
5823
|
};
|
|
5228
5824
|
return {
|
|
5825
|
+
/**
|
|
5826
|
+
* Register config components and seed currentUrl from the document.
|
|
5827
|
+
*
|
|
5828
|
+
* @example
|
|
5829
|
+
* kernel.init();
|
|
5830
|
+
*/
|
|
5229
5831
|
init() {
|
|
5230
5832
|
for (const component of resolved.components) registerComponent(state, component);
|
|
5231
5833
|
state.currentUrl = currentLocationUrl();
|
|
5232
5834
|
},
|
|
5835
|
+
/**
|
|
5836
|
+
* Boot navigation interception + initial scan (throws if already started).
|
|
5837
|
+
*
|
|
5838
|
+
* @example
|
|
5839
|
+
* kernel.boot();
|
|
5840
|
+
*/
|
|
5233
5841
|
boot() {
|
|
5234
5842
|
if (typeof document === "undefined") return;
|
|
5235
5843
|
if (state.started) throw new Error(`${ERROR_PREFIX} spa kernel already started.\n Call app.stop() before booting again (single boot per app).`);
|
|
@@ -5239,16 +5847,42 @@ function createSpaKernel(state, config, emit, deps) {
|
|
|
5239
5847
|
scanAndMount(state, emit, resolved.swapSelector);
|
|
5240
5848
|
state.started = true;
|
|
5241
5849
|
},
|
|
5850
|
+
/**
|
|
5851
|
+
* Register a component definition (last-registered-wins).
|
|
5852
|
+
*
|
|
5853
|
+
* @param component - The component definition to register.
|
|
5854
|
+
* @example
|
|
5855
|
+
* kernel.register(counter);
|
|
5856
|
+
*/
|
|
5242
5857
|
register(component) {
|
|
5243
5858
|
registerComponent(state, component);
|
|
5244
5859
|
},
|
|
5860
|
+
/**
|
|
5861
|
+
* Process a navigation to `path` (fetch then swap; full reload on error).
|
|
5862
|
+
*
|
|
5863
|
+
* @param path - The target path to navigate to.
|
|
5864
|
+
* @example
|
|
5865
|
+
* kernel.processNav("/about");
|
|
5866
|
+
*/
|
|
5245
5867
|
processNav(path) {
|
|
5246
5868
|
if (typeof document === "undefined") return;
|
|
5247
5869
|
performNavigation(path, handlers).catch(() => {});
|
|
5248
5870
|
},
|
|
5871
|
+
/**
|
|
5872
|
+
* Scan the swap region and mount components for matching elements.
|
|
5873
|
+
*
|
|
5874
|
+
* @example
|
|
5875
|
+
* kernel.scan();
|
|
5876
|
+
*/
|
|
5249
5877
|
scan() {
|
|
5250
5878
|
scanAndMount(state, emit, resolved.swapSelector);
|
|
5251
5879
|
},
|
|
5880
|
+
/**
|
|
5881
|
+
* Tear down router listeners, dispose all instances, reset boot state.
|
|
5882
|
+
*
|
|
5883
|
+
* @example
|
|
5884
|
+
* kernel.dispose();
|
|
5885
|
+
*/
|
|
5252
5886
|
dispose() {
|
|
5253
5887
|
state.destroyRouter?.();
|
|
5254
5888
|
state.destroyRouter = null;
|
|
@@ -5277,7 +5911,6 @@ function initSpa(ctx) {
|
|
|
5277
5911
|
kernelRef.current = kernel;
|
|
5278
5912
|
kernel.init();
|
|
5279
5913
|
}
|
|
5280
|
-
|
|
5281
5914
|
//#endregion
|
|
5282
5915
|
//#region src/plugins/spa/lifecycle.ts
|
|
5283
5916
|
/** Router/instance teardown captured during onStart (undefined when stopped). */
|
|
@@ -5325,7 +5958,6 @@ function disposeSpa() {
|
|
|
5325
5958
|
logRef = void 0;
|
|
5326
5959
|
}
|
|
5327
5960
|
}
|
|
5328
|
-
|
|
5329
5961
|
//#endregion
|
|
5330
5962
|
//#region src/plugins/spa/index.ts
|
|
5331
5963
|
/**
|
|
@@ -5336,6 +5968,26 @@ function disposeSpa() {
|
|
|
5336
5968
|
* Emits: spa:navigate, spa:navigated, spa:component-mount, spa:component-unmount.
|
|
5337
5969
|
* @see README.md
|
|
5338
5970
|
*/
|
|
5971
|
+
/**
|
|
5972
|
+
* SPA plugin — progressive client-side navigation layered over the static site:
|
|
5973
|
+
* swaps a page region on navigation, with an optional progress bar and View
|
|
5974
|
+
* Transitions. Register interactive islands with {@link createComponent}. Depends
|
|
5975
|
+
* on router and head; emits `spa:navigate`, `spa:navigated`, `spa:component-mount`,
|
|
5976
|
+
* and `spa:component-unmount`.
|
|
5977
|
+
*
|
|
5978
|
+
* @example Enable view transitions and a custom swap region
|
|
5979
|
+
* ```ts
|
|
5980
|
+
* const app = createApp({
|
|
5981
|
+
* pluginConfigs: {
|
|
5982
|
+
* spa: {
|
|
5983
|
+
* swapSelector: "main > section",
|
|
5984
|
+
* viewTransitions: true,
|
|
5985
|
+
* progressBar: true
|
|
5986
|
+
* }
|
|
5987
|
+
* }
|
|
5988
|
+
* });
|
|
5989
|
+
* ```
|
|
5990
|
+
*/
|
|
5339
5991
|
const spaPlugin = createPlugin$1("spa", {
|
|
5340
5992
|
depends: [routerPlugin, headPlugin],
|
|
5341
5993
|
config: defaultSpaConfig,
|
|
@@ -5349,35 +6001,27 @@ const spaPlugin = createPlugin$1("spa", {
|
|
|
5349
6001
|
},
|
|
5350
6002
|
onStop: disposeSpa
|
|
5351
6003
|
});
|
|
5352
|
-
|
|
5353
6004
|
//#endregion
|
|
5354
6005
|
//#region src/plugins/build/types.ts
|
|
5355
6006
|
var types_exports = /* @__PURE__ */ __exportAll({});
|
|
5356
|
-
|
|
5357
6007
|
//#endregion
|
|
5358
6008
|
//#region src/plugins/content/types.ts
|
|
5359
6009
|
var types_exports$1 = /* @__PURE__ */ __exportAll({});
|
|
5360
|
-
|
|
5361
6010
|
//#endregion
|
|
5362
6011
|
//#region src/plugins/deploy/types.ts
|
|
5363
6012
|
var types_exports$2 = /* @__PURE__ */ __exportAll({});
|
|
5364
|
-
|
|
5365
6013
|
//#endregion
|
|
5366
6014
|
//#region src/plugins/env/types.ts
|
|
5367
6015
|
var types_exports$3 = /* @__PURE__ */ __exportAll({});
|
|
5368
|
-
|
|
5369
6016
|
//#endregion
|
|
5370
6017
|
//#region src/plugins/head/types.ts
|
|
5371
6018
|
var types_exports$4 = /* @__PURE__ */ __exportAll({});
|
|
5372
|
-
|
|
5373
6019
|
//#endregion
|
|
5374
6020
|
//#region src/plugins/log/types.ts
|
|
5375
6021
|
var types_exports$5 = /* @__PURE__ */ __exportAll({});
|
|
5376
|
-
|
|
5377
6022
|
//#endregion
|
|
5378
6023
|
//#region src/plugins/router/types.ts
|
|
5379
6024
|
var types_exports$6 = /* @__PURE__ */ __exportAll({});
|
|
5380
|
-
|
|
5381
6025
|
//#endregion
|
|
5382
6026
|
//#region src/index.ts
|
|
5383
6027
|
/**
|
|
@@ -5397,7 +6041,45 @@ const framework = createCore(coreConfig, {
|
|
|
5397
6041
|
],
|
|
5398
6042
|
pluginConfigs: {}
|
|
5399
6043
|
});
|
|
5400
|
-
|
|
5401
|
-
|
|
6044
|
+
/**
|
|
6045
|
+
* Create and initialize a `@moku-labs/web` application — the Layer-3 entry point.
|
|
6046
|
+
* Your overrides are merged over the framework defaults through the 4-level config
|
|
6047
|
+
* cascade, every plugin's lifecycle runs, and a fully-typed, frozen app is returned.
|
|
6048
|
+
*
|
|
6049
|
+
* @param options - Optional configuration:
|
|
6050
|
+
* - `pluginConfigs` — per-plugin overrides, keyed by plugin name
|
|
6051
|
+
* (`site`, `i18n`, `router`, `content`, `head`, `build`, `spa`, `deploy`, `env`).
|
|
6052
|
+
* - `config` — global framework config (e.g. `{ mode: "development" }`).
|
|
6053
|
+
* - `plugins` — extra consumer plugins, merged into the app and its return type.
|
|
6054
|
+
* - `onReady` / `onError` / `onStart` / `onStop` — lifecycle callbacks.
|
|
6055
|
+
* @returns The initialized app: `start()`, `stop()`, every plugin's API, and `log`.
|
|
6056
|
+
* @example
|
|
6057
|
+
* ```ts
|
|
6058
|
+
* const app = createApp({
|
|
6059
|
+
* pluginConfigs: {
|
|
6060
|
+
* site: { name: "My Blog", url: "https://blog.dev", author: "Ada", description: "Notes" },
|
|
6061
|
+
* router: { routes: defineRoutes({ home: route("/"), post: route("/blog/{slug}/") }) }
|
|
6062
|
+
* }
|
|
6063
|
+
* });
|
|
6064
|
+
* await app.start();
|
|
6065
|
+
* ```
|
|
6066
|
+
*/
|
|
6067
|
+
const createApp = framework.createApp;
|
|
6068
|
+
/**
|
|
6069
|
+
* Create a custom plugin bound to this framework's `Config`/`Events` and core
|
|
6070
|
+
* APIs. Plugin types are inferred from the spec object — never written explicitly.
|
|
6071
|
+
* Pass the result to {@link createApp} via `plugins`.
|
|
6072
|
+
*
|
|
6073
|
+
* @example
|
|
6074
|
+
* ```ts
|
|
6075
|
+
* const analytics = createPlugin("analytics", {
|
|
6076
|
+
* config: { writeKey: "" },
|
|
6077
|
+
* api: (ctx) => ({ track: (event: string) => ctx.log.info("analytics:track", { event }) })
|
|
6078
|
+
* });
|
|
6079
|
+
*
|
|
6080
|
+
* const app = createApp({ plugins: [analytics] });
|
|
6081
|
+
* ```
|
|
6082
|
+
*/
|
|
6083
|
+
const createPlugin = framework.createPlugin;
|
|
5402
6084
|
//#endregion
|
|
5403
|
-
export { types_exports as Build, types_exports$1 as Content, types_exports$2 as Deploy, types_exports$3 as Env, types_exports$4 as Head, types_exports$5 as Log, types_exports$6 as Router, types_exports$7 as Spa, buildArticleHead, buildPlugin, canonical, contentPlugin, createApp, createPlugin, defineRoutes, deployPlugin, envPlugin, feedLink, headPlugin, hreflang, i18nPlugin, jsonLd, logPlugin, meta, og, route, routerPlugin, sitePlugin, spaPlugin, twitter };
|
|
6085
|
+
export { types_exports as Build, types_exports$1 as Content, types_exports$2 as Deploy, types_exports$3 as Env, types_exports$4 as Head, types_exports$5 as Log, types_exports$6 as Router, types_exports$7 as Spa, buildArticleHead, buildPlugin, canonical, contentPlugin, createApp, createPlugin, defineRoutes, deployPlugin, envPlugin, feedLink, headPlugin, hreflang, i18nPlugin, jsonLd, logPlugin, meta, og, route, routerPlugin, sitePlugin, spaPlugin, twitter };
|