@moku-labs/web 0.2.0 → 0.3.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/chunk-D7D4PA-g.mjs +13 -0
- package/dist/index.cjs +732 -280
- package/dist/index.d.cts +24 -25
- package/dist/index.d.mts +23 -24
- package/dist/index.mjs +649 -189
- package/package.json +8 -8
- package/dist/chunk-DQk6qfdC.mjs +0 -18
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value:
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
//#region \0rolldown/runtime.js
|
|
3
3
|
var __create = Object.create;
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -8,28 +8,20 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
9
|
var __exportAll = (all, no_symbols) => {
|
|
10
10
|
let target = {};
|
|
11
|
-
for (var name in all) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
if (!no_symbols) {
|
|
18
|
-
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
19
|
-
}
|
|
11
|
+
for (var name in all) __defProp(target, name, {
|
|
12
|
+
get: all[name],
|
|
13
|
+
enumerable: true
|
|
14
|
+
});
|
|
15
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
20
16
|
return target;
|
|
21
17
|
};
|
|
22
18
|
var __copyProps = (to, from, except, desc) => {
|
|
23
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
}
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
20
|
+
key = keys[i];
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
22
|
+
get: ((k) => from[k]).bind(null, key),
|
|
23
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
24
|
+
});
|
|
33
25
|
}
|
|
34
26
|
return to;
|
|
35
27
|
};
|
|
@@ -37,38 +29,38 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
37
29
|
value: mod,
|
|
38
30
|
enumerable: true
|
|
39
31
|
}) : target, mod));
|
|
40
|
-
|
|
41
32
|
//#endregion
|
|
42
33
|
let _moku_labs_core = require("@moku-labs/core");
|
|
43
34
|
let node_fs = require("node:fs");
|
|
44
35
|
let node_fs_promises = require("node:fs/promises");
|
|
45
36
|
let node_path = require("node:path");
|
|
37
|
+
let node_path$1 = __toESM(node_path, 1);
|
|
46
38
|
node_path = __toESM(node_path);
|
|
47
39
|
let gray_matter = require("gray-matter");
|
|
48
|
-
gray_matter = __toESM(gray_matter);
|
|
40
|
+
gray_matter = __toESM(gray_matter, 1);
|
|
49
41
|
let _shikijs_rehype = require("@shikijs/rehype");
|
|
50
|
-
_shikijs_rehype = __toESM(_shikijs_rehype);
|
|
42
|
+
_shikijs_rehype = __toESM(_shikijs_rehype, 1);
|
|
51
43
|
let rehype_sanitize = require("rehype-sanitize");
|
|
52
|
-
rehype_sanitize = __toESM(rehype_sanitize);
|
|
44
|
+
rehype_sanitize = __toESM(rehype_sanitize, 1);
|
|
53
45
|
let rehype_stringify = require("rehype-stringify");
|
|
54
|
-
rehype_stringify = __toESM(rehype_stringify);
|
|
46
|
+
rehype_stringify = __toESM(rehype_stringify, 1);
|
|
55
47
|
let unified = require("unified");
|
|
56
48
|
let rehype_raw = require("rehype-raw");
|
|
57
|
-
rehype_raw = __toESM(rehype_raw);
|
|
49
|
+
rehype_raw = __toESM(rehype_raw, 1);
|
|
58
50
|
let remark_directive = require("remark-directive");
|
|
59
|
-
remark_directive = __toESM(remark_directive);
|
|
51
|
+
remark_directive = __toESM(remark_directive, 1);
|
|
60
52
|
let remark_frontmatter = require("remark-frontmatter");
|
|
61
|
-
remark_frontmatter = __toESM(remark_frontmatter);
|
|
53
|
+
remark_frontmatter = __toESM(remark_frontmatter, 1);
|
|
62
54
|
let remark_gfm = require("remark-gfm");
|
|
63
|
-
remark_gfm = __toESM(remark_gfm);
|
|
55
|
+
remark_gfm = __toESM(remark_gfm, 1);
|
|
64
56
|
let remark_parse = require("remark-parse");
|
|
65
|
-
remark_parse = __toESM(remark_parse);
|
|
57
|
+
remark_parse = __toESM(remark_parse, 1);
|
|
66
58
|
let remark_rehype = require("remark-rehype");
|
|
67
|
-
remark_rehype = __toESM(remark_rehype);
|
|
59
|
+
remark_rehype = __toESM(remark_rehype, 1);
|
|
68
60
|
let unist_util_visit = require("unist-util-visit");
|
|
69
61
|
let hast_util_sanitize = require("hast-util-sanitize");
|
|
70
62
|
let reading_time = require("reading-time");
|
|
71
|
-
reading_time = __toESM(reading_time);
|
|
63
|
+
reading_time = __toESM(reading_time, 1);
|
|
72
64
|
let node_crypto = require("node:crypto");
|
|
73
65
|
let feed = require("feed");
|
|
74
66
|
let _resvg_resvg_js = require("@resvg/resvg-js");
|
|
@@ -76,7 +68,6 @@ let satori = require("satori");
|
|
|
76
68
|
satori = __toESM(satori);
|
|
77
69
|
let preact_jsx_runtime = require("preact/jsx-runtime");
|
|
78
70
|
let preact_render_to_string = require("preact-render-to-string");
|
|
79
|
-
|
|
80
71
|
//#region src/plugins/env/api.ts
|
|
81
72
|
/** Error prefix for all env API failures. */
|
|
82
73
|
const ERROR_PREFIX$13 = "[web]";
|
|
@@ -97,26 +88,75 @@ const ERROR_PREFIX$13 = "[web]";
|
|
|
97
88
|
function createEnvApi(ctx) {
|
|
98
89
|
const { resolved, publicMap } = ctx.state;
|
|
99
90
|
return {
|
|
91
|
+
/**
|
|
92
|
+
* Reads a resolved variable.
|
|
93
|
+
*
|
|
94
|
+
* @param key - Variable name.
|
|
95
|
+
* @returns The value, or `undefined` if not present.
|
|
96
|
+
* @example
|
|
97
|
+
* ```ts
|
|
98
|
+
* api.get("PUBLIC_API_URL");
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
100
101
|
get(key) {
|
|
101
102
|
return resolved.get(key);
|
|
102
103
|
},
|
|
104
|
+
/**
|
|
105
|
+
* Reads a variable that must exist.
|
|
106
|
+
*
|
|
107
|
+
* @param key - Variable name.
|
|
108
|
+
* @returns The value.
|
|
109
|
+
* @throws {Error} If the variable is undefined.
|
|
110
|
+
* @example
|
|
111
|
+
* ```ts
|
|
112
|
+
* api.require("DEPLOY_TOKEN");
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
103
115
|
require(key) {
|
|
104
116
|
const value = resolved.get(key);
|
|
105
117
|
if (value === void 0) throw new Error(`${ERROR_PREFIX$13} env: required variable "${key}" is not defined.`);
|
|
106
118
|
return value;
|
|
107
119
|
},
|
|
120
|
+
/**
|
|
121
|
+
* Tests presence of a resolved variable.
|
|
122
|
+
*
|
|
123
|
+
* @param key - Variable name.
|
|
124
|
+
* @returns `true` if a value is present.
|
|
125
|
+
* @example
|
|
126
|
+
* ```ts
|
|
127
|
+
* api.has("PUBLIC_API_URL");
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
108
130
|
has(key) {
|
|
109
131
|
return resolved.has(key);
|
|
110
132
|
},
|
|
133
|
+
/**
|
|
134
|
+
* Returns all public variables as a frozen plain object — a fresh copy,
|
|
135
|
+
* never the raw state map.
|
|
136
|
+
*
|
|
137
|
+
* @returns A frozen `Record` of public variable names to values.
|
|
138
|
+
* @example
|
|
139
|
+
* ```ts
|
|
140
|
+
* const payload = { ...api.getPublic() };
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
111
143
|
getPublic() {
|
|
112
144
|
return Object.freeze(Object.fromEntries(publicMap));
|
|
113
145
|
},
|
|
146
|
+
/**
|
|
147
|
+
* Returns the already-frozen map of public variables.
|
|
148
|
+
*
|
|
149
|
+
* @returns The frozen public map.
|
|
150
|
+
* @example
|
|
151
|
+
* ```ts
|
|
152
|
+
* [...api.getPublicMap()];
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
114
155
|
getPublicMap() {
|
|
115
156
|
return publicMap;
|
|
116
157
|
}
|
|
117
158
|
};
|
|
118
159
|
}
|
|
119
|
-
|
|
120
160
|
//#endregion
|
|
121
161
|
//#region src/plugins/env/state.ts
|
|
122
162
|
/**
|
|
@@ -136,7 +176,6 @@ function createEnvState() {
|
|
|
136
176
|
publicMap: /* @__PURE__ */ new Map()
|
|
137
177
|
};
|
|
138
178
|
}
|
|
139
|
-
|
|
140
179
|
//#endregion
|
|
141
180
|
//#region src/plugins/env/validate.ts
|
|
142
181
|
/** Error message thrown by every frozen-map mutator. */
|
|
@@ -255,7 +294,6 @@ function validateSchema(ctx) {
|
|
|
255
294
|
freezeMap(state.resolved);
|
|
256
295
|
freezeMap(state.publicMap);
|
|
257
296
|
}
|
|
258
|
-
|
|
259
297
|
//#endregion
|
|
260
298
|
//#region src/plugins/env/providers.ts
|
|
261
299
|
/**
|
|
@@ -324,6 +362,15 @@ function parseDotenv(text) {
|
|
|
324
362
|
function dotenv(path = DEFAULT_DOTENV_PATH) {
|
|
325
363
|
return {
|
|
326
364
|
name: `dotenv:${path}`,
|
|
365
|
+
/**
|
|
366
|
+
* Reads and parses the dotenv file fresh from disk; `{}` if it is missing.
|
|
367
|
+
*
|
|
368
|
+
* @returns The parsed environment record, or `{}` when the file is absent.
|
|
369
|
+
* @example
|
|
370
|
+
* ```ts
|
|
371
|
+
* dotenv(".env.local").load();
|
|
372
|
+
* ```
|
|
373
|
+
*/
|
|
327
374
|
load() {
|
|
328
375
|
if (!(0, node_fs.existsSync)(path)) return {};
|
|
329
376
|
return parseDotenv((0, node_fs.readFileSync)(path, "utf8"));
|
|
@@ -343,24 +390,20 @@ function dotenv(path = DEFAULT_DOTENV_PATH) {
|
|
|
343
390
|
function processEnv() {
|
|
344
391
|
return {
|
|
345
392
|
name: "process-env",
|
|
393
|
+
/**
|
|
394
|
+
* Returns a shallow copy of `process.env` at call time.
|
|
395
|
+
*
|
|
396
|
+
* @returns A fresh shallow copy of `process.env`.
|
|
397
|
+
* @example
|
|
398
|
+
* ```ts
|
|
399
|
+
* processEnv().load();
|
|
400
|
+
* ```
|
|
401
|
+
*/
|
|
346
402
|
load() {
|
|
347
403
|
return { ...process.env };
|
|
348
404
|
}
|
|
349
405
|
};
|
|
350
406
|
}
|
|
351
|
-
|
|
352
|
-
//#endregion
|
|
353
|
-
//#region src/plugins/env/index.ts
|
|
354
|
-
/**
|
|
355
|
-
* @file Core plugin: universal env injection — schema + providers + PUBLIC_ cross-validation at onInit.
|
|
356
|
-
* @see README.md
|
|
357
|
-
*/
|
|
358
|
-
/** Plugin config defaults (R6 typed const). `providers: []` — framework sets `[dotenv(), processEnv()]` via the 4-level cascade. */
|
|
359
|
-
const defaultEnvConfig = {
|
|
360
|
-
schema: {},
|
|
361
|
-
providers: [],
|
|
362
|
-
publicPrefix: "PUBLIC_"
|
|
363
|
-
};
|
|
364
407
|
/**
|
|
365
408
|
* Core plugin that resolves, validates, and freezes the environment at `onInit`,
|
|
366
409
|
* exposing a read-only accessor at `ctx.env`. No `onStart`/`onStop` — holds no resource.
|
|
@@ -371,12 +414,15 @@ const defaultEnvConfig = {
|
|
|
371
414
|
* ```
|
|
372
415
|
*/
|
|
373
416
|
const envPlugin = (0, _moku_labs_core.createCorePlugin)("env", {
|
|
374
|
-
config:
|
|
417
|
+
config: {
|
|
418
|
+
schema: {},
|
|
419
|
+
providers: [],
|
|
420
|
+
publicPrefix: "PUBLIC_"
|
|
421
|
+
},
|
|
375
422
|
createState: createEnvState,
|
|
376
423
|
api: createEnvApi,
|
|
377
424
|
onInit: validateSchema
|
|
378
425
|
});
|
|
379
|
-
|
|
380
426
|
//#endregion
|
|
381
427
|
//#region src/plugins/log/expect.ts
|
|
382
428
|
/**
|
|
@@ -487,10 +533,33 @@ function describePartial(partial) {
|
|
|
487
533
|
*/
|
|
488
534
|
function createExpectChain(entries) {
|
|
489
535
|
const chain = {
|
|
536
|
+
/**
|
|
537
|
+
* Assert at least one entry has `event`, optionally matching `partial`.
|
|
538
|
+
*
|
|
539
|
+
* @param event - Event name to find.
|
|
540
|
+
* @param partial - Optional partial data shape (subset-matched).
|
|
541
|
+
* @returns The same chain for chaining.
|
|
542
|
+
* @throws {LogExpectAssertionError} When no matching entry exists.
|
|
543
|
+
* @example
|
|
544
|
+
* ```ts
|
|
545
|
+
* chain.toHaveEvent("build:phase", { status: "start" });
|
|
546
|
+
* ```
|
|
547
|
+
*/
|
|
490
548
|
toHaveEvent(event, partial) {
|
|
491
549
|
if (!entries.some((entry) => entryMatches(entry, event, partial))) throw new LogExpectAssertionError(`Expected trace to contain event "${event}"${describePartial(partial)}, but none was found.`);
|
|
492
550
|
return chain;
|
|
493
551
|
},
|
|
552
|
+
/**
|
|
553
|
+
* Assert all of `events` appear in the trace in the given relative order.
|
|
554
|
+
*
|
|
555
|
+
* @param events - Ordered list of event names (gaps allowed).
|
|
556
|
+
* @returns The same chain for chaining.
|
|
557
|
+
* @throws {LogExpectAssertionError} When the ordering cannot be satisfied.
|
|
558
|
+
* @example
|
|
559
|
+
* ```ts
|
|
560
|
+
* chain.toHaveEventInOrder(["build:phase", "build:complete"]);
|
|
561
|
+
* ```
|
|
562
|
+
*/
|
|
494
563
|
toHaveEventInOrder(events) {
|
|
495
564
|
let cursor = 0;
|
|
496
565
|
for (const [position, event] of events.entries()) {
|
|
@@ -504,6 +573,18 @@ function createExpectChain(entries) {
|
|
|
504
573
|
}
|
|
505
574
|
return chain;
|
|
506
575
|
},
|
|
576
|
+
/**
|
|
577
|
+
* Assert NO entry has `event` (optionally narrowed by `partial`).
|
|
578
|
+
*
|
|
579
|
+
* @param event - Event name that must be absent.
|
|
580
|
+
* @param partial - Optional partial data shape; only matching entries violate.
|
|
581
|
+
* @returns The same chain for chaining.
|
|
582
|
+
* @throws {LogExpectAssertionError} When a matching entry exists.
|
|
583
|
+
* @example
|
|
584
|
+
* ```ts
|
|
585
|
+
* chain.toNotHaveEvent("deploy:failed");
|
|
586
|
+
* ```
|
|
587
|
+
*/
|
|
507
588
|
toNotHaveEvent(event, partial) {
|
|
508
589
|
const offending = entries.findIndex((entry) => entryMatches(entry, event, partial));
|
|
509
590
|
if (offending !== -1) throw new LogExpectAssertionError(`Expected trace to NOT contain event "${event}"${describePartial(partial)}, but found one at index ${offending}.`);
|
|
@@ -512,7 +593,6 @@ function createExpectChain(entries) {
|
|
|
512
593
|
};
|
|
513
594
|
return chain;
|
|
514
595
|
}
|
|
515
|
-
|
|
516
596
|
//#endregion
|
|
517
597
|
//#region src/plugins/log/api.ts
|
|
518
598
|
/**
|
|
@@ -581,33 +661,110 @@ function mergeError(data, error) {
|
|
|
581
661
|
function createLogApi(ctx) {
|
|
582
662
|
const { state } = ctx;
|
|
583
663
|
return {
|
|
664
|
+
/**
|
|
665
|
+
* Append an `info` entry and fan it out to every sink.
|
|
666
|
+
*
|
|
667
|
+
* @param event - Event identifier (convention: `domain:action`).
|
|
668
|
+
* @param data - Optional structured payload.
|
|
669
|
+
* @example
|
|
670
|
+
* ```ts
|
|
671
|
+
* log.info("content:ready", { count: 12 });
|
|
672
|
+
* ```
|
|
673
|
+
*/
|
|
584
674
|
info(event, data) {
|
|
585
675
|
append(state, "info", event, data);
|
|
586
676
|
},
|
|
677
|
+
/**
|
|
678
|
+
* Append a `debug` entry and fan it out to every sink.
|
|
679
|
+
*
|
|
680
|
+
* @param event - Event identifier (convention: `domain:action`).
|
|
681
|
+
* @param data - Optional structured payload.
|
|
682
|
+
* @example
|
|
683
|
+
* ```ts
|
|
684
|
+
* log.debug("router:match", { path: "/blog/" });
|
|
685
|
+
* ```
|
|
686
|
+
*/
|
|
587
687
|
debug(event, data) {
|
|
588
688
|
append(state, "debug", event, data);
|
|
589
689
|
},
|
|
690
|
+
/**
|
|
691
|
+
* Append a `warn` entry and fan it out to every sink.
|
|
692
|
+
*
|
|
693
|
+
* @param event - Event identifier (convention: `domain:action`).
|
|
694
|
+
* @param data - Optional structured payload.
|
|
695
|
+
* @example
|
|
696
|
+
* ```ts
|
|
697
|
+
* log.warn("build:skip", { reason: "no sitemap" });
|
|
698
|
+
* ```
|
|
699
|
+
*/
|
|
590
700
|
warn(event, data) {
|
|
591
701
|
append(state, "warn", event, data);
|
|
592
702
|
},
|
|
703
|
+
/**
|
|
704
|
+
* Append an `error` entry. When `error` is provided, its `message`/`stack`
|
|
705
|
+
* are merged into `data` under an `error` key (existing keys preserved);
|
|
706
|
+
* otherwise `data` is recorded as-is.
|
|
707
|
+
*
|
|
708
|
+
* @param event - Event identifier (convention: `domain:action`).
|
|
709
|
+
* @param data - Optional structured payload.
|
|
710
|
+
* @param error - Optional originating Error to merge into `data`.
|
|
711
|
+
* @example
|
|
712
|
+
* ```ts
|
|
713
|
+
* log.error("deploy:failed", { target: "cf" }, err);
|
|
714
|
+
* ```
|
|
715
|
+
*/
|
|
593
716
|
error(event, data, error) {
|
|
594
717
|
append(state, "error", event, error === void 0 ? data : mergeError(data, error));
|
|
595
718
|
},
|
|
719
|
+
/**
|
|
720
|
+
* Return a frozen snapshot (fresh copy) of the entries recorded so far.
|
|
721
|
+
*
|
|
722
|
+
* @returns A readonly, frozen copy of the recorded entries.
|
|
723
|
+
* @example
|
|
724
|
+
* ```ts
|
|
725
|
+
* const entries = log.trace();
|
|
726
|
+
* ```
|
|
727
|
+
*/
|
|
596
728
|
trace() {
|
|
597
729
|
return Object.freeze([...state.entries]);
|
|
598
730
|
},
|
|
731
|
+
/**
|
|
732
|
+
* Return a fluent assertion chain bound to the live entries array.
|
|
733
|
+
*
|
|
734
|
+
* @returns A fresh {@link ExpectChain} reading `state.entries` live.
|
|
735
|
+
* @example
|
|
736
|
+
* ```ts
|
|
737
|
+
* log.expect().toHaveEvent("build:complete");
|
|
738
|
+
* ```
|
|
739
|
+
*/
|
|
599
740
|
expect() {
|
|
600
741
|
return createExpectChain(state.entries);
|
|
601
742
|
},
|
|
743
|
+
/**
|
|
744
|
+
* Register an additional output sink at runtime.
|
|
745
|
+
*
|
|
746
|
+
* @param sink - The sink to add to the fan-out list.
|
|
747
|
+
* @example
|
|
748
|
+
* ```ts
|
|
749
|
+
* log.addSink({ write: (e) => stream.write(JSON.stringify(e)) });
|
|
750
|
+
* ```
|
|
751
|
+
*/
|
|
602
752
|
addSink(sink) {
|
|
603
753
|
state.sinks.push(sink);
|
|
604
754
|
},
|
|
755
|
+
/**
|
|
756
|
+
* Clear all recorded entries while keeping registered sinks.
|
|
757
|
+
*
|
|
758
|
+
* @example
|
|
759
|
+
* ```ts
|
|
760
|
+
* log.reset();
|
|
761
|
+
* ```
|
|
762
|
+
*/
|
|
605
763
|
reset() {
|
|
606
764
|
state.entries.length = 0;
|
|
607
765
|
}
|
|
608
766
|
};
|
|
609
767
|
}
|
|
610
|
-
|
|
611
768
|
//#endregion
|
|
612
769
|
//#region src/plugins/log/sinks.ts
|
|
613
770
|
/**
|
|
@@ -622,7 +779,17 @@ function createLogApi(ctx) {
|
|
|
622
779
|
* ```
|
|
623
780
|
*/
|
|
624
781
|
function consoleSink() {
|
|
625
|
-
return {
|
|
782
|
+
return {
|
|
783
|
+
/**
|
|
784
|
+
* Route a single entry to the console channel matching its level.
|
|
785
|
+
*
|
|
786
|
+
* @param entry - The entry to emit.
|
|
787
|
+
* @example
|
|
788
|
+
* ```ts
|
|
789
|
+
* sink.write({ level: "warn", event: "build:skip", ts: Date.now() });
|
|
790
|
+
* ```
|
|
791
|
+
*/
|
|
792
|
+
write(entry) {
|
|
626
793
|
if (entry.level === "error") console.error(entry);
|
|
627
794
|
else if (entry.level === "warn") console.warn(entry);
|
|
628
795
|
else console.log(entry);
|
|
@@ -643,7 +810,6 @@ function consoleSink() {
|
|
|
643
810
|
function installDefaultSinks(ctx) {
|
|
644
811
|
if (ctx.config.mode === "dev" || ctx.config.mode === "production") ctx.state.sinks.push(consoleSink());
|
|
645
812
|
}
|
|
646
|
-
|
|
647
813
|
//#endregion
|
|
648
814
|
//#region src/plugins/log/state.ts
|
|
649
815
|
/**
|
|
@@ -664,15 +830,6 @@ function createLogState(_ctx) {
|
|
|
664
830
|
sinks: []
|
|
665
831
|
};
|
|
666
832
|
}
|
|
667
|
-
|
|
668
|
-
//#endregion
|
|
669
|
-
//#region src/plugins/log/index.ts
|
|
670
|
-
/**
|
|
671
|
-
* @file log — Core Plugin (Standard tier): in-memory trace + expect() DSL.
|
|
672
|
-
* @see README.md
|
|
673
|
-
*/
|
|
674
|
-
/** Default config; overridden via the 4-level pluginConfigs.log merge. */
|
|
675
|
-
const defaultLogConfig = { mode: "production" };
|
|
676
833
|
/**
|
|
677
834
|
* Core logging plugin — always-on in-memory trace + `expect()` event-trace DSL.
|
|
678
835
|
* API injected as `ctx.log` on every regular plugin and surfaced as `app.log`.
|
|
@@ -681,21 +838,13 @@ const defaultLogConfig = { mode: "production" };
|
|
|
681
838
|
* @see README.md
|
|
682
839
|
*/
|
|
683
840
|
const logPlugin = (0, _moku_labs_core.createCorePlugin)("log", {
|
|
684
|
-
config:
|
|
841
|
+
config: { mode: "production" },
|
|
685
842
|
createState: createLogState,
|
|
686
843
|
api: createLogApi,
|
|
687
844
|
onInit: installDefaultSinks
|
|
688
845
|
});
|
|
689
|
-
|
|
690
|
-
//#endregion
|
|
691
|
-
//#region src/config.ts
|
|
692
|
-
/**
|
|
693
|
-
* @file Framework configuration — Config + Events types, core plugin registration.
|
|
694
|
-
* @see README.md
|
|
695
|
-
*/
|
|
696
|
-
const defaultConfig$6 = { mode: "production" };
|
|
697
846
|
const coreConfig = (0, _moku_labs_core.createCoreConfig)("web", {
|
|
698
|
-
config:
|
|
847
|
+
config: { mode: "production" },
|
|
699
848
|
plugins: [logPlugin, envPlugin],
|
|
700
849
|
pluginConfigs: {
|
|
701
850
|
log: { mode: "production" },
|
|
@@ -703,7 +852,6 @@ const coreConfig = (0, _moku_labs_core.createCoreConfig)("web", {
|
|
|
703
852
|
}
|
|
704
853
|
});
|
|
705
854
|
const { createPlugin: createPlugin$1, createCore } = coreConfig;
|
|
706
|
-
|
|
707
855
|
//#endregion
|
|
708
856
|
//#region src/plugins/i18n/api.ts
|
|
709
857
|
/** Error prefix for all i18n lifecycle failures. */
|
|
@@ -743,21 +891,83 @@ function validateI18nConfig(ctx) {
|
|
|
743
891
|
function createI18nApi(ctx) {
|
|
744
892
|
const { config } = ctx;
|
|
745
893
|
return {
|
|
894
|
+
/**
|
|
895
|
+
* Returns the configured supported locales in declared order.
|
|
896
|
+
*
|
|
897
|
+
* @returns The configured `locales` list (priority/display order).
|
|
898
|
+
* @example
|
|
899
|
+
* ```ts
|
|
900
|
+
* api.locales(); // ["en", "uk"]
|
|
901
|
+
* ```
|
|
902
|
+
*/
|
|
746
903
|
locales() {
|
|
747
904
|
return config.locales;
|
|
748
905
|
},
|
|
906
|
+
/**
|
|
907
|
+
* Returns the fallback locale used when a requested locale is absent.
|
|
908
|
+
*
|
|
909
|
+
* @returns The configured `defaultLocale`.
|
|
910
|
+
* @example
|
|
911
|
+
* ```ts
|
|
912
|
+
* api.defaultLocale(); // "en"
|
|
913
|
+
* ```
|
|
914
|
+
*/
|
|
749
915
|
defaultLocale() {
|
|
750
916
|
return config.defaultLocale;
|
|
751
917
|
},
|
|
918
|
+
/**
|
|
919
|
+
* Membership guard: whether `x` is one of the supported locales
|
|
920
|
+
* (case-sensitive).
|
|
921
|
+
*
|
|
922
|
+
* @param x - Candidate locale code.
|
|
923
|
+
* @returns `true` if `x ∈ locales`, else `false`.
|
|
924
|
+
* @example
|
|
925
|
+
* ```ts
|
|
926
|
+
* api.isLocale("uk"); // true
|
|
927
|
+
* ```
|
|
928
|
+
*/
|
|
752
929
|
isLocale(x) {
|
|
753
930
|
return config.locales.includes(x);
|
|
754
931
|
},
|
|
932
|
+
/**
|
|
933
|
+
* Human-readable display name for a locale.
|
|
934
|
+
*
|
|
935
|
+
* @param locale - Locale code to look up.
|
|
936
|
+
* @returns The display name, or `undefined` if unmapped.
|
|
937
|
+
* @example
|
|
938
|
+
* ```ts
|
|
939
|
+
* api.localeName("uk"); // "Українська"
|
|
940
|
+
* ```
|
|
941
|
+
*/
|
|
755
942
|
localeName(locale) {
|
|
756
943
|
return config.localeNames?.[locale];
|
|
757
944
|
},
|
|
945
|
+
/**
|
|
946
|
+
* Open Graph `og:locale` value for a locale.
|
|
947
|
+
*
|
|
948
|
+
* @param locale - Locale code to look up.
|
|
949
|
+
* @returns The `og:locale` value (e.g. `"en_US"`), or `undefined` if unmapped.
|
|
950
|
+
* @example
|
|
951
|
+
* ```ts
|
|
952
|
+
* api.ogLocale("en"); // "en_US"
|
|
953
|
+
* ```
|
|
954
|
+
*/
|
|
758
955
|
ogLocale(locale) {
|
|
759
956
|
return config.ogLocaleMap?.[locale];
|
|
760
957
|
},
|
|
958
|
+
/**
|
|
959
|
+
* Translate `key` for `locale` with a deterministic fallback chain
|
|
960
|
+
* (requested locale → default locale → the key itself). The default-locale
|
|
961
|
+
* lookup is skipped when `locale === defaultLocale`.
|
|
962
|
+
*
|
|
963
|
+
* @param locale - Requested locale code.
|
|
964
|
+
* @param key - Translation key (e.g. `"nav.home"`).
|
|
965
|
+
* @returns The translated value, the default-locale value, or `key`.
|
|
966
|
+
* @example
|
|
967
|
+
* ```ts
|
|
968
|
+
* api.t("uk", "nav.home"); // "Головна"
|
|
969
|
+
* ```
|
|
970
|
+
*/
|
|
761
971
|
t(locale, key) {
|
|
762
972
|
const exact = config.translations?.[locale]?.[key];
|
|
763
973
|
if (exact !== void 0) return exact;
|
|
@@ -769,34 +979,17 @@ function createI18nApi(ctx) {
|
|
|
769
979
|
}
|
|
770
980
|
};
|
|
771
981
|
}
|
|
772
|
-
|
|
773
|
-
//#endregion
|
|
774
|
-
//#region src/plugins/i18n/index.ts
|
|
775
|
-
/**
|
|
776
|
-
* i18n — Micro tier. Multi-file layout (index wiring + api.ts + types.ts) so
|
|
777
|
-
* index.ts stays within the ≤30-line wiring-only hook; logic lives in api.ts.
|
|
778
|
-
*
|
|
779
|
-
* Locale registry + flat translation helper with default-locale fallback.
|
|
780
|
-
* Pure config-as-data: no state, no events, no lifecycle resources.
|
|
781
|
-
* Consumed read-only by content/router/head/build via `ctx.require(i18nPlugin)`.
|
|
782
|
-
*
|
|
783
|
-
* @file i18n plugin wiring harness.
|
|
784
|
-
* @see README.md
|
|
785
|
-
*/
|
|
786
|
-
/** Typed default config (R6: no inline `as`). Optional maps default to `{}` so every lookup is total. */
|
|
787
|
-
const defaultConfig$5 = {
|
|
788
|
-
locales: ["en"],
|
|
789
|
-
defaultLocale: "en",
|
|
790
|
-
localeNames: {},
|
|
791
|
-
ogLocaleMap: {},
|
|
792
|
-
translations: {}
|
|
793
|
-
};
|
|
794
982
|
const i18nPlugin = createPlugin$1("i18n", {
|
|
795
|
-
config:
|
|
983
|
+
config: {
|
|
984
|
+
locales: ["en"],
|
|
985
|
+
defaultLocale: "en",
|
|
986
|
+
localeNames: {},
|
|
987
|
+
ogLocaleMap: {},
|
|
988
|
+
translations: {}
|
|
989
|
+
},
|
|
796
990
|
onInit: validateI18nConfig,
|
|
797
991
|
api: createI18nApi
|
|
798
992
|
});
|
|
799
|
-
|
|
800
993
|
//#endregion
|
|
801
994
|
//#region src/plugins/content/pipeline/frontmatter.ts
|
|
802
995
|
/**
|
|
@@ -842,7 +1035,6 @@ function parseFrontmatter(raw, config) {
|
|
|
842
1035
|
body: parsed.content
|
|
843
1036
|
};
|
|
844
1037
|
}
|
|
845
|
-
|
|
846
1038
|
//#endregion
|
|
847
1039
|
//#region src/plugins/content/pipeline/plugins.ts
|
|
848
1040
|
/**
|
|
@@ -999,7 +1191,6 @@ function defaultRehypePlugins() {
|
|
|
999
1191
|
sectionDividerPlugin
|
|
1000
1192
|
];
|
|
1001
1193
|
}
|
|
1002
|
-
|
|
1003
1194
|
//#endregion
|
|
1004
1195
|
//#region src/plugins/content/pipeline/sanitize.ts
|
|
1005
1196
|
/**
|
|
@@ -1086,7 +1277,6 @@ function buildSanitizeSchema() {
|
|
|
1086
1277
|
}
|
|
1087
1278
|
};
|
|
1088
1279
|
}
|
|
1089
|
-
|
|
1090
1280
|
//#endregion
|
|
1091
1281
|
//#region src/plugins/content/pipeline/markdown.ts
|
|
1092
1282
|
/**
|
|
@@ -1144,7 +1334,6 @@ function applyPluggable(processor, plugin) {
|
|
|
1144
1334
|
}
|
|
1145
1335
|
processor.use(plugin);
|
|
1146
1336
|
}
|
|
1147
|
-
|
|
1148
1337
|
//#endregion
|
|
1149
1338
|
//#region src/plugins/content/pipeline/reading-time.ts
|
|
1150
1339
|
/**
|
|
@@ -1170,7 +1359,6 @@ function calculateReadingTime(text) {
|
|
|
1170
1359
|
wordCount: stats.words
|
|
1171
1360
|
};
|
|
1172
1361
|
}
|
|
1173
|
-
|
|
1174
1362
|
//#endregion
|
|
1175
1363
|
//#region src/plugins/content/api.ts
|
|
1176
1364
|
/**
|
|
@@ -1278,7 +1466,7 @@ async function discoverSlugs(dir) {
|
|
|
1278
1466
|
* ```
|
|
1279
1467
|
*/
|
|
1280
1468
|
async function readArticle(ctx, slug, fileLocale, outLocale, isFallback) {
|
|
1281
|
-
const filePath = node_path.default.join(ctx.config.contentDir, slug, `${fileLocale}.md`);
|
|
1469
|
+
const filePath = node_path$1.default.join(ctx.config.contentDir, slug, `${fileLocale}.md`);
|
|
1282
1470
|
let raw;
|
|
1283
1471
|
try {
|
|
1284
1472
|
raw = await (0, node_fs_promises.readFile)(filePath, "utf8");
|
|
@@ -1382,6 +1570,18 @@ function toCard(article) {
|
|
|
1382
1570
|
*/
|
|
1383
1571
|
function createContentApi(ctx) {
|
|
1384
1572
|
return {
|
|
1573
|
+
/**
|
|
1574
|
+
* Load every article across every active locale, returning a locale-keyed
|
|
1575
|
+
* map of date-descending Article arrays. Lazily builds the processor and
|
|
1576
|
+
* discovers slugs, applies locale fallback, excludes drafts in production,
|
|
1577
|
+
* assigns `contentId` after sorting, then emits `content:ready`.
|
|
1578
|
+
*
|
|
1579
|
+
* @returns A locale-keyed map of date-descending articles.
|
|
1580
|
+
* @example
|
|
1581
|
+
* ```ts
|
|
1582
|
+
* const byLocale = await api.loadAll();
|
|
1583
|
+
* ```
|
|
1584
|
+
*/
|
|
1385
1585
|
async loadAll() {
|
|
1386
1586
|
const slugs = ctx.state.slugs ?? await discoverSlugs(ctx.config.contentDir);
|
|
1387
1587
|
ctx.state.slugs = slugs;
|
|
@@ -1409,6 +1609,19 @@ function createContentApi(ctx) {
|
|
|
1409
1609
|
});
|
|
1410
1610
|
return result;
|
|
1411
1611
|
},
|
|
1612
|
+
/**
|
|
1613
|
+
* Resolve and render a single article for one locale with locale fallback.
|
|
1614
|
+
* Throws a `[web] content` error when neither the requested nor the
|
|
1615
|
+
* default-locale file exists.
|
|
1616
|
+
*
|
|
1617
|
+
* @param slug - Article directory name.
|
|
1618
|
+
* @param locale - Requested locale code.
|
|
1619
|
+
* @returns The resolved Article.
|
|
1620
|
+
* @example
|
|
1621
|
+
* ```ts
|
|
1622
|
+
* const article = await api.load("intro", "uk");
|
|
1623
|
+
* ```
|
|
1624
|
+
*/
|
|
1412
1625
|
async load(slug, locale) {
|
|
1413
1626
|
const article = await resolveArticle(ctx, slug, locale);
|
|
1414
1627
|
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.`);
|
|
@@ -1417,10 +1630,33 @@ function createContentApi(ctx) {
|
|
|
1417
1630
|
ctx.state.articles.set(locale, cache);
|
|
1418
1631
|
return article;
|
|
1419
1632
|
},
|
|
1633
|
+
/**
|
|
1634
|
+
* Render a raw Markdown string to HTML through the full pipeline (sanitize
|
|
1635
|
+
* last when `trustedContent` is false). Lazily builds the processor.
|
|
1636
|
+
*
|
|
1637
|
+
* @param md - Raw Markdown source.
|
|
1638
|
+
* @returns The rendered HTML string.
|
|
1639
|
+
* @example
|
|
1640
|
+
* ```ts
|
|
1641
|
+
* const html = await api.renderMarkdown("# Hi");
|
|
1642
|
+
* ```
|
|
1643
|
+
*/
|
|
1420
1644
|
async renderMarkdown(md) {
|
|
1421
1645
|
const processor = ensureProcessor(ctx.state, ctx.config);
|
|
1422
1646
|
return String(await processor.process(md));
|
|
1423
1647
|
},
|
|
1648
|
+
/**
|
|
1649
|
+
* Mark file paths stale for incremental dev rebuilds. Each non-blank path is
|
|
1650
|
+
* added to `dirtyPaths` and its derived slug cache entry is dropped so the
|
|
1651
|
+
* next `loadAll()` re-reads only those files. Empty/whitespace paths are
|
|
1652
|
+
* ignored. Emits `content:invalidated` with the accepted paths.
|
|
1653
|
+
*
|
|
1654
|
+
* @param paths - File paths to invalidate.
|
|
1655
|
+
* @example
|
|
1656
|
+
* ```ts
|
|
1657
|
+
* api.invalidate(["src/content/intro/en.md"]);
|
|
1658
|
+
* ```
|
|
1659
|
+
*/
|
|
1424
1660
|
invalidate(paths) {
|
|
1425
1661
|
const accepted = [];
|
|
1426
1662
|
for (const path of paths) {
|
|
@@ -1433,12 +1669,22 @@ function createContentApi(ctx) {
|
|
|
1433
1669
|
ctx.state.slugs = null;
|
|
1434
1670
|
ctx.emit("content:invalidated", { paths: accepted });
|
|
1435
1671
|
},
|
|
1672
|
+
/**
|
|
1673
|
+
* Project a full Article to a lightweight ArticleCard for list/grid
|
|
1674
|
+
* rendering without shipping rendered HTML.
|
|
1675
|
+
*
|
|
1676
|
+
* @param article - The source article.
|
|
1677
|
+
* @returns The card projection.
|
|
1678
|
+
* @example
|
|
1679
|
+
* ```ts
|
|
1680
|
+
* const card = api.articleToCard(article);
|
|
1681
|
+
* ```
|
|
1682
|
+
*/
|
|
1436
1683
|
articleToCard(article) {
|
|
1437
1684
|
return toCard(article);
|
|
1438
1685
|
}
|
|
1439
1686
|
};
|
|
1440
1687
|
}
|
|
1441
|
-
|
|
1442
1688
|
//#endregion
|
|
1443
1689
|
//#region src/plugins/content/config.ts
|
|
1444
1690
|
/**
|
|
@@ -1459,7 +1705,6 @@ const defaultContentConfig = {
|
|
|
1459
1705
|
extraRehypePlugins: [],
|
|
1460
1706
|
shikiTheme: "github-dark"
|
|
1461
1707
|
};
|
|
1462
|
-
|
|
1463
1708
|
//#endregion
|
|
1464
1709
|
//#region src/plugins/content/events.ts
|
|
1465
1710
|
/**
|
|
@@ -1478,7 +1723,6 @@ const contentEvents = (register) => ({
|
|
|
1478
1723
|
"content:ready": register("All articles loaded across locales"),
|
|
1479
1724
|
"content:invalidated": register("Article paths marked stale for dev rebuild")
|
|
1480
1725
|
});
|
|
1481
|
-
|
|
1482
1726
|
//#endregion
|
|
1483
1727
|
//#region src/plugins/content/state.ts
|
|
1484
1728
|
/**
|
|
@@ -1502,7 +1746,6 @@ function createContentState(_ctx) {
|
|
|
1502
1746
|
dirtyPaths: /* @__PURE__ */ new Set()
|
|
1503
1747
|
};
|
|
1504
1748
|
}
|
|
1505
|
-
|
|
1506
1749
|
//#endregion
|
|
1507
1750
|
//#region src/plugins/content/validate.ts
|
|
1508
1751
|
/**
|
|
@@ -1521,7 +1764,6 @@ function validateContentConfig(config) {
|
|
|
1521
1764
|
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.");
|
|
1522
1765
|
if (typeof config.trustedContent !== "boolean") throw new TypeError("[web] content.trustedContent must be a boolean.");
|
|
1523
1766
|
}
|
|
1524
|
-
|
|
1525
1767
|
//#endregion
|
|
1526
1768
|
//#region src/plugins/content/index.ts
|
|
1527
1769
|
/**
|
|
@@ -1540,7 +1782,6 @@ const contentPlugin = createPlugin$1("content", {
|
|
|
1540
1782
|
onInit: (ctx) => validateContentConfig(ctx.config),
|
|
1541
1783
|
api: contentApi
|
|
1542
1784
|
});
|
|
1543
|
-
|
|
1544
1785
|
//#endregion
|
|
1545
1786
|
//#region src/plugins/site/api.ts
|
|
1546
1787
|
/** Error prefix for all site lifecycle/validation failures. */
|
|
@@ -1632,50 +1873,80 @@ function validateSiteConfig(ctx) {
|
|
|
1632
1873
|
function createSiteApi(ctx) {
|
|
1633
1874
|
const { config } = ctx;
|
|
1634
1875
|
return {
|
|
1876
|
+
/**
|
|
1877
|
+
* Returns the configured site name.
|
|
1878
|
+
*
|
|
1879
|
+
* @returns The human-readable site name from `config.name`.
|
|
1880
|
+
* @example
|
|
1881
|
+
* ```ts
|
|
1882
|
+
* api.name(); // "My Blog"
|
|
1883
|
+
* ```
|
|
1884
|
+
*/
|
|
1635
1885
|
name() {
|
|
1636
1886
|
return config.name;
|
|
1637
1887
|
},
|
|
1888
|
+
/**
|
|
1889
|
+
* Returns the configured absolute base URL of the site.
|
|
1890
|
+
*
|
|
1891
|
+
* @returns The base URL from `config.url`.
|
|
1892
|
+
* @example
|
|
1893
|
+
* ```ts
|
|
1894
|
+
* api.url(); // "https://blog.dev"
|
|
1895
|
+
* ```
|
|
1896
|
+
*/
|
|
1638
1897
|
url() {
|
|
1639
1898
|
return config.url;
|
|
1640
1899
|
},
|
|
1900
|
+
/**
|
|
1901
|
+
* Returns the configured site author/byline.
|
|
1902
|
+
*
|
|
1903
|
+
* @returns The author from `config.author`.
|
|
1904
|
+
* @example
|
|
1905
|
+
* ```ts
|
|
1906
|
+
* api.author(); // "Alex"
|
|
1907
|
+
* ```
|
|
1908
|
+
*/
|
|
1641
1909
|
author() {
|
|
1642
1910
|
return config.author;
|
|
1643
1911
|
},
|
|
1912
|
+
/**
|
|
1913
|
+
* Returns the configured site description.
|
|
1914
|
+
*
|
|
1915
|
+
* @returns The description from `config.description`.
|
|
1916
|
+
* @example
|
|
1917
|
+
* ```ts
|
|
1918
|
+
* api.description(); // "A personal blog about web frameworks."
|
|
1919
|
+
* ```
|
|
1920
|
+
*/
|
|
1644
1921
|
description() {
|
|
1645
1922
|
return config.description;
|
|
1646
1923
|
},
|
|
1924
|
+
/**
|
|
1925
|
+
* Joins a path against the configured base `url` to produce an absolute
|
|
1926
|
+
* canonical URL. An empty path (or "/") returns the base URL unchanged.
|
|
1927
|
+
*
|
|
1928
|
+
* @param path - Relative path for the page, e.g. "/about/".
|
|
1929
|
+
* @returns The absolute canonical URL.
|
|
1930
|
+
* @example
|
|
1931
|
+
* ```ts
|
|
1932
|
+
* api.canonical("/about/"); // "https://blog.dev/about/"
|
|
1933
|
+
* ```
|
|
1934
|
+
*/
|
|
1647
1935
|
canonical(path) {
|
|
1648
1936
|
return joinCanonical(config.url, path);
|
|
1649
1937
|
}
|
|
1650
1938
|
};
|
|
1651
1939
|
}
|
|
1652
|
-
|
|
1653
|
-
//#endregion
|
|
1654
|
-
//#region src/plugins/site/index.ts
|
|
1655
|
-
/**
|
|
1656
|
-
* site — Micro tier. Multi-file layout (index wiring + api.ts + types.ts) so
|
|
1657
|
-
* index.ts stays within the ≤30-line wiring-only hook; logic lives in api.ts.
|
|
1658
|
-
*
|
|
1659
|
-
* Holds global, frozen site metadata (name, url, author, description) and
|
|
1660
|
-
* constructs canonical URLs. Consumed by router/head/build via
|
|
1661
|
-
* `ctx.require(sitePlugin)`. No events, no dependencies, no state.
|
|
1662
|
-
*
|
|
1663
|
-
* @file site plugin wiring harness.
|
|
1664
|
-
* @see README.md
|
|
1665
|
-
*/
|
|
1666
|
-
/** Typed default config (R6: no inline `as`). Consumers override via `pluginConfigs.site`. */
|
|
1667
|
-
const defaultConfig$4 = {
|
|
1668
|
-
name: "",
|
|
1669
|
-
url: "",
|
|
1670
|
-
author: "",
|
|
1671
|
-
description: ""
|
|
1672
|
-
};
|
|
1673
1940
|
const sitePlugin = createPlugin$1("site", {
|
|
1674
|
-
config:
|
|
1941
|
+
config: {
|
|
1942
|
+
name: "",
|
|
1943
|
+
url: "",
|
|
1944
|
+
author: "",
|
|
1945
|
+
description: ""
|
|
1946
|
+
},
|
|
1675
1947
|
onInit: validateSiteConfig,
|
|
1676
1948
|
api: createSiteApi
|
|
1677
1949
|
});
|
|
1678
|
-
|
|
1679
1950
|
//#endregion
|
|
1680
1951
|
//#region src/plugins/router/builders/match.ts
|
|
1681
1952
|
/**
|
|
@@ -1745,7 +2016,6 @@ function matchRoute(compiled, pathname) {
|
|
|
1745
2016
|
}
|
|
1746
2017
|
return null;
|
|
1747
2018
|
}
|
|
1748
|
-
|
|
1749
2019
|
//#endregion
|
|
1750
2020
|
//#region src/plugins/router/api.ts
|
|
1751
2021
|
/**
|
|
@@ -1808,23 +2078,63 @@ function toTypedRoute(entry) {
|
|
|
1808
2078
|
function createApi$4(ctx) {
|
|
1809
2079
|
const { state } = ctx;
|
|
1810
2080
|
return {
|
|
2081
|
+
/**
|
|
2082
|
+
* Match a pathname against the compiled route table (specificity-sorted).
|
|
2083
|
+
*
|
|
2084
|
+
* @param pathname - URL pathname, e.g. `/en/hello/`.
|
|
2085
|
+
* @returns `{ params, route }` for the most specific match, or `null`.
|
|
2086
|
+
* @example
|
|
2087
|
+
* ```ts
|
|
2088
|
+
* api.match("/en/hello/");
|
|
2089
|
+
* ```
|
|
2090
|
+
*/
|
|
1811
2091
|
match(pathname) {
|
|
1812
2092
|
return matchRoute(readTable(state).compiled, pathname);
|
|
1813
2093
|
},
|
|
2094
|
+
/**
|
|
2095
|
+
* Build a URL for a named route from params.
|
|
2096
|
+
*
|
|
2097
|
+
* @param routeName - Route name key from the route map.
|
|
2098
|
+
* @param params - Param values to substitute into the pattern.
|
|
2099
|
+
* @returns The resolved URL string (e.g. `/en/hello/`).
|
|
2100
|
+
* @throws {Error} If `routeName` is unknown.
|
|
2101
|
+
* @example
|
|
2102
|
+
* ```ts
|
|
2103
|
+
* api.toUrl("article", { lang: "en", slug: "hello" });
|
|
2104
|
+
* ```
|
|
2105
|
+
*/
|
|
1814
2106
|
toUrl(routeName, params) {
|
|
1815
2107
|
const entry = readTable(state).byName.get(routeName);
|
|
1816
2108
|
if (!entry) throw new Error(`${ERROR_PREFIX$9}: unknown route name "${routeName}".`);
|
|
1817
2109
|
return entry.toUrl(params);
|
|
1818
2110
|
},
|
|
2111
|
+
/**
|
|
2112
|
+
* All resolved routes as typed URL utilities, in specificity order.
|
|
2113
|
+
*
|
|
2114
|
+
* @returns A fresh read-only array of resolved typed routes.
|
|
2115
|
+
* @example
|
|
2116
|
+
* ```ts
|
|
2117
|
+
* for (const r of api.entries()) r.toUrl({ slug: "x" });
|
|
2118
|
+
* ```
|
|
2119
|
+
*/
|
|
1819
2120
|
entries() {
|
|
1820
2121
|
return readTable(state).compiled.map((entry) => toTypedRoute(entry));
|
|
1821
2122
|
},
|
|
2123
|
+
/**
|
|
2124
|
+
* The typed route set for build-time consumption (declaration order). An API
|
|
2125
|
+
* return, NOT a config readback — preserves per-route types despite erasure.
|
|
2126
|
+
*
|
|
2127
|
+
* @returns A fresh read-only array of the typed route definitions.
|
|
2128
|
+
* @example
|
|
2129
|
+
* ```ts
|
|
2130
|
+
* for (const def of api.manifest()) def._handlers.load?.({}, "en");
|
|
2131
|
+
* ```
|
|
2132
|
+
*/
|
|
1822
2133
|
manifest() {
|
|
1823
2134
|
return [...readTable(state).byName.values()].map((entry) => entry.definition);
|
|
1824
2135
|
}
|
|
1825
2136
|
};
|
|
1826
2137
|
}
|
|
1827
|
-
|
|
1828
2138
|
//#endregion
|
|
1829
2139
|
//#region src/plugins/router/builders/compile.ts
|
|
1830
2140
|
/** Shared `[web]` error prefix for router validation failures. */
|
|
@@ -1986,9 +2296,31 @@ function compileRoute(name, definition, input) {
|
|
|
1986
2296
|
dynamicSegmentCount: countDynamicSegments(pattern),
|
|
1987
2297
|
matchers,
|
|
1988
2298
|
matchFn: createMatchFunction(matchers, input.defaultLocale),
|
|
2299
|
+
/**
|
|
2300
|
+
* Build a URL for this route from params.
|
|
2301
|
+
*
|
|
2302
|
+
* @param params - Param values to substitute.
|
|
2303
|
+
* @returns The resolved relative URL.
|
|
2304
|
+
* @example
|
|
2305
|
+
* ```ts
|
|
2306
|
+
* entry.toUrl({ slug: "x" });
|
|
2307
|
+
* ```
|
|
2308
|
+
*/
|
|
1989
2309
|
toUrl(params) {
|
|
1990
2310
|
return buildUrl(pattern, params, input.baseUrl);
|
|
1991
2311
|
},
|
|
2312
|
+
/**
|
|
2313
|
+
* Build the output file path for this route from params. Honors a custom
|
|
2314
|
+
* `.toFile()` override (captured in `_handlers.toFile`) when present, falling
|
|
2315
|
+
* back to the pattern-derived `…/index.html` path otherwise.
|
|
2316
|
+
*
|
|
2317
|
+
* @param params - Param values to substitute.
|
|
2318
|
+
* @returns The output file path.
|
|
2319
|
+
* @example
|
|
2320
|
+
* ```ts
|
|
2321
|
+
* entry.toFile({ slug: "x" });
|
|
2322
|
+
* ```
|
|
2323
|
+
*/
|
|
1992
2324
|
toFile(params) {
|
|
1993
2325
|
return definition._handlers.toFile?.(params) ?? buildFilePath(pattern, params);
|
|
1994
2326
|
},
|
|
@@ -2049,7 +2381,6 @@ function buildRouterTable(config, baseUrl, locales, defaultLocale) {
|
|
|
2049
2381
|
defaultLocale
|
|
2050
2382
|
});
|
|
2051
2383
|
}
|
|
2052
|
-
|
|
2053
2384
|
//#endregion
|
|
2054
2385
|
//#region src/plugins/router/builders/route-builder.ts
|
|
2055
2386
|
/**
|
|
@@ -2095,28 +2426,108 @@ function route(pattern) {
|
|
|
2095
2426
|
pattern: carrier.pattern,
|
|
2096
2427
|
_meta: carrier._meta,
|
|
2097
2428
|
_handlers: carrier._handlers,
|
|
2429
|
+
/**
|
|
2430
|
+
* Attach a data loader; widens the data generic for downstream handlers.
|
|
2431
|
+
*
|
|
2432
|
+
* @param loader - The loader producing this route's data.
|
|
2433
|
+
* @returns The same builder, with the data generic widened.
|
|
2434
|
+
* @example
|
|
2435
|
+
* ```ts
|
|
2436
|
+
* route("/{slug}/").load(({ slug }) => ({ slug }));
|
|
2437
|
+
* ```
|
|
2438
|
+
*/
|
|
2098
2439
|
load(loader) {
|
|
2099
2440
|
return set("load", loader);
|
|
2100
2441
|
},
|
|
2442
|
+
/**
|
|
2443
|
+
* Attach a layout wrapper component.
|
|
2444
|
+
*
|
|
2445
|
+
* @param component - The layout component.
|
|
2446
|
+
* @returns The same builder for chaining.
|
|
2447
|
+
* @example
|
|
2448
|
+
* ```ts
|
|
2449
|
+
* route("/").layout((children) => children);
|
|
2450
|
+
* ```
|
|
2451
|
+
*/
|
|
2101
2452
|
layout(component) {
|
|
2102
2453
|
return set("layout", component);
|
|
2103
2454
|
},
|
|
2455
|
+
/**
|
|
2456
|
+
* Attach the page render handler.
|
|
2457
|
+
*
|
|
2458
|
+
* @param handler - The render handler.
|
|
2459
|
+
* @returns The same builder for chaining.
|
|
2460
|
+
* @example
|
|
2461
|
+
* ```ts
|
|
2462
|
+
* route("/").render(() => null);
|
|
2463
|
+
* ```
|
|
2464
|
+
*/
|
|
2104
2465
|
render(handler) {
|
|
2105
2466
|
return set("render", handler);
|
|
2106
2467
|
},
|
|
2468
|
+
/**
|
|
2469
|
+
* Attach the head/SEO handler.
|
|
2470
|
+
*
|
|
2471
|
+
* @param handler - The head handler.
|
|
2472
|
+
* @returns The same builder for chaining.
|
|
2473
|
+
* @example
|
|
2474
|
+
* ```ts
|
|
2475
|
+
* route("/").head(() => ({ title: "Home" }));
|
|
2476
|
+
* ```
|
|
2477
|
+
*/
|
|
2107
2478
|
head(handler) {
|
|
2108
2479
|
return set("head", handler);
|
|
2109
2480
|
},
|
|
2481
|
+
/**
|
|
2482
|
+
* Attach a static-generation param producer.
|
|
2483
|
+
*
|
|
2484
|
+
* @param handler - The param producer.
|
|
2485
|
+
* @returns The same builder for chaining.
|
|
2486
|
+
* @example
|
|
2487
|
+
* ```ts
|
|
2488
|
+
* route("/{slug}/").generate(() => [{ slug: "x" }]);
|
|
2489
|
+
* ```
|
|
2490
|
+
*/
|
|
2110
2491
|
generate(handler) {
|
|
2111
2492
|
return set("generate", handler);
|
|
2112
2493
|
},
|
|
2494
|
+
/**
|
|
2495
|
+
* Merge an arbitrary metadata bag into the route's `_meta`.
|
|
2496
|
+
*
|
|
2497
|
+
* @param meta - Metadata to merge.
|
|
2498
|
+
* @returns The same builder for chaining.
|
|
2499
|
+
* @example
|
|
2500
|
+
* ```ts
|
|
2501
|
+
* route("/").meta({ activeTab: "home" });
|
|
2502
|
+
* ```
|
|
2503
|
+
*/
|
|
2113
2504
|
meta(meta) {
|
|
2114
2505
|
Object.assign(carrier._meta, meta);
|
|
2115
2506
|
return builder;
|
|
2116
2507
|
},
|
|
2508
|
+
/**
|
|
2509
|
+
* Attach a JSON serializer for the route's data.
|
|
2510
|
+
*
|
|
2511
|
+
* @param handler - The JSON serializer.
|
|
2512
|
+
* @returns The same builder for chaining.
|
|
2513
|
+
* @example
|
|
2514
|
+
* ```ts
|
|
2515
|
+
* route("/api/").toJson(() => ({ ok: true }));
|
|
2516
|
+
* ```
|
|
2517
|
+
*/
|
|
2117
2518
|
toJson(handler) {
|
|
2118
2519
|
return set("toJson", handler);
|
|
2119
2520
|
},
|
|
2521
|
+
/**
|
|
2522
|
+
* Override the output file-path producer.
|
|
2523
|
+
*
|
|
2524
|
+
* @param handler - The file-path producer.
|
|
2525
|
+
* @returns The same builder for chaining.
|
|
2526
|
+
* @example
|
|
2527
|
+
* ```ts
|
|
2528
|
+
* route("/feed/").toFile(() => "feed.xml");
|
|
2529
|
+
* ```
|
|
2530
|
+
*/
|
|
2120
2531
|
toFile(handler) {
|
|
2121
2532
|
return set("toFile", handler);
|
|
2122
2533
|
}
|
|
@@ -2137,7 +2548,6 @@ function route(pattern) {
|
|
|
2137
2548
|
function defineRoutes(routes) {
|
|
2138
2549
|
return routes;
|
|
2139
2550
|
}
|
|
2140
|
-
|
|
2141
2551
|
//#endregion
|
|
2142
2552
|
//#region src/plugins/router/state.ts
|
|
2143
2553
|
/**
|
|
@@ -2156,25 +2566,16 @@ function defineRoutes(routes) {
|
|
|
2156
2566
|
function createState$4(_ctx) {
|
|
2157
2567
|
return { table: null };
|
|
2158
2568
|
}
|
|
2159
|
-
|
|
2160
|
-
//#endregion
|
|
2161
|
-
//#region src/plugins/router/index.ts
|
|
2162
|
-
/**
|
|
2163
|
-
* @file router — Complex plugin wiring (logic in builders/, api.ts, state.ts).
|
|
2164
|
-
* @see README.md
|
|
2165
|
-
*/
|
|
2166
|
-
/** Default router config: empty route map (validated in onInit), hybrid mode. */
|
|
2167
|
-
const defaultConfig$3 = {
|
|
2168
|
-
routes: {},
|
|
2169
|
-
mode: "hybrid"
|
|
2170
|
-
};
|
|
2171
2569
|
const routerPlugin = createPlugin$1("router", {
|
|
2172
2570
|
depends: [sitePlugin, i18nPlugin],
|
|
2173
2571
|
helpers: {
|
|
2174
2572
|
route,
|
|
2175
2573
|
defineRoutes
|
|
2176
2574
|
},
|
|
2177
|
-
config:
|
|
2575
|
+
config: {
|
|
2576
|
+
routes: {},
|
|
2577
|
+
mode: "hybrid"
|
|
2578
|
+
},
|
|
2178
2579
|
createState: createState$4,
|
|
2179
2580
|
api: createApi$4,
|
|
2180
2581
|
onInit(ctx) {
|
|
@@ -2183,7 +2584,6 @@ const routerPlugin = createPlugin$1("router", {
|
|
|
2183
2584
|
ctx.state.table = buildRouterTable(ctx.config, baseUrl, i18n.locales(), i18n.defaultLocale());
|
|
2184
2585
|
}
|
|
2185
2586
|
});
|
|
2186
|
-
|
|
2187
2587
|
//#endregion
|
|
2188
2588
|
//#region src/plugins/head/primitives.ts
|
|
2189
2589
|
/** OG/Twitter article-meta property prefixes (factored to satisfy no-duplicate-string). */
|
|
@@ -2347,7 +2747,6 @@ function buildArticleHead(articleMeta, canonicalUrl) {
|
|
|
2347
2747
|
elements.push(jsonLd(ld));
|
|
2348
2748
|
return elements;
|
|
2349
2749
|
}
|
|
2350
|
-
|
|
2351
2750
|
//#endregion
|
|
2352
2751
|
//#region src/plugins/head/compose.ts
|
|
2353
2752
|
/**
|
|
@@ -2508,7 +2907,6 @@ function serializeElement(element) {
|
|
|
2508
2907
|
function serializeHead(elements) {
|
|
2509
2908
|
return elements.map((element) => serializeElement(element)).join("");
|
|
2510
2909
|
}
|
|
2511
|
-
|
|
2512
2910
|
//#endregion
|
|
2513
2911
|
//#region src/plugins/head/api.ts
|
|
2514
2912
|
/**
|
|
@@ -2550,7 +2948,19 @@ function readDefaults(state) {
|
|
|
2550
2948
|
* ```
|
|
2551
2949
|
*/
|
|
2552
2950
|
function createApi$3(ctx) {
|
|
2553
|
-
return {
|
|
2951
|
+
return {
|
|
2952
|
+
/**
|
|
2953
|
+
* Compose the final `<head>` inner HTML for a route (pulled by `build`).
|
|
2954
|
+
*
|
|
2955
|
+
* @param route - The resolved route descriptor (incl. its `.head()` HeadConfig).
|
|
2956
|
+
* @param data - The page data object passed to the route's loader/render.
|
|
2957
|
+
* @returns The serialized inner HTML of `<head>`.
|
|
2958
|
+
* @example
|
|
2959
|
+
* ```ts
|
|
2960
|
+
* api.render(route, { title: "Post" });
|
|
2961
|
+
* ```
|
|
2962
|
+
*/
|
|
2963
|
+
render(route, data) {
|
|
2554
2964
|
return serializeHead(composeHead({
|
|
2555
2965
|
route,
|
|
2556
2966
|
data,
|
|
@@ -2561,7 +2971,6 @@ function createApi$3(ctx) {
|
|
|
2561
2971
|
}));
|
|
2562
2972
|
} };
|
|
2563
2973
|
}
|
|
2564
|
-
|
|
2565
2974
|
//#endregion
|
|
2566
2975
|
//#region src/plugins/head/config.ts
|
|
2567
2976
|
/** Error prefix for all head config-validation failures. */
|
|
@@ -2616,7 +3025,6 @@ function normalizeHeadConfig(config) {
|
|
|
2616
3025
|
if (config.twitterHandle !== void 0) defaults.twitterHandle = config.twitterHandle;
|
|
2617
3026
|
return Object.freeze(defaults);
|
|
2618
3027
|
}
|
|
2619
|
-
|
|
2620
3028
|
//#endregion
|
|
2621
3029
|
//#region src/plugins/head/helpers.ts
|
|
2622
3030
|
/**
|
|
@@ -2645,7 +3053,6 @@ const headHelpers = {
|
|
|
2645
3053
|
feedLink,
|
|
2646
3054
|
buildArticleHead
|
|
2647
3055
|
};
|
|
2648
|
-
|
|
2649
3056
|
//#endregion
|
|
2650
3057
|
//#region src/plugins/head/state.ts
|
|
2651
3058
|
/**
|
|
@@ -2666,7 +3073,6 @@ const headHelpers = {
|
|
|
2666
3073
|
function createState$3(_ctx) {
|
|
2667
3074
|
return { defaults: null };
|
|
2668
3075
|
}
|
|
2669
|
-
|
|
2670
3076
|
//#endregion
|
|
2671
3077
|
//#region src/plugins/head/index.ts
|
|
2672
3078
|
/**
|
|
@@ -2687,7 +3093,6 @@ const headPlugin = createPlugin$1("head", {
|
|
|
2687
3093
|
ctx.state.defaults = normalizeHeadConfig(ctx.config);
|
|
2688
3094
|
}
|
|
2689
3095
|
});
|
|
2690
|
-
|
|
2691
3096
|
//#endregion
|
|
2692
3097
|
//#region src/plugins/build/phases/bundle.ts
|
|
2693
3098
|
/**
|
|
@@ -2761,7 +3166,7 @@ async function runOne(ctx, runner, kind, entrypoints, outdir, minify) {
|
|
|
2761
3166
|
});
|
|
2762
3167
|
if (!result.success) throw new Error(`[web] build.bundle ${kind} build failed`);
|
|
2763
3168
|
const hashed = {};
|
|
2764
|
-
for (const output of result.outputs) hashed[node_path.default.basename(output.path)] = output.path;
|
|
3169
|
+
for (const output of result.outputs) hashed[node_path$1.default.basename(output.path)] = output.path;
|
|
2765
3170
|
ctx.state.buildCache.set(kind, hashed);
|
|
2766
3171
|
ctx.log.debug("build:bundle", {
|
|
2767
3172
|
kind,
|
|
@@ -2786,10 +3191,9 @@ async function bundle(ctx, options = {}) {
|
|
|
2786
3191
|
const { minify, outDir } = ctx.config;
|
|
2787
3192
|
const cssEntrypoints = options.cssEntrypoints ?? resolveEntrypoints(CSS_ENTRY_CANDIDATES);
|
|
2788
3193
|
const jsEntrypoints = options.jsEntrypoints ?? resolveEntrypoints(JS_ENTRY_CANDIDATES);
|
|
2789
|
-
await runOne(ctx, runner, "css", cssEntrypoints, node_path.default.join(outDir, "assets"), minify);
|
|
2790
|
-
await runOne(ctx, runner, "js", jsEntrypoints, node_path.default.join(outDir, "assets"), minify);
|
|
3194
|
+
await runOne(ctx, runner, "css", cssEntrypoints, node_path$1.default.join(outDir, "assets"), minify);
|
|
3195
|
+
await runOne(ctx, runner, "js", jsEntrypoints, node_path$1.default.join(outDir, "assets"), minify);
|
|
2791
3196
|
}
|
|
2792
|
-
|
|
2793
3197
|
//#endregion
|
|
2794
3198
|
//#region src/plugins/build/phases/content.ts
|
|
2795
3199
|
/**
|
|
@@ -2832,7 +3236,6 @@ function readCachedContent(ctx) {
|
|
|
2832
3236
|
const cached = ctx.state.buildCache.get(CONTENT_CACHE_KEY);
|
|
2833
3237
|
return cached instanceof Map ? cached : /* @__PURE__ */ new Map();
|
|
2834
3238
|
}
|
|
2835
|
-
|
|
2836
3239
|
//#endregion
|
|
2837
3240
|
//#region src/plugins/build/phases/feeds.ts
|
|
2838
3241
|
/**
|
|
@@ -2907,14 +3310,13 @@ async function generateFeeds(ctx) {
|
|
|
2907
3310
|
};
|
|
2908
3311
|
await (0, node_fs_promises.mkdir)(ctx.config.outDir, { recursive: true });
|
|
2909
3312
|
await Promise.all([
|
|
2910
|
-
(0, node_fs_promises.writeFile)(node_path.default.join(ctx.config.outDir, "feed.xml"), result.rss, "utf8"),
|
|
2911
|
-
(0, node_fs_promises.writeFile)(node_path.default.join(ctx.config.outDir, "atom.xml"), result.atom, "utf8"),
|
|
2912
|
-
(0, node_fs_promises.writeFile)(node_path.default.join(ctx.config.outDir, "feed.json"), result.json, "utf8")
|
|
3313
|
+
(0, node_fs_promises.writeFile)(node_path$1.default.join(ctx.config.outDir, "feed.xml"), result.rss, "utf8"),
|
|
3314
|
+
(0, node_fs_promises.writeFile)(node_path$1.default.join(ctx.config.outDir, "atom.xml"), result.atom, "utf8"),
|
|
3315
|
+
(0, node_fs_promises.writeFile)(node_path$1.default.join(ctx.config.outDir, "feed.json"), result.json, "utf8")
|
|
2913
3316
|
]);
|
|
2914
3317
|
ctx.log.debug("build:feeds", { items: guids.length });
|
|
2915
3318
|
return result;
|
|
2916
3319
|
}
|
|
2917
|
-
|
|
2918
3320
|
//#endregion
|
|
2919
3321
|
//#region src/plugins/build/phases/images.ts
|
|
2920
3322
|
/**
|
|
@@ -2942,7 +3344,7 @@ async function processImages(ctx, options = {}) {
|
|
|
2942
3344
|
return 0;
|
|
2943
3345
|
}
|
|
2944
3346
|
const sourceDirectories = options.sourceDirectories ?? IMAGE_SOURCE_DIRECTORIES;
|
|
2945
|
-
const target = node_path.default.join(ctx.config.outDir, "assets");
|
|
3347
|
+
const target = node_path$1.default.join(ctx.config.outDir, "assets");
|
|
2946
3348
|
let copied = 0;
|
|
2947
3349
|
for (const directory of sourceDirectories) {
|
|
2948
3350
|
if (!(0, node_fs.existsSync)(directory)) continue;
|
|
@@ -2954,7 +3356,6 @@ async function processImages(ctx, options = {}) {
|
|
|
2954
3356
|
ctx.log.debug("build:images", { copied });
|
|
2955
3357
|
return copied;
|
|
2956
3358
|
}
|
|
2957
|
-
|
|
2958
3359
|
//#endregion
|
|
2959
3360
|
//#region src/plugins/build/phases/og-images.tsx
|
|
2960
3361
|
/**
|
|
@@ -2968,8 +3369,6 @@ const DEFAULT_SIZE = {
|
|
|
2968
3369
|
width: 1200,
|
|
2969
3370
|
height: 630
|
|
2970
3371
|
};
|
|
2971
|
-
/** The fixed concurrency bound for the OG render pool. */
|
|
2972
|
-
const OG_CONCURRENCY = 4;
|
|
2973
3372
|
/** Recognized font file extensions. */
|
|
2974
3373
|
const FONT_EXTENSIONS$1 = [
|
|
2975
3374
|
".ttf",
|
|
@@ -3090,7 +3489,7 @@ async function generateOgImages(ctx, options = {}) {
|
|
|
3090
3489
|
const articles = selectArticles(readCachedContent(ctx));
|
|
3091
3490
|
const cache = ctx.state.ogImageHashCache;
|
|
3092
3491
|
await loadDiskCache(ctx.config.outDir, cache);
|
|
3093
|
-
const limit = pLimit(
|
|
3492
|
+
const limit = pLimit(4);
|
|
3094
3493
|
let active = 0;
|
|
3095
3494
|
let peakConcurrency = 0;
|
|
3096
3495
|
let rendered = 0;
|
|
@@ -3163,7 +3562,6 @@ async function persistDiskCache(outDir, cache) {
|
|
|
3163
3562
|
await (0, node_fs_promises.mkdir)(dir, { recursive: true });
|
|
3164
3563
|
await (0, node_fs_promises.writeFile)(node_path.default.join(dir, "og-images.json"), JSON.stringify(Object.fromEntries(cache)), "utf8");
|
|
3165
3564
|
}
|
|
3166
|
-
|
|
3167
3565
|
//#endregion
|
|
3168
3566
|
//#region src/plugins/build/phases/pages.tsx
|
|
3169
3567
|
/**
|
|
@@ -3331,7 +3729,6 @@ async function renderPages(ctx) {
|
|
|
3331
3729
|
rootHtml: root?.html ?? null
|
|
3332
3730
|
};
|
|
3333
3731
|
}
|
|
3334
|
-
|
|
3335
3732
|
//#endregion
|
|
3336
3733
|
//#region src/plugins/build/phases/sitemap.ts
|
|
3337
3734
|
/**
|
|
@@ -3405,7 +3802,7 @@ async function generateSitemap(ctx) {
|
|
|
3405
3802
|
const xml = serializeSitemap(urls);
|
|
3406
3803
|
const robots = `User-agent: *\nAllow: /\nSitemap: ${site.canonical("/sitemap.xml")}\n`;
|
|
3407
3804
|
await (0, node_fs_promises.mkdir)(ctx.config.outDir, { recursive: true });
|
|
3408
|
-
await Promise.all([(0, node_fs_promises.writeFile)(node_path.default.join(ctx.config.outDir, "sitemap.xml"), xml, "utf8"), (0, node_fs_promises.writeFile)(node_path.default.join(ctx.config.outDir, "robots.txt"), robots, "utf8")]);
|
|
3805
|
+
await Promise.all([(0, node_fs_promises.writeFile)(node_path$1.default.join(ctx.config.outDir, "sitemap.xml"), xml, "utf8"), (0, node_fs_promises.writeFile)(node_path$1.default.join(ctx.config.outDir, "robots.txt"), robots, "utf8")]);
|
|
3409
3806
|
ctx.log.debug("build:sitemap", { urls: urls.length });
|
|
3410
3807
|
return {
|
|
3411
3808
|
urls,
|
|
@@ -3413,7 +3810,6 @@ async function generateSitemap(ctx) {
|
|
|
3413
3810
|
robots
|
|
3414
3811
|
};
|
|
3415
3812
|
}
|
|
3416
|
-
|
|
3417
3813
|
//#endregion
|
|
3418
3814
|
//#region src/plugins/build/pipeline.ts
|
|
3419
3815
|
/**
|
|
@@ -3534,7 +3930,7 @@ async function runPipeline(ctx, options) {
|
|
|
3534
3930
|
const pages = await withPhase(phaseContext, "pages", () => renderPages(phaseContext));
|
|
3535
3931
|
await runOutputs(phaseContext);
|
|
3536
3932
|
await withPhase(phaseContext, "root-index", async () => {
|
|
3537
|
-
if (pages.rootHtml !== null) await (0, node_fs_promises.writeFile)(node_path.default.join(outDir, "index.html"), pages.rootHtml, "utf8");
|
|
3933
|
+
if (pages.rootHtml !== null) await (0, node_fs_promises.writeFile)(node_path$1.default.join(outDir, "index.html"), pages.rootHtml, "utf8");
|
|
3538
3934
|
});
|
|
3539
3935
|
const result = {
|
|
3540
3936
|
outDir,
|
|
@@ -3544,7 +3940,6 @@ async function runPipeline(ctx, options) {
|
|
|
3544
3940
|
phaseContext.emit("build:complete", result);
|
|
3545
3941
|
return result;
|
|
3546
3942
|
}
|
|
3547
|
-
|
|
3548
3943
|
//#endregion
|
|
3549
3944
|
//#region src/plugins/build/api.ts
|
|
3550
3945
|
/**
|
|
@@ -3583,9 +3978,29 @@ const defaultConfig$1 = {
|
|
|
3583
3978
|
*/
|
|
3584
3979
|
function createApi$2(ctx) {
|
|
3585
3980
|
return {
|
|
3981
|
+
/**
|
|
3982
|
+
* Run the full SSG pipeline and write the site to disk.
|
|
3983
|
+
*
|
|
3984
|
+
* @param options - Optional run overrides.
|
|
3985
|
+
* @param options.outDir - Override the configured output directory for this run.
|
|
3986
|
+
* @returns The build result (outDir, pageCount, durationMs).
|
|
3987
|
+
* @example
|
|
3988
|
+
* ```ts
|
|
3989
|
+
* await api.run({ outDir: "./preview" });
|
|
3990
|
+
* ```
|
|
3991
|
+
*/
|
|
3586
3992
|
run(options) {
|
|
3587
3993
|
return runPipeline(ctx, options);
|
|
3588
3994
|
},
|
|
3995
|
+
/**
|
|
3996
|
+
* List the phases in execution order (introspection / tooling).
|
|
3997
|
+
*
|
|
3998
|
+
* @returns A fresh array of the static ordered phase names.
|
|
3999
|
+
* @example
|
|
4000
|
+
* ```ts
|
|
4001
|
+
* api.phases();
|
|
4002
|
+
* ```
|
|
4003
|
+
*/
|
|
3589
4004
|
phases() {
|
|
3590
4005
|
return [...PHASE_ORDER];
|
|
3591
4006
|
}
|
|
@@ -3620,7 +4035,6 @@ function validateConfig$1(config) {
|
|
|
3620
4035
|
if (typeof config.outDir !== "string" || config.outDir.trim().length === 0) throw new Error(`${ERROR_PREFIX$5}.outDir: must be a non-empty string.`);
|
|
3621
4036
|
if (config.ogImage) validateFonts(config.ogImage);
|
|
3622
4037
|
}
|
|
3623
|
-
|
|
3624
4038
|
//#endregion
|
|
3625
4039
|
//#region src/plugins/build/events.ts
|
|
3626
4040
|
/**
|
|
@@ -3640,7 +4054,6 @@ function createEvents(register) {
|
|
|
3640
4054
|
"build:complete": register("Emitted once after a successful build run")
|
|
3641
4055
|
};
|
|
3642
4056
|
}
|
|
3643
|
-
|
|
3644
4057
|
//#endregion
|
|
3645
4058
|
//#region src/plugins/build/state.ts
|
|
3646
4059
|
/**
|
|
@@ -3667,7 +4080,6 @@ function createState$2(ctx) {
|
|
|
3667
4080
|
ogImageHashCache: /* @__PURE__ */ new Map()
|
|
3668
4081
|
};
|
|
3669
4082
|
}
|
|
3670
|
-
|
|
3671
4083
|
//#endregion
|
|
3672
4084
|
//#region src/plugins/build/index.ts
|
|
3673
4085
|
/**
|
|
@@ -3688,7 +4100,6 @@ const buildPlugin = createPlugin$1("build", {
|
|
|
3688
4100
|
api: createApi$2,
|
|
3689
4101
|
onInit: (ctx) => validateConfig$1(ctx.config)
|
|
3690
4102
|
});
|
|
3691
|
-
|
|
3692
4103
|
//#endregion
|
|
3693
4104
|
//#region src/plugins/deploy/wrangler.ts
|
|
3694
4105
|
/**
|
|
@@ -3795,9 +4206,9 @@ function guardBranch(branch) {
|
|
|
3795
4206
|
* assertWithinRoot("dist", process.cwd());
|
|
3796
4207
|
*/
|
|
3797
4208
|
function assertWithinRoot(outDir, root) {
|
|
3798
|
-
const resolved = node_path.default.isAbsolute(outDir) ? node_path.default.resolve(outDir) : node_path.default.resolve(root, outDir);
|
|
3799
|
-
const rootResolved = node_path.default.resolve(root);
|
|
3800
|
-
if (resolved !== rootResolved && !resolved.startsWith(rootResolved + node_path.default.sep)) throw deployError("ERR_DEPLOY_PATH_TRAVERSAL", `${ERROR_PREFIX$4}: outDir ${JSON.stringify(outDir)} resolves outside the project root.\n Point outDir at a directory inside ${JSON.stringify(rootResolved)}.`);
|
|
4209
|
+
const resolved = node_path$1.default.isAbsolute(outDir) ? node_path$1.default.resolve(outDir) : node_path$1.default.resolve(root, outDir);
|
|
4210
|
+
const rootResolved = node_path$1.default.resolve(root);
|
|
4211
|
+
if (resolved !== rootResolved && !resolved.startsWith(rootResolved + node_path$1.default.sep)) throw deployError("ERR_DEPLOY_PATH_TRAVERSAL", `${ERROR_PREFIX$4}: outDir ${JSON.stringify(outDir)} resolves outside the project root.\n Point outDir at a directory inside ${JSON.stringify(rootResolved)}.`);
|
|
3801
4212
|
return resolved;
|
|
3802
4213
|
}
|
|
3803
4214
|
/**
|
|
@@ -3866,8 +4277,6 @@ const ERROR_SIGNATURES = [
|
|
|
3866
4277
|
advice: "A network failure occurred. Check connectivity and retry."
|
|
3867
4278
|
}
|
|
3868
4279
|
];
|
|
3869
|
-
/** Number of trailing characters of scrubbed stderr to surface on an unknown failure. */
|
|
3870
|
-
const STDERR_TAIL_LENGTH = 500;
|
|
3871
4280
|
/**
|
|
3872
4281
|
* Map a non-zero wrangler exit and scrubbed stderr to an actionable error
|
|
3873
4282
|
* `code` + message. Matching is case-insensitive against the scrubbed stderr;
|
|
@@ -3888,7 +4297,7 @@ function classifyWranglerError(exitCode, scrubbedStderr) {
|
|
|
3888
4297
|
};
|
|
3889
4298
|
return {
|
|
3890
4299
|
code: "ERR_DEPLOY_WRANGLER_FAILED",
|
|
3891
|
-
message: `${ERROR_PREFIX$4}: wrangler failed (exit ${exitCode}).\n ${scrubbedStderr.trim().slice(-
|
|
4300
|
+
message: `${ERROR_PREFIX$4}: wrangler failed (exit ${exitCode}).\n ${scrubbedStderr.trim().slice(-500)}`
|
|
3892
4301
|
};
|
|
3893
4302
|
}
|
|
3894
4303
|
/**
|
|
@@ -3947,7 +4356,6 @@ async function runWrangler(input) {
|
|
|
3947
4356
|
exitCode
|
|
3948
4357
|
};
|
|
3949
4358
|
}
|
|
3950
|
-
|
|
3951
4359
|
//#endregion
|
|
3952
4360
|
//#region src/plugins/deploy/generators/github-workflow.ts
|
|
3953
4361
|
/**
|
|
@@ -4002,7 +4410,6 @@ jobs:
|
|
|
4002
4410
|
command: pages deploy dist --project-name ${input.slug}
|
|
4003
4411
|
`;
|
|
4004
4412
|
}
|
|
4005
|
-
|
|
4006
4413
|
//#endregion
|
|
4007
4414
|
//#region src/plugins/deploy/generators/wrangler-config.ts
|
|
4008
4415
|
/**
|
|
@@ -4041,12 +4448,11 @@ function generateWranglerConfig(input) {
|
|
|
4041
4448
|
*/
|
|
4042
4449
|
async function readWranglerConfig(cwd) {
|
|
4043
4450
|
try {
|
|
4044
|
-
return await (0, node_fs_promises.readFile)(node_path.default.join(cwd, "wrangler.jsonc"), "utf8");
|
|
4451
|
+
return await (0, node_fs_promises.readFile)(node_path$1.default.join(cwd, "wrangler.jsonc"), "utf8");
|
|
4045
4452
|
} catch {
|
|
4046
4453
|
return null;
|
|
4047
4454
|
}
|
|
4048
4455
|
}
|
|
4049
|
-
|
|
4050
4456
|
//#endregion
|
|
4051
4457
|
//#region src/plugins/deploy/init.ts
|
|
4052
4458
|
/**
|
|
@@ -4068,7 +4474,7 @@ const WORKFLOW_PATH = ".github/workflows/deploy.yml";
|
|
|
4068
4474
|
*/
|
|
4069
4475
|
async function readMaybe(cwd, relativePath) {
|
|
4070
4476
|
try {
|
|
4071
|
-
return await (0, node_fs_promises.readFile)(node_path.default.join(cwd, relativePath), "utf8");
|
|
4477
|
+
return await (0, node_fs_promises.readFile)(node_path$1.default.join(cwd, relativePath), "utf8");
|
|
4072
4478
|
} catch {
|
|
4073
4479
|
return null;
|
|
4074
4480
|
}
|
|
@@ -4146,11 +4552,10 @@ async function reconcile(input) {
|
|
|
4146
4552
|
result.skipped.push(relativePath);
|
|
4147
4553
|
return;
|
|
4148
4554
|
}
|
|
4149
|
-
await (0, node_fs_promises.mkdir)(node_path.default.dirname(node_path.default.join(cwd, relativePath)), { recursive: true });
|
|
4150
|
-
await (0, node_fs_promises.writeFile)(node_path.default.join(cwd, relativePath), expected, "utf8");
|
|
4555
|
+
await (0, node_fs_promises.mkdir)(node_path$1.default.dirname(node_path$1.default.join(cwd, relativePath)), { recursive: true });
|
|
4556
|
+
await (0, node_fs_promises.writeFile)(node_path$1.default.join(cwd, relativePath), expected, "utf8");
|
|
4151
4557
|
result.written.push(relativePath);
|
|
4152
4558
|
}
|
|
4153
|
-
|
|
4154
4559
|
//#endregion
|
|
4155
4560
|
//#region src/plugins/deploy/preflight.ts
|
|
4156
4561
|
/**
|
|
@@ -4202,7 +4607,7 @@ async function inspectOutdir(dir) {
|
|
|
4202
4607
|
if (current === void 0) break;
|
|
4203
4608
|
const entries = await (0, node_fs_promises.readdir)(current, { withFileTypes: true });
|
|
4204
4609
|
for (const entry of entries) {
|
|
4205
|
-
const entryPath = node_path.default.join(current, entry.name);
|
|
4610
|
+
const entryPath = node_path$1.default.join(current, entry.name);
|
|
4206
4611
|
if (entry.isDirectory()) stack.push(entryPath);
|
|
4207
4612
|
else if (entry.isFile()) {
|
|
4208
4613
|
result.fileCount += 1;
|
|
@@ -4230,13 +4635,13 @@ async function inspectOutdir(dir) {
|
|
|
4230
4635
|
* await runPreflight(config, process.cwd());
|
|
4231
4636
|
*/
|
|
4232
4637
|
async function runPreflight(config, root, env = process.env) {
|
|
4233
|
-
const wranglerPath = node_path.default.join(root, "wrangler.jsonc");
|
|
4638
|
+
const wranglerPath = node_path$1.default.join(root, "wrangler.jsonc");
|
|
4234
4639
|
try {
|
|
4235
4640
|
await (0, node_fs_promises.stat)(wranglerPath);
|
|
4236
4641
|
} catch {
|
|
4237
4642
|
throw deployError("ERR_DEPLOY_NO_WRANGLER_CONFIG", `${ERROR_PREFIX$3}: wrangler.jsonc not found.\n Run \`app.deploy.init()\` to scaffold it, then retry.`);
|
|
4238
4643
|
}
|
|
4239
|
-
const stats = await inspectOutdir(node_path.default.isAbsolute(config.outDir) ? node_path.default.resolve(config.outDir) : node_path.default.resolve(root, config.outDir)).catch(() => {
|
|
4644
|
+
const stats = await inspectOutdir(node_path$1.default.isAbsolute(config.outDir) ? node_path$1.default.resolve(config.outDir) : node_path$1.default.resolve(root, config.outDir)).catch(() => {
|
|
4240
4645
|
throw deployError("ERR_DEPLOY_EMPTY_OUTDIR", `${ERROR_PREFIX$3}: outDir ${JSON.stringify(config.outDir)} is missing.\n Run your build first, then retry.`);
|
|
4241
4646
|
});
|
|
4242
4647
|
if (stats.fileCount === 0) throw deployError("ERR_DEPLOY_EMPTY_OUTDIR", `${ERROR_PREFIX$3}: outDir ${JSON.stringify(config.outDir)} is empty — nothing to deploy.`);
|
|
@@ -4244,7 +4649,6 @@ async function runPreflight(config, root, env = process.env) {
|
|
|
4244
4649
|
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.`);
|
|
4245
4650
|
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.`);
|
|
4246
4651
|
}
|
|
4247
|
-
|
|
4248
4652
|
//#endregion
|
|
4249
4653
|
//#region src/plugins/deploy/slug.ts
|
|
4250
4654
|
/**
|
|
@@ -4285,7 +4689,6 @@ function toSlug(name) {
|
|
|
4285
4689
|
slug = slug.slice(0, end);
|
|
4286
4690
|
return slug.length > 0 ? slug : FALLBACK_SLUG;
|
|
4287
4691
|
}
|
|
4288
|
-
|
|
4289
4692
|
//#endregion
|
|
4290
4693
|
//#region src/plugins/deploy/api.ts
|
|
4291
4694
|
/** Error prefix for deploy config/validation failures (spec/11 Part-3). */
|
|
@@ -4325,6 +4728,15 @@ function validateConfig(ctx) {
|
|
|
4325
4728
|
*/
|
|
4326
4729
|
function createApi$1(ctx) {
|
|
4327
4730
|
return {
|
|
4731
|
+
/**
|
|
4732
|
+
* Deploy the built outDir to Cloudflare Pages via the wrangler subprocess.
|
|
4733
|
+
*
|
|
4734
|
+
* @param options - Optional branch override and build toggle.
|
|
4735
|
+
* @returns The deploy result (url, deploymentId, branch, durationMs).
|
|
4736
|
+
* @throws {Error} With a `code` from the deploy error taxonomy on any failure.
|
|
4737
|
+
* @example
|
|
4738
|
+
* await api.run();
|
|
4739
|
+
*/
|
|
4328
4740
|
async run(options = {}) {
|
|
4329
4741
|
const root = process.cwd();
|
|
4330
4742
|
const slug = toSlug(ctx.require(sitePlugin).name());
|
|
@@ -4364,10 +4776,25 @@ function createApi$1(ctx) {
|
|
|
4364
4776
|
});
|
|
4365
4777
|
return result;
|
|
4366
4778
|
},
|
|
4779
|
+
/**
|
|
4780
|
+
* Return the most recent successful deploy result, or null if none occurred.
|
|
4781
|
+
*
|
|
4782
|
+
* @returns A frozen snapshot of the last DeployResult, or null.
|
|
4783
|
+
* @example
|
|
4784
|
+
* const last = api.getLastDeployment();
|
|
4785
|
+
*/
|
|
4367
4786
|
getLastDeployment() {
|
|
4368
4787
|
const last = ctx.state.lastDeployment;
|
|
4369
4788
|
return last ? Object.freeze({ ...last }) : null;
|
|
4370
4789
|
},
|
|
4790
|
+
/**
|
|
4791
|
+
* Generate deploy scaffolding (wrangler.jsonc + optional GitHub workflow).
|
|
4792
|
+
*
|
|
4793
|
+
* @param options - Optional ci toggle and check (drift-only) mode.
|
|
4794
|
+
* @returns Which files were written, skipped, or would drift.
|
|
4795
|
+
* @example
|
|
4796
|
+
* await api.init({ ci: true });
|
|
4797
|
+
*/
|
|
4371
4798
|
async init(options = {}) {
|
|
4372
4799
|
const slug = toSlug(ctx.require(sitePlugin).name());
|
|
4373
4800
|
return writeScaffolding({
|
|
@@ -4379,7 +4806,6 @@ function createApi$1(ctx) {
|
|
|
4379
4806
|
}
|
|
4380
4807
|
};
|
|
4381
4808
|
}
|
|
4382
|
-
|
|
4383
4809
|
//#endregion
|
|
4384
4810
|
//#region src/plugins/deploy/defaults.ts
|
|
4385
4811
|
/**
|
|
@@ -4399,7 +4825,6 @@ const defaultConfig = {
|
|
|
4399
4825
|
compatibilityDate: "2024-01-01",
|
|
4400
4826
|
ci: false
|
|
4401
4827
|
};
|
|
4402
|
-
|
|
4403
4828
|
//#endregion
|
|
4404
4829
|
//#region src/plugins/deploy/events.ts
|
|
4405
4830
|
/**
|
|
@@ -4414,7 +4839,6 @@ const defaultConfig = {
|
|
|
4414
4839
|
* ```
|
|
4415
4840
|
*/
|
|
4416
4841
|
const deployEvents = (register) => ({ "deploy:complete": register("Deployment completed successfully") });
|
|
4417
|
-
|
|
4418
4842
|
//#endregion
|
|
4419
4843
|
//#region src/plugins/deploy/state.ts
|
|
4420
4844
|
/**
|
|
@@ -4452,7 +4876,6 @@ function createState$1(_ctx) {
|
|
|
4452
4876
|
spawn: defaultSpawn
|
|
4453
4877
|
};
|
|
4454
4878
|
}
|
|
4455
|
-
|
|
4456
4879
|
//#endregion
|
|
4457
4880
|
//#region src/plugins/deploy/index.ts
|
|
4458
4881
|
/**
|
|
@@ -4470,7 +4893,6 @@ const deployPlugin = createPlugin$1("deploy", {
|
|
|
4470
4893
|
onInit: validateConfig,
|
|
4471
4894
|
api: createApi$1
|
|
4472
4895
|
});
|
|
4473
|
-
|
|
4474
4896
|
//#endregion
|
|
4475
4897
|
//#region src/plugins/spa/api.ts
|
|
4476
4898
|
/**
|
|
@@ -4485,19 +4907,39 @@ const deployPlugin = createPlugin$1("deploy", {
|
|
|
4485
4907
|
*/
|
|
4486
4908
|
function createApi(ctx) {
|
|
4487
4909
|
return {
|
|
4910
|
+
/**
|
|
4911
|
+
* Register a component definition (last-registered-wins); warns on collision.
|
|
4912
|
+
*
|
|
4913
|
+
* @param component - The component definition created via `createComponent`.
|
|
4914
|
+
* @example
|
|
4915
|
+
* app.spa.register(counter);
|
|
4916
|
+
*/
|
|
4488
4917
|
register(component) {
|
|
4489
4918
|
if (ctx.state.registeredComponents.has(component.name)) ctx.log.warn("spa:component-collision", { name: component.name });
|
|
4490
4919
|
ctx.state.kernel?.register(component);
|
|
4491
4920
|
},
|
|
4921
|
+
/**
|
|
4922
|
+
* Programmatically navigate to a path (client runtime; no-op without a DOM).
|
|
4923
|
+
*
|
|
4924
|
+
* @param path - Target path (pathname, optionally with search/hash).
|
|
4925
|
+
* @example
|
|
4926
|
+
* app.spa.navigate("/about");
|
|
4927
|
+
*/
|
|
4492
4928
|
navigate(path) {
|
|
4493
4929
|
ctx.state.kernel?.processNav(path);
|
|
4494
4930
|
},
|
|
4931
|
+
/**
|
|
4932
|
+
* Read the current resolved URL.
|
|
4933
|
+
*
|
|
4934
|
+
* @returns The current pathname + search.
|
|
4935
|
+
* @example
|
|
4936
|
+
* app.spa.current();
|
|
4937
|
+
*/
|
|
4495
4938
|
current() {
|
|
4496
4939
|
return ctx.state.currentUrl;
|
|
4497
4940
|
}
|
|
4498
4941
|
};
|
|
4499
4942
|
}
|
|
4500
|
-
|
|
4501
4943
|
//#endregion
|
|
4502
4944
|
//#region src/plugins/spa/events.ts
|
|
4503
4945
|
/**
|
|
@@ -4517,7 +4959,6 @@ function spaEvents(register) {
|
|
|
4517
4959
|
"spa:component-unmount": register("A component instance detached from an element.")
|
|
4518
4960
|
};
|
|
4519
4961
|
}
|
|
4520
|
-
|
|
4521
4962
|
//#endregion
|
|
4522
4963
|
//#region src/plugins/spa/types.ts
|
|
4523
4964
|
var types_exports$7 = /* @__PURE__ */ __exportAll({ COMPONENT_HOOK_NAMES: () => COMPONENT_HOOK_NAMES });
|
|
@@ -4530,11 +4971,7 @@ const COMPONENT_HOOK_NAMES = [
|
|
|
4530
4971
|
"onUnMount",
|
|
4531
4972
|
"onDestroy"
|
|
4532
4973
|
];
|
|
4533
|
-
|
|
4534
|
-
//#endregion
|
|
4535
|
-
//#region src/plugins/spa/components.ts
|
|
4536
|
-
/** The set of legal hook names, frozen for O(1) membership checks. */
|
|
4537
|
-
const HOOK_NAME_SET = new Set(COMPONENT_HOOK_NAMES);
|
|
4974
|
+
new Set(COMPONENT_HOOK_NAMES);
|
|
4538
4975
|
/**
|
|
4539
4976
|
* Extracts the page data payload from the inline `script#__DATA__` element.
|
|
4540
4977
|
* Returns an empty object when the script is absent, empty, or invalid JSON.
|
|
@@ -4702,7 +5139,6 @@ function notifyNavEnd(state) {
|
|
|
4702
5139
|
const data = typeof document === "undefined" ? {} : extractPageData(document);
|
|
4703
5140
|
for (const [element, instance] of state.instances) if (instance.persistent) runHook(instance, "onNavEnd", makeContext(element, data));
|
|
4704
5141
|
}
|
|
4705
|
-
|
|
4706
5142
|
//#endregion
|
|
4707
5143
|
//#region src/plugins/spa/head.ts
|
|
4708
5144
|
/** Single-element head selectors synced by replace/append/remove on navigation. */
|
|
@@ -4777,7 +5213,6 @@ function syncHead(_head, doc) {
|
|
|
4777
5213
|
for (const selector of META_SELECTORS) syncElement(selector, doc);
|
|
4778
5214
|
for (const selector of REPLACE_ALL_SELECTORS) replaceAllBySelector(selector, doc);
|
|
4779
5215
|
}
|
|
4780
|
-
|
|
4781
5216
|
//#endregion
|
|
4782
5217
|
//#region src/plugins/spa/progress.ts
|
|
4783
5218
|
/** Delay before the bar appears, so fast navigations show no indicator. */
|
|
@@ -4861,7 +5296,6 @@ function createProgressBar(enabled) {
|
|
|
4861
5296
|
done
|
|
4862
5297
|
};
|
|
4863
5298
|
}
|
|
4864
|
-
|
|
4865
5299
|
//#endregion
|
|
4866
5300
|
//#region src/plugins/spa/router.ts
|
|
4867
5301
|
/**
|
|
@@ -5096,7 +5530,6 @@ function attachRouter(handlers) {
|
|
|
5096
5530
|
const navigation = getNavigation();
|
|
5097
5531
|
return navigation ? attachNavigationApi(navigation, handlers) : attachHistoryFallback(handlers);
|
|
5098
5532
|
}
|
|
5099
|
-
|
|
5100
5533
|
//#endregion
|
|
5101
5534
|
//#region src/plugins/spa/state.ts
|
|
5102
5535
|
/** Error prefix for spa config-validation failures (spec/11 Part-3). */
|
|
@@ -5170,7 +5603,6 @@ function createState(_ctx) {
|
|
|
5170
5603
|
kernel: null
|
|
5171
5604
|
};
|
|
5172
5605
|
}
|
|
5173
|
-
|
|
5174
5606
|
//#endregion
|
|
5175
5607
|
//#region src/plugins/spa/kernel.ts
|
|
5176
5608
|
/**
|
|
@@ -5279,10 +5711,22 @@ function createSpaKernel(state, config, emit, deps) {
|
|
|
5279
5711
|
onError: handleError
|
|
5280
5712
|
};
|
|
5281
5713
|
return {
|
|
5714
|
+
/**
|
|
5715
|
+
* Register config components and seed currentUrl from the document.
|
|
5716
|
+
*
|
|
5717
|
+
* @example
|
|
5718
|
+
* kernel.init();
|
|
5719
|
+
*/
|
|
5282
5720
|
init() {
|
|
5283
5721
|
for (const component of resolved.components) registerComponent(state, component);
|
|
5284
5722
|
state.currentUrl = currentLocationUrl();
|
|
5285
5723
|
},
|
|
5724
|
+
/**
|
|
5725
|
+
* Boot navigation interception + initial scan (throws if already started).
|
|
5726
|
+
*
|
|
5727
|
+
* @example
|
|
5728
|
+
* kernel.boot();
|
|
5729
|
+
*/
|
|
5286
5730
|
boot() {
|
|
5287
5731
|
if (typeof document === "undefined") return;
|
|
5288
5732
|
if (state.started) throw new Error(`${ERROR_PREFIX} spa kernel already started.\n Call app.stop() before booting again (single boot per app).`);
|
|
@@ -5292,16 +5736,42 @@ function createSpaKernel(state, config, emit, deps) {
|
|
|
5292
5736
|
scanAndMount(state, emit, resolved.swapSelector);
|
|
5293
5737
|
state.started = true;
|
|
5294
5738
|
},
|
|
5739
|
+
/**
|
|
5740
|
+
* Register a component definition (last-registered-wins).
|
|
5741
|
+
*
|
|
5742
|
+
* @param component - The component definition to register.
|
|
5743
|
+
* @example
|
|
5744
|
+
* kernel.register(counter);
|
|
5745
|
+
*/
|
|
5295
5746
|
register(component) {
|
|
5296
5747
|
registerComponent(state, component);
|
|
5297
5748
|
},
|
|
5749
|
+
/**
|
|
5750
|
+
* Process a navigation to `path` (fetch then swap; full reload on error).
|
|
5751
|
+
*
|
|
5752
|
+
* @param path - The target path to navigate to.
|
|
5753
|
+
* @example
|
|
5754
|
+
* kernel.processNav("/about");
|
|
5755
|
+
*/
|
|
5298
5756
|
processNav(path) {
|
|
5299
5757
|
if (typeof document === "undefined") return;
|
|
5300
5758
|
performNavigation(path, handlers).catch(() => {});
|
|
5301
5759
|
},
|
|
5760
|
+
/**
|
|
5761
|
+
* Scan the swap region and mount components for matching elements.
|
|
5762
|
+
*
|
|
5763
|
+
* @example
|
|
5764
|
+
* kernel.scan();
|
|
5765
|
+
*/
|
|
5302
5766
|
scan() {
|
|
5303
5767
|
scanAndMount(state, emit, resolved.swapSelector);
|
|
5304
5768
|
},
|
|
5769
|
+
/**
|
|
5770
|
+
* Tear down router listeners, dispose all instances, reset boot state.
|
|
5771
|
+
*
|
|
5772
|
+
* @example
|
|
5773
|
+
* kernel.dispose();
|
|
5774
|
+
*/
|
|
5305
5775
|
dispose() {
|
|
5306
5776
|
state.destroyRouter?.();
|
|
5307
5777
|
state.destroyRouter = null;
|
|
@@ -5330,7 +5800,6 @@ function initSpa(ctx) {
|
|
|
5330
5800
|
kernelRef.current = kernel;
|
|
5331
5801
|
kernel.init();
|
|
5332
5802
|
}
|
|
5333
|
-
|
|
5334
5803
|
//#endregion
|
|
5335
5804
|
//#region src/plugins/spa/lifecycle.ts
|
|
5336
5805
|
/** Router/instance teardown captured during onStart (undefined when stopped). */
|
|
@@ -5378,7 +5847,6 @@ function disposeSpa() {
|
|
|
5378
5847
|
logRef = void 0;
|
|
5379
5848
|
}
|
|
5380
5849
|
}
|
|
5381
|
-
|
|
5382
5850
|
//#endregion
|
|
5383
5851
|
//#region src/plugins/spa/index.ts
|
|
5384
5852
|
/**
|
|
@@ -5402,42 +5870,28 @@ const spaPlugin = createPlugin$1("spa", {
|
|
|
5402
5870
|
},
|
|
5403
5871
|
onStop: disposeSpa
|
|
5404
5872
|
});
|
|
5405
|
-
|
|
5406
5873
|
//#endregion
|
|
5407
5874
|
//#region src/plugins/build/types.ts
|
|
5408
5875
|
var types_exports = /* @__PURE__ */ __exportAll({});
|
|
5409
|
-
|
|
5410
5876
|
//#endregion
|
|
5411
5877
|
//#region src/plugins/content/types.ts
|
|
5412
5878
|
var types_exports$1 = /* @__PURE__ */ __exportAll({});
|
|
5413
|
-
|
|
5414
5879
|
//#endregion
|
|
5415
5880
|
//#region src/plugins/deploy/types.ts
|
|
5416
5881
|
var types_exports$2 = /* @__PURE__ */ __exportAll({});
|
|
5417
|
-
|
|
5418
5882
|
//#endregion
|
|
5419
5883
|
//#region src/plugins/env/types.ts
|
|
5420
5884
|
var types_exports$3 = /* @__PURE__ */ __exportAll({});
|
|
5421
|
-
|
|
5422
5885
|
//#endregion
|
|
5423
5886
|
//#region src/plugins/head/types.ts
|
|
5424
5887
|
var types_exports$4 = /* @__PURE__ */ __exportAll({});
|
|
5425
|
-
|
|
5426
5888
|
//#endregion
|
|
5427
5889
|
//#region src/plugins/log/types.ts
|
|
5428
5890
|
var types_exports$5 = /* @__PURE__ */ __exportAll({});
|
|
5429
|
-
|
|
5430
5891
|
//#endregion
|
|
5431
5892
|
//#region src/plugins/router/types.ts
|
|
5432
5893
|
var types_exports$6 = /* @__PURE__ */ __exportAll({});
|
|
5433
|
-
|
|
5434
|
-
//#endregion
|
|
5435
|
-
//#region src/index.ts
|
|
5436
|
-
/**
|
|
5437
|
-
* @file `@moku-labs/web` — a Moku Layer-2 content static-site + SPA framework.
|
|
5438
|
-
* @see README.md
|
|
5439
|
-
*/
|
|
5440
|
-
const framework = createCore(coreConfig, {
|
|
5894
|
+
const { createApp, createPlugin } = createCore(coreConfig, {
|
|
5441
5895
|
plugins: [
|
|
5442
5896
|
sitePlugin,
|
|
5443
5897
|
i18nPlugin,
|
|
@@ -5450,56 +5904,54 @@ const framework = createCore(coreConfig, {
|
|
|
5450
5904
|
],
|
|
5451
5905
|
pluginConfigs: {}
|
|
5452
5906
|
});
|
|
5453
|
-
const { createApp, createPlugin } = framework;
|
|
5454
|
-
|
|
5455
5907
|
//#endregion
|
|
5456
|
-
Object.defineProperty(exports,
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5908
|
+
Object.defineProperty(exports, "Build", {
|
|
5909
|
+
enumerable: true,
|
|
5910
|
+
get: function() {
|
|
5911
|
+
return types_exports;
|
|
5912
|
+
}
|
|
5461
5913
|
});
|
|
5462
|
-
Object.defineProperty(exports,
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5914
|
+
Object.defineProperty(exports, "Content", {
|
|
5915
|
+
enumerable: true,
|
|
5916
|
+
get: function() {
|
|
5917
|
+
return types_exports$1;
|
|
5918
|
+
}
|
|
5467
5919
|
});
|
|
5468
|
-
Object.defineProperty(exports,
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5920
|
+
Object.defineProperty(exports, "Deploy", {
|
|
5921
|
+
enumerable: true,
|
|
5922
|
+
get: function() {
|
|
5923
|
+
return types_exports$2;
|
|
5924
|
+
}
|
|
5473
5925
|
});
|
|
5474
|
-
Object.defineProperty(exports,
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5926
|
+
Object.defineProperty(exports, "Env", {
|
|
5927
|
+
enumerable: true,
|
|
5928
|
+
get: function() {
|
|
5929
|
+
return types_exports$3;
|
|
5930
|
+
}
|
|
5479
5931
|
});
|
|
5480
|
-
Object.defineProperty(exports,
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5932
|
+
Object.defineProperty(exports, "Head", {
|
|
5933
|
+
enumerable: true,
|
|
5934
|
+
get: function() {
|
|
5935
|
+
return types_exports$4;
|
|
5936
|
+
}
|
|
5485
5937
|
});
|
|
5486
|
-
Object.defineProperty(exports,
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5938
|
+
Object.defineProperty(exports, "Log", {
|
|
5939
|
+
enumerable: true,
|
|
5940
|
+
get: function() {
|
|
5941
|
+
return types_exports$5;
|
|
5942
|
+
}
|
|
5491
5943
|
});
|
|
5492
|
-
Object.defineProperty(exports,
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5944
|
+
Object.defineProperty(exports, "Router", {
|
|
5945
|
+
enumerable: true,
|
|
5946
|
+
get: function() {
|
|
5947
|
+
return types_exports$6;
|
|
5948
|
+
}
|
|
5497
5949
|
});
|
|
5498
|
-
Object.defineProperty(exports,
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5950
|
+
Object.defineProperty(exports, "Spa", {
|
|
5951
|
+
enumerable: true,
|
|
5952
|
+
get: function() {
|
|
5953
|
+
return types_exports$7;
|
|
5954
|
+
}
|
|
5503
5955
|
});
|
|
5504
5956
|
exports.buildArticleHead = buildArticleHead;
|
|
5505
5957
|
exports.buildPlugin = buildPlugin;
|
|
@@ -5522,4 +5974,4 @@ exports.route = route;
|
|
|
5522
5974
|
exports.routerPlugin = routerPlugin;
|
|
5523
5975
|
exports.sitePlugin = sitePlugin;
|
|
5524
5976
|
exports.spaPlugin = spaPlugin;
|
|
5525
|
-
exports.twitter = twitter;
|
|
5977
|
+
exports.twitter = twitter;
|