@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.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,29 +838,40 @@ 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" },
|
|
702
851
|
env: { providers: [dotenv(), processEnv()] }
|
|
703
852
|
}
|
|
704
853
|
});
|
|
705
|
-
|
|
706
|
-
|
|
854
|
+
/**
|
|
855
|
+
* Create a custom plugin bound to this framework's `Config`/`Events` and the core
|
|
856
|
+
* plugin APIs (`log`, `env`). Plugin types are fully inferred from the spec
|
|
857
|
+
* object — never write them explicitly. This is the binding every built-in
|
|
858
|
+
* plugin is wired with, and the one consumer plugins should use too.
|
|
859
|
+
*
|
|
860
|
+
* @example
|
|
861
|
+
* ```ts
|
|
862
|
+
* const analytics = createPlugin("analytics", {
|
|
863
|
+
* config: { writeKey: "" },
|
|
864
|
+
* api: (ctx) => ({ track: (event: string) => ctx.log.info("analytics:track", { event }) })
|
|
865
|
+
* });
|
|
866
|
+
* ```
|
|
867
|
+
*/
|
|
868
|
+
const createPlugin$1 = coreConfig.createPlugin;
|
|
869
|
+
/**
|
|
870
|
+
* Step 2 of the factory chain — captures the framework's default plugin set and
|
|
871
|
+
* returns the consumer entry points ({@link createApp} + a re-exported
|
|
872
|
+
* `createPlugin`). Wired once in `src/index.ts`; consumers don't call it directly.
|
|
873
|
+
*/
|
|
874
|
+
const createCore = coreConfig.createCore;
|
|
707
875
|
//#endregion
|
|
708
876
|
//#region src/plugins/i18n/api.ts
|
|
709
877
|
/** Error prefix for all i18n lifecycle failures. */
|
|
@@ -743,21 +911,83 @@ function validateI18nConfig(ctx) {
|
|
|
743
911
|
function createI18nApi(ctx) {
|
|
744
912
|
const { config } = ctx;
|
|
745
913
|
return {
|
|
914
|
+
/**
|
|
915
|
+
* Returns the configured supported locales in declared order.
|
|
916
|
+
*
|
|
917
|
+
* @returns The configured `locales` list (priority/display order).
|
|
918
|
+
* @example
|
|
919
|
+
* ```ts
|
|
920
|
+
* api.locales(); // ["en", "uk"]
|
|
921
|
+
* ```
|
|
922
|
+
*/
|
|
746
923
|
locales() {
|
|
747
924
|
return config.locales;
|
|
748
925
|
},
|
|
926
|
+
/**
|
|
927
|
+
* Returns the fallback locale used when a requested locale is absent.
|
|
928
|
+
*
|
|
929
|
+
* @returns The configured `defaultLocale`.
|
|
930
|
+
* @example
|
|
931
|
+
* ```ts
|
|
932
|
+
* api.defaultLocale(); // "en"
|
|
933
|
+
* ```
|
|
934
|
+
*/
|
|
749
935
|
defaultLocale() {
|
|
750
936
|
return config.defaultLocale;
|
|
751
937
|
},
|
|
938
|
+
/**
|
|
939
|
+
* Membership guard: whether `x` is one of the supported locales
|
|
940
|
+
* (case-sensitive).
|
|
941
|
+
*
|
|
942
|
+
* @param x - Candidate locale code.
|
|
943
|
+
* @returns `true` if `x ∈ locales`, else `false`.
|
|
944
|
+
* @example
|
|
945
|
+
* ```ts
|
|
946
|
+
* api.isLocale("uk"); // true
|
|
947
|
+
* ```
|
|
948
|
+
*/
|
|
752
949
|
isLocale(x) {
|
|
753
950
|
return config.locales.includes(x);
|
|
754
951
|
},
|
|
952
|
+
/**
|
|
953
|
+
* Human-readable display name for a locale.
|
|
954
|
+
*
|
|
955
|
+
* @param locale - Locale code to look up.
|
|
956
|
+
* @returns The display name, or `undefined` if unmapped.
|
|
957
|
+
* @example
|
|
958
|
+
* ```ts
|
|
959
|
+
* api.localeName("uk"); // "Українська"
|
|
960
|
+
* ```
|
|
961
|
+
*/
|
|
755
962
|
localeName(locale) {
|
|
756
963
|
return config.localeNames?.[locale];
|
|
757
964
|
},
|
|
965
|
+
/**
|
|
966
|
+
* Open Graph `og:locale` value for a locale.
|
|
967
|
+
*
|
|
968
|
+
* @param locale - Locale code to look up.
|
|
969
|
+
* @returns The `og:locale` value (e.g. `"en_US"`), or `undefined` if unmapped.
|
|
970
|
+
* @example
|
|
971
|
+
* ```ts
|
|
972
|
+
* api.ogLocale("en"); // "en_US"
|
|
973
|
+
* ```
|
|
974
|
+
*/
|
|
758
975
|
ogLocale(locale) {
|
|
759
976
|
return config.ogLocaleMap?.[locale];
|
|
760
977
|
},
|
|
978
|
+
/**
|
|
979
|
+
* Translate `key` for `locale` with a deterministic fallback chain
|
|
980
|
+
* (requested locale → default locale → the key itself). The default-locale
|
|
981
|
+
* lookup is skipped when `locale === defaultLocale`.
|
|
982
|
+
*
|
|
983
|
+
* @param locale - Requested locale code.
|
|
984
|
+
* @param key - Translation key (e.g. `"nav.home"`).
|
|
985
|
+
* @returns The translated value, the default-locale value, or `key`.
|
|
986
|
+
* @example
|
|
987
|
+
* ```ts
|
|
988
|
+
* api.t("uk", "nav.home"); // "Головна"
|
|
989
|
+
* ```
|
|
990
|
+
*/
|
|
761
991
|
t(locale, key) {
|
|
762
992
|
const exact = config.translations?.[locale]?.[key];
|
|
763
993
|
if (exact !== void 0) return exact;
|
|
@@ -769,34 +999,36 @@ function createI18nApi(ctx) {
|
|
|
769
999
|
}
|
|
770
1000
|
};
|
|
771
1001
|
}
|
|
772
|
-
|
|
773
|
-
//#endregion
|
|
774
|
-
//#region src/plugins/i18n/index.ts
|
|
775
1002
|
/**
|
|
776
|
-
*
|
|
777
|
-
*
|
|
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)`.
|
|
1003
|
+
* Internationalization plugin — locale registry plus a flat translation helper
|
|
1004
|
+
* with default-locale fallback. Pure config-as-data (no state or events);
|
|
1005
|
+
* consumed read-only by content, router, head, and build.
|
|
782
1006
|
*
|
|
783
|
-
* @
|
|
784
|
-
*
|
|
1007
|
+
* @example Register locales and translations
|
|
1008
|
+
* ```ts
|
|
1009
|
+
* const app = createApp({
|
|
1010
|
+
* pluginConfigs: {
|
|
1011
|
+
* i18n: {
|
|
1012
|
+
* locales: ["en", "uk"],
|
|
1013
|
+
* defaultLocale: "en",
|
|
1014
|
+
* localeNames: { en: "English", uk: "Українська" },
|
|
1015
|
+
* translations: { uk: { "nav.home": "Головна" } }
|
|
1016
|
+
* }
|
|
1017
|
+
* }
|
|
1018
|
+
* });
|
|
1019
|
+
* ```
|
|
785
1020
|
*/
|
|
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
1021
|
const i18nPlugin = createPlugin$1("i18n", {
|
|
795
|
-
config:
|
|
1022
|
+
config: {
|
|
1023
|
+
locales: ["en"],
|
|
1024
|
+
defaultLocale: "en",
|
|
1025
|
+
localeNames: {},
|
|
1026
|
+
ogLocaleMap: {},
|
|
1027
|
+
translations: {}
|
|
1028
|
+
},
|
|
796
1029
|
onInit: validateI18nConfig,
|
|
797
1030
|
api: createI18nApi
|
|
798
1031
|
});
|
|
799
|
-
|
|
800
1032
|
//#endregion
|
|
801
1033
|
//#region src/plugins/content/pipeline/frontmatter.ts
|
|
802
1034
|
/**
|
|
@@ -842,7 +1074,6 @@ function parseFrontmatter(raw, config) {
|
|
|
842
1074
|
body: parsed.content
|
|
843
1075
|
};
|
|
844
1076
|
}
|
|
845
|
-
|
|
846
1077
|
//#endregion
|
|
847
1078
|
//#region src/plugins/content/pipeline/plugins.ts
|
|
848
1079
|
/**
|
|
@@ -999,7 +1230,6 @@ function defaultRehypePlugins() {
|
|
|
999
1230
|
sectionDividerPlugin
|
|
1000
1231
|
];
|
|
1001
1232
|
}
|
|
1002
|
-
|
|
1003
1233
|
//#endregion
|
|
1004
1234
|
//#region src/plugins/content/pipeline/sanitize.ts
|
|
1005
1235
|
/**
|
|
@@ -1086,7 +1316,6 @@ function buildSanitizeSchema() {
|
|
|
1086
1316
|
}
|
|
1087
1317
|
};
|
|
1088
1318
|
}
|
|
1089
|
-
|
|
1090
1319
|
//#endregion
|
|
1091
1320
|
//#region src/plugins/content/pipeline/markdown.ts
|
|
1092
1321
|
/**
|
|
@@ -1144,7 +1373,6 @@ function applyPluggable(processor, plugin) {
|
|
|
1144
1373
|
}
|
|
1145
1374
|
processor.use(plugin);
|
|
1146
1375
|
}
|
|
1147
|
-
|
|
1148
1376
|
//#endregion
|
|
1149
1377
|
//#region src/plugins/content/pipeline/reading-time.ts
|
|
1150
1378
|
/**
|
|
@@ -1170,7 +1398,6 @@ function calculateReadingTime(text) {
|
|
|
1170
1398
|
wordCount: stats.words
|
|
1171
1399
|
};
|
|
1172
1400
|
}
|
|
1173
|
-
|
|
1174
1401
|
//#endregion
|
|
1175
1402
|
//#region src/plugins/content/api.ts
|
|
1176
1403
|
/**
|
|
@@ -1278,7 +1505,7 @@ async function discoverSlugs(dir) {
|
|
|
1278
1505
|
* ```
|
|
1279
1506
|
*/
|
|
1280
1507
|
async function readArticle(ctx, slug, fileLocale, outLocale, isFallback) {
|
|
1281
|
-
const filePath = node_path.default.join(ctx.config.contentDir, slug, `${fileLocale}.md`);
|
|
1508
|
+
const filePath = node_path$1.default.join(ctx.config.contentDir, slug, `${fileLocale}.md`);
|
|
1282
1509
|
let raw;
|
|
1283
1510
|
try {
|
|
1284
1511
|
raw = await (0, node_fs_promises.readFile)(filePath, "utf8");
|
|
@@ -1382,6 +1609,18 @@ function toCard(article) {
|
|
|
1382
1609
|
*/
|
|
1383
1610
|
function createContentApi(ctx) {
|
|
1384
1611
|
return {
|
|
1612
|
+
/**
|
|
1613
|
+
* Load every article across every active locale, returning a locale-keyed
|
|
1614
|
+
* map of date-descending Article arrays. Lazily builds the processor and
|
|
1615
|
+
* discovers slugs, applies locale fallback, excludes drafts in production,
|
|
1616
|
+
* assigns `contentId` after sorting, then emits `content:ready`.
|
|
1617
|
+
*
|
|
1618
|
+
* @returns A locale-keyed map of date-descending articles.
|
|
1619
|
+
* @example
|
|
1620
|
+
* ```ts
|
|
1621
|
+
* const byLocale = await api.loadAll();
|
|
1622
|
+
* ```
|
|
1623
|
+
*/
|
|
1385
1624
|
async loadAll() {
|
|
1386
1625
|
const slugs = ctx.state.slugs ?? await discoverSlugs(ctx.config.contentDir);
|
|
1387
1626
|
ctx.state.slugs = slugs;
|
|
@@ -1409,6 +1648,19 @@ function createContentApi(ctx) {
|
|
|
1409
1648
|
});
|
|
1410
1649
|
return result;
|
|
1411
1650
|
},
|
|
1651
|
+
/**
|
|
1652
|
+
* Resolve and render a single article for one locale with locale fallback.
|
|
1653
|
+
* Throws a `[web] content` error when neither the requested nor the
|
|
1654
|
+
* default-locale file exists.
|
|
1655
|
+
*
|
|
1656
|
+
* @param slug - Article directory name.
|
|
1657
|
+
* @param locale - Requested locale code.
|
|
1658
|
+
* @returns The resolved Article.
|
|
1659
|
+
* @example
|
|
1660
|
+
* ```ts
|
|
1661
|
+
* const article = await api.load("intro", "uk");
|
|
1662
|
+
* ```
|
|
1663
|
+
*/
|
|
1412
1664
|
async load(slug, locale) {
|
|
1413
1665
|
const article = await resolveArticle(ctx, slug, locale);
|
|
1414
1666
|
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 +1669,33 @@ function createContentApi(ctx) {
|
|
|
1417
1669
|
ctx.state.articles.set(locale, cache);
|
|
1418
1670
|
return article;
|
|
1419
1671
|
},
|
|
1672
|
+
/**
|
|
1673
|
+
* Render a raw Markdown string to HTML through the full pipeline (sanitize
|
|
1674
|
+
* last when `trustedContent` is false). Lazily builds the processor.
|
|
1675
|
+
*
|
|
1676
|
+
* @param md - Raw Markdown source.
|
|
1677
|
+
* @returns The rendered HTML string.
|
|
1678
|
+
* @example
|
|
1679
|
+
* ```ts
|
|
1680
|
+
* const html = await api.renderMarkdown("# Hi");
|
|
1681
|
+
* ```
|
|
1682
|
+
*/
|
|
1420
1683
|
async renderMarkdown(md) {
|
|
1421
1684
|
const processor = ensureProcessor(ctx.state, ctx.config);
|
|
1422
1685
|
return String(await processor.process(md));
|
|
1423
1686
|
},
|
|
1687
|
+
/**
|
|
1688
|
+
* Mark file paths stale for incremental dev rebuilds. Each non-blank path is
|
|
1689
|
+
* added to `dirtyPaths` and its derived slug cache entry is dropped so the
|
|
1690
|
+
* next `loadAll()` re-reads only those files. Empty/whitespace paths are
|
|
1691
|
+
* ignored. Emits `content:invalidated` with the accepted paths.
|
|
1692
|
+
*
|
|
1693
|
+
* @param paths - File paths to invalidate.
|
|
1694
|
+
* @example
|
|
1695
|
+
* ```ts
|
|
1696
|
+
* api.invalidate(["src/content/intro/en.md"]);
|
|
1697
|
+
* ```
|
|
1698
|
+
*/
|
|
1424
1699
|
invalidate(paths) {
|
|
1425
1700
|
const accepted = [];
|
|
1426
1701
|
for (const path of paths) {
|
|
@@ -1433,12 +1708,22 @@ function createContentApi(ctx) {
|
|
|
1433
1708
|
ctx.state.slugs = null;
|
|
1434
1709
|
ctx.emit("content:invalidated", { paths: accepted });
|
|
1435
1710
|
},
|
|
1711
|
+
/**
|
|
1712
|
+
* Project a full Article to a lightweight ArticleCard for list/grid
|
|
1713
|
+
* rendering without shipping rendered HTML.
|
|
1714
|
+
*
|
|
1715
|
+
* @param article - The source article.
|
|
1716
|
+
* @returns The card projection.
|
|
1717
|
+
* @example
|
|
1718
|
+
* ```ts
|
|
1719
|
+
* const card = api.articleToCard(article);
|
|
1720
|
+
* ```
|
|
1721
|
+
*/
|
|
1436
1722
|
articleToCard(article) {
|
|
1437
1723
|
return toCard(article);
|
|
1438
1724
|
}
|
|
1439
1725
|
};
|
|
1440
1726
|
}
|
|
1441
|
-
|
|
1442
1727
|
//#endregion
|
|
1443
1728
|
//#region src/plugins/content/config.ts
|
|
1444
1729
|
/**
|
|
@@ -1459,7 +1744,6 @@ const defaultContentConfig = {
|
|
|
1459
1744
|
extraRehypePlugins: [],
|
|
1460
1745
|
shikiTheme: "github-dark"
|
|
1461
1746
|
};
|
|
1462
|
-
|
|
1463
1747
|
//#endregion
|
|
1464
1748
|
//#region src/plugins/content/events.ts
|
|
1465
1749
|
/**
|
|
@@ -1478,7 +1762,6 @@ const contentEvents = (register) => ({
|
|
|
1478
1762
|
"content:ready": register("All articles loaded across locales"),
|
|
1479
1763
|
"content:invalidated": register("Article paths marked stale for dev rebuild")
|
|
1480
1764
|
});
|
|
1481
|
-
|
|
1482
1765
|
//#endregion
|
|
1483
1766
|
//#region src/plugins/content/state.ts
|
|
1484
1767
|
/**
|
|
@@ -1502,7 +1785,6 @@ function createContentState(_ctx) {
|
|
|
1502
1785
|
dirtyPaths: /* @__PURE__ */ new Set()
|
|
1503
1786
|
};
|
|
1504
1787
|
}
|
|
1505
|
-
|
|
1506
1788
|
//#endregion
|
|
1507
1789
|
//#region src/plugins/content/validate.ts
|
|
1508
1790
|
/**
|
|
@@ -1521,7 +1803,6 @@ function validateContentConfig(config) {
|
|
|
1521
1803
|
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
1804
|
if (typeof config.trustedContent !== "boolean") throw new TypeError("[web] content.trustedContent must be a boolean.");
|
|
1523
1805
|
}
|
|
1524
|
-
|
|
1525
1806
|
//#endregion
|
|
1526
1807
|
//#region src/plugins/content/index.ts
|
|
1527
1808
|
/**
|
|
@@ -1532,6 +1813,26 @@ function validateContentConfig(config) {
|
|
|
1532
1813
|
* and `content:invalidated`.
|
|
1533
1814
|
* @see README.md
|
|
1534
1815
|
*/
|
|
1816
|
+
/**
|
|
1817
|
+
* Content plugin — Markdown pipeline: discovers files, parses frontmatter, renders
|
|
1818
|
+
* to sanitized HTML (rehype-sanitize unless `trustedContent`), and exposes a
|
|
1819
|
+
* locale-keyed Article model. Depends on i18n; emits `content:ready` and
|
|
1820
|
+
* `content:invalidated`.
|
|
1821
|
+
*
|
|
1822
|
+
* @example Point at a content directory and pick a Shiki theme
|
|
1823
|
+
* ```ts
|
|
1824
|
+
* const app = createApp({
|
|
1825
|
+
* pluginConfigs: {
|
|
1826
|
+
* content: {
|
|
1827
|
+
* contentDir: "./content",
|
|
1828
|
+
* shikiTheme: "github-dark",
|
|
1829
|
+
* defaultAuthor: "Ada Lovelace"
|
|
1830
|
+
* // trustedContent: true // ONLY for fully author-controlled Markdown — disables sanitize
|
|
1831
|
+
* }
|
|
1832
|
+
* }
|
|
1833
|
+
* });
|
|
1834
|
+
* ```
|
|
1835
|
+
*/
|
|
1535
1836
|
const contentPlugin = createPlugin$1("content", {
|
|
1536
1837
|
depends: [i18nPlugin],
|
|
1537
1838
|
events: contentEvents,
|
|
@@ -1540,7 +1841,6 @@ const contentPlugin = createPlugin$1("content", {
|
|
|
1540
1841
|
onInit: (ctx) => validateContentConfig(ctx.config),
|
|
1541
1842
|
api: contentApi
|
|
1542
1843
|
});
|
|
1543
|
-
|
|
1544
1844
|
//#endregion
|
|
1545
1845
|
//#region src/plugins/site/api.ts
|
|
1546
1846
|
/** Error prefix for all site lifecycle/validation failures. */
|
|
@@ -1632,50 +1932,99 @@ function validateSiteConfig(ctx) {
|
|
|
1632
1932
|
function createSiteApi(ctx) {
|
|
1633
1933
|
const { config } = ctx;
|
|
1634
1934
|
return {
|
|
1935
|
+
/**
|
|
1936
|
+
* Returns the configured site name.
|
|
1937
|
+
*
|
|
1938
|
+
* @returns The human-readable site name from `config.name`.
|
|
1939
|
+
* @example
|
|
1940
|
+
* ```ts
|
|
1941
|
+
* api.name(); // "My Blog"
|
|
1942
|
+
* ```
|
|
1943
|
+
*/
|
|
1635
1944
|
name() {
|
|
1636
1945
|
return config.name;
|
|
1637
1946
|
},
|
|
1947
|
+
/**
|
|
1948
|
+
* Returns the configured absolute base URL of the site.
|
|
1949
|
+
*
|
|
1950
|
+
* @returns The base URL from `config.url`.
|
|
1951
|
+
* @example
|
|
1952
|
+
* ```ts
|
|
1953
|
+
* api.url(); // "https://blog.dev"
|
|
1954
|
+
* ```
|
|
1955
|
+
*/
|
|
1638
1956
|
url() {
|
|
1639
1957
|
return config.url;
|
|
1640
1958
|
},
|
|
1959
|
+
/**
|
|
1960
|
+
* Returns the configured site author/byline.
|
|
1961
|
+
*
|
|
1962
|
+
* @returns The author from `config.author`.
|
|
1963
|
+
* @example
|
|
1964
|
+
* ```ts
|
|
1965
|
+
* api.author(); // "Alex"
|
|
1966
|
+
* ```
|
|
1967
|
+
*/
|
|
1641
1968
|
author() {
|
|
1642
1969
|
return config.author;
|
|
1643
1970
|
},
|
|
1971
|
+
/**
|
|
1972
|
+
* Returns the configured site description.
|
|
1973
|
+
*
|
|
1974
|
+
* @returns The description from `config.description`.
|
|
1975
|
+
* @example
|
|
1976
|
+
* ```ts
|
|
1977
|
+
* api.description(); // "A personal blog about web frameworks."
|
|
1978
|
+
* ```
|
|
1979
|
+
*/
|
|
1644
1980
|
description() {
|
|
1645
1981
|
return config.description;
|
|
1646
1982
|
},
|
|
1983
|
+
/**
|
|
1984
|
+
* Joins a path against the configured base `url` to produce an absolute
|
|
1985
|
+
* canonical URL. An empty path (or "/") returns the base URL unchanged.
|
|
1986
|
+
*
|
|
1987
|
+
* @param path - Relative path for the page, e.g. "/about/".
|
|
1988
|
+
* @returns The absolute canonical URL.
|
|
1989
|
+
* @example
|
|
1990
|
+
* ```ts
|
|
1991
|
+
* api.canonical("/about/"); // "https://blog.dev/about/"
|
|
1992
|
+
* ```
|
|
1993
|
+
*/
|
|
1647
1994
|
canonical(path) {
|
|
1648
1995
|
return joinCanonical(config.url, path);
|
|
1649
1996
|
}
|
|
1650
1997
|
};
|
|
1651
1998
|
}
|
|
1652
|
-
|
|
1653
|
-
//#endregion
|
|
1654
|
-
//#region src/plugins/site/index.ts
|
|
1655
1999
|
/**
|
|
1656
|
-
*
|
|
1657
|
-
*
|
|
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.
|
|
2000
|
+
* Site plugin — holds global, frozen site metadata (name, url, author,
|
|
2001
|
+
* description) and builds canonical URLs. Consumed by router, head, and build.
|
|
2002
|
+
* `name` and `url` must be non-empty (validated at `onInit`).
|
|
1662
2003
|
*
|
|
1663
|
-
* @
|
|
1664
|
-
*
|
|
2004
|
+
* @example Set your site identity
|
|
2005
|
+
* ```ts
|
|
2006
|
+
* const app = createApp({
|
|
2007
|
+
* pluginConfigs: {
|
|
2008
|
+
* site: {
|
|
2009
|
+
* name: "My Blog",
|
|
2010
|
+
* url: "https://blog.dev",
|
|
2011
|
+
* author: "Ada Lovelace",
|
|
2012
|
+
* description: "Notes on computing"
|
|
2013
|
+
* }
|
|
2014
|
+
* }
|
|
2015
|
+
* });
|
|
2016
|
+
* ```
|
|
1665
2017
|
*/
|
|
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
2018
|
const sitePlugin = createPlugin$1("site", {
|
|
1674
|
-
config:
|
|
2019
|
+
config: {
|
|
2020
|
+
name: "",
|
|
2021
|
+
url: "",
|
|
2022
|
+
author: "",
|
|
2023
|
+
description: ""
|
|
2024
|
+
},
|
|
1675
2025
|
onInit: validateSiteConfig,
|
|
1676
2026
|
api: createSiteApi
|
|
1677
2027
|
});
|
|
1678
|
-
|
|
1679
2028
|
//#endregion
|
|
1680
2029
|
//#region src/plugins/router/builders/match.ts
|
|
1681
2030
|
/**
|
|
@@ -1745,7 +2094,6 @@ function matchRoute(compiled, pathname) {
|
|
|
1745
2094
|
}
|
|
1746
2095
|
return null;
|
|
1747
2096
|
}
|
|
1748
|
-
|
|
1749
2097
|
//#endregion
|
|
1750
2098
|
//#region src/plugins/router/api.ts
|
|
1751
2099
|
/**
|
|
@@ -1808,23 +2156,63 @@ function toTypedRoute(entry) {
|
|
|
1808
2156
|
function createApi$4(ctx) {
|
|
1809
2157
|
const { state } = ctx;
|
|
1810
2158
|
return {
|
|
2159
|
+
/**
|
|
2160
|
+
* Match a pathname against the compiled route table (specificity-sorted).
|
|
2161
|
+
*
|
|
2162
|
+
* @param pathname - URL pathname, e.g. `/en/hello/`.
|
|
2163
|
+
* @returns `{ params, route }` for the most specific match, or `null`.
|
|
2164
|
+
* @example
|
|
2165
|
+
* ```ts
|
|
2166
|
+
* api.match("/en/hello/");
|
|
2167
|
+
* ```
|
|
2168
|
+
*/
|
|
1811
2169
|
match(pathname) {
|
|
1812
2170
|
return matchRoute(readTable(state).compiled, pathname);
|
|
1813
2171
|
},
|
|
2172
|
+
/**
|
|
2173
|
+
* Build a URL for a named route from params.
|
|
2174
|
+
*
|
|
2175
|
+
* @param routeName - Route name key from the route map.
|
|
2176
|
+
* @param params - Param values to substitute into the pattern.
|
|
2177
|
+
* @returns The resolved URL string (e.g. `/en/hello/`).
|
|
2178
|
+
* @throws {Error} If `routeName` is unknown.
|
|
2179
|
+
* @example
|
|
2180
|
+
* ```ts
|
|
2181
|
+
* api.toUrl("article", { lang: "en", slug: "hello" });
|
|
2182
|
+
* ```
|
|
2183
|
+
*/
|
|
1814
2184
|
toUrl(routeName, params) {
|
|
1815
2185
|
const entry = readTable(state).byName.get(routeName);
|
|
1816
2186
|
if (!entry) throw new Error(`${ERROR_PREFIX$9}: unknown route name "${routeName}".`);
|
|
1817
2187
|
return entry.toUrl(params);
|
|
1818
2188
|
},
|
|
2189
|
+
/**
|
|
2190
|
+
* All resolved routes as typed URL utilities, in specificity order.
|
|
2191
|
+
*
|
|
2192
|
+
* @returns A fresh read-only array of resolved typed routes.
|
|
2193
|
+
* @example
|
|
2194
|
+
* ```ts
|
|
2195
|
+
* for (const r of api.entries()) r.toUrl({ slug: "x" });
|
|
2196
|
+
* ```
|
|
2197
|
+
*/
|
|
1819
2198
|
entries() {
|
|
1820
2199
|
return readTable(state).compiled.map((entry) => toTypedRoute(entry));
|
|
1821
2200
|
},
|
|
2201
|
+
/**
|
|
2202
|
+
* The typed route set for build-time consumption (declaration order). An API
|
|
2203
|
+
* return, NOT a config readback — preserves per-route types despite erasure.
|
|
2204
|
+
*
|
|
2205
|
+
* @returns A fresh read-only array of the typed route definitions.
|
|
2206
|
+
* @example
|
|
2207
|
+
* ```ts
|
|
2208
|
+
* for (const def of api.manifest()) def._handlers.load?.({}, "en");
|
|
2209
|
+
* ```
|
|
2210
|
+
*/
|
|
1822
2211
|
manifest() {
|
|
1823
2212
|
return [...readTable(state).byName.values()].map((entry) => entry.definition);
|
|
1824
2213
|
}
|
|
1825
2214
|
};
|
|
1826
2215
|
}
|
|
1827
|
-
|
|
1828
2216
|
//#endregion
|
|
1829
2217
|
//#region src/plugins/router/builders/compile.ts
|
|
1830
2218
|
/** Shared `[web]` error prefix for router validation failures. */
|
|
@@ -1986,9 +2374,31 @@ function compileRoute(name, definition, input) {
|
|
|
1986
2374
|
dynamicSegmentCount: countDynamicSegments(pattern),
|
|
1987
2375
|
matchers,
|
|
1988
2376
|
matchFn: createMatchFunction(matchers, input.defaultLocale),
|
|
2377
|
+
/**
|
|
2378
|
+
* Build a URL for this route from params.
|
|
2379
|
+
*
|
|
2380
|
+
* @param params - Param values to substitute.
|
|
2381
|
+
* @returns The resolved relative URL.
|
|
2382
|
+
* @example
|
|
2383
|
+
* ```ts
|
|
2384
|
+
* entry.toUrl({ slug: "x" });
|
|
2385
|
+
* ```
|
|
2386
|
+
*/
|
|
1989
2387
|
toUrl(params) {
|
|
1990
2388
|
return buildUrl(pattern, params, input.baseUrl);
|
|
1991
2389
|
},
|
|
2390
|
+
/**
|
|
2391
|
+
* Build the output file path for this route from params. Honors a custom
|
|
2392
|
+
* `.toFile()` override (captured in `_handlers.toFile`) when present, falling
|
|
2393
|
+
* back to the pattern-derived `…/index.html` path otherwise.
|
|
2394
|
+
*
|
|
2395
|
+
* @param params - Param values to substitute.
|
|
2396
|
+
* @returns The output file path.
|
|
2397
|
+
* @example
|
|
2398
|
+
* ```ts
|
|
2399
|
+
* entry.toFile({ slug: "x" });
|
|
2400
|
+
* ```
|
|
2401
|
+
*/
|
|
1992
2402
|
toFile(params) {
|
|
1993
2403
|
return definition._handlers.toFile?.(params) ?? buildFilePath(pattern, params);
|
|
1994
2404
|
},
|
|
@@ -2049,7 +2459,6 @@ function buildRouterTable(config, baseUrl, locales, defaultLocale) {
|
|
|
2049
2459
|
defaultLocale
|
|
2050
2460
|
});
|
|
2051
2461
|
}
|
|
2052
|
-
|
|
2053
2462
|
//#endregion
|
|
2054
2463
|
//#region src/plugins/router/builders/route-builder.ts
|
|
2055
2464
|
/**
|
|
@@ -2095,28 +2504,108 @@ function route(pattern) {
|
|
|
2095
2504
|
pattern: carrier.pattern,
|
|
2096
2505
|
_meta: carrier._meta,
|
|
2097
2506
|
_handlers: carrier._handlers,
|
|
2507
|
+
/**
|
|
2508
|
+
* Attach a data loader; widens the data generic for downstream handlers.
|
|
2509
|
+
*
|
|
2510
|
+
* @param loader - The loader producing this route's data.
|
|
2511
|
+
* @returns The same builder, with the data generic widened.
|
|
2512
|
+
* @example
|
|
2513
|
+
* ```ts
|
|
2514
|
+
* route("/{slug}/").load(({ slug }) => ({ slug }));
|
|
2515
|
+
* ```
|
|
2516
|
+
*/
|
|
2098
2517
|
load(loader) {
|
|
2099
2518
|
return set("load", loader);
|
|
2100
2519
|
},
|
|
2520
|
+
/**
|
|
2521
|
+
* Attach a layout wrapper component.
|
|
2522
|
+
*
|
|
2523
|
+
* @param component - The layout component.
|
|
2524
|
+
* @returns The same builder for chaining.
|
|
2525
|
+
* @example
|
|
2526
|
+
* ```ts
|
|
2527
|
+
* route("/").layout((children) => children);
|
|
2528
|
+
* ```
|
|
2529
|
+
*/
|
|
2101
2530
|
layout(component) {
|
|
2102
2531
|
return set("layout", component);
|
|
2103
2532
|
},
|
|
2533
|
+
/**
|
|
2534
|
+
* Attach the page render handler.
|
|
2535
|
+
*
|
|
2536
|
+
* @param handler - The render handler.
|
|
2537
|
+
* @returns The same builder for chaining.
|
|
2538
|
+
* @example
|
|
2539
|
+
* ```ts
|
|
2540
|
+
* route("/").render(() => null);
|
|
2541
|
+
* ```
|
|
2542
|
+
*/
|
|
2104
2543
|
render(handler) {
|
|
2105
2544
|
return set("render", handler);
|
|
2106
2545
|
},
|
|
2546
|
+
/**
|
|
2547
|
+
* Attach the head/SEO handler.
|
|
2548
|
+
*
|
|
2549
|
+
* @param handler - The head handler.
|
|
2550
|
+
* @returns The same builder for chaining.
|
|
2551
|
+
* @example
|
|
2552
|
+
* ```ts
|
|
2553
|
+
* route("/").head(() => ({ title: "Home" }));
|
|
2554
|
+
* ```
|
|
2555
|
+
*/
|
|
2107
2556
|
head(handler) {
|
|
2108
2557
|
return set("head", handler);
|
|
2109
2558
|
},
|
|
2559
|
+
/**
|
|
2560
|
+
* Attach a static-generation param producer.
|
|
2561
|
+
*
|
|
2562
|
+
* @param handler - The param producer.
|
|
2563
|
+
* @returns The same builder for chaining.
|
|
2564
|
+
* @example
|
|
2565
|
+
* ```ts
|
|
2566
|
+
* route("/{slug}/").generate(() => [{ slug: "x" }]);
|
|
2567
|
+
* ```
|
|
2568
|
+
*/
|
|
2110
2569
|
generate(handler) {
|
|
2111
2570
|
return set("generate", handler);
|
|
2112
2571
|
},
|
|
2572
|
+
/**
|
|
2573
|
+
* Merge an arbitrary metadata bag into the route's `_meta`.
|
|
2574
|
+
*
|
|
2575
|
+
* @param meta - Metadata to merge.
|
|
2576
|
+
* @returns The same builder for chaining.
|
|
2577
|
+
* @example
|
|
2578
|
+
* ```ts
|
|
2579
|
+
* route("/").meta({ activeTab: "home" });
|
|
2580
|
+
* ```
|
|
2581
|
+
*/
|
|
2113
2582
|
meta(meta) {
|
|
2114
2583
|
Object.assign(carrier._meta, meta);
|
|
2115
2584
|
return builder;
|
|
2116
2585
|
},
|
|
2586
|
+
/**
|
|
2587
|
+
* Attach a JSON serializer for the route's data.
|
|
2588
|
+
*
|
|
2589
|
+
* @param handler - The JSON serializer.
|
|
2590
|
+
* @returns The same builder for chaining.
|
|
2591
|
+
* @example
|
|
2592
|
+
* ```ts
|
|
2593
|
+
* route("/api/").toJson(() => ({ ok: true }));
|
|
2594
|
+
* ```
|
|
2595
|
+
*/
|
|
2117
2596
|
toJson(handler) {
|
|
2118
2597
|
return set("toJson", handler);
|
|
2119
2598
|
},
|
|
2599
|
+
/**
|
|
2600
|
+
* Override the output file-path producer.
|
|
2601
|
+
*
|
|
2602
|
+
* @param handler - The file-path producer.
|
|
2603
|
+
* @returns The same builder for chaining.
|
|
2604
|
+
* @example
|
|
2605
|
+
* ```ts
|
|
2606
|
+
* route("/feed/").toFile(() => "feed.xml");
|
|
2607
|
+
* ```
|
|
2608
|
+
*/
|
|
2120
2609
|
toFile(handler) {
|
|
2121
2610
|
return set("toFile", handler);
|
|
2122
2611
|
}
|
|
@@ -2137,7 +2626,6 @@ function route(pattern) {
|
|
|
2137
2626
|
function defineRoutes(routes) {
|
|
2138
2627
|
return routes;
|
|
2139
2628
|
}
|
|
2140
|
-
|
|
2141
2629
|
//#endregion
|
|
2142
2630
|
//#region src/plugins/router/state.ts
|
|
2143
2631
|
/**
|
|
@@ -2156,25 +2644,36 @@ function defineRoutes(routes) {
|
|
|
2156
2644
|
function createState$4(_ctx) {
|
|
2157
2645
|
return { table: null };
|
|
2158
2646
|
}
|
|
2159
|
-
|
|
2160
|
-
//#endregion
|
|
2161
|
-
//#region src/plugins/router/index.ts
|
|
2162
2647
|
/**
|
|
2163
|
-
*
|
|
2164
|
-
* @
|
|
2648
|
+
* Router plugin — typed, named route definitions with locale-aware URL generation
|
|
2649
|
+
* and matching. Author routes with {@link route} + {@link defineRoutes}. Depends
|
|
2650
|
+
* on site (base URL) and i18n (locales).
|
|
2651
|
+
*
|
|
2652
|
+
* @example Define routes and choose a render mode
|
|
2653
|
+
* ```ts
|
|
2654
|
+
* const app = createApp({
|
|
2655
|
+
* pluginConfigs: {
|
|
2656
|
+
* router: {
|
|
2657
|
+
* routes: defineRoutes({
|
|
2658
|
+
* home: route("/"),
|
|
2659
|
+
* article: route("/blog/{slug}/")
|
|
2660
|
+
* }),
|
|
2661
|
+
* mode: "hybrid" // "ssg" | "spa" | "hybrid" (default)
|
|
2662
|
+
* }
|
|
2663
|
+
* }
|
|
2664
|
+
* });
|
|
2665
|
+
* ```
|
|
2165
2666
|
*/
|
|
2166
|
-
/** Default router config: empty route map (validated in onInit), hybrid mode. */
|
|
2167
|
-
const defaultConfig$3 = {
|
|
2168
|
-
routes: {},
|
|
2169
|
-
mode: "hybrid"
|
|
2170
|
-
};
|
|
2171
2667
|
const routerPlugin = createPlugin$1("router", {
|
|
2172
2668
|
depends: [sitePlugin, i18nPlugin],
|
|
2173
2669
|
helpers: {
|
|
2174
2670
|
route,
|
|
2175
2671
|
defineRoutes
|
|
2176
2672
|
},
|
|
2177
|
-
config:
|
|
2673
|
+
config: {
|
|
2674
|
+
routes: {},
|
|
2675
|
+
mode: "hybrid"
|
|
2676
|
+
},
|
|
2178
2677
|
createState: createState$4,
|
|
2179
2678
|
api: createApi$4,
|
|
2180
2679
|
onInit(ctx) {
|
|
@@ -2183,7 +2682,6 @@ const routerPlugin = createPlugin$1("router", {
|
|
|
2183
2682
|
ctx.state.table = buildRouterTable(ctx.config, baseUrl, i18n.locales(), i18n.defaultLocale());
|
|
2184
2683
|
}
|
|
2185
2684
|
});
|
|
2186
|
-
|
|
2187
2685
|
//#endregion
|
|
2188
2686
|
//#region src/plugins/head/primitives.ts
|
|
2189
2687
|
/** OG/Twitter article-meta property prefixes (factored to satisfy no-duplicate-string). */
|
|
@@ -2347,7 +2845,6 @@ function buildArticleHead(articleMeta, canonicalUrl) {
|
|
|
2347
2845
|
elements.push(jsonLd(ld));
|
|
2348
2846
|
return elements;
|
|
2349
2847
|
}
|
|
2350
|
-
|
|
2351
2848
|
//#endregion
|
|
2352
2849
|
//#region src/plugins/head/compose.ts
|
|
2353
2850
|
/**
|
|
@@ -2508,7 +3005,6 @@ function serializeElement(element) {
|
|
|
2508
3005
|
function serializeHead(elements) {
|
|
2509
3006
|
return elements.map((element) => serializeElement(element)).join("");
|
|
2510
3007
|
}
|
|
2511
|
-
|
|
2512
3008
|
//#endregion
|
|
2513
3009
|
//#region src/plugins/head/api.ts
|
|
2514
3010
|
/**
|
|
@@ -2550,7 +3046,19 @@ function readDefaults(state) {
|
|
|
2550
3046
|
* ```
|
|
2551
3047
|
*/
|
|
2552
3048
|
function createApi$3(ctx) {
|
|
2553
|
-
return {
|
|
3049
|
+
return {
|
|
3050
|
+
/**
|
|
3051
|
+
* Compose the final `<head>` inner HTML for a route (pulled by `build`).
|
|
3052
|
+
*
|
|
3053
|
+
* @param route - The resolved route descriptor (incl. its `.head()` HeadConfig).
|
|
3054
|
+
* @param data - The page data object passed to the route's loader/render.
|
|
3055
|
+
* @returns The serialized inner HTML of `<head>`.
|
|
3056
|
+
* @example
|
|
3057
|
+
* ```ts
|
|
3058
|
+
* api.render(route, { title: "Post" });
|
|
3059
|
+
* ```
|
|
3060
|
+
*/
|
|
3061
|
+
render(route, data) {
|
|
2554
3062
|
return serializeHead(composeHead({
|
|
2555
3063
|
route,
|
|
2556
3064
|
data,
|
|
@@ -2561,7 +3069,6 @@ function createApi$3(ctx) {
|
|
|
2561
3069
|
}));
|
|
2562
3070
|
} };
|
|
2563
3071
|
}
|
|
2564
|
-
|
|
2565
3072
|
//#endregion
|
|
2566
3073
|
//#region src/plugins/head/config.ts
|
|
2567
3074
|
/** Error prefix for all head config-validation failures. */
|
|
@@ -2616,7 +3123,6 @@ function normalizeHeadConfig(config) {
|
|
|
2616
3123
|
if (config.twitterHandle !== void 0) defaults.twitterHandle = config.twitterHandle;
|
|
2617
3124
|
return Object.freeze(defaults);
|
|
2618
3125
|
}
|
|
2619
|
-
|
|
2620
3126
|
//#endregion
|
|
2621
3127
|
//#region src/plugins/head/helpers.ts
|
|
2622
3128
|
/**
|
|
@@ -2645,7 +3151,6 @@ const headHelpers = {
|
|
|
2645
3151
|
feedLink,
|
|
2646
3152
|
buildArticleHead
|
|
2647
3153
|
};
|
|
2648
|
-
|
|
2649
3154
|
//#endregion
|
|
2650
3155
|
//#region src/plugins/head/state.ts
|
|
2651
3156
|
/**
|
|
@@ -2666,13 +3171,31 @@ const headHelpers = {
|
|
|
2666
3171
|
function createState$3(_ctx) {
|
|
2667
3172
|
return { defaults: null };
|
|
2668
3173
|
}
|
|
2669
|
-
|
|
2670
3174
|
//#endregion
|
|
2671
3175
|
//#region src/plugins/head/index.ts
|
|
2672
3176
|
/**
|
|
2673
3177
|
* @file head — Standard Plugin wiring harness (logic in primitives/compose/api/config).
|
|
2674
3178
|
* @see README.md
|
|
2675
3179
|
*/
|
|
3180
|
+
/**
|
|
3181
|
+
* Head plugin — composes per-route `<head>` metadata (title template, Open Graph,
|
|
3182
|
+
* Twitter cards, canonical, hreflang). Use the re-exported SEO primitives
|
|
3183
|
+
* ({@link meta}, {@link og}, {@link twitter}, …) inside a route's `.head()`.
|
|
3184
|
+
* Depends on site, i18n, and router.
|
|
3185
|
+
*
|
|
3186
|
+
* @example Set global head defaults
|
|
3187
|
+
* ```ts
|
|
3188
|
+
* const app = createApp({
|
|
3189
|
+
* pluginConfigs: {
|
|
3190
|
+
* head: {
|
|
3191
|
+
* titleTemplate: "%s — My Blog",
|
|
3192
|
+
* twitterCard: "summary_large_image",
|
|
3193
|
+
* twitterHandle: "@moku_labs"
|
|
3194
|
+
* }
|
|
3195
|
+
* }
|
|
3196
|
+
* });
|
|
3197
|
+
* ```
|
|
3198
|
+
*/
|
|
2676
3199
|
const headPlugin = createPlugin$1("head", {
|
|
2677
3200
|
depends: [
|
|
2678
3201
|
sitePlugin,
|
|
@@ -2687,7 +3210,6 @@ const headPlugin = createPlugin$1("head", {
|
|
|
2687
3210
|
ctx.state.defaults = normalizeHeadConfig(ctx.config);
|
|
2688
3211
|
}
|
|
2689
3212
|
});
|
|
2690
|
-
|
|
2691
3213
|
//#endregion
|
|
2692
3214
|
//#region src/plugins/build/phases/bundle.ts
|
|
2693
3215
|
/**
|
|
@@ -2761,7 +3283,7 @@ async function runOne(ctx, runner, kind, entrypoints, outdir, minify) {
|
|
|
2761
3283
|
});
|
|
2762
3284
|
if (!result.success) throw new Error(`[web] build.bundle ${kind} build failed`);
|
|
2763
3285
|
const hashed = {};
|
|
2764
|
-
for (const output of result.outputs) hashed[node_path.default.basename(output.path)] = output.path;
|
|
3286
|
+
for (const output of result.outputs) hashed[node_path$1.default.basename(output.path)] = output.path;
|
|
2765
3287
|
ctx.state.buildCache.set(kind, hashed);
|
|
2766
3288
|
ctx.log.debug("build:bundle", {
|
|
2767
3289
|
kind,
|
|
@@ -2786,10 +3308,9 @@ async function bundle(ctx, options = {}) {
|
|
|
2786
3308
|
const { minify, outDir } = ctx.config;
|
|
2787
3309
|
const cssEntrypoints = options.cssEntrypoints ?? resolveEntrypoints(CSS_ENTRY_CANDIDATES);
|
|
2788
3310
|
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);
|
|
3311
|
+
await runOne(ctx, runner, "css", cssEntrypoints, node_path$1.default.join(outDir, "assets"), minify);
|
|
3312
|
+
await runOne(ctx, runner, "js", jsEntrypoints, node_path$1.default.join(outDir, "assets"), minify);
|
|
2791
3313
|
}
|
|
2792
|
-
|
|
2793
3314
|
//#endregion
|
|
2794
3315
|
//#region src/plugins/build/phases/content.ts
|
|
2795
3316
|
/**
|
|
@@ -2832,7 +3353,6 @@ function readCachedContent(ctx) {
|
|
|
2832
3353
|
const cached = ctx.state.buildCache.get(CONTENT_CACHE_KEY);
|
|
2833
3354
|
return cached instanceof Map ? cached : /* @__PURE__ */ new Map();
|
|
2834
3355
|
}
|
|
2835
|
-
|
|
2836
3356
|
//#endregion
|
|
2837
3357
|
//#region src/plugins/build/phases/feeds.ts
|
|
2838
3358
|
/**
|
|
@@ -2907,14 +3427,13 @@ async function generateFeeds(ctx) {
|
|
|
2907
3427
|
};
|
|
2908
3428
|
await (0, node_fs_promises.mkdir)(ctx.config.outDir, { recursive: true });
|
|
2909
3429
|
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")
|
|
3430
|
+
(0, node_fs_promises.writeFile)(node_path$1.default.join(ctx.config.outDir, "feed.xml"), result.rss, "utf8"),
|
|
3431
|
+
(0, node_fs_promises.writeFile)(node_path$1.default.join(ctx.config.outDir, "atom.xml"), result.atom, "utf8"),
|
|
3432
|
+
(0, node_fs_promises.writeFile)(node_path$1.default.join(ctx.config.outDir, "feed.json"), result.json, "utf8")
|
|
2913
3433
|
]);
|
|
2914
3434
|
ctx.log.debug("build:feeds", { items: guids.length });
|
|
2915
3435
|
return result;
|
|
2916
3436
|
}
|
|
2917
|
-
|
|
2918
3437
|
//#endregion
|
|
2919
3438
|
//#region src/plugins/build/phases/images.ts
|
|
2920
3439
|
/**
|
|
@@ -2942,7 +3461,7 @@ async function processImages(ctx, options = {}) {
|
|
|
2942
3461
|
return 0;
|
|
2943
3462
|
}
|
|
2944
3463
|
const sourceDirectories = options.sourceDirectories ?? IMAGE_SOURCE_DIRECTORIES;
|
|
2945
|
-
const target = node_path.default.join(ctx.config.outDir, "assets");
|
|
3464
|
+
const target = node_path$1.default.join(ctx.config.outDir, "assets");
|
|
2946
3465
|
let copied = 0;
|
|
2947
3466
|
for (const directory of sourceDirectories) {
|
|
2948
3467
|
if (!(0, node_fs.existsSync)(directory)) continue;
|
|
@@ -2954,7 +3473,6 @@ async function processImages(ctx, options = {}) {
|
|
|
2954
3473
|
ctx.log.debug("build:images", { copied });
|
|
2955
3474
|
return copied;
|
|
2956
3475
|
}
|
|
2957
|
-
|
|
2958
3476
|
//#endregion
|
|
2959
3477
|
//#region src/plugins/build/phases/og-images.tsx
|
|
2960
3478
|
/**
|
|
@@ -2968,8 +3486,6 @@ const DEFAULT_SIZE = {
|
|
|
2968
3486
|
width: 1200,
|
|
2969
3487
|
height: 630
|
|
2970
3488
|
};
|
|
2971
|
-
/** The fixed concurrency bound for the OG render pool. */
|
|
2972
|
-
const OG_CONCURRENCY = 4;
|
|
2973
3489
|
/** Recognized font file extensions. */
|
|
2974
3490
|
const FONT_EXTENSIONS$1 = [
|
|
2975
3491
|
".ttf",
|
|
@@ -3090,7 +3606,7 @@ async function generateOgImages(ctx, options = {}) {
|
|
|
3090
3606
|
const articles = selectArticles(readCachedContent(ctx));
|
|
3091
3607
|
const cache = ctx.state.ogImageHashCache;
|
|
3092
3608
|
await loadDiskCache(ctx.config.outDir, cache);
|
|
3093
|
-
const limit = pLimit(
|
|
3609
|
+
const limit = pLimit(4);
|
|
3094
3610
|
let active = 0;
|
|
3095
3611
|
let peakConcurrency = 0;
|
|
3096
3612
|
let rendered = 0;
|
|
@@ -3163,7 +3679,6 @@ async function persistDiskCache(outDir, cache) {
|
|
|
3163
3679
|
await (0, node_fs_promises.mkdir)(dir, { recursive: true });
|
|
3164
3680
|
await (0, node_fs_promises.writeFile)(node_path.default.join(dir, "og-images.json"), JSON.stringify(Object.fromEntries(cache)), "utf8");
|
|
3165
3681
|
}
|
|
3166
|
-
|
|
3167
3682
|
//#endregion
|
|
3168
3683
|
//#region src/plugins/build/phases/pages.tsx
|
|
3169
3684
|
/**
|
|
@@ -3331,7 +3846,6 @@ async function renderPages(ctx) {
|
|
|
3331
3846
|
rootHtml: root?.html ?? null
|
|
3332
3847
|
};
|
|
3333
3848
|
}
|
|
3334
|
-
|
|
3335
3849
|
//#endregion
|
|
3336
3850
|
//#region src/plugins/build/phases/sitemap.ts
|
|
3337
3851
|
/**
|
|
@@ -3405,7 +3919,7 @@ async function generateSitemap(ctx) {
|
|
|
3405
3919
|
const xml = serializeSitemap(urls);
|
|
3406
3920
|
const robots = `User-agent: *\nAllow: /\nSitemap: ${site.canonical("/sitemap.xml")}\n`;
|
|
3407
3921
|
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")]);
|
|
3922
|
+
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
3923
|
ctx.log.debug("build:sitemap", { urls: urls.length });
|
|
3410
3924
|
return {
|
|
3411
3925
|
urls,
|
|
@@ -3413,7 +3927,6 @@ async function generateSitemap(ctx) {
|
|
|
3413
3927
|
robots
|
|
3414
3928
|
};
|
|
3415
3929
|
}
|
|
3416
|
-
|
|
3417
3930
|
//#endregion
|
|
3418
3931
|
//#region src/plugins/build/pipeline.ts
|
|
3419
3932
|
/**
|
|
@@ -3534,7 +4047,7 @@ async function runPipeline(ctx, options) {
|
|
|
3534
4047
|
const pages = await withPhase(phaseContext, "pages", () => renderPages(phaseContext));
|
|
3535
4048
|
await runOutputs(phaseContext);
|
|
3536
4049
|
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");
|
|
4050
|
+
if (pages.rootHtml !== null) await (0, node_fs_promises.writeFile)(node_path$1.default.join(outDir, "index.html"), pages.rootHtml, "utf8");
|
|
3538
4051
|
});
|
|
3539
4052
|
const result = {
|
|
3540
4053
|
outDir,
|
|
@@ -3544,7 +4057,6 @@ async function runPipeline(ctx, options) {
|
|
|
3544
4057
|
phaseContext.emit("build:complete", result);
|
|
3545
4058
|
return result;
|
|
3546
4059
|
}
|
|
3547
|
-
|
|
3548
4060
|
//#endregion
|
|
3549
4061
|
//#region src/plugins/build/api.ts
|
|
3550
4062
|
/**
|
|
@@ -3583,9 +4095,29 @@ const defaultConfig$1 = {
|
|
|
3583
4095
|
*/
|
|
3584
4096
|
function createApi$2(ctx) {
|
|
3585
4097
|
return {
|
|
4098
|
+
/**
|
|
4099
|
+
* Run the full SSG pipeline and write the site to disk.
|
|
4100
|
+
*
|
|
4101
|
+
* @param options - Optional run overrides.
|
|
4102
|
+
* @param options.outDir - Override the configured output directory for this run.
|
|
4103
|
+
* @returns The build result (outDir, pageCount, durationMs).
|
|
4104
|
+
* @example
|
|
4105
|
+
* ```ts
|
|
4106
|
+
* await api.run({ outDir: "./preview" });
|
|
4107
|
+
* ```
|
|
4108
|
+
*/
|
|
3586
4109
|
run(options) {
|
|
3587
4110
|
return runPipeline(ctx, options);
|
|
3588
4111
|
},
|
|
4112
|
+
/**
|
|
4113
|
+
* List the phases in execution order (introspection / tooling).
|
|
4114
|
+
*
|
|
4115
|
+
* @returns A fresh array of the static ordered phase names.
|
|
4116
|
+
* @example
|
|
4117
|
+
* ```ts
|
|
4118
|
+
* api.phases();
|
|
4119
|
+
* ```
|
|
4120
|
+
*/
|
|
3589
4121
|
phases() {
|
|
3590
4122
|
return [...PHASE_ORDER];
|
|
3591
4123
|
}
|
|
@@ -3620,7 +4152,6 @@ function validateConfig$1(config) {
|
|
|
3620
4152
|
if (typeof config.outDir !== "string" || config.outDir.trim().length === 0) throw new Error(`${ERROR_PREFIX$5}.outDir: must be a non-empty string.`);
|
|
3621
4153
|
if (config.ogImage) validateFonts(config.ogImage);
|
|
3622
4154
|
}
|
|
3623
|
-
|
|
3624
4155
|
//#endregion
|
|
3625
4156
|
//#region src/plugins/build/events.ts
|
|
3626
4157
|
/**
|
|
@@ -3640,7 +4171,6 @@ function createEvents(register) {
|
|
|
3640
4171
|
"build:complete": register("Emitted once after a successful build run")
|
|
3641
4172
|
};
|
|
3642
4173
|
}
|
|
3643
|
-
|
|
3644
4174
|
//#endregion
|
|
3645
4175
|
//#region src/plugins/build/state.ts
|
|
3646
4176
|
/**
|
|
@@ -3667,13 +4197,33 @@ function createState$2(ctx) {
|
|
|
3667
4197
|
ogImageHashCache: /* @__PURE__ */ new Map()
|
|
3668
4198
|
};
|
|
3669
4199
|
}
|
|
3670
|
-
|
|
3671
4200
|
//#endregion
|
|
3672
4201
|
//#region src/plugins/build/index.ts
|
|
3673
4202
|
/**
|
|
3674
4203
|
* @file build — Complex plugin: SSG orchestrator (wiring harness only).
|
|
3675
4204
|
* @see README.md
|
|
3676
4205
|
*/
|
|
4206
|
+
/**
|
|
4207
|
+
* Build plugin — the static-site-generation orchestrator. Renders every route to
|
|
4208
|
+
* `outDir`, and optionally emits feeds, a sitemap, optimized images, and OG
|
|
4209
|
+
* images. Depends on site, i18n, content, router, and head; emits `build:phase`.
|
|
4210
|
+
*
|
|
4211
|
+
* @example Configure the production build
|
|
4212
|
+
* ```ts
|
|
4213
|
+
* const app = createApp({
|
|
4214
|
+
* pluginConfigs: {
|
|
4215
|
+
* build: {
|
|
4216
|
+
* outDir: "dist",
|
|
4217
|
+
* minify: true,
|
|
4218
|
+
* feeds: true,
|
|
4219
|
+
* sitemap: true,
|
|
4220
|
+
* images: true,
|
|
4221
|
+
* ogImage: false // or an object to enable + configure OG-image generation
|
|
4222
|
+
* }
|
|
4223
|
+
* }
|
|
4224
|
+
* });
|
|
4225
|
+
* ```
|
|
4226
|
+
*/
|
|
3677
4227
|
const buildPlugin = createPlugin$1("build", {
|
|
3678
4228
|
depends: [
|
|
3679
4229
|
sitePlugin,
|
|
@@ -3688,7 +4238,6 @@ const buildPlugin = createPlugin$1("build", {
|
|
|
3688
4238
|
api: createApi$2,
|
|
3689
4239
|
onInit: (ctx) => validateConfig$1(ctx.config)
|
|
3690
4240
|
});
|
|
3691
|
-
|
|
3692
4241
|
//#endregion
|
|
3693
4242
|
//#region src/plugins/deploy/wrangler.ts
|
|
3694
4243
|
/**
|
|
@@ -3795,9 +4344,9 @@ function guardBranch(branch) {
|
|
|
3795
4344
|
* assertWithinRoot("dist", process.cwd());
|
|
3796
4345
|
*/
|
|
3797
4346
|
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)}.`);
|
|
4347
|
+
const resolved = node_path$1.default.isAbsolute(outDir) ? node_path$1.default.resolve(outDir) : node_path$1.default.resolve(root, outDir);
|
|
4348
|
+
const rootResolved = node_path$1.default.resolve(root);
|
|
4349
|
+
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
4350
|
return resolved;
|
|
3802
4351
|
}
|
|
3803
4352
|
/**
|
|
@@ -3866,8 +4415,6 @@ const ERROR_SIGNATURES = [
|
|
|
3866
4415
|
advice: "A network failure occurred. Check connectivity and retry."
|
|
3867
4416
|
}
|
|
3868
4417
|
];
|
|
3869
|
-
/** Number of trailing characters of scrubbed stderr to surface on an unknown failure. */
|
|
3870
|
-
const STDERR_TAIL_LENGTH = 500;
|
|
3871
4418
|
/**
|
|
3872
4419
|
* Map a non-zero wrangler exit and scrubbed stderr to an actionable error
|
|
3873
4420
|
* `code` + message. Matching is case-insensitive against the scrubbed stderr;
|
|
@@ -3888,7 +4435,7 @@ function classifyWranglerError(exitCode, scrubbedStderr) {
|
|
|
3888
4435
|
};
|
|
3889
4436
|
return {
|
|
3890
4437
|
code: "ERR_DEPLOY_WRANGLER_FAILED",
|
|
3891
|
-
message: `${ERROR_PREFIX$4}: wrangler failed (exit ${exitCode}).\n ${scrubbedStderr.trim().slice(-
|
|
4438
|
+
message: `${ERROR_PREFIX$4}: wrangler failed (exit ${exitCode}).\n ${scrubbedStderr.trim().slice(-500)}`
|
|
3892
4439
|
};
|
|
3893
4440
|
}
|
|
3894
4441
|
/**
|
|
@@ -3947,7 +4494,6 @@ async function runWrangler(input) {
|
|
|
3947
4494
|
exitCode
|
|
3948
4495
|
};
|
|
3949
4496
|
}
|
|
3950
|
-
|
|
3951
4497
|
//#endregion
|
|
3952
4498
|
//#region src/plugins/deploy/generators/github-workflow.ts
|
|
3953
4499
|
/**
|
|
@@ -4002,7 +4548,6 @@ jobs:
|
|
|
4002
4548
|
command: pages deploy dist --project-name ${input.slug}
|
|
4003
4549
|
`;
|
|
4004
4550
|
}
|
|
4005
|
-
|
|
4006
4551
|
//#endregion
|
|
4007
4552
|
//#region src/plugins/deploy/generators/wrangler-config.ts
|
|
4008
4553
|
/**
|
|
@@ -4041,12 +4586,11 @@ function generateWranglerConfig(input) {
|
|
|
4041
4586
|
*/
|
|
4042
4587
|
async function readWranglerConfig(cwd) {
|
|
4043
4588
|
try {
|
|
4044
|
-
return await (0, node_fs_promises.readFile)(node_path.default.join(cwd, "wrangler.jsonc"), "utf8");
|
|
4589
|
+
return await (0, node_fs_promises.readFile)(node_path$1.default.join(cwd, "wrangler.jsonc"), "utf8");
|
|
4045
4590
|
} catch {
|
|
4046
4591
|
return null;
|
|
4047
4592
|
}
|
|
4048
4593
|
}
|
|
4049
|
-
|
|
4050
4594
|
//#endregion
|
|
4051
4595
|
//#region src/plugins/deploy/init.ts
|
|
4052
4596
|
/**
|
|
@@ -4068,7 +4612,7 @@ const WORKFLOW_PATH = ".github/workflows/deploy.yml";
|
|
|
4068
4612
|
*/
|
|
4069
4613
|
async function readMaybe(cwd, relativePath) {
|
|
4070
4614
|
try {
|
|
4071
|
-
return await (0, node_fs_promises.readFile)(node_path.default.join(cwd, relativePath), "utf8");
|
|
4615
|
+
return await (0, node_fs_promises.readFile)(node_path$1.default.join(cwd, relativePath), "utf8");
|
|
4072
4616
|
} catch {
|
|
4073
4617
|
return null;
|
|
4074
4618
|
}
|
|
@@ -4146,11 +4690,10 @@ async function reconcile(input) {
|
|
|
4146
4690
|
result.skipped.push(relativePath);
|
|
4147
4691
|
return;
|
|
4148
4692
|
}
|
|
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");
|
|
4693
|
+
await (0, node_fs_promises.mkdir)(node_path$1.default.dirname(node_path$1.default.join(cwd, relativePath)), { recursive: true });
|
|
4694
|
+
await (0, node_fs_promises.writeFile)(node_path$1.default.join(cwd, relativePath), expected, "utf8");
|
|
4151
4695
|
result.written.push(relativePath);
|
|
4152
4696
|
}
|
|
4153
|
-
|
|
4154
4697
|
//#endregion
|
|
4155
4698
|
//#region src/plugins/deploy/preflight.ts
|
|
4156
4699
|
/**
|
|
@@ -4202,7 +4745,7 @@ async function inspectOutdir(dir) {
|
|
|
4202
4745
|
if (current === void 0) break;
|
|
4203
4746
|
const entries = await (0, node_fs_promises.readdir)(current, { withFileTypes: true });
|
|
4204
4747
|
for (const entry of entries) {
|
|
4205
|
-
const entryPath = node_path.default.join(current, entry.name);
|
|
4748
|
+
const entryPath = node_path$1.default.join(current, entry.name);
|
|
4206
4749
|
if (entry.isDirectory()) stack.push(entryPath);
|
|
4207
4750
|
else if (entry.isFile()) {
|
|
4208
4751
|
result.fileCount += 1;
|
|
@@ -4230,13 +4773,13 @@ async function inspectOutdir(dir) {
|
|
|
4230
4773
|
* await runPreflight(config, process.cwd());
|
|
4231
4774
|
*/
|
|
4232
4775
|
async function runPreflight(config, root, env = process.env) {
|
|
4233
|
-
const wranglerPath = node_path.default.join(root, "wrangler.jsonc");
|
|
4776
|
+
const wranglerPath = node_path$1.default.join(root, "wrangler.jsonc");
|
|
4234
4777
|
try {
|
|
4235
4778
|
await (0, node_fs_promises.stat)(wranglerPath);
|
|
4236
4779
|
} catch {
|
|
4237
4780
|
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
4781
|
}
|
|
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(() => {
|
|
4782
|
+
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
4783
|
throw deployError("ERR_DEPLOY_EMPTY_OUTDIR", `${ERROR_PREFIX$3}: outDir ${JSON.stringify(config.outDir)} is missing.\n Run your build first, then retry.`);
|
|
4241
4784
|
});
|
|
4242
4785
|
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 +4787,6 @@ async function runPreflight(config, root, env = process.env) {
|
|
|
4244
4787
|
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
4788
|
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
4789
|
}
|
|
4247
|
-
|
|
4248
4790
|
//#endregion
|
|
4249
4791
|
//#region src/plugins/deploy/slug.ts
|
|
4250
4792
|
/**
|
|
@@ -4285,7 +4827,6 @@ function toSlug(name) {
|
|
|
4285
4827
|
slug = slug.slice(0, end);
|
|
4286
4828
|
return slug.length > 0 ? slug : FALLBACK_SLUG;
|
|
4287
4829
|
}
|
|
4288
|
-
|
|
4289
4830
|
//#endregion
|
|
4290
4831
|
//#region src/plugins/deploy/api.ts
|
|
4291
4832
|
/** Error prefix for deploy config/validation failures (spec/11 Part-3). */
|
|
@@ -4325,6 +4866,15 @@ function validateConfig(ctx) {
|
|
|
4325
4866
|
*/
|
|
4326
4867
|
function createApi$1(ctx) {
|
|
4327
4868
|
return {
|
|
4869
|
+
/**
|
|
4870
|
+
* Deploy the built outDir to Cloudflare Pages via the wrangler subprocess.
|
|
4871
|
+
*
|
|
4872
|
+
* @param options - Optional branch override and build toggle.
|
|
4873
|
+
* @returns The deploy result (url, deploymentId, branch, durationMs).
|
|
4874
|
+
* @throws {Error} With a `code` from the deploy error taxonomy on any failure.
|
|
4875
|
+
* @example
|
|
4876
|
+
* await api.run();
|
|
4877
|
+
*/
|
|
4328
4878
|
async run(options = {}) {
|
|
4329
4879
|
const root = process.cwd();
|
|
4330
4880
|
const slug = toSlug(ctx.require(sitePlugin).name());
|
|
@@ -4364,10 +4914,25 @@ function createApi$1(ctx) {
|
|
|
4364
4914
|
});
|
|
4365
4915
|
return result;
|
|
4366
4916
|
},
|
|
4917
|
+
/**
|
|
4918
|
+
* Return the most recent successful deploy result, or null if none occurred.
|
|
4919
|
+
*
|
|
4920
|
+
* @returns A frozen snapshot of the last DeployResult, or null.
|
|
4921
|
+
* @example
|
|
4922
|
+
* const last = api.getLastDeployment();
|
|
4923
|
+
*/
|
|
4367
4924
|
getLastDeployment() {
|
|
4368
4925
|
const last = ctx.state.lastDeployment;
|
|
4369
4926
|
return last ? Object.freeze({ ...last }) : null;
|
|
4370
4927
|
},
|
|
4928
|
+
/**
|
|
4929
|
+
* Generate deploy scaffolding (wrangler.jsonc + optional GitHub workflow).
|
|
4930
|
+
*
|
|
4931
|
+
* @param options - Optional ci toggle and check (drift-only) mode.
|
|
4932
|
+
* @returns Which files were written, skipped, or would drift.
|
|
4933
|
+
* @example
|
|
4934
|
+
* await api.init({ ci: true });
|
|
4935
|
+
*/
|
|
4371
4936
|
async init(options = {}) {
|
|
4372
4937
|
const slug = toSlug(ctx.require(sitePlugin).name());
|
|
4373
4938
|
return writeScaffolding({
|
|
@@ -4379,7 +4944,6 @@ function createApi$1(ctx) {
|
|
|
4379
4944
|
}
|
|
4380
4945
|
};
|
|
4381
4946
|
}
|
|
4382
|
-
|
|
4383
4947
|
//#endregion
|
|
4384
4948
|
//#region src/plugins/deploy/defaults.ts
|
|
4385
4949
|
/**
|
|
@@ -4399,7 +4963,6 @@ const defaultConfig = {
|
|
|
4399
4963
|
compatibilityDate: "2024-01-01",
|
|
4400
4964
|
ci: false
|
|
4401
4965
|
};
|
|
4402
|
-
|
|
4403
4966
|
//#endregion
|
|
4404
4967
|
//#region src/plugins/deploy/events.ts
|
|
4405
4968
|
/**
|
|
@@ -4414,7 +4977,6 @@ const defaultConfig = {
|
|
|
4414
4977
|
* ```
|
|
4415
4978
|
*/
|
|
4416
4979
|
const deployEvents = (register) => ({ "deploy:complete": register("Deployment completed successfully") });
|
|
4417
|
-
|
|
4418
4980
|
//#endregion
|
|
4419
4981
|
//#region src/plugins/deploy/state.ts
|
|
4420
4982
|
/**
|
|
@@ -4452,7 +5014,6 @@ function createState$1(_ctx) {
|
|
|
4452
5014
|
spawn: defaultSpawn
|
|
4453
5015
|
};
|
|
4454
5016
|
}
|
|
4455
|
-
|
|
4456
5017
|
//#endregion
|
|
4457
5018
|
//#region src/plugins/deploy/index.ts
|
|
4458
5019
|
/**
|
|
@@ -4462,6 +5023,24 @@ function createState$1(_ctx) {
|
|
|
4462
5023
|
* Depends: site. Emits: deploy:complete.
|
|
4463
5024
|
* @see README.md
|
|
4464
5025
|
*/
|
|
5026
|
+
/**
|
|
5027
|
+
* Deploy plugin — ships the built `outDir` to Cloudflare Pages via the injectable
|
|
5028
|
+
* wrangler subprocess, with entropy-gated secret scrubbing of logged output.
|
|
5029
|
+
* Depends on site; emits `deploy:complete`.
|
|
5030
|
+
*
|
|
5031
|
+
* @example Configure the deploy target
|
|
5032
|
+
* ```ts
|
|
5033
|
+
* const app = createApp({
|
|
5034
|
+
* pluginConfigs: {
|
|
5035
|
+
* deploy: {
|
|
5036
|
+
* target: "cloudflare-pages",
|
|
5037
|
+
* outDir: "dist",
|
|
5038
|
+
* productionBranch: "main"
|
|
5039
|
+
* }
|
|
5040
|
+
* }
|
|
5041
|
+
* });
|
|
5042
|
+
* ```
|
|
5043
|
+
*/
|
|
4465
5044
|
const deployPlugin = createPlugin$1("deploy", {
|
|
4466
5045
|
config: defaultConfig,
|
|
4467
5046
|
depends: [sitePlugin],
|
|
@@ -4470,7 +5049,6 @@ const deployPlugin = createPlugin$1("deploy", {
|
|
|
4470
5049
|
onInit: validateConfig,
|
|
4471
5050
|
api: createApi$1
|
|
4472
5051
|
});
|
|
4473
|
-
|
|
4474
5052
|
//#endregion
|
|
4475
5053
|
//#region src/plugins/spa/api.ts
|
|
4476
5054
|
/**
|
|
@@ -4485,19 +5063,39 @@ const deployPlugin = createPlugin$1("deploy", {
|
|
|
4485
5063
|
*/
|
|
4486
5064
|
function createApi(ctx) {
|
|
4487
5065
|
return {
|
|
5066
|
+
/**
|
|
5067
|
+
* Register a component definition (last-registered-wins); warns on collision.
|
|
5068
|
+
*
|
|
5069
|
+
* @param component - The component definition created via `createComponent`.
|
|
5070
|
+
* @example
|
|
5071
|
+
* app.spa.register(counter);
|
|
5072
|
+
*/
|
|
4488
5073
|
register(component) {
|
|
4489
5074
|
if (ctx.state.registeredComponents.has(component.name)) ctx.log.warn("spa:component-collision", { name: component.name });
|
|
4490
5075
|
ctx.state.kernel?.register(component);
|
|
4491
5076
|
},
|
|
5077
|
+
/**
|
|
5078
|
+
* Programmatically navigate to a path (client runtime; no-op without a DOM).
|
|
5079
|
+
*
|
|
5080
|
+
* @param path - Target path (pathname, optionally with search/hash).
|
|
5081
|
+
* @example
|
|
5082
|
+
* app.spa.navigate("/about");
|
|
5083
|
+
*/
|
|
4492
5084
|
navigate(path) {
|
|
4493
5085
|
ctx.state.kernel?.processNav(path);
|
|
4494
5086
|
},
|
|
5087
|
+
/**
|
|
5088
|
+
* Read the current resolved URL.
|
|
5089
|
+
*
|
|
5090
|
+
* @returns The current pathname + search.
|
|
5091
|
+
* @example
|
|
5092
|
+
* app.spa.current();
|
|
5093
|
+
*/
|
|
4495
5094
|
current() {
|
|
4496
5095
|
return ctx.state.currentUrl;
|
|
4497
5096
|
}
|
|
4498
5097
|
};
|
|
4499
5098
|
}
|
|
4500
|
-
|
|
4501
5099
|
//#endregion
|
|
4502
5100
|
//#region src/plugins/spa/events.ts
|
|
4503
5101
|
/**
|
|
@@ -4517,7 +5115,6 @@ function spaEvents(register) {
|
|
|
4517
5115
|
"spa:component-unmount": register("A component instance detached from an element.")
|
|
4518
5116
|
};
|
|
4519
5117
|
}
|
|
4520
|
-
|
|
4521
5118
|
//#endregion
|
|
4522
5119
|
//#region src/plugins/spa/types.ts
|
|
4523
5120
|
var types_exports$7 = /* @__PURE__ */ __exportAll({ COMPONENT_HOOK_NAMES: () => COMPONENT_HOOK_NAMES });
|
|
@@ -4530,11 +5127,7 @@ const COMPONENT_HOOK_NAMES = [
|
|
|
4530
5127
|
"onUnMount",
|
|
4531
5128
|
"onDestroy"
|
|
4532
5129
|
];
|
|
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);
|
|
5130
|
+
new Set(COMPONENT_HOOK_NAMES);
|
|
4538
5131
|
/**
|
|
4539
5132
|
* Extracts the page data payload from the inline `script#__DATA__` element.
|
|
4540
5133
|
* Returns an empty object when the script is absent, empty, or invalid JSON.
|
|
@@ -4702,7 +5295,6 @@ function notifyNavEnd(state) {
|
|
|
4702
5295
|
const data = typeof document === "undefined" ? {} : extractPageData(document);
|
|
4703
5296
|
for (const [element, instance] of state.instances) if (instance.persistent) runHook(instance, "onNavEnd", makeContext(element, data));
|
|
4704
5297
|
}
|
|
4705
|
-
|
|
4706
5298
|
//#endregion
|
|
4707
5299
|
//#region src/plugins/spa/head.ts
|
|
4708
5300
|
/** Single-element head selectors synced by replace/append/remove on navigation. */
|
|
@@ -4777,7 +5369,6 @@ function syncHead(_head, doc) {
|
|
|
4777
5369
|
for (const selector of META_SELECTORS) syncElement(selector, doc);
|
|
4778
5370
|
for (const selector of REPLACE_ALL_SELECTORS) replaceAllBySelector(selector, doc);
|
|
4779
5371
|
}
|
|
4780
|
-
|
|
4781
5372
|
//#endregion
|
|
4782
5373
|
//#region src/plugins/spa/progress.ts
|
|
4783
5374
|
/** Delay before the bar appears, so fast navigations show no indicator. */
|
|
@@ -4861,7 +5452,6 @@ function createProgressBar(enabled) {
|
|
|
4861
5452
|
done
|
|
4862
5453
|
};
|
|
4863
5454
|
}
|
|
4864
|
-
|
|
4865
5455
|
//#endregion
|
|
4866
5456
|
//#region src/plugins/spa/router.ts
|
|
4867
5457
|
/**
|
|
@@ -5096,7 +5686,6 @@ function attachRouter(handlers) {
|
|
|
5096
5686
|
const navigation = getNavigation();
|
|
5097
5687
|
return navigation ? attachNavigationApi(navigation, handlers) : attachHistoryFallback(handlers);
|
|
5098
5688
|
}
|
|
5099
|
-
|
|
5100
5689
|
//#endregion
|
|
5101
5690
|
//#region src/plugins/spa/state.ts
|
|
5102
5691
|
/** Error prefix for spa config-validation failures (spec/11 Part-3). */
|
|
@@ -5170,7 +5759,6 @@ function createState(_ctx) {
|
|
|
5170
5759
|
kernel: null
|
|
5171
5760
|
};
|
|
5172
5761
|
}
|
|
5173
|
-
|
|
5174
5762
|
//#endregion
|
|
5175
5763
|
//#region src/plugins/spa/kernel.ts
|
|
5176
5764
|
/**
|
|
@@ -5279,10 +5867,22 @@ function createSpaKernel(state, config, emit, deps) {
|
|
|
5279
5867
|
onError: handleError
|
|
5280
5868
|
};
|
|
5281
5869
|
return {
|
|
5870
|
+
/**
|
|
5871
|
+
* Register config components and seed currentUrl from the document.
|
|
5872
|
+
*
|
|
5873
|
+
* @example
|
|
5874
|
+
* kernel.init();
|
|
5875
|
+
*/
|
|
5282
5876
|
init() {
|
|
5283
5877
|
for (const component of resolved.components) registerComponent(state, component);
|
|
5284
5878
|
state.currentUrl = currentLocationUrl();
|
|
5285
5879
|
},
|
|
5880
|
+
/**
|
|
5881
|
+
* Boot navigation interception + initial scan (throws if already started).
|
|
5882
|
+
*
|
|
5883
|
+
* @example
|
|
5884
|
+
* kernel.boot();
|
|
5885
|
+
*/
|
|
5286
5886
|
boot() {
|
|
5287
5887
|
if (typeof document === "undefined") return;
|
|
5288
5888
|
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 +5892,42 @@ function createSpaKernel(state, config, emit, deps) {
|
|
|
5292
5892
|
scanAndMount(state, emit, resolved.swapSelector);
|
|
5293
5893
|
state.started = true;
|
|
5294
5894
|
},
|
|
5895
|
+
/**
|
|
5896
|
+
* Register a component definition (last-registered-wins).
|
|
5897
|
+
*
|
|
5898
|
+
* @param component - The component definition to register.
|
|
5899
|
+
* @example
|
|
5900
|
+
* kernel.register(counter);
|
|
5901
|
+
*/
|
|
5295
5902
|
register(component) {
|
|
5296
5903
|
registerComponent(state, component);
|
|
5297
5904
|
},
|
|
5905
|
+
/**
|
|
5906
|
+
* Process a navigation to `path` (fetch then swap; full reload on error).
|
|
5907
|
+
*
|
|
5908
|
+
* @param path - The target path to navigate to.
|
|
5909
|
+
* @example
|
|
5910
|
+
* kernel.processNav("/about");
|
|
5911
|
+
*/
|
|
5298
5912
|
processNav(path) {
|
|
5299
5913
|
if (typeof document === "undefined") return;
|
|
5300
5914
|
performNavigation(path, handlers).catch(() => {});
|
|
5301
5915
|
},
|
|
5916
|
+
/**
|
|
5917
|
+
* Scan the swap region and mount components for matching elements.
|
|
5918
|
+
*
|
|
5919
|
+
* @example
|
|
5920
|
+
* kernel.scan();
|
|
5921
|
+
*/
|
|
5302
5922
|
scan() {
|
|
5303
5923
|
scanAndMount(state, emit, resolved.swapSelector);
|
|
5304
5924
|
},
|
|
5925
|
+
/**
|
|
5926
|
+
* Tear down router listeners, dispose all instances, reset boot state.
|
|
5927
|
+
*
|
|
5928
|
+
* @example
|
|
5929
|
+
* kernel.dispose();
|
|
5930
|
+
*/
|
|
5305
5931
|
dispose() {
|
|
5306
5932
|
state.destroyRouter?.();
|
|
5307
5933
|
state.destroyRouter = null;
|
|
@@ -5330,7 +5956,6 @@ function initSpa(ctx) {
|
|
|
5330
5956
|
kernelRef.current = kernel;
|
|
5331
5957
|
kernel.init();
|
|
5332
5958
|
}
|
|
5333
|
-
|
|
5334
5959
|
//#endregion
|
|
5335
5960
|
//#region src/plugins/spa/lifecycle.ts
|
|
5336
5961
|
/** Router/instance teardown captured during onStart (undefined when stopped). */
|
|
@@ -5378,7 +6003,6 @@ function disposeSpa() {
|
|
|
5378
6003
|
logRef = void 0;
|
|
5379
6004
|
}
|
|
5380
6005
|
}
|
|
5381
|
-
|
|
5382
6006
|
//#endregion
|
|
5383
6007
|
//#region src/plugins/spa/index.ts
|
|
5384
6008
|
/**
|
|
@@ -5389,6 +6013,26 @@ function disposeSpa() {
|
|
|
5389
6013
|
* Emits: spa:navigate, spa:navigated, spa:component-mount, spa:component-unmount.
|
|
5390
6014
|
* @see README.md
|
|
5391
6015
|
*/
|
|
6016
|
+
/**
|
|
6017
|
+
* SPA plugin — progressive client-side navigation layered over the static site:
|
|
6018
|
+
* swaps a page region on navigation, with an optional progress bar and View
|
|
6019
|
+
* Transitions. Register interactive islands with {@link createComponent}. Depends
|
|
6020
|
+
* on router and head; emits `spa:navigate`, `spa:navigated`, `spa:component-mount`,
|
|
6021
|
+
* and `spa:component-unmount`.
|
|
6022
|
+
*
|
|
6023
|
+
* @example Enable view transitions and a custom swap region
|
|
6024
|
+
* ```ts
|
|
6025
|
+
* const app = createApp({
|
|
6026
|
+
* pluginConfigs: {
|
|
6027
|
+
* spa: {
|
|
6028
|
+
* swapSelector: "main > section",
|
|
6029
|
+
* viewTransitions: true,
|
|
6030
|
+
* progressBar: true
|
|
6031
|
+
* }
|
|
6032
|
+
* }
|
|
6033
|
+
* });
|
|
6034
|
+
* ```
|
|
6035
|
+
*/
|
|
5392
6036
|
const spaPlugin = createPlugin$1("spa", {
|
|
5393
6037
|
depends: [routerPlugin, headPlugin],
|
|
5394
6038
|
config: defaultSpaConfig,
|
|
@@ -5402,35 +6046,27 @@ const spaPlugin = createPlugin$1("spa", {
|
|
|
5402
6046
|
},
|
|
5403
6047
|
onStop: disposeSpa
|
|
5404
6048
|
});
|
|
5405
|
-
|
|
5406
6049
|
//#endregion
|
|
5407
6050
|
//#region src/plugins/build/types.ts
|
|
5408
6051
|
var types_exports = /* @__PURE__ */ __exportAll({});
|
|
5409
|
-
|
|
5410
6052
|
//#endregion
|
|
5411
6053
|
//#region src/plugins/content/types.ts
|
|
5412
6054
|
var types_exports$1 = /* @__PURE__ */ __exportAll({});
|
|
5413
|
-
|
|
5414
6055
|
//#endregion
|
|
5415
6056
|
//#region src/plugins/deploy/types.ts
|
|
5416
6057
|
var types_exports$2 = /* @__PURE__ */ __exportAll({});
|
|
5417
|
-
|
|
5418
6058
|
//#endregion
|
|
5419
6059
|
//#region src/plugins/env/types.ts
|
|
5420
6060
|
var types_exports$3 = /* @__PURE__ */ __exportAll({});
|
|
5421
|
-
|
|
5422
6061
|
//#endregion
|
|
5423
6062
|
//#region src/plugins/head/types.ts
|
|
5424
6063
|
var types_exports$4 = /* @__PURE__ */ __exportAll({});
|
|
5425
|
-
|
|
5426
6064
|
//#endregion
|
|
5427
6065
|
//#region src/plugins/log/types.ts
|
|
5428
6066
|
var types_exports$5 = /* @__PURE__ */ __exportAll({});
|
|
5429
|
-
|
|
5430
6067
|
//#endregion
|
|
5431
6068
|
//#region src/plugins/router/types.ts
|
|
5432
6069
|
var types_exports$6 = /* @__PURE__ */ __exportAll({});
|
|
5433
|
-
|
|
5434
6070
|
//#endregion
|
|
5435
6071
|
//#region src/index.ts
|
|
5436
6072
|
/**
|
|
@@ -5450,56 +6086,94 @@ const framework = createCore(coreConfig, {
|
|
|
5450
6086
|
],
|
|
5451
6087
|
pluginConfigs: {}
|
|
5452
6088
|
});
|
|
5453
|
-
|
|
5454
|
-
|
|
6089
|
+
/**
|
|
6090
|
+
* Create and initialize a `@moku-labs/web` application — the Layer-3 entry point.
|
|
6091
|
+
* Your overrides are merged over the framework defaults through the 4-level config
|
|
6092
|
+
* cascade, every plugin's lifecycle runs, and a fully-typed, frozen app is returned.
|
|
6093
|
+
*
|
|
6094
|
+
* @param options - Optional configuration:
|
|
6095
|
+
* - `pluginConfigs` — per-plugin overrides, keyed by plugin name
|
|
6096
|
+
* (`site`, `i18n`, `router`, `content`, `head`, `build`, `spa`, `deploy`, `env`).
|
|
6097
|
+
* - `config` — global framework config (e.g. `{ mode: "development" }`).
|
|
6098
|
+
* - `plugins` — extra consumer plugins, merged into the app and its return type.
|
|
6099
|
+
* - `onReady` / `onError` / `onStart` / `onStop` — lifecycle callbacks.
|
|
6100
|
+
* @returns The initialized app: `start()`, `stop()`, every plugin's API, and `log`.
|
|
6101
|
+
* @example
|
|
6102
|
+
* ```ts
|
|
6103
|
+
* const app = createApp({
|
|
6104
|
+
* pluginConfigs: {
|
|
6105
|
+
* site: { name: "My Blog", url: "https://blog.dev", author: "Ada", description: "Notes" },
|
|
6106
|
+
* router: { routes: defineRoutes({ home: route("/"), post: route("/blog/{slug}/") }) }
|
|
6107
|
+
* }
|
|
6108
|
+
* });
|
|
6109
|
+
* await app.start();
|
|
6110
|
+
* ```
|
|
6111
|
+
*/
|
|
6112
|
+
const createApp = framework.createApp;
|
|
6113
|
+
/**
|
|
6114
|
+
* Create a custom plugin bound to this framework's `Config`/`Events` and core
|
|
6115
|
+
* APIs. Plugin types are inferred from the spec object — never written explicitly.
|
|
6116
|
+
* Pass the result to {@link createApp} via `plugins`.
|
|
6117
|
+
*
|
|
6118
|
+
* @example
|
|
6119
|
+
* ```ts
|
|
6120
|
+
* const analytics = createPlugin("analytics", {
|
|
6121
|
+
* config: { writeKey: "" },
|
|
6122
|
+
* api: (ctx) => ({ track: (event: string) => ctx.log.info("analytics:track", { event }) })
|
|
6123
|
+
* });
|
|
6124
|
+
*
|
|
6125
|
+
* const app = createApp({ plugins: [analytics] });
|
|
6126
|
+
* ```
|
|
6127
|
+
*/
|
|
6128
|
+
const createPlugin = framework.createPlugin;
|
|
5455
6129
|
//#endregion
|
|
5456
|
-
Object.defineProperty(exports,
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
6130
|
+
Object.defineProperty(exports, "Build", {
|
|
6131
|
+
enumerable: true,
|
|
6132
|
+
get: function() {
|
|
6133
|
+
return types_exports;
|
|
6134
|
+
}
|
|
5461
6135
|
});
|
|
5462
|
-
Object.defineProperty(exports,
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
6136
|
+
Object.defineProperty(exports, "Content", {
|
|
6137
|
+
enumerable: true,
|
|
6138
|
+
get: function() {
|
|
6139
|
+
return types_exports$1;
|
|
6140
|
+
}
|
|
5467
6141
|
});
|
|
5468
|
-
Object.defineProperty(exports,
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
6142
|
+
Object.defineProperty(exports, "Deploy", {
|
|
6143
|
+
enumerable: true,
|
|
6144
|
+
get: function() {
|
|
6145
|
+
return types_exports$2;
|
|
6146
|
+
}
|
|
5473
6147
|
});
|
|
5474
|
-
Object.defineProperty(exports,
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
6148
|
+
Object.defineProperty(exports, "Env", {
|
|
6149
|
+
enumerable: true,
|
|
6150
|
+
get: function() {
|
|
6151
|
+
return types_exports$3;
|
|
6152
|
+
}
|
|
5479
6153
|
});
|
|
5480
|
-
Object.defineProperty(exports,
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
6154
|
+
Object.defineProperty(exports, "Head", {
|
|
6155
|
+
enumerable: true,
|
|
6156
|
+
get: function() {
|
|
6157
|
+
return types_exports$4;
|
|
6158
|
+
}
|
|
5485
6159
|
});
|
|
5486
|
-
Object.defineProperty(exports,
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
6160
|
+
Object.defineProperty(exports, "Log", {
|
|
6161
|
+
enumerable: true,
|
|
6162
|
+
get: function() {
|
|
6163
|
+
return types_exports$5;
|
|
6164
|
+
}
|
|
5491
6165
|
});
|
|
5492
|
-
Object.defineProperty(exports,
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
6166
|
+
Object.defineProperty(exports, "Router", {
|
|
6167
|
+
enumerable: true,
|
|
6168
|
+
get: function() {
|
|
6169
|
+
return types_exports$6;
|
|
6170
|
+
}
|
|
5497
6171
|
});
|
|
5498
|
-
Object.defineProperty(exports,
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
6172
|
+
Object.defineProperty(exports, "Spa", {
|
|
6173
|
+
enumerable: true,
|
|
6174
|
+
get: function() {
|
|
6175
|
+
return types_exports$7;
|
|
6176
|
+
}
|
|
5503
6177
|
});
|
|
5504
6178
|
exports.buildArticleHead = buildArticleHead;
|
|
5505
6179
|
exports.buildPlugin = buildPlugin;
|
|
@@ -5522,4 +6196,4 @@ exports.route = route;
|
|
|
5522
6196
|
exports.routerPlugin = routerPlugin;
|
|
5523
6197
|
exports.sitePlugin = sitePlugin;
|
|
5524
6198
|
exports.spaPlugin = spaPlugin;
|
|
5525
|
-
exports.twitter = twitter;
|
|
6199
|
+
exports.twitter = twitter;
|