@askrjs/askr 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/access-CEA2pOry.js +1046 -0
- package/dist/actions/index.d.ts +16 -2
- package/dist/actions/index.js +5 -5
- package/dist/{activity-ZxJXI9hF.js → activity-oy2YFTFF.js} +22 -16
- package/dist/benchmark.js +8 -9
- package/dist/boot/index.d.ts +21 -9
- package/dist/boot/index.js +1 -1
- package/dist/{boot-D4GaLA0d.js → boot-BMBMWUGI.js} +680 -440
- package/dist/capabilities-Bbf0mlp9.js +42 -0
- package/dist/components/index.d.ts +11 -8
- package/dist/components/index.js +1 -1
- package/dist/{compose-ref-Bfaf4kUC.js → compose-ref-v0qGlHoN.js} +7 -1
- package/dist/control/index.d.ts +20 -2
- package/dist/control/index.js +1 -1
- package/dist/{control-57x3wa4J.js → control-BR_mrX__.js} +4 -4
- package/dist/core.d.ts +163 -0
- package/dist/csp-nonce-BfFWcd0w.js +173 -0
- package/dist/data/index.d.ts +74 -2
- package/dist/data/index.js +3 -3
- package/dist/{data-B7XCDeC8.js → data-2edW0jhF.js} +133 -138
- package/dist/{data-runtime-7t_Y0iv3.js → data-runtime-CxoY98lX.js} +22 -15
- package/dist/{deferred-BELyvMsC.js → deferred-CbgNhf67.js} +5 -5
- package/dist/dom-renderer.d.ts +77 -0
- package/dist/domains/actions.d.ts +70 -0
- package/dist/domains/component.d.ts +119 -0
- package/dist/domains/context.d.ts +73 -0
- package/dist/domains/control.d.ts +328 -0
- package/dist/domains/data.d.ts +461 -0
- package/dist/domains/diagnostics.d.ts +21 -0
- package/dist/domains/lifecycle.d.ts +112 -0
- package/dist/domains/refs.d.ts +18 -0
- package/dist/domains/renderer.d.ts +127 -0
- package/dist/domains/routing.d.ts +501 -0
- package/dist/domains/scheduler.d.ts +79 -0
- package/dist/domains/server.d.ts +112 -0
- package/dist/domains/state.d.ts +129 -0
- package/dist/domains/telemetry.d.ts +29 -0
- package/dist/eager-control.d.ts +8 -0
- package/dist/{effect-4LwBwV58.js → effect-C3Zsc1k6.js} +14 -41
- package/dist/{types-DkVJhVs9.d.ts → elements.d.ts} +84 -42
- package/dist/{for-internal-DeoSg_AV.js → for-state-BDykeOJL.js} +623 -511
- package/dist/foundations/icon/index.d.ts +67 -16
- package/dist/foundations/icon/index.js +1 -1
- package/dist/foundations/index.d.ts +26 -3
- package/dist/foundations/index.js +1 -1
- package/dist/foundations/interactions/index.d.ts +73 -34
- package/dist/foundations/interactions/index.js +1 -1
- package/dist/foundations/state/index.d.ts +12 -5
- package/dist/foundations/state/index.js +1 -1
- package/dist/foundations/structures/index.d.ts +44 -3
- package/dist/foundations/structures/index.js +1 -1
- package/dist/foundations/utilities/index.d.ts +34 -3
- package/dist/foundations/utilities/index.js +1 -1
- package/dist/fx/index.d.ts +72 -25
- package/dist/fx/index.js +8 -8
- package/dist/index.d.ts +121 -4
- package/dist/index.js +316 -47
- package/dist/{component-internal-Dl02WQKS.js → instance-VAFYVJOa.js} +495 -283
- package/dist/{interactions-D102JxBR.js → interactions-CTeACdT_.js} +1 -1
- package/dist/jsx-dev-runtime.d.ts +51 -11
- package/dist/jsx-globals.d.ts +2 -0
- package/dist/jsx-runtime-CPJXAOBF.js +43 -0
- package/dist/jsx-runtime.d.ts +17 -3
- package/dist/jsx-runtime.js +2 -42
- package/dist/jsx.d.ts +108 -0
- package/dist/{manifest-D6rfj_jC.js → manifest-D1P3UNWV.js} +4 -9
- package/dist/{navigate-C46GtScG.js → navigate-prC5V8XV.js} +204 -476
- package/dist/{navigate-wel1e6cU.d.ts → navigation.d.ts} +10 -8
- package/dist/{readable-CEcteIDE.js → notify-DxxuDPeY.js} +96 -34
- package/dist/{query-registry-Bua3cDyb.js → query-registry-LrYpQsDJ.js} +1 -1
- package/dist/{compose-ref-DOJHoW5X.d.ts → refs.d.ts} +11 -7
- package/dist/{ssr-BaC5qHEw.js → render-resolved-BC0W7_5b.js} +165 -558
- package/dist/{renderer-BhXofszH.js → renderer-CT0HeY58.js} +6866 -5715
- package/dist/{resolution-t8J0yNaU.js → resolution-3DskUkjG.js} +189 -180
- package/dist/{resource-operation-B8q-QRUx.js → resource-18-sFRl9.js} +98 -58
- package/dist/resources/index.d.ts +64 -46
- package/dist/resources/index.js +215 -9
- package/dist/route-activity.d.ts +21 -0
- package/dist/{route-matching-Bo6UqG-5.js → route-matching-CUzZ31gT.js} +68 -82
- package/dist/router/index.d.ts +378 -79
- package/dist/router/index.js +9 -9
- package/dist/router-internal-7nuNEHfG.js +36 -0
- package/dist/scope-Cq_iYbTR.js +403 -0
- package/dist/{selector-C7rIpOnN.js → selector-BSPVkjKP.js} +41 -41
- package/dist/{shared-CGrx2AOL.js → shared-kU8yvryp.js} +3 -6
- package/dist/{snapshot-source-BB42xgwb.js → snapshot-source-An4iKajG.js} +1 -1
- package/dist/ssg/index.d.ts +51 -15
- package/dist/ssg/index.js +269 -209
- package/dist/ssr/index.d.ts +121 -68
- package/dist/ssr/index.js +2 -1
- package/dist/ssr-CiNm6Vzl.js +393 -0
- package/dist/{ssr-DSHd_Q9K.js → ssr-EPjwJH5R.js} +5 -1
- package/dist/{state-BzhOW2TX.js → state-BXJfudq1.js} +10 -15
- package/dist/{structures-BwfeO4Iz.js → structures-CjEzfj4h.js} +37 -57
- package/dist/{index-r5THqzPN.d.ts → structures.d.ts} +57 -36
- package/dist/testing/index.d.ts +125 -24
- package/dist/testing/index.js +26 -62
- package/dist/timer-C7uqnJVt.js +85 -0
- package/dist/{index-B1gu0_m0.d.ts → utilities.d.ts} +24 -13
- package/dist/{verify-hydration-_Q6avFp0.js → verify-hydration-ikaxotf7.js} +8 -5
- package/package.json +13 -15
- package/dist/access-Rr7CMa06.js +0 -507
- package/dist/activity-CAJ5eho-.d.ts +0 -14
- package/dist/benchmark.d.ts +0 -26
- package/dist/cleanup-DsrbV-rr.js +0 -535
- package/dist/component-cleanup-DjpwO9WX.js +0 -143
- package/dist/component-scope-4I3yEMkb.js +0 -195
- package/dist/control-DdFw-Caz.d.ts +0 -8
- package/dist/csp-nonce-C9OuML0U.js +0 -72
- package/dist/css-BuPaTOfe.js +0 -70
- package/dist/fastlane-Buz1Rvlr.js +0 -166
- package/dist/index-DSqXWc77.d.ts +0 -1402
- package/dist/jsx-runtime-BTzDdBJ_.d.ts +0 -2
- package/dist/jsx-runtime-Cdd6Gkb0.d.ts +0 -38
- package/dist/lifecycle-batch-Be-CLDjV.js +0 -253
- package/dist/lifecycle-operations-C5m03NQc.js +0 -195
package/dist/ssg/index.js
CHANGED
|
@@ -1,14 +1,109 @@
|
|
|
1
|
-
import "../access-
|
|
2
|
-
import { n as createRouteRegistry } from "../manifest-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { n as renderDocument } from "../ssr-
|
|
7
|
-
import { n as renderToString } from "../ssr-
|
|
8
|
-
import * as fs from "node:fs/promises";
|
|
9
|
-
import * as pathModule from "node:path";
|
|
1
|
+
import "../access-CEA2pOry.js";
|
|
2
|
+
import { n as createRouteRegistry } from "../manifest-D1P3UNWV.js";
|
|
3
|
+
import { _ as prepareRouteHydrationData, d as _preloadRouteHandler, m as RouteDataLoadError, t as bindResolvedRouteData, u as getRenderHandler } from "../resolution-3DskUkjG.js";
|
|
4
|
+
import { o as addRouteBasePath } from "../route-matching-CUzZ31gT.js";
|
|
5
|
+
import { c as route, n as resolveDeferredValues } from "../deferred-CbgNhf67.js";
|
|
6
|
+
import { n as renderDocument } from "../ssr-EPjwJH5R.js";
|
|
7
|
+
import { n as renderToString } from "../ssr-CiNm6Vzl.js";
|
|
8
|
+
import * as fs$1 from "node:fs/promises";
|
|
10
9
|
import * as os from "node:os";
|
|
11
|
-
import * as
|
|
10
|
+
import * as pathModule from "node:path";
|
|
11
|
+
import * as fs from "node:fs";
|
|
12
|
+
//#region src/ssg/parallelism.ts
|
|
13
|
+
function resolveParallelism(requested) {
|
|
14
|
+
if (requested !== "auto") return Math.max(1, requested ?? 1);
|
|
15
|
+
const maybeNavigator = globalThis;
|
|
16
|
+
const envWorkers = Number(maybeNavigator.process?.env?.ASKR_SSG_WORKERS ?? maybeNavigator.process?.env?.NUMBER_OF_PROCESSORS ?? maybeNavigator.process?.env?.UV_THREADPOOL_SIZE);
|
|
17
|
+
if (Number.isFinite(envWorkers) && envWorkers > 0) return Math.max(1, Math.trunc(envWorkers));
|
|
18
|
+
if (typeof maybeNavigator.navigator?.hardwareConcurrency === "number") return Math.max(1, maybeNavigator.navigator.hardwareConcurrency);
|
|
19
|
+
const availableParallelism = os.availableParallelism;
|
|
20
|
+
return Math.max(1, availableParallelism?.() ?? os.cpus().length ?? 1);
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/ssg/output-publication.ts
|
|
24
|
+
const outputDirectoryQueues = /* @__PURE__ */ new Map();
|
|
25
|
+
async function withOutputDirectoryLock(outputDir, operation) {
|
|
26
|
+
const key = pathModule.resolve(outputDir).normalize("NFC").toLowerCase();
|
|
27
|
+
const previous = outputDirectoryQueues.get(key) ?? Promise.resolve();
|
|
28
|
+
let release;
|
|
29
|
+
const current = new Promise((resolve) => {
|
|
30
|
+
release = resolve;
|
|
31
|
+
});
|
|
32
|
+
const tail = previous.then(() => current, () => current);
|
|
33
|
+
outputDirectoryQueues.set(key, tail);
|
|
34
|
+
await previous.catch(() => void 0);
|
|
35
|
+
try {
|
|
36
|
+
return await operation();
|
|
37
|
+
} finally {
|
|
38
|
+
release();
|
|
39
|
+
if (outputDirectoryQueues.get(key) === tail) outputDirectoryQueues.delete(key);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async function createStagingDirectory(outputDir) {
|
|
43
|
+
const parent = pathModule.dirname(outputDir);
|
|
44
|
+
const base = pathModule.basename(outputDir);
|
|
45
|
+
await fs$1.mkdir(parent, { recursive: true });
|
|
46
|
+
return fs$1.mkdtemp(pathModule.join(parent, `.${base}.askr-staging-`));
|
|
47
|
+
}
|
|
48
|
+
function resolveAssetDestination(outputDir, source) {
|
|
49
|
+
const relativeDestination = source.to ?? pathModule.basename(source.from);
|
|
50
|
+
const destination = pathModule.resolve(outputDir, relativeDestination);
|
|
51
|
+
const relative = pathModule.relative(pathModule.resolve(outputDir), destination);
|
|
52
|
+
if (relative.startsWith("..") || pathModule.isAbsolute(relative)) throw new Error(`SSG asset destination must stay inside outputDir: ${relativeDestination}`);
|
|
53
|
+
return destination;
|
|
54
|
+
}
|
|
55
|
+
async function copyStaticAssets(assets, outputDir) {
|
|
56
|
+
for (const source of assets ?? []) {
|
|
57
|
+
if (!source.from || source.from.trim().length === 0) throw new Error("SSG asset source requires a non-empty from path");
|
|
58
|
+
const from = pathModule.resolve(source.from);
|
|
59
|
+
const to = resolveAssetDestination(outputDir, source);
|
|
60
|
+
await fs$1.mkdir(pathModule.dirname(to), { recursive: true });
|
|
61
|
+
await fs$1.cp(from, to, {
|
|
62
|
+
recursive: true,
|
|
63
|
+
force: true
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async function replaceOutputDirectory(stagingDir, outputDir, fileOperations = fs$1) {
|
|
68
|
+
const parent = pathModule.dirname(outputDir);
|
|
69
|
+
const backupDir = pathModule.join(parent, `.${pathModule.basename(outputDir)}.askr-backup-${Date.now()}`);
|
|
70
|
+
let backedUp = false;
|
|
71
|
+
let failed = false;
|
|
72
|
+
let failure;
|
|
73
|
+
try {
|
|
74
|
+
try {
|
|
75
|
+
await fileOperations.rename(outputDir, backupDir);
|
|
76
|
+
backedUp = true;
|
|
77
|
+
} catch (error) {
|
|
78
|
+
if (error.code !== "ENOENT") throw error;
|
|
79
|
+
}
|
|
80
|
+
await fileOperations.rename(stagingDir, outputDir);
|
|
81
|
+
await fileOperations.rm(backupDir, {
|
|
82
|
+
recursive: true,
|
|
83
|
+
force: true
|
|
84
|
+
});
|
|
85
|
+
} catch (error) {
|
|
86
|
+
failed = true;
|
|
87
|
+
failure = error;
|
|
88
|
+
if (backedUp) try {
|
|
89
|
+
await fileOperations.rm(outputDir, {
|
|
90
|
+
recursive: true,
|
|
91
|
+
force: true
|
|
92
|
+
});
|
|
93
|
+
await fileOperations.rename(backupDir, outputDir);
|
|
94
|
+
} catch {}
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
await fileOperations.rm(stagingDir, {
|
|
98
|
+
recursive: true,
|
|
99
|
+
force: true
|
|
100
|
+
});
|
|
101
|
+
} catch (error) {
|
|
102
|
+
if (!failed) throw error;
|
|
103
|
+
}
|
|
104
|
+
if (failed) throw failure;
|
|
105
|
+
}
|
|
106
|
+
//#endregion
|
|
12
107
|
//#region src/ssg/route-utils.ts
|
|
13
108
|
function getPortableOutputPathKey(filePath) {
|
|
14
109
|
return filePath.normalize("NFC").replaceAll("\\", "/").toLowerCase();
|
|
@@ -312,15 +407,13 @@ function resolveSsgOutputPath(outputDir, filePath) {
|
|
|
312
407
|
* Write rendered routes to disk
|
|
313
408
|
* Creates outputDir/{route-path}/index.html structure
|
|
314
409
|
*/
|
|
315
|
-
async function writeStaticFiles(results, outputDir, options = {}, fileOperations = fs) {
|
|
410
|
+
async function writeStaticFiles(results, outputDir, options = {}, fileOperations = fs$1) {
|
|
316
411
|
await fileOperations.mkdir(outputDir, { recursive: true });
|
|
317
412
|
for (const result of results) {
|
|
318
413
|
if (result.status !== "removed") continue;
|
|
319
414
|
const fullPath = resolveSsgOutputPath(outputDir, result.filePath);
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
await pruneEmptyDirs(pathModule.dirname(fullPath), outputDir, fileOperations);
|
|
323
|
-
}
|
|
415
|
+
await fileOperations.rm(fullPath, { force: true });
|
|
416
|
+
await pruneEmptyDirs(pathModule.dirname(fullPath), outputDir, fileOperations);
|
|
324
417
|
}
|
|
325
418
|
const pendingWrites = [];
|
|
326
419
|
const reportedErrors = /* @__PURE__ */ new Set();
|
|
@@ -348,23 +441,42 @@ async function writeStaticFiles(results, outputDir, options = {}, fileOperations
|
|
|
348
441
|
for (const dir of directories) await fileOperations.mkdir(dir, { recursive: true });
|
|
349
442
|
const concurrency = Math.max(1, Math.min(options.concurrency ?? 8, pendingWrites.length || 1));
|
|
350
443
|
let nextIndex = 0;
|
|
444
|
+
let failed = false;
|
|
445
|
+
let failure;
|
|
446
|
+
const recordFailure = (error) => {
|
|
447
|
+
if (!failed) {
|
|
448
|
+
failed = true;
|
|
449
|
+
failure = error;
|
|
450
|
+
}
|
|
451
|
+
};
|
|
351
452
|
const worker = async () => {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
453
|
+
try {
|
|
454
|
+
while (!failed) {
|
|
455
|
+
const current = nextIndex;
|
|
456
|
+
nextIndex += 1;
|
|
457
|
+
if (current >= pendingWrites.length) return;
|
|
458
|
+
const result = pendingWrites[current];
|
|
459
|
+
const fullPath = resolveSsgOutputPath(outputDir, result.filePath);
|
|
460
|
+
const tempPath = pathModule.join(pathModule.dirname(fullPath), `.${pathModule.basename(fullPath)}.askr-${process.pid}-${current}.tmp`);
|
|
461
|
+
try {
|
|
462
|
+
await fileOperations.writeFile(tempPath, result.html, "utf8");
|
|
463
|
+
await fileOperations.rename(tempPath, fullPath);
|
|
464
|
+
} catch (error) {
|
|
465
|
+
recordFailure(error);
|
|
466
|
+
} finally {
|
|
467
|
+
try {
|
|
468
|
+
await fileOperations.rm(tempPath, { force: true });
|
|
469
|
+
} catch (error) {
|
|
470
|
+
recordFailure(error);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
364
473
|
}
|
|
474
|
+
} catch (error) {
|
|
475
|
+
recordFailure(error);
|
|
365
476
|
}
|
|
366
477
|
};
|
|
367
478
|
await Promise.all(Array.from({ length: concurrency }, () => worker()));
|
|
479
|
+
if (failed) throw failure;
|
|
368
480
|
}
|
|
369
481
|
async function pruneEmptyDirs(startDir, rootDir, fileOperations) {
|
|
370
482
|
let current = startDir;
|
|
@@ -372,8 +484,12 @@ async function pruneEmptyDirs(startDir, rootDir, fileOperations) {
|
|
|
372
484
|
while (pathModule.resolve(current) !== normalizedRoot) {
|
|
373
485
|
const relative = pathModule.relative(normalizedRoot, current);
|
|
374
486
|
if (relative === ".." || relative.startsWith(`..${pathModule.sep}`) || pathModule.isAbsolute(relative)) break;
|
|
375
|
-
|
|
376
|
-
|
|
487
|
+
try {
|
|
488
|
+
if ((await fileOperations.readdir(current)).length > 0) break;
|
|
489
|
+
} catch (error) {
|
|
490
|
+
if (error.code === "ENOENT") break;
|
|
491
|
+
throw error;
|
|
492
|
+
}
|
|
377
493
|
await fileOperations.rmdir(current);
|
|
378
494
|
current = pathModule.dirname(current);
|
|
379
495
|
}
|
|
@@ -464,8 +580,8 @@ function resultToMetadata(result) {
|
|
|
464
580
|
*/
|
|
465
581
|
async function writeMetadata(metadata, outputDir) {
|
|
466
582
|
const filePath = pathModule.join(outputDir, "metadata.json");
|
|
467
|
-
await fs.mkdir(outputDir, { recursive: true });
|
|
468
|
-
await fs.writeFile(filePath, JSON.stringify(metadata, null, 2), "utf8");
|
|
583
|
+
await fs$1.mkdir(outputDir, { recursive: true });
|
|
584
|
+
await fs$1.writeFile(filePath, JSON.stringify(metadata, null, 2), "utf8");
|
|
469
585
|
}
|
|
470
586
|
const MANIFEST_DIR = ".askr";
|
|
471
587
|
const MANIFEST_FILE = "ssg-manifest.json";
|
|
@@ -482,9 +598,9 @@ function getIncrementalManifestPath(outputDir) {
|
|
|
482
598
|
}
|
|
483
599
|
function readIncrementalManifest(outputDir, seed) {
|
|
484
600
|
const manifestPath = getIncrementalManifestPath(outputDir);
|
|
485
|
-
if (!
|
|
601
|
+
if (!fs.existsSync(manifestPath)) return null;
|
|
486
602
|
try {
|
|
487
|
-
const raw =
|
|
603
|
+
const raw = fs.readFileSync(manifestPath, "utf8");
|
|
488
604
|
const parsed = JSON.parse(raw);
|
|
489
605
|
if (parsed.schemaVersion !== 1 || parsed.seed !== seed || !Array.isArray(parsed.routes)) return null;
|
|
490
606
|
const routes = parsed.routes.filter((entry) => !!entry && typeof entry.routeId === "string" && typeof entry.path === "string" && typeof entry.filePath === "string" && isContainedManifestFilePath(outputDir, entry.filePath) && Array.isArray(entry.invalidationKeys) && (entry.htmlHash === null || typeof entry.htmlHash === "string") && (entry.lastStatus === "success" || entry.lastStatus === "error"));
|
|
@@ -510,20 +626,20 @@ function isContainedManifestFilePath(outputDir, filePath) {
|
|
|
510
626
|
async function writeIncrementalManifest(manifest, outputDir) {
|
|
511
627
|
if (!manifest.routes.every((entry) => isContainedManifestFilePath(outputDir, entry.filePath))) throw new Error("SSG manifest contains a path outside outputDir.");
|
|
512
628
|
const manifestPath = getIncrementalManifestPath(outputDir);
|
|
513
|
-
await fs.mkdir(pathModule.dirname(manifestPath), { recursive: true });
|
|
514
|
-
await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf8");
|
|
629
|
+
await fs$1.mkdir(pathModule.dirname(manifestPath), { recursive: true });
|
|
630
|
+
await fs$1.writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf8");
|
|
515
631
|
}
|
|
516
632
|
function getExistingOutputFileSize(outputDir, filePath) {
|
|
517
633
|
const fullPath = resolveSsgOutputPath(outputDir, filePath);
|
|
518
|
-
if (!
|
|
634
|
+
if (!fs.existsSync(fullPath)) return 0;
|
|
519
635
|
try {
|
|
520
|
-
return
|
|
636
|
+
return fs.statSync(fullPath).size;
|
|
521
637
|
} catch {
|
|
522
638
|
return 0;
|
|
523
639
|
}
|
|
524
640
|
}
|
|
525
641
|
function outputFileExists(outputDir, filePath) {
|
|
526
|
-
return
|
|
642
|
+
return fs.existsSync(resolveSsgOutputPath(outputDir, filePath));
|
|
527
643
|
}
|
|
528
644
|
//#endregion
|
|
529
645
|
//#region src/ssg/generation-plan.ts
|
|
@@ -671,107 +787,114 @@ function routeRegistryToRouteConfigs(registry) {
|
|
|
671
787
|
}
|
|
672
788
|
//#endregion
|
|
673
789
|
//#region src/ssg/create-static-gen.ts
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
const
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
790
|
+
/**
|
|
791
|
+
* Turn selected routes and their renders into results and manifest entries.
|
|
792
|
+
*
|
|
793
|
+
* A route arrives in one of three states: unchanged and therefore skipped,
|
|
794
|
+
* rendered successfully, or rendered with an error. Each produces a different
|
|
795
|
+
* result shape and a different manifest entry, and a successful render is only
|
|
796
|
+
* written when its HTML actually differs from what the previous build left.
|
|
797
|
+
*/
|
|
798
|
+
function assembleRouteResults({ selected, renderedByRouteId, routeResultsById, dataMap, effectiveMode, outputDir }) {
|
|
799
|
+
let cacheHits = 0;
|
|
800
|
+
const nextManifestRoutes = [];
|
|
801
|
+
for (const entry of selected) {
|
|
802
|
+
const { descriptor, previous, reason } = entry;
|
|
803
|
+
const resolvedData = resolveSsgRouteData(dataMap, descriptor.route.path, descriptor.path);
|
|
804
|
+
const baseData = resolvedData.hasData ? resolvedData.data : void 0;
|
|
805
|
+
const resourceCount = resolvedData.hasData && baseData ? Object.keys(baseData).length : 0;
|
|
806
|
+
if (reason === "unchanged") {
|
|
807
|
+
routeResultsById.set(descriptor.routeId, {
|
|
808
|
+
path: descriptor.path,
|
|
809
|
+
filePath: descriptor.filePath,
|
|
810
|
+
html: "",
|
|
811
|
+
fileSize: previous?.htmlHash !== null && previous !== null ? getExistingOutputFileSize(outputDir, descriptor.filePath) : 0,
|
|
812
|
+
renderDuration: 0,
|
|
813
|
+
resourceCount,
|
|
814
|
+
status: "skipped",
|
|
815
|
+
reason: "unchanged",
|
|
816
|
+
written: false
|
|
817
|
+
});
|
|
818
|
+
if (previous) nextManifestRoutes.push({
|
|
819
|
+
...previous,
|
|
820
|
+
path: descriptor.path,
|
|
821
|
+
filePath: descriptor.filePath,
|
|
822
|
+
invalidationKeys: descriptor.invalidationKeys.slice()
|
|
823
|
+
});
|
|
824
|
+
continue;
|
|
825
|
+
}
|
|
826
|
+
const rendered = renderedByRouteId.get(descriptor.routeId);
|
|
827
|
+
if (!rendered) throw new Error(`Missing rendered result for route "${descriptor.path}"`);
|
|
828
|
+
const nextResult = {
|
|
829
|
+
...rendered,
|
|
830
|
+
path: descriptor.path,
|
|
831
|
+
filePath: descriptor.filePath,
|
|
832
|
+
resourceCount,
|
|
833
|
+
reason,
|
|
834
|
+
written: false
|
|
835
|
+
};
|
|
836
|
+
if (nextResult.status === "success") {
|
|
837
|
+
const htmlDigest = hashHtml(nextResult.html);
|
|
838
|
+
const shouldWrite = effectiveMode === "full" || !previous || previous.htmlHash !== htmlDigest || !outputFileExists(outputDir, descriptor.filePath);
|
|
839
|
+
nextResult.written = shouldWrite;
|
|
840
|
+
if (!shouldWrite) cacheHits += 1;
|
|
841
|
+
nextManifestRoutes.push({
|
|
842
|
+
routeId: descriptor.routeId,
|
|
843
|
+
path: descriptor.path,
|
|
844
|
+
filePath: descriptor.filePath,
|
|
845
|
+
invalidationKeys: descriptor.invalidationKeys.slice(),
|
|
846
|
+
htmlHash: htmlDigest,
|
|
847
|
+
lastStatus: "success"
|
|
848
|
+
});
|
|
849
|
+
} else nextManifestRoutes.push({
|
|
850
|
+
routeId: descriptor.routeId,
|
|
851
|
+
path: descriptor.path,
|
|
852
|
+
filePath: descriptor.filePath,
|
|
853
|
+
invalidationKeys: descriptor.invalidationKeys.slice(),
|
|
854
|
+
htmlHash: previous?.htmlHash ?? null,
|
|
855
|
+
lastStatus: "error"
|
|
723
856
|
});
|
|
857
|
+
routeResultsById.set(descriptor.routeId, nextResult);
|
|
724
858
|
}
|
|
859
|
+
return {
|
|
860
|
+
cacheHits,
|
|
861
|
+
nextManifestRoutes
|
|
862
|
+
};
|
|
725
863
|
}
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
864
|
+
/**
|
|
865
|
+
* Write a completed generation to disk.
|
|
866
|
+
*
|
|
867
|
+
* A full build is all-or-nothing, so it writes into a sibling staging directory
|
|
868
|
+
* and swaps it into place only once every file, the metadata and the manifest
|
|
869
|
+
* have landed; a failure removes the staging directory and rethrows. Incremental
|
|
870
|
+
* builds write in place, because they are already only touching routes that
|
|
871
|
+
* changed.
|
|
872
|
+
*/
|
|
873
|
+
async function publishGeneration({ routeResults, nextManifestRoutes, effectiveMode, seed, concurrency, outputDir, assets, metadata }) {
|
|
874
|
+
const targetOutputDir = effectiveMode === "full" ? await createStagingDirectory(outputDir) : outputDir;
|
|
730
875
|
try {
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
876
|
+
const writeStartTime = performance.now();
|
|
877
|
+
await writeStaticFiles(routeResults, targetOutputDir, { concurrency });
|
|
878
|
+
performance.now() - writeStartTime;
|
|
879
|
+
await writeMetadata(metadata, targetOutputDir);
|
|
880
|
+
await writeIncrementalManifest({
|
|
881
|
+
schemaVersion: 1,
|
|
882
|
+
seed,
|
|
883
|
+
mode: effectiveMode,
|
|
884
|
+
routes: nextManifestRoutes
|
|
885
|
+
}, targetOutputDir);
|
|
886
|
+
await copyStaticAssets(assets, targetOutputDir);
|
|
887
|
+
if (effectiveMode === "full") await replaceOutputDirectory(targetOutputDir, outputDir);
|
|
742
888
|
} catch (error) {
|
|
743
|
-
if (
|
|
744
|
-
await
|
|
889
|
+
if (effectiveMode === "full") try {
|
|
890
|
+
await fs$1.rm(targetOutputDir, {
|
|
745
891
|
recursive: true,
|
|
746
892
|
force: true
|
|
747
893
|
});
|
|
748
|
-
|
|
749
|
-
}
|
|
894
|
+
} catch {}
|
|
750
895
|
throw error;
|
|
751
|
-
} finally {
|
|
752
|
-
await fileOperations.rm(stagingDir, {
|
|
753
|
-
recursive: true,
|
|
754
|
-
force: true
|
|
755
|
-
});
|
|
756
896
|
}
|
|
757
897
|
}
|
|
758
|
-
/**
|
|
759
|
-
* Create a Static Site Generator
|
|
760
|
-
*
|
|
761
|
-
* Usage:
|
|
762
|
-
* ```ts
|
|
763
|
-
* const ssg = createStaticGen({
|
|
764
|
-
* registry,
|
|
765
|
-
* outputDir: './dist',
|
|
766
|
-
* dataOverrides: {
|
|
767
|
-
* '/api/posts': { posts: [...] }
|
|
768
|
-
* }
|
|
769
|
-
* });
|
|
770
|
-
*
|
|
771
|
-
* const result = await ssg.generate();
|
|
772
|
-
* console.log(`Generated ${result.successful}/${result.totalRoutes} routes`);
|
|
773
|
-
* ```
|
|
774
|
-
*/
|
|
775
898
|
function createStaticGen(options) {
|
|
776
899
|
let result = null;
|
|
777
900
|
if (!options || !("registry" in options) || !options.registry) throw new Error("route registry is required");
|
|
@@ -824,66 +947,14 @@ function createStaticGen(options) {
|
|
|
824
947
|
performance.now() - renderStartTime;
|
|
825
948
|
const renderedByRouteId = /* @__PURE__ */ new Map();
|
|
826
949
|
for (let index = 0; index < renderedResults.length; index += 1) renderedByRouteId.set(routesToRender[index].descriptor.routeId, renderedResults[index]);
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
routeResultsById.set(descriptor.routeId, {
|
|
836
|
-
path: descriptor.path,
|
|
837
|
-
filePath: descriptor.filePath,
|
|
838
|
-
html: "",
|
|
839
|
-
fileSize: previous?.htmlHash !== null && previous !== null ? getExistingOutputFileSize(options.outputDir, descriptor.filePath) : 0,
|
|
840
|
-
renderDuration: 0,
|
|
841
|
-
resourceCount,
|
|
842
|
-
status: "skipped",
|
|
843
|
-
reason: "unchanged",
|
|
844
|
-
written: false
|
|
845
|
-
});
|
|
846
|
-
if (previous) nextManifestRoutes.push({
|
|
847
|
-
...previous,
|
|
848
|
-
path: descriptor.path,
|
|
849
|
-
filePath: descriptor.filePath,
|
|
850
|
-
invalidationKeys: descriptor.invalidationKeys.slice()
|
|
851
|
-
});
|
|
852
|
-
continue;
|
|
853
|
-
}
|
|
854
|
-
const rendered = renderedByRouteId.get(descriptor.routeId);
|
|
855
|
-
if (!rendered) throw new Error(`Missing rendered result for route "${descriptor.path}"`);
|
|
856
|
-
const nextResult = {
|
|
857
|
-
...rendered,
|
|
858
|
-
path: descriptor.path,
|
|
859
|
-
filePath: descriptor.filePath,
|
|
860
|
-
resourceCount,
|
|
861
|
-
reason,
|
|
862
|
-
written: false
|
|
863
|
-
};
|
|
864
|
-
if (nextResult.status === "success") {
|
|
865
|
-
const htmlDigest = hashHtml(nextResult.html);
|
|
866
|
-
const shouldWrite = effectiveMode === "full" || !previous || previous.htmlHash !== htmlDigest || !outputFileExists(options.outputDir, descriptor.filePath);
|
|
867
|
-
nextResult.written = shouldWrite;
|
|
868
|
-
if (!shouldWrite) cacheHits += 1;
|
|
869
|
-
nextManifestRoutes.push({
|
|
870
|
-
routeId: descriptor.routeId,
|
|
871
|
-
path: descriptor.path,
|
|
872
|
-
filePath: descriptor.filePath,
|
|
873
|
-
invalidationKeys: descriptor.invalidationKeys.slice(),
|
|
874
|
-
htmlHash: htmlDigest,
|
|
875
|
-
lastStatus: "success"
|
|
876
|
-
});
|
|
877
|
-
} else nextManifestRoutes.push({
|
|
878
|
-
routeId: descriptor.routeId,
|
|
879
|
-
path: descriptor.path,
|
|
880
|
-
filePath: descriptor.filePath,
|
|
881
|
-
invalidationKeys: descriptor.invalidationKeys.slice(),
|
|
882
|
-
htmlHash: previous?.htmlHash ?? null,
|
|
883
|
-
lastStatus: "error"
|
|
884
|
-
});
|
|
885
|
-
routeResultsById.set(descriptor.routeId, nextResult);
|
|
886
|
-
}
|
|
950
|
+
const { cacheHits, nextManifestRoutes } = assembleRouteResults({
|
|
951
|
+
selected,
|
|
952
|
+
renderedByRouteId,
|
|
953
|
+
routeResultsById,
|
|
954
|
+
dataMap,
|
|
955
|
+
effectiveMode,
|
|
956
|
+
outputDir: options.outputDir
|
|
957
|
+
});
|
|
887
958
|
const removedResults = collectRemovedRouteResults(previousManifest, currentRouteIds);
|
|
888
959
|
const routeResults = [];
|
|
889
960
|
for (const routeId of splitRoutes.outputRouteIds) {
|
|
@@ -901,27 +972,16 @@ function createStaticGen(options) {
|
|
|
901
972
|
for (const routeResult of result.routes) if (routeResult.status === "success") routeResult.written = false;
|
|
902
973
|
return result;
|
|
903
974
|
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
routes: nextManifestRoutes
|
|
915
|
-
}, targetOutputDir);
|
|
916
|
-
await copyStaticAssets(options.assets, targetOutputDir);
|
|
917
|
-
if (effectiveMode === "full") await replaceOutputDirectory(targetOutputDir, options.outputDir);
|
|
918
|
-
} catch (error) {
|
|
919
|
-
if (effectiveMode === "full") await fs.rm(targetOutputDir, {
|
|
920
|
-
recursive: true,
|
|
921
|
-
force: true
|
|
922
|
-
});
|
|
923
|
-
throw error;
|
|
924
|
-
}
|
|
975
|
+
await publishGeneration({
|
|
976
|
+
routeResults,
|
|
977
|
+
nextManifestRoutes,
|
|
978
|
+
effectiveMode,
|
|
979
|
+
seed,
|
|
980
|
+
concurrency: resolvedConcurrency,
|
|
981
|
+
outputDir: options.outputDir,
|
|
982
|
+
assets: options.assets,
|
|
983
|
+
metadata: resultToMetadata(result)
|
|
984
|
+
});
|
|
925
985
|
return result;
|
|
926
986
|
});
|
|
927
987
|
},
|