@riebeckite/core 0.0.26 → 0.0.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1392 -1010
- package/dist/src/content/content_build_coordinator.d.ts +1 -0
- package/dist/src/content/content_build_state.d.ts +4 -3
- package/dist/src/content/content_change_set.d.ts +0 -1
- package/dist/src/content/content_fingerprint.d.ts +1 -1
- package/dist/src/content/content_links.d.ts +1 -0
- package/dist/src/content/content_manager.d.ts +1 -3
- package/dist/src/content/content_manifest_reuse.d.ts +14 -0
- package/dist/src/content/content_source.d.ts +1 -0
- package/dist/src/content/manifest_assembler.d.ts +13 -0
- package/dist/src/content/manifest_builder.d.ts +2 -1
- package/dist/src/content/manifest_hook_cache.d.ts +11 -3
- package/dist/src/content/output_dependency.d.ts +4 -2
- package/dist/src/markdown_fragments.d.ts +2 -0
- package/dist/src/markdown_links.d.ts +8 -0
- package/dist/src/plugin/manifest_hooks.d.ts +42 -0
- package/dist/src/plugin/plugin_pages.d.ts +23 -0
- package/dist/src/plugin/plugin_runtime.d.ts +6 -35
- package/dist/src/plugin/plugin_runtime_context.d.ts +3 -0
- package/dist/src/types/content_manifest.d.ts +6 -0
- package/dist/src/types/output_dependency.d.ts +7 -0
- package/dist/src/types/plugin.d.ts +7 -1
- package/dist/src/utils/concurrency.d.ts +1 -0
- package/dist/src/utils/markdown.d.ts +1 -0
- package/dist/src/utils/text.d.ts +1 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -826,7 +826,7 @@ function getFileName(filePath) {
|
|
|
826
826
|
}
|
|
827
827
|
|
|
828
828
|
// src/content/content_build_state.ts
|
|
829
|
-
var CONTENT_BUILD_STATE_VERSION =
|
|
829
|
+
var CONTENT_BUILD_STATE_VERSION = 19;
|
|
830
830
|
var CONTENT_BUILD_STATE_DIRECTORY = ".riebeckite";
|
|
831
831
|
var CONTENT_BUILD_STATE_EXCLUDE = `${CONTENT_BUILD_STATE_DIRECTORY}/**`;
|
|
832
832
|
|
|
@@ -922,9 +922,12 @@ function isContentBuildStateShape(value) {
|
|
|
922
922
|
}
|
|
923
923
|
function isManifestHookCache(value) {
|
|
924
924
|
return Array.isArray(value) && value.every(
|
|
925
|
-
(entry) => isRecord2(entry) && typeof entry.pluginName === "string" && typeof entry.cacheable === "boolean" && (entry.pipelineFingerprint === void 0 || typeof entry.pipelineFingerprint === "string") && typeof entry.inputFingerprint === "string" && typeof entry.outputFingerprint === "string" &&
|
|
925
|
+
(entry) => isRecord2(entry) && typeof entry.pluginName === "string" && typeof entry.cacheable === "boolean" && (entry.pipelineFingerprint === void 0 || typeof entry.pipelineFingerprint === "string") && typeof entry.inputFingerprint === "string" && (entry.outputFingerprint === void 0 || typeof entry.outputFingerprint === "string") && (entry.snapshot === void 0 || isManifestHookSnapshot(entry.snapshot))
|
|
926
926
|
);
|
|
927
927
|
}
|
|
928
|
+
function isManifestHookSnapshot(value) {
|
|
929
|
+
return isRecord2(value) && Array.isArray(value.entries) && (value.order === void 0 || Array.isArray(value.order) && value.order.every((slug) => typeof slug === "string"));
|
|
930
|
+
}
|
|
928
931
|
function isOutputDescriptors(value) {
|
|
929
932
|
return Array.isArray(value) && value.every(
|
|
930
933
|
(output) => isRecord2(output) && (output.kind === "content" || output.kind === "redirect" || output.kind === "plugin-page" || output.kind === "generated") && isOutputPath(output.path) && typeof output.producer === "string" && Array.isArray(output.dependencies) && output.dependencies.every(isOutputDependency)
|
|
@@ -939,7 +942,10 @@ function isOutputPath(value) {
|
|
|
939
942
|
function isOutputDependency(value) {
|
|
940
943
|
if (!isRecord2(value) || typeof value.type !== "string") return false;
|
|
941
944
|
if (value.type === "global" || value.type === "unknown") return true;
|
|
942
|
-
|
|
945
|
+
if (value.type === "content-query") {
|
|
946
|
+
return Array.isArray(value.properties) && value.properties.every((property) => typeof property === "string") && (value.unknown === void 0 || typeof value.unknown === "boolean");
|
|
947
|
+
}
|
|
948
|
+
return value.type === "content" && typeof value.slug === "string" || value.type === "tag" && typeof value.tag === "string" || value.type === "folder" && typeof value.folder === "string" || value.type === "file" && typeof value.path === "string" || value.type === "public-location" && typeof value.pathname === "string" || value.type === "external" && typeof value.id === "string" || value.type === "projection" && (value.projection === "navigation" || value.projection === "recent-posts" || value.projection === "taxonomy-tags" || value.projection === "taxonomy-folders");
|
|
943
949
|
}
|
|
944
950
|
function isExternalDependencies(value) {
|
|
945
951
|
return Array.isArray(value) && value.every(
|
|
@@ -1291,15 +1297,8 @@ function normalizePath(value) {
|
|
|
1291
1297
|
|
|
1292
1298
|
// src/content/content_fingerprint.ts
|
|
1293
1299
|
import { createHash } from "node:crypto";
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
(left, right) => left.path.localeCompare(right.path)
|
|
1297
|
-
);
|
|
1298
|
-
return await mapConcurrent(sortedEntries, 64, async (entry) => ({
|
|
1299
|
-
entry,
|
|
1300
|
-
fingerprint: await fingerprintContentEntry(entry, read)
|
|
1301
|
-
}));
|
|
1302
|
-
}
|
|
1300
|
+
|
|
1301
|
+
// src/utils/concurrency.ts
|
|
1303
1302
|
async function mapConcurrent(values, concurrency, map) {
|
|
1304
1303
|
const results = new Array(values.length);
|
|
1305
1304
|
let nextIndex = 0;
|
|
@@ -1319,12 +1318,37 @@ async function mapConcurrent(values, concurrency, map) {
|
|
|
1319
1318
|
);
|
|
1320
1319
|
return results;
|
|
1321
1320
|
}
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1321
|
+
|
|
1322
|
+
// src/content/content_fingerprint.ts
|
|
1323
|
+
async function fingerprintContentEntries(entries, read, includeMetadata = true) {
|
|
1324
|
+
const sortedEntries = [...entries].sort(
|
|
1325
|
+
(left, right) => left.path.localeCompare(right.path)
|
|
1326
|
+
);
|
|
1327
|
+
return await mapConcurrent(sortedEntries, 64, async (entry) => ({
|
|
1328
|
+
entry,
|
|
1329
|
+
fingerprint: await fingerprintContentEntry(entry, read, includeMetadata)
|
|
1330
|
+
}));
|
|
1331
|
+
}
|
|
1332
|
+
async function fingerprintContentEntry(entry, read, includeMetadata) {
|
|
1333
|
+
const contentFingerprint = entry.metadata?.hash ? `hash:${entry.metadata.hash}` : entry.metadata?.etag ? `etag:${entry.metadata.etag}` : await fingerprintContent(entry, read);
|
|
1334
|
+
return includeMetadata ? `${contentFingerprint}:${fingerprintMetadata(entry)}` : contentFingerprint;
|
|
1335
|
+
}
|
|
1336
|
+
async function fingerprintContent(entry, read) {
|
|
1325
1337
|
const content = await read(entry);
|
|
1326
1338
|
return `sha256:${createHash("sha256").update(typeof content === "string" ? content : content).digest("hex")}`;
|
|
1327
1339
|
}
|
|
1340
|
+
function fingerprintMetadata(entry) {
|
|
1341
|
+
const metadata = entry.metadata;
|
|
1342
|
+
if (!metadata) return "metadata:none";
|
|
1343
|
+
return JSON.stringify({
|
|
1344
|
+
createdAt: finiteMetadataValue(metadata.createdAt),
|
|
1345
|
+
modifiedAt: finiteMetadataValue(metadata.modifiedAt),
|
|
1346
|
+
size: finiteMetadataValue(metadata.size)
|
|
1347
|
+
});
|
|
1348
|
+
}
|
|
1349
|
+
function finiteMetadataValue(value) {
|
|
1350
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1351
|
+
}
|
|
1328
1352
|
|
|
1329
1353
|
// src/utils/collections.ts
|
|
1330
1354
|
function uniqueStrings(values) {
|
|
@@ -1391,6 +1415,48 @@ function selectEntriesByLanguage(entries, language) {
|
|
|
1391
1415
|
return localized.filter((entry) => getEntryLanguage(entry) === language);
|
|
1392
1416
|
}
|
|
1393
1417
|
|
|
1418
|
+
// src/content/content_links.ts
|
|
1419
|
+
import remarkParse from "remark-parse";
|
|
1420
|
+
import { unified } from "unified";
|
|
1421
|
+
var WIKILINK_PATTERN = /(!)?\[\[([^\]|#^]+)(?:[#^][^\]|]+)?(?:\|[^\]]+)?\]\]/g;
|
|
1422
|
+
function extractContentLinks(markdown, contentIndex) {
|
|
1423
|
+
WIKILINK_PATTERN.lastIndex = 0;
|
|
1424
|
+
const links = [];
|
|
1425
|
+
for (let match = WIKILINK_PATTERN.exec(markdown); match !== null; match = WIKILINK_PATTERN.exec(markdown)) {
|
|
1426
|
+
const rawTarget = match[2]?.trim();
|
|
1427
|
+
if (!rawTarget) continue;
|
|
1428
|
+
const resolved = contentIndex.get(rawTarget.toLowerCase()) ?? null;
|
|
1429
|
+
links.push({
|
|
1430
|
+
raw: rawTarget,
|
|
1431
|
+
slug: resolved,
|
|
1432
|
+
kind: resolved ? linkKind(resolved) : "unresolved",
|
|
1433
|
+
embed: match[1] === "!"
|
|
1434
|
+
});
|
|
1435
|
+
}
|
|
1436
|
+
return links;
|
|
1437
|
+
}
|
|
1438
|
+
function extractMarkdownLinkUrls(markdown) {
|
|
1439
|
+
const urls = [];
|
|
1440
|
+
visitMarkdownNodes(
|
|
1441
|
+
unified().use(remarkParse).parse(markdown),
|
|
1442
|
+
(node) => {
|
|
1443
|
+
if (node.type === "link" && typeof node.url === "string") {
|
|
1444
|
+
urls.push(node.url);
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
);
|
|
1448
|
+
return urls;
|
|
1449
|
+
}
|
|
1450
|
+
function visitMarkdownNodes(node, visit) {
|
|
1451
|
+
visit(node);
|
|
1452
|
+
for (const child of node.children ?? []) visitMarkdownNodes(child, visit);
|
|
1453
|
+
}
|
|
1454
|
+
function linkKind(value) {
|
|
1455
|
+
if (isImagePath(value)) return "image";
|
|
1456
|
+
if (isAttachmentPath(value)) return "attachment";
|
|
1457
|
+
return "note";
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1394
1460
|
// src/content/content_location.ts
|
|
1395
1461
|
function resolveDefaultContentLocation(content) {
|
|
1396
1462
|
return {
|
|
@@ -1541,9 +1607,9 @@ import remarkDirective from "remark-directive";
|
|
|
1541
1607
|
import remarkFrontmatter from "remark-frontmatter";
|
|
1542
1608
|
import remarkGfm from "remark-gfm";
|
|
1543
1609
|
import remarkMath from "remark-math";
|
|
1544
|
-
import
|
|
1610
|
+
import remarkParse3 from "remark-parse";
|
|
1545
1611
|
import remarkRehype from "remark-rehype";
|
|
1546
|
-
import { unified as
|
|
1612
|
+
import { unified as unified3 } from "unified";
|
|
1547
1613
|
import { matter as matter2 } from "vfile-matter";
|
|
1548
1614
|
|
|
1549
1615
|
// src/content/content_dependency_tracker.ts
|
|
@@ -1556,7 +1622,7 @@ function createContentDependencyTracker(source) {
|
|
|
1556
1622
|
const normalized = normalizeDependencyIdentity(identity);
|
|
1557
1623
|
dependencies.set(dependencyKey(normalized), {
|
|
1558
1624
|
...normalized,
|
|
1559
|
-
fingerprint:
|
|
1625
|
+
fingerprint: fingerprintContent2(content)
|
|
1560
1626
|
});
|
|
1561
1627
|
};
|
|
1562
1628
|
return {
|
|
@@ -1581,7 +1647,7 @@ function createContentDependencyTracker(source) {
|
|
|
1581
1647
|
)
|
|
1582
1648
|
};
|
|
1583
1649
|
}
|
|
1584
|
-
function
|
|
1650
|
+
function fingerprintContent2(content) {
|
|
1585
1651
|
return `sha256:${createHash2("sha256").update(typeof content === "string" ? content : content).digest("hex")}`;
|
|
1586
1652
|
}
|
|
1587
1653
|
function normalizeDependencyIdentity(dependency) {
|
|
@@ -1595,30 +1661,6 @@ function dependencyKey(dependency) {
|
|
|
1595
1661
|
return `${normalized.kind}:${normalized.id}`;
|
|
1596
1662
|
}
|
|
1597
1663
|
|
|
1598
|
-
// src/content/content_links.ts
|
|
1599
|
-
var WIKILINK_PATTERN = /(!)?\[\[([^\]|#^]+)(?:[#^][^\]|]+)?(?:\|[^\]]+)?\]\]/g;
|
|
1600
|
-
function extractContentLinks(markdown, contentIndex) {
|
|
1601
|
-
WIKILINK_PATTERN.lastIndex = 0;
|
|
1602
|
-
const links = [];
|
|
1603
|
-
for (let match = WIKILINK_PATTERN.exec(markdown); match !== null; match = WIKILINK_PATTERN.exec(markdown)) {
|
|
1604
|
-
const rawTarget = match[2]?.trim();
|
|
1605
|
-
if (!rawTarget) continue;
|
|
1606
|
-
const resolved = contentIndex.get(rawTarget.toLowerCase()) ?? null;
|
|
1607
|
-
links.push({
|
|
1608
|
-
raw: rawTarget,
|
|
1609
|
-
slug: resolved,
|
|
1610
|
-
kind: resolved ? linkKind(resolved) : "unresolved",
|
|
1611
|
-
embed: match[1] === "!"
|
|
1612
|
-
});
|
|
1613
|
-
}
|
|
1614
|
-
return links;
|
|
1615
|
-
}
|
|
1616
|
-
function linkKind(value) {
|
|
1617
|
-
if (isImagePath(value)) return "image";
|
|
1618
|
-
if (isAttachmentPath(value)) return "attachment";
|
|
1619
|
-
return "note";
|
|
1620
|
-
}
|
|
1621
|
-
|
|
1622
1664
|
// src/content/content_persistent_cache.ts
|
|
1623
1665
|
import { createHash as createHash4 } from "node:crypto";
|
|
1624
1666
|
import fs2 from "node:fs/promises";
|
|
@@ -2005,14 +2047,47 @@ function extractFrontmatter(markdown) {
|
|
|
2005
2047
|
}
|
|
2006
2048
|
|
|
2007
2049
|
// src/content/markdown_image.ts
|
|
2008
|
-
import
|
|
2009
|
-
import { unified } from "unified";
|
|
2050
|
+
import remarkParse2 from "remark-parse";
|
|
2051
|
+
import { unified as unified2 } from "unified";
|
|
2052
|
+
|
|
2053
|
+
// src/utils/markdown.ts
|
|
2054
|
+
var FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})(.*)$/;
|
|
2055
|
+
var FENCE_CLOSE = /^ {0,3}(`{3,}|~{3,})\s*$/;
|
|
2056
|
+
function splitOnce(value, separator) {
|
|
2057
|
+
const index = value.indexOf(separator);
|
|
2058
|
+
if (index < 0) return [value];
|
|
2059
|
+
return [value.slice(0, index), value.slice(index + separator.length)];
|
|
2060
|
+
}
|
|
2061
|
+
function scanMarkdownFenceLines(source) {
|
|
2062
|
+
const lines = [];
|
|
2063
|
+
let fence = null;
|
|
2064
|
+
for (const value of source.split("\n")) {
|
|
2065
|
+
if (fence === null) {
|
|
2066
|
+
const open = FENCE_OPEN.exec(value);
|
|
2067
|
+
if (open && !(open[1][0] === "`" && open[2].includes("`"))) {
|
|
2068
|
+
fence = { marker: open[1][0], length: open[1].length };
|
|
2069
|
+
}
|
|
2070
|
+
lines.push({ value, inFence: false });
|
|
2071
|
+
continue;
|
|
2072
|
+
}
|
|
2073
|
+
const close = FENCE_CLOSE.exec(value);
|
|
2074
|
+
if (close && close[1][0] === fence.marker && close[1].length >= fence.length) {
|
|
2075
|
+
fence = null;
|
|
2076
|
+
lines.push({ value, inFence: false });
|
|
2077
|
+
continue;
|
|
2078
|
+
}
|
|
2079
|
+
lines.push({ value, inFence: true });
|
|
2080
|
+
}
|
|
2081
|
+
return lines;
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
// src/content/markdown_image.ts
|
|
2010
2085
|
function extractMarkdownImages(markdown, sourceSlug) {
|
|
2011
|
-
const tree =
|
|
2086
|
+
const tree = unified2().use(remarkParse2).parse(markdown);
|
|
2012
2087
|
const definitions = /* @__PURE__ */ new Map();
|
|
2013
2088
|
const references = [];
|
|
2014
2089
|
const urls = [];
|
|
2015
|
-
|
|
2090
|
+
visitMarkdownNodes2(tree, (node) => {
|
|
2016
2091
|
if (node.type === "definition" && typeof node.url === "string") {
|
|
2017
2092
|
const identifier = normalizeIdentifier(node.identifier);
|
|
2018
2093
|
if (identifier) definitions.set(identifier, node.url);
|
|
@@ -2037,7 +2112,7 @@ function normalizeMarkdownImages(sourceSlug) {
|
|
|
2037
2112
|
return () => (tree) => {
|
|
2038
2113
|
const definitions = /* @__PURE__ */ new Map();
|
|
2039
2114
|
const references = /* @__PURE__ */ new Set();
|
|
2040
|
-
|
|
2115
|
+
visitMarkdownNodes2(tree, (node) => {
|
|
2041
2116
|
if (node.type === "definition" && typeof node.url === "string") {
|
|
2042
2117
|
const identifier = normalizeIdentifier(node.identifier);
|
|
2043
2118
|
if (identifier) definitions.set(identifier, node);
|
|
@@ -2091,11 +2166,11 @@ function resolveContentAssetPath(sourceSlug, targetPath) {
|
|
|
2091
2166
|
}
|
|
2092
2167
|
return normalized.length > 0 ? normalized.join("/").normalize("NFC") : null;
|
|
2093
2168
|
}
|
|
2094
|
-
function
|
|
2169
|
+
function visitMarkdownNodes2(node, visitor) {
|
|
2095
2170
|
const markdownNode = node;
|
|
2096
2171
|
visitor(markdownNode);
|
|
2097
2172
|
for (const child of markdownNode.children ?? [])
|
|
2098
|
-
|
|
2173
|
+
visitMarkdownNodes2(child, visitor);
|
|
2099
2174
|
}
|
|
2100
2175
|
function normalizeIdentifier(value) {
|
|
2101
2176
|
return typeof value === "string" ? value.toLowerCase() : null;
|
|
@@ -2107,114 +2182,264 @@ function decodeUriPath(path6) {
|
|
|
2107
2182
|
return null;
|
|
2108
2183
|
}
|
|
2109
2184
|
}
|
|
2110
|
-
function splitOnce(value, separator) {
|
|
2111
|
-
const index = value.indexOf(separator);
|
|
2112
|
-
return index < 0 ? [value] : [value.slice(0, index), value.slice(index + 1)];
|
|
2113
|
-
}
|
|
2114
2185
|
function uniqueImages(images) {
|
|
2115
2186
|
return [...new Map(images.map((image) => [image.path, image])).values()];
|
|
2116
2187
|
}
|
|
2117
2188
|
|
|
2118
|
-
// src/
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
import path3 from "node:path";
|
|
2122
|
-
function createUnavailablePluginCache() {
|
|
2123
|
-
return {
|
|
2124
|
-
get: unavailableAtRuntime,
|
|
2125
|
-
set: unavailableAtRuntime,
|
|
2126
|
-
delete: unavailableAtRuntime,
|
|
2127
|
-
clear: unavailableAtRuntime
|
|
2128
|
-
};
|
|
2129
|
-
}
|
|
2130
|
-
function createPluginCache(options) {
|
|
2131
|
-
const pluginDirectory = path3.join(
|
|
2132
|
-
options.cacheDirectory,
|
|
2133
|
-
pluginNamespace(options.pluginName)
|
|
2134
|
-
);
|
|
2135
|
-
const versionDirectory2 = path3.join(
|
|
2136
|
-
pluginDirectory,
|
|
2137
|
-
hash3(options.cacheVersion ?? "1")
|
|
2138
|
-
);
|
|
2139
|
-
const writes = /* @__PURE__ */ new Map();
|
|
2140
|
-
return {
|
|
2141
|
-
async get(key) {
|
|
2142
|
-
let raw;
|
|
2143
|
-
try {
|
|
2144
|
-
raw = await fs3.readFile(cachePath(versionDirectory2, key), "utf8");
|
|
2145
|
-
} catch (error) {
|
|
2146
|
-
if (isNotFoundError2(error)) {
|
|
2147
|
-
options.tracer?.event("cache.miss", { plugin: options.pluginName });
|
|
2148
|
-
return void 0;
|
|
2149
|
-
}
|
|
2150
|
-
throw error;
|
|
2151
|
-
}
|
|
2152
|
-
try {
|
|
2153
|
-
const value = JSON.parse(raw);
|
|
2154
|
-
options.tracer?.event("cache.hit", { plugin: options.pluginName });
|
|
2155
|
-
return value;
|
|
2156
|
-
} catch {
|
|
2157
|
-
options.logger?.warn(
|
|
2158
|
-
"Plugin cache entry is corrupted and will be ignored.",
|
|
2159
|
-
{
|
|
2160
|
-
plugin: options.pluginName
|
|
2161
|
-
}
|
|
2162
|
-
);
|
|
2163
|
-
return void 0;
|
|
2164
|
-
}
|
|
2165
|
-
},
|
|
2166
|
-
async set(key, value) {
|
|
2167
|
-
const targetPath = cachePath(versionDirectory2, key);
|
|
2168
|
-
const serialized = JSON.stringify(value);
|
|
2169
|
-
if (serialized === void 0) {
|
|
2170
|
-
throw new TypeError("Plugin cache values must be JSON serializable.");
|
|
2171
|
-
}
|
|
2172
|
-
const previousWrite = writes.get(targetPath) ?? Promise.resolve();
|
|
2173
|
-
const write = previousWrite.catch(() => void 0).then(() => writeAtomically2(targetPath, versionDirectory2, serialized));
|
|
2174
|
-
writes.set(targetPath, write);
|
|
2175
|
-
try {
|
|
2176
|
-
await write;
|
|
2177
|
-
} finally {
|
|
2178
|
-
if (writes.get(targetPath) === write) {
|
|
2179
|
-
writes.delete(targetPath);
|
|
2180
|
-
}
|
|
2181
|
-
}
|
|
2182
|
-
},
|
|
2183
|
-
async delete(key) {
|
|
2184
|
-
await fs3.rm(cachePath(versionDirectory2, key), { force: true });
|
|
2185
|
-
},
|
|
2186
|
-
async clear() {
|
|
2187
|
-
await fs3.rm(pluginDirectory, { recursive: true, force: true });
|
|
2188
|
-
}
|
|
2189
|
-
};
|
|
2189
|
+
// src/utils/text.ts
|
|
2190
|
+
function escapeRegExp(value) {
|
|
2191
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2190
2192
|
}
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
const temporaryPath = `${targetPath}.${randomUUID2()}.tmp`;
|
|
2194
|
-
try {
|
|
2195
|
-
await fs3.writeFile(temporaryPath, serialized, "utf8");
|
|
2196
|
-
await fs3.rename(temporaryPath, targetPath);
|
|
2197
|
-
} catch (error) {
|
|
2198
|
-
await fs3.rm(temporaryPath, { force: true }).catch(() => void 0);
|
|
2199
|
-
throw error;
|
|
2200
|
-
}
|
|
2193
|
+
function stripHtml(html) {
|
|
2194
|
+
return html.replace(/<script\b[\s\S]*?<\/script>/gi, " ").replace(/<style\b[\s\S]*?<\/style>/gi, " ").replace(/<[^>]+>/g, " ");
|
|
2201
2195
|
}
|
|
2202
|
-
function
|
|
2203
|
-
|
|
2204
|
-
if (
|
|
2205
|
-
|
|
2206
|
-
|
|
2196
|
+
function calculateReadingTime(html) {
|
|
2197
|
+
const text = stripHtml(html).trim();
|
|
2198
|
+
if (!text) return 1;
|
|
2199
|
+
const cjkCharacters = text.match(/\p{Script=Han}|\p{Script=Hiragana}|\p{Script=Katakana}/gu)?.length ?? 0;
|
|
2200
|
+
const latinWords = text.replace(/\p{Script=Han}|\p{Script=Hiragana}|\p{Script=Katakana}/gu, " ").match(/[\p{L}\p{N}]+/gu)?.length ?? 0;
|
|
2201
|
+
return Math.max(1, Math.ceil(cjkCharacters / 500 + latinWords / 220));
|
|
2207
2202
|
}
|
|
2208
|
-
|
|
2209
|
-
|
|
2203
|
+
|
|
2204
|
+
// src/markdown_fragments.ts
|
|
2205
|
+
function selectEmbedMarkdownFragment(markdown, fragment) {
|
|
2206
|
+
if (!fragment) return markdown;
|
|
2207
|
+
if (fragment.kind === "block")
|
|
2208
|
+
return selectBlockFragment(markdown, fragment.value);
|
|
2209
|
+
return selectHeadingFragment(markdown, fragment.value);
|
|
2210
2210
|
}
|
|
2211
|
-
function
|
|
2212
|
-
|
|
2211
|
+
function selectBlockFragment(markdown, blockId) {
|
|
2212
|
+
const lines = markdown.split(/\r?\n/);
|
|
2213
|
+
const blockIdRe = new RegExp(`(?:^|\\s)\\^${escapeRegExp(blockId)}\\s*$`);
|
|
2214
|
+
const line = lines.find((currentLine) => blockIdRe.test(currentLine));
|
|
2215
|
+
return line?.replace(blockIdRe, "").trimEnd() || null;
|
|
2213
2216
|
}
|
|
2214
|
-
function
|
|
2215
|
-
|
|
2217
|
+
function selectHeadingFragment(markdown, heading) {
|
|
2218
|
+
const lines = markdown.split(/\r?\n/);
|
|
2219
|
+
const targetHeading = heading.trim().toLowerCase();
|
|
2220
|
+
const startIndex = lines.findIndex((line) => {
|
|
2221
|
+
const match = line.match(/^(#{1,6})\s+(.+?)\s*#*\s*$/);
|
|
2222
|
+
return match?.[2]?.trim().toLowerCase() === targetHeading;
|
|
2223
|
+
});
|
|
2224
|
+
if (startIndex < 0) return null;
|
|
2225
|
+
const level = lines[startIndex]?.match(/^(#{1,6})\s+/)?.[1]?.length ?? 6;
|
|
2226
|
+
const endIndex = lines.findIndex((line, index) => {
|
|
2227
|
+
if (index <= startIndex) return false;
|
|
2228
|
+
const match = line.match(/^(#{1,6})\s+/);
|
|
2229
|
+
return match !== null && match[1].length <= level;
|
|
2230
|
+
});
|
|
2231
|
+
const selectedLines = lines.slice(
|
|
2232
|
+
startIndex,
|
|
2233
|
+
endIndex === -1 ? void 0 : endIndex
|
|
2234
|
+
);
|
|
2235
|
+
return selectedLines.join("\n").trim() || null;
|
|
2216
2236
|
}
|
|
2217
|
-
|
|
2237
|
+
|
|
2238
|
+
// src/markdown_links.ts
|
|
2239
|
+
function normalizeMarkdownLinks(context) {
|
|
2240
|
+
return (tree) => {
|
|
2241
|
+
visitMarkdownLinkNodes(tree, (node) => {
|
|
2242
|
+
const normalizedUrl = normalizeMarkdownLinkUrl(node.url, context);
|
|
2243
|
+
if (normalizedUrl) node.url = normalizedUrl;
|
|
2244
|
+
});
|
|
2245
|
+
};
|
|
2246
|
+
}
|
|
2247
|
+
function resolveLinkDependencyValue(id, contentIndex, lookupPermalink, isRoutable) {
|
|
2248
|
+
if (id.startsWith(LINK_INDEX_PREFIX)) {
|
|
2249
|
+
return contentIndex.get(id.slice(LINK_INDEX_PREFIX.length)) ?? "";
|
|
2250
|
+
}
|
|
2251
|
+
if (id.startsWith(LINK_LOCATION_PREFIX)) {
|
|
2252
|
+
const slug = id.slice(LINK_LOCATION_PREFIX.length);
|
|
2253
|
+
const permalink = lookupPermalink(slug);
|
|
2254
|
+
if (permalink === void 0) return "";
|
|
2255
|
+
if (isRoutable && !isRoutable(slug)) return "";
|
|
2256
|
+
return permalink;
|
|
2257
|
+
}
|
|
2258
|
+
return "";
|
|
2259
|
+
}
|
|
2260
|
+
function visitMarkdownLinkNodes(node, visitor) {
|
|
2261
|
+
const linkNode = node;
|
|
2262
|
+
if ((node.type === "link" || node.type === "definition") && typeof linkNode.url === "string") {
|
|
2263
|
+
visitor(linkNode);
|
|
2264
|
+
}
|
|
2265
|
+
for (const child of linkNode.children ?? []) {
|
|
2266
|
+
visitMarkdownLinkNodes(child, visitor);
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
function normalizeMarkdownLinkUrl(url, context) {
|
|
2270
|
+
if (!context.sourceSlug) return null;
|
|
2271
|
+
if (!isMarkdownContentLink(url)) return null;
|
|
2272
|
+
const [pathAndQuery, hash4 = ""] = splitOnce(url, "#");
|
|
2273
|
+
const [rawPath, query = ""] = splitOnce(pathAndQuery, "?");
|
|
2274
|
+
const decodedPath = decodeUriPath2(rawPath);
|
|
2275
|
+
if (!decodedPath?.toLowerCase().endsWith(".md")) return null;
|
|
2276
|
+
const resolvedSlug = resolveRelativeMarkdownSlug(
|
|
2277
|
+
context.sourceSlug,
|
|
2278
|
+
decodedPath
|
|
2279
|
+
);
|
|
2280
|
+
if (!resolvedSlug) return null;
|
|
2281
|
+
const indexedSlug = getExactContentSlug(context, resolvedSlug);
|
|
2282
|
+
if (!indexedSlug) return null;
|
|
2283
|
+
recordLinkResolution(context, LINK_LOCATION_PREFIX, indexedSlug);
|
|
2284
|
+
if (context.isRoutable && !context.isRoutable(indexedSlug)) return null;
|
|
2285
|
+
const suffix = `${query ? `?${query}` : ""}${hash4 ? `#${hash4}` : ""}`;
|
|
2286
|
+
return `${context.resolvePermalink(indexedSlug)}${suffix}`;
|
|
2287
|
+
}
|
|
2288
|
+
function recordLinkResolution(context, prefix, key) {
|
|
2289
|
+
if (!context.recordLinkResolution) return;
|
|
2290
|
+
const value = prefix === LINK_INDEX_PREFIX ? context.contentIndex.get(key) ?? "" : resolveLinkDependencyValue(
|
|
2291
|
+
`${prefix}${key}`,
|
|
2292
|
+
context.contentIndex,
|
|
2293
|
+
(slug) => {
|
|
2294
|
+
try {
|
|
2295
|
+
return context.resolvePermalink(slug);
|
|
2296
|
+
} catch {
|
|
2297
|
+
return void 0;
|
|
2298
|
+
}
|
|
2299
|
+
},
|
|
2300
|
+
context.isRoutable
|
|
2301
|
+
);
|
|
2302
|
+
context.recordLinkResolution(`${prefix}${key}`, value);
|
|
2303
|
+
}
|
|
2304
|
+
function getExactContentSlug(context, slug) {
|
|
2305
|
+
try {
|
|
2306
|
+
context.resolvePermalink(slug);
|
|
2307
|
+
return slug;
|
|
2308
|
+
} catch {
|
|
2309
|
+
const indexKey = slug.toLowerCase();
|
|
2310
|
+
recordLinkResolution(context, LINK_INDEX_PREFIX, indexKey);
|
|
2311
|
+
return context.contentIndex.get(indexKey) ?? null;
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
function isMarkdownContentLink(url) {
|
|
2315
|
+
if (url.startsWith("#") || url.startsWith("//")) return false;
|
|
2316
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(url)) return false;
|
|
2317
|
+
return true;
|
|
2318
|
+
}
|
|
2319
|
+
function resolveRelativeMarkdownSlug(sourceSlug, targetPath) {
|
|
2320
|
+
const sourceDirectory = sourceSlug.includes("/") ? sourceSlug.slice(0, sourceSlug.lastIndexOf("/")) : "";
|
|
2321
|
+
const baseParts = targetPath.startsWith("/") ? [] : sourceDirectory.split("/");
|
|
2322
|
+
const parts = [...baseParts, ...targetPath.replace(/^\/+/, "").split("/")];
|
|
2323
|
+
const normalizedParts = [];
|
|
2324
|
+
for (const part of parts) {
|
|
2325
|
+
if (!part || part === ".") continue;
|
|
2326
|
+
if (part === "..") {
|
|
2327
|
+
if (normalizedParts.length === 0) return null;
|
|
2328
|
+
normalizedParts.pop();
|
|
2329
|
+
continue;
|
|
2330
|
+
}
|
|
2331
|
+
normalizedParts.push(part);
|
|
2332
|
+
}
|
|
2333
|
+
return normalizedParts.join("/").replace(/\.md$/i, "");
|
|
2334
|
+
}
|
|
2335
|
+
function decodeUriPath2(path6) {
|
|
2336
|
+
try {
|
|
2337
|
+
return decodeURI(path6);
|
|
2338
|
+
} catch {
|
|
2339
|
+
return null;
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
|
|
2343
|
+
// src/plugin/plugin_cache.ts
|
|
2344
|
+
import { createHash as createHash5, randomUUID as randomUUID2 } from "node:crypto";
|
|
2345
|
+
import fs3 from "node:fs/promises";
|
|
2346
|
+
import path3 from "node:path";
|
|
2347
|
+
function createUnavailablePluginCache() {
|
|
2348
|
+
return {
|
|
2349
|
+
get: unavailableAtRuntime,
|
|
2350
|
+
set: unavailableAtRuntime,
|
|
2351
|
+
delete: unavailableAtRuntime,
|
|
2352
|
+
clear: unavailableAtRuntime
|
|
2353
|
+
};
|
|
2354
|
+
}
|
|
2355
|
+
function createPluginCache(options) {
|
|
2356
|
+
const pluginDirectory = path3.join(
|
|
2357
|
+
options.cacheDirectory,
|
|
2358
|
+
pluginNamespace(options.pluginName)
|
|
2359
|
+
);
|
|
2360
|
+
const versionDirectory2 = path3.join(
|
|
2361
|
+
pluginDirectory,
|
|
2362
|
+
hash3(options.cacheVersion ?? "1")
|
|
2363
|
+
);
|
|
2364
|
+
const writes = /* @__PURE__ */ new Map();
|
|
2365
|
+
return {
|
|
2366
|
+
async get(key) {
|
|
2367
|
+
let raw;
|
|
2368
|
+
try {
|
|
2369
|
+
raw = await fs3.readFile(cachePath(versionDirectory2, key), "utf8");
|
|
2370
|
+
} catch (error) {
|
|
2371
|
+
if (isNotFoundError2(error)) {
|
|
2372
|
+
options.tracer?.event("cache.miss", { plugin: options.pluginName });
|
|
2373
|
+
return void 0;
|
|
2374
|
+
}
|
|
2375
|
+
throw error;
|
|
2376
|
+
}
|
|
2377
|
+
try {
|
|
2378
|
+
const value = JSON.parse(raw);
|
|
2379
|
+
options.tracer?.event("cache.hit", { plugin: options.pluginName });
|
|
2380
|
+
return value;
|
|
2381
|
+
} catch {
|
|
2382
|
+
options.logger?.warn(
|
|
2383
|
+
"Plugin cache entry is corrupted and will be ignored.",
|
|
2384
|
+
{
|
|
2385
|
+
plugin: options.pluginName
|
|
2386
|
+
}
|
|
2387
|
+
);
|
|
2388
|
+
return void 0;
|
|
2389
|
+
}
|
|
2390
|
+
},
|
|
2391
|
+
async set(key, value) {
|
|
2392
|
+
const targetPath = cachePath(versionDirectory2, key);
|
|
2393
|
+
const serialized = JSON.stringify(value);
|
|
2394
|
+
if (serialized === void 0) {
|
|
2395
|
+
throw new TypeError("Plugin cache values must be JSON serializable.");
|
|
2396
|
+
}
|
|
2397
|
+
const previousWrite = writes.get(targetPath) ?? Promise.resolve();
|
|
2398
|
+
const write = previousWrite.catch(() => void 0).then(() => writeAtomically2(targetPath, versionDirectory2, serialized));
|
|
2399
|
+
writes.set(targetPath, write);
|
|
2400
|
+
try {
|
|
2401
|
+
await write;
|
|
2402
|
+
} finally {
|
|
2403
|
+
if (writes.get(targetPath) === write) {
|
|
2404
|
+
writes.delete(targetPath);
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
},
|
|
2408
|
+
async delete(key) {
|
|
2409
|
+
await fs3.rm(cachePath(versionDirectory2, key), { force: true });
|
|
2410
|
+
},
|
|
2411
|
+
async clear() {
|
|
2412
|
+
await fs3.rm(pluginDirectory, { recursive: true, force: true });
|
|
2413
|
+
}
|
|
2414
|
+
};
|
|
2415
|
+
}
|
|
2416
|
+
async function writeAtomically2(targetPath, versionDirectory2, serialized) {
|
|
2417
|
+
await fs3.mkdir(versionDirectory2, { recursive: true });
|
|
2418
|
+
const temporaryPath = `${targetPath}.${randomUUID2()}.tmp`;
|
|
2419
|
+
try {
|
|
2420
|
+
await fs3.writeFile(temporaryPath, serialized, "utf8");
|
|
2421
|
+
await fs3.rename(temporaryPath, targetPath);
|
|
2422
|
+
} catch (error) {
|
|
2423
|
+
await fs3.rm(temporaryPath, { force: true }).catch(() => void 0);
|
|
2424
|
+
throw error;
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
function resolvePluginCacheDirectory(config) {
|
|
2428
|
+
if (!config) return path3.resolve(process.cwd(), ".riebeckite", "cache");
|
|
2429
|
+
if (config.cache?.directory) return config.cache.directory;
|
|
2430
|
+
if (config.buildDirectory) return path3.join(config.buildDirectory, "cache");
|
|
2431
|
+
return path3.resolve(config.content.directory, "..", ".riebeckite", "cache");
|
|
2432
|
+
}
|
|
2433
|
+
function cachePath(versionDirectory2, key) {
|
|
2434
|
+
return path3.join(versionDirectory2, `${hash3(key)}.json`);
|
|
2435
|
+
}
|
|
2436
|
+
function pluginNamespace(pluginName) {
|
|
2437
|
+
return /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(pluginName) ? pluginName : hash3(pluginName);
|
|
2438
|
+
}
|
|
2439
|
+
function hash3(value) {
|
|
2440
|
+
return createHash5("sha256").update(value).digest("hex");
|
|
2441
|
+
}
|
|
2442
|
+
function isNotFoundError2(error) {
|
|
2218
2443
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
2219
2444
|
}
|
|
2220
2445
|
async function unavailableAtRuntime() {
|
|
@@ -2409,9 +2634,9 @@ var Pipeline = class {
|
|
|
2409
2634
|
this.options.onPersistentContentCacheResult?.("bypass");
|
|
2410
2635
|
}
|
|
2411
2636
|
}
|
|
2412
|
-
const processor =
|
|
2637
|
+
const processor = unified3();
|
|
2413
2638
|
const plugins = this.resolvePlugins();
|
|
2414
|
-
this.use(processor,
|
|
2639
|
+
this.use(processor, remarkParse3);
|
|
2415
2640
|
this.use(processor, remarkDirective);
|
|
2416
2641
|
this.use(processor, remarkFrontmatter, ["yaml", "toml"]);
|
|
2417
2642
|
this.use(processor, function loadFrontmatter() {
|
|
@@ -2535,14 +2760,14 @@ var Pipeline = class {
|
|
|
2535
2760
|
dep.id,
|
|
2536
2761
|
async () => await this.getMarkdownBySlug?.(dep.id) ?? ""
|
|
2537
2762
|
);
|
|
2538
|
-
if (
|
|
2763
|
+
if (fingerprintContent2(content) !== dep.fingerprint) return dep;
|
|
2539
2764
|
} else if (dep.kind === "file") {
|
|
2540
2765
|
const fileContent = await validationTracker.contentSource.read({
|
|
2541
2766
|
path: dep.id
|
|
2542
2767
|
});
|
|
2543
|
-
if (
|
|
2768
|
+
if (fingerprintContent2(fileContent) !== dep.fingerprint) return dep;
|
|
2544
2769
|
} else if (dep.kind === "link") {
|
|
2545
|
-
if (
|
|
2770
|
+
if (fingerprintContent2(this.resolveLinkDependency(dep.id)) !== dep.fingerprint) {
|
|
2546
2771
|
return dep;
|
|
2547
2772
|
}
|
|
2548
2773
|
}
|
|
@@ -2666,170 +2891,135 @@ var Pipeline = class {
|
|
|
2666
2891
|
return cache;
|
|
2667
2892
|
}
|
|
2668
2893
|
};
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
const blockIdRe = new RegExp(`(?:^|\\s)\\^${escapeRegExp(blockId)}\\s*$`);
|
|
2678
|
-
const line = lines.find((currentLine) => blockIdRe.test(currentLine));
|
|
2679
|
-
return line?.replace(blockIdRe, "").trimEnd() || null;
|
|
2894
|
+
|
|
2895
|
+
// src/types/plugin_asset.ts
|
|
2896
|
+
function createStyleAsset(pluginName, moduleSpecifier) {
|
|
2897
|
+
return {
|
|
2898
|
+
pluginName,
|
|
2899
|
+
kind: "style",
|
|
2900
|
+
moduleSpecifier
|
|
2901
|
+
};
|
|
2680
2902
|
}
|
|
2681
|
-
function
|
|
2682
|
-
return
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
}
|
|
2903
|
+
function createClientEntry(pluginName, moduleSpecifier, exportName, publicConfig) {
|
|
2904
|
+
return {
|
|
2905
|
+
pluginName,
|
|
2906
|
+
moduleSpecifier,
|
|
2907
|
+
...exportName === void 0 ? {} : { exportName },
|
|
2908
|
+
...publicConfig === void 0 ? {} : { publicConfig }
|
|
2687
2909
|
};
|
|
2688
2910
|
}
|
|
2689
|
-
function
|
|
2690
|
-
|
|
2691
|
-
|
|
2911
|
+
function serializePublicClientConfig(value) {
|
|
2912
|
+
assertJsonValue(value, /* @__PURE__ */ new Set());
|
|
2913
|
+
return JSON.stringify(value);
|
|
2914
|
+
}
|
|
2915
|
+
function assertJsonValue(value, ancestors) {
|
|
2916
|
+
if (value === null || typeof value === "boolean" || typeof value === "string") {
|
|
2917
|
+
return;
|
|
2692
2918
|
}
|
|
2693
|
-
if (
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
if (permalink === void 0) return "";
|
|
2697
|
-
if (isRoutable && !isRoutable(slug)) return "";
|
|
2698
|
-
return permalink;
|
|
2919
|
+
if (typeof value === "number") {
|
|
2920
|
+
if (Number.isFinite(value)) return;
|
|
2921
|
+
throw new TypeError("Public client configuration numbers must be finite.");
|
|
2699
2922
|
}
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
if ((node.type === "link" || node.type === "definition") && typeof linkNode.url === "string") {
|
|
2705
|
-
visitor(linkNode);
|
|
2923
|
+
if (typeof value !== "object") {
|
|
2924
|
+
throw new TypeError(
|
|
2925
|
+
"Public client configuration must be JSON-serializable."
|
|
2926
|
+
);
|
|
2706
2927
|
}
|
|
2707
|
-
|
|
2708
|
-
|
|
2928
|
+
if (ancestors.has(value)) {
|
|
2929
|
+
throw new TypeError("Public client configuration must not contain cycles.");
|
|
2709
2930
|
}
|
|
2931
|
+
const prototype = Object.getPrototypeOf(value);
|
|
2932
|
+
if (prototype !== Object.prototype && prototype !== null && !Array.isArray(value)) {
|
|
2933
|
+
throw new TypeError(
|
|
2934
|
+
"Public client configuration must contain only plain objects."
|
|
2935
|
+
);
|
|
2936
|
+
}
|
|
2937
|
+
ancestors.add(value);
|
|
2938
|
+
for (const item of Array.isArray(value) ? value : Object.values(value)) {
|
|
2939
|
+
assertJsonValue(item, ancestors);
|
|
2940
|
+
}
|
|
2941
|
+
ancestors.delete(value);
|
|
2710
2942
|
}
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
const indexedSlug = getExactContentSlug(context, resolvedSlug);
|
|
2724
|
-
if (!indexedSlug) return null;
|
|
2725
|
-
recordLinkResolution(context, LINK_LOCATION_PREFIX, indexedSlug);
|
|
2726
|
-
if (context.isRoutable && !context.isRoutable(indexedSlug)) return null;
|
|
2727
|
-
const suffix = `${query ? `?${query}` : ""}${hash4 ? `#${hash4}` : ""}`;
|
|
2728
|
-
return `${context.resolvePermalink(indexedSlug)}${suffix}`;
|
|
2729
|
-
}
|
|
2730
|
-
function recordLinkResolution(context, prefix, key) {
|
|
2731
|
-
if (!context.recordLinkResolution) return;
|
|
2732
|
-
const value = prefix === LINK_INDEX_PREFIX ? context.contentIndex.get(key) ?? "" : resolveLinkDependencyValue(
|
|
2733
|
-
`${prefix}${key}`,
|
|
2734
|
-
context.contentIndex,
|
|
2735
|
-
(slug) => {
|
|
2736
|
-
try {
|
|
2737
|
-
return context.resolvePermalink(slug);
|
|
2738
|
-
} catch {
|
|
2739
|
-
return void 0;
|
|
2943
|
+
|
|
2944
|
+
// src/plugin/generated_output_registry.ts
|
|
2945
|
+
var GeneratedOutputRegistry = class {
|
|
2946
|
+
outputs = [];
|
|
2947
|
+
owners = /* @__PURE__ */ new Map();
|
|
2948
|
+
sinkFor(pluginName) {
|
|
2949
|
+
const register = (path6, output) => {
|
|
2950
|
+
const owner = this.owners.get(path6);
|
|
2951
|
+
if (owner) {
|
|
2952
|
+
throw new Error(
|
|
2953
|
+
`Duplicate generated output path "${path6}" declared by "${owner}" and "${pluginName}".`
|
|
2954
|
+
);
|
|
2740
2955
|
}
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
return
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2956
|
+
this.owners.set(path6, pluginName);
|
|
2957
|
+
this.outputs.push({
|
|
2958
|
+
path: path6,
|
|
2959
|
+
content: output.content,
|
|
2960
|
+
dependencies: output.dependencies,
|
|
2961
|
+
owner: pluginName
|
|
2962
|
+
});
|
|
2963
|
+
};
|
|
2964
|
+
return {
|
|
2965
|
+
emit: (output) => {
|
|
2966
|
+
register(normalizeGeneratedOutputPath(output.path), output);
|
|
2967
|
+
},
|
|
2968
|
+
emitAsset: (output) => {
|
|
2969
|
+
register(normalizeContentAssetOutputPath(output.path), output);
|
|
2970
|
+
}
|
|
2971
|
+
};
|
|
2754
2972
|
}
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
return true;
|
|
2760
|
-
}
|
|
2761
|
-
function resolveRelativeMarkdownSlug(sourceSlug, targetPath) {
|
|
2762
|
-
const sourceDirectory = sourceSlug.includes("/") ? sourceSlug.slice(0, sourceSlug.lastIndexOf("/")) : "";
|
|
2763
|
-
const baseParts = targetPath.startsWith("/") ? [] : sourceDirectory.split("/");
|
|
2764
|
-
const parts = [...baseParts, ...targetPath.replace(/^\/+/, "").split("/")];
|
|
2765
|
-
const normalizedParts = [];
|
|
2766
|
-
for (const part of parts) {
|
|
2767
|
-
if (!part || part === ".") continue;
|
|
2768
|
-
if (part === "..") {
|
|
2769
|
-
if (normalizedParts.length === 0) return null;
|
|
2770
|
-
normalizedParts.pop();
|
|
2771
|
-
continue;
|
|
2772
|
-
}
|
|
2773
|
-
normalizedParts.push(part);
|
|
2973
|
+
all() {
|
|
2974
|
+
return [...this.outputs].sort(
|
|
2975
|
+
(a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0
|
|
2976
|
+
);
|
|
2774
2977
|
}
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
function decodeUriPath2(path6) {
|
|
2778
|
-
try {
|
|
2779
|
-
return decodeURI(path6);
|
|
2780
|
-
} catch {
|
|
2781
|
-
return null;
|
|
2978
|
+
count() {
|
|
2979
|
+
return this.outputs.length;
|
|
2782
2980
|
}
|
|
2783
|
-
}
|
|
2784
|
-
function splitOnce2(value, separator) {
|
|
2785
|
-
const index = value.indexOf(separator);
|
|
2786
|
-
if (index < 0) return [value];
|
|
2787
|
-
return [value.slice(0, index), value.slice(index + separator.length)];
|
|
2788
|
-
}
|
|
2789
|
-
function selectHeadingFragment(markdown, heading) {
|
|
2790
|
-
const lines = markdown.split(/\r?\n/);
|
|
2791
|
-
const targetHeading = heading.trim().toLowerCase();
|
|
2792
|
-
const startIndex = lines.findIndex((line) => {
|
|
2793
|
-
const match = line.match(/^(#{1,6})\s+(.+?)\s*#*\s*$/);
|
|
2794
|
-
return match?.[2]?.trim().toLowerCase() === targetHeading;
|
|
2795
|
-
});
|
|
2796
|
-
if (startIndex < 0) return null;
|
|
2797
|
-
const level = lines[startIndex]?.match(/^(#{1,6})\s+/)?.[1]?.length ?? 6;
|
|
2798
|
-
const endIndex = lines.findIndex((line, index) => {
|
|
2799
|
-
if (index <= startIndex) return false;
|
|
2800
|
-
const match = line.match(/^(#{1,6})\s+/);
|
|
2801
|
-
return match !== null && match[1].length <= level;
|
|
2802
|
-
});
|
|
2803
|
-
const selectedLines = lines.slice(
|
|
2804
|
-
startIndex,
|
|
2805
|
-
endIndex === -1 ? void 0 : endIndex
|
|
2806
|
-
);
|
|
2807
|
-
return selectedLines.join("\n").trim() || null;
|
|
2808
|
-
}
|
|
2809
|
-
function escapeRegExp(value) {
|
|
2810
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2811
|
-
}
|
|
2981
|
+
};
|
|
2812
2982
|
|
|
2813
2983
|
// src/content/manifest_hook_cache.ts
|
|
2814
2984
|
import { createHash as createHash6 } from "node:crypto";
|
|
2815
2985
|
function fingerprintManifestHookInput(manifest) {
|
|
2816
2986
|
return fingerprintManifest(manifestFingerprintValue(manifest));
|
|
2817
2987
|
}
|
|
2818
|
-
function
|
|
2819
|
-
const
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2988
|
+
function serializeManifestEntries(entries) {
|
|
2989
|
+
const serialized = /* @__PURE__ */ new Map();
|
|
2990
|
+
for (const entry of entries)
|
|
2991
|
+
serialized.set(entry.slug, JSON.stringify(entry));
|
|
2992
|
+
return serialized;
|
|
2993
|
+
}
|
|
2994
|
+
function createManifestHookCacheEntry(pluginName, cacheable, pipelineFingerprint, inputFingerprint, manifest, beforeEntries, afterEntries) {
|
|
2995
|
+
if (!cacheable)
|
|
2996
|
+
return {
|
|
2997
|
+
pluginName,
|
|
2998
|
+
cacheable,
|
|
2999
|
+
pipelineFingerprint,
|
|
3000
|
+
inputFingerprint
|
|
3001
|
+
};
|
|
2824
3002
|
return {
|
|
2825
3003
|
pluginName,
|
|
2826
3004
|
cacheable,
|
|
2827
3005
|
pipelineFingerprint,
|
|
2828
3006
|
inputFingerprint,
|
|
2829
|
-
outputFingerprint: fingerprintManifestHookSnapshot(
|
|
2830
|
-
snapshot
|
|
3007
|
+
outputFingerprint: fingerprintManifestHookSnapshot(manifest),
|
|
3008
|
+
snapshot: createManifestHookSnapshot(beforeEntries, afterEntries)
|
|
2831
3009
|
};
|
|
2832
3010
|
}
|
|
3011
|
+
function createManifestHookSnapshot(beforeEntries, afterEntries) {
|
|
3012
|
+
const changed = [];
|
|
3013
|
+
const order = [];
|
|
3014
|
+
for (const [slug, serialized] of afterEntries) {
|
|
3015
|
+
order.push(slug);
|
|
3016
|
+
if (beforeEntries.get(slug) === serialized) continue;
|
|
3017
|
+
changed.push(JSON.parse(serialized));
|
|
3018
|
+
}
|
|
3019
|
+
const beforeOrder = [...beforeEntries.keys()];
|
|
3020
|
+
const orderChanged = beforeOrder.length !== order.length || beforeOrder.some((slug, index) => slug !== order[index]);
|
|
3021
|
+
return orderChanged ? { entries: changed, order } : { entries: changed };
|
|
3022
|
+
}
|
|
2833
3023
|
function fingerprintManifestHookSideEffects(manifest) {
|
|
2834
3024
|
const value = {
|
|
2835
3025
|
...manifest,
|
|
@@ -2837,70 +3027,138 @@ function fingerprintManifestHookSideEffects(manifest) {
|
|
|
2837
3027
|
};
|
|
2838
3028
|
return fingerprintManifest(manifestFingerprintValue(value));
|
|
2839
3029
|
}
|
|
2840
|
-
function fingerprintManifestHookSnapshot(
|
|
2841
|
-
return fingerprintManifest(
|
|
2842
|
-
manifestFingerprintValue({
|
|
2843
|
-
...manifest,
|
|
2844
|
-
entries: [...snapshot.entries]
|
|
2845
|
-
})
|
|
2846
|
-
);
|
|
3030
|
+
function fingerprintManifestHookSnapshot(manifest) {
|
|
3031
|
+
return fingerprintManifest(manifestFingerprintValue(manifest));
|
|
2847
3032
|
}
|
|
2848
|
-
function
|
|
3033
|
+
function captureManifestHookRestorePoint(manifest) {
|
|
3034
|
+
return {
|
|
3035
|
+
entries: manifest.entries,
|
|
3036
|
+
publicEntries: manifest.publicEntries,
|
|
3037
|
+
discoverableEntries: manifest.discoverableEntries
|
|
3038
|
+
};
|
|
3039
|
+
}
|
|
3040
|
+
function restoreManifestHookEntries(manifest, point) {
|
|
2849
3041
|
try {
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
const entriesBySlug = new Map(
|
|
2853
|
-
|
|
2854
|
-
manifest.entries = entries;
|
|
2855
|
-
manifest.publicEntries = manifest.publicEntries.map((entry) => entriesBySlug.get(entry.slug)).filter((entry) => entry !== void 0);
|
|
2856
|
-
manifest.discoverableEntries = manifest.discoverableEntries.map((entry) => entriesBySlug.get(entry.slug)).filter((entry) => entry !== void 0);
|
|
2857
|
-
manifest.bySlug = new Map(entries.map((entry) => [entry.slug, entry]));
|
|
2858
|
-
manifest.byContentId = new Map(
|
|
2859
|
-
entries.filter((entry) => entry.contentId !== void 0).map((entry) => [entry.contentId, entry])
|
|
2860
|
-
);
|
|
2861
|
-
manifest.byPermalink = new Map(
|
|
2862
|
-
entries.map((entry) => [entry.permalink, entry])
|
|
3042
|
+
manifest.publicEntries = point.publicEntries;
|
|
3043
|
+
manifest.discoverableEntries = point.discoverableEntries;
|
|
3044
|
+
const entriesBySlug = new Map(
|
|
3045
|
+
point.entries.map((entry) => [entry.slug, entry])
|
|
2863
3046
|
);
|
|
2864
|
-
|
|
2865
|
-
|
|
3047
|
+
if (entriesBySlug.size !== point.entries.length) return false;
|
|
3048
|
+
return commitManifestEntries(manifest, entriesBySlug);
|
|
3049
|
+
} catch {
|
|
3050
|
+
return false;
|
|
3051
|
+
}
|
|
3052
|
+
}
|
|
3053
|
+
function applyManifestHookSnapshot(manifest, snapshot) {
|
|
3054
|
+
try {
|
|
3055
|
+
const entriesBySlug = new Map(
|
|
3056
|
+
manifest.entries.map((entry) => [entry.slug, entry])
|
|
2866
3057
|
);
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
manifest.outgoingLinks.set(entry.slug, entry.links);
|
|
2874
|
-
for (const alias of entry.aliases ?? []) {
|
|
2875
|
-
const aliases = manifest.byAlias.get(alias.toLowerCase()) ?? [];
|
|
2876
|
-
aliases.push(entry);
|
|
2877
|
-
manifest.byAlias.set(alias.toLowerCase(), aliases);
|
|
2878
|
-
}
|
|
2879
|
-
for (const tag of entry.tags) {
|
|
2880
|
-
const tagged = manifest.byTag.get(tag) ?? [];
|
|
2881
|
-
tagged.push(entry);
|
|
2882
|
-
manifest.byTag.set(tag, tagged);
|
|
2883
|
-
}
|
|
2884
|
-
for (const asset of entry.assets) {
|
|
2885
|
-
const assets = manifest.byAsset.get(asset.path) ?? [];
|
|
2886
|
-
assets.push(entry);
|
|
2887
|
-
manifest.byAsset.set(asset.path, assets);
|
|
2888
|
-
}
|
|
2889
|
-
for (const link of entry.links) {
|
|
2890
|
-
if (link.kind !== "note" || link.slug === null || link.slug === entry.slug)
|
|
2891
|
-
continue;
|
|
2892
|
-
const incoming = manifest.incomingLinks.get(link.slug) ?? [];
|
|
2893
|
-
if (!incoming.includes(entry.slug)) incoming.push(entry.slug);
|
|
2894
|
-
manifest.incomingLinks.set(link.slug, incoming);
|
|
2895
|
-
}
|
|
3058
|
+
const delta = [];
|
|
3059
|
+
for (const entry of structuredClone(snapshot.entries).map(
|
|
3060
|
+
reviveManifestEntry
|
|
3061
|
+
)) {
|
|
3062
|
+
if (entry === null || !isManifestEntry(entry)) return false;
|
|
3063
|
+
delta.push(entry);
|
|
2896
3064
|
}
|
|
2897
|
-
for (const entry of
|
|
2898
|
-
|
|
2899
|
-
return true;
|
|
3065
|
+
for (const entry of delta) entriesBySlug.set(entry.slug, entry);
|
|
3066
|
+
return commitManifestEntries(manifest, entriesBySlug, snapshot.order);
|
|
2900
3067
|
} catch {
|
|
2901
3068
|
return false;
|
|
2902
3069
|
}
|
|
2903
3070
|
}
|
|
3071
|
+
function commitManifestEntries(manifest, entriesBySlug, order) {
|
|
3072
|
+
let entries;
|
|
3073
|
+
if (order) {
|
|
3074
|
+
entries = [];
|
|
3075
|
+
for (const slug of order) {
|
|
3076
|
+
const entry = entriesBySlug.get(slug);
|
|
3077
|
+
if (!entry) return false;
|
|
3078
|
+
entries.push(entry);
|
|
3079
|
+
}
|
|
3080
|
+
if (entries.length !== entriesBySlug.size) return false;
|
|
3081
|
+
} else {
|
|
3082
|
+
entries = [...entriesBySlug.values()];
|
|
3083
|
+
}
|
|
3084
|
+
manifest.entries = entries;
|
|
3085
|
+
manifest.publicEntries = manifest.publicEntries.map((entry) => entriesBySlug.get(entry.slug)).filter((entry) => entry !== void 0);
|
|
3086
|
+
manifest.discoverableEntries = manifest.discoverableEntries.map((entry) => entriesBySlug.get(entry.slug)).filter((entry) => entry !== void 0);
|
|
3087
|
+
manifest.bySlug = new Map(entries.map((entry) => [entry.slug, entry]));
|
|
3088
|
+
manifest.byContentId = new Map(
|
|
3089
|
+
entries.filter((entry) => entry.contentId !== void 0).map((entry) => [entry.contentId, entry])
|
|
3090
|
+
);
|
|
3091
|
+
manifest.byPermalink = new Map(
|
|
3092
|
+
entries.map((entry) => [entry.permalink, entry])
|
|
3093
|
+
);
|
|
3094
|
+
manifest.byRoutablePermalink = new Map(
|
|
3095
|
+
manifest.publicEntries.map((entry) => [entry.permalink, entry])
|
|
3096
|
+
);
|
|
3097
|
+
manifest.byAlias = /* @__PURE__ */ new Map();
|
|
3098
|
+
manifest.byTag = /* @__PURE__ */ new Map();
|
|
3099
|
+
manifest.byAsset = /* @__PURE__ */ new Map();
|
|
3100
|
+
manifest.outgoingLinks = /* @__PURE__ */ new Map();
|
|
3101
|
+
manifest.incomingLinks = /* @__PURE__ */ new Map();
|
|
3102
|
+
for (const entry of entries) {
|
|
3103
|
+
manifest.outgoingLinks.set(entry.slug, entry.links);
|
|
3104
|
+
for (const alias of entry.aliases ?? []) {
|
|
3105
|
+
const aliases = manifest.byAlias.get(alias.toLowerCase()) ?? [];
|
|
3106
|
+
aliases.push(entry);
|
|
3107
|
+
manifest.byAlias.set(alias.toLowerCase(), aliases);
|
|
3108
|
+
}
|
|
3109
|
+
for (const tag of entry.tags) {
|
|
3110
|
+
const tagged = manifest.byTag.get(tag) ?? [];
|
|
3111
|
+
tagged.push(entry);
|
|
3112
|
+
manifest.byTag.set(tag, tagged);
|
|
3113
|
+
}
|
|
3114
|
+
for (const asset of entry.assets) {
|
|
3115
|
+
const assets = manifest.byAsset.get(asset.path) ?? [];
|
|
3116
|
+
assets.push(entry);
|
|
3117
|
+
manifest.byAsset.set(asset.path, assets);
|
|
3118
|
+
}
|
|
3119
|
+
for (const link of entry.links) {
|
|
3120
|
+
if (link.kind !== "note" || link.slug === null || link.slug === entry.slug)
|
|
3121
|
+
continue;
|
|
3122
|
+
const incoming = manifest.incomingLinks.get(link.slug) ?? [];
|
|
3123
|
+
if (!incoming.includes(entry.slug)) incoming.push(entry.slug);
|
|
3124
|
+
manifest.incomingLinks.set(link.slug, incoming);
|
|
3125
|
+
}
|
|
3126
|
+
}
|
|
3127
|
+
for (const entry of entries)
|
|
3128
|
+
entry.backlinks = manifest.incomingLinks.get(entry.slug) ?? [];
|
|
3129
|
+
return true;
|
|
3130
|
+
}
|
|
3131
|
+
function reviveManifestEntry(entry) {
|
|
3132
|
+
const publishAt = entry.publishing.publishAt;
|
|
3133
|
+
const parsed = typeof publishAt === "string" ? new Date(publishAt) : publishAt;
|
|
3134
|
+
if (parsed instanceof Date && Number.isNaN(parsed.getTime())) return null;
|
|
3135
|
+
const file = reviveFileMetadata(entry.file);
|
|
3136
|
+
if (file === null) return null;
|
|
3137
|
+
return {
|
|
3138
|
+
...entry,
|
|
3139
|
+
publishing: {
|
|
3140
|
+
...entry.publishing,
|
|
3141
|
+
...parsed ? { publishAt: parsed } : {}
|
|
3142
|
+
},
|
|
3143
|
+
...file ? { file } : {}
|
|
3144
|
+
};
|
|
3145
|
+
}
|
|
3146
|
+
function reviveFileMetadata(file) {
|
|
3147
|
+
if (file === void 0) return void 0;
|
|
3148
|
+
const ctime = reviveDate(file.ctime);
|
|
3149
|
+
const mtime = reviveDate(file.mtime);
|
|
3150
|
+
if (ctime === null || mtime === null) return null;
|
|
3151
|
+
return {
|
|
3152
|
+
...typeof file.size === "number" && Number.isSafeInteger(file.size) && file.size >= 0 ? { size: file.size } : {},
|
|
3153
|
+
...ctime ? { ctime } : {},
|
|
3154
|
+
...mtime ? { mtime } : {}
|
|
3155
|
+
};
|
|
3156
|
+
}
|
|
3157
|
+
function reviveDate(value) {
|
|
3158
|
+
if (value === void 0) return void 0;
|
|
3159
|
+
const parsed = value instanceof Date ? value : new Date(value);
|
|
3160
|
+
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
|
3161
|
+
}
|
|
2904
3162
|
function manifestFingerprintValue(manifest) {
|
|
2905
3163
|
return {
|
|
2906
3164
|
entries: manifest.entries,
|
|
@@ -2933,212 +3191,457 @@ function isManifestEntry(value) {
|
|
|
2933
3191
|
return typeof value === "object" && value !== null && typeof value.slug === "string" && typeof value.html === "string";
|
|
2934
3192
|
}
|
|
2935
3193
|
|
|
2936
|
-
// src/
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
function createClientEntry(pluginName, moduleSpecifier, exportName, publicConfig) {
|
|
2945
|
-
return {
|
|
2946
|
-
pluginName,
|
|
2947
|
-
moduleSpecifier,
|
|
2948
|
-
...exportName === void 0 ? {} : { exportName },
|
|
2949
|
-
...publicConfig === void 0 ? {} : { publicConfig }
|
|
2950
|
-
};
|
|
2951
|
-
}
|
|
2952
|
-
function serializePublicClientConfig(value) {
|
|
2953
|
-
assertJsonValue(value, /* @__PURE__ */ new Set());
|
|
2954
|
-
return JSON.stringify(value);
|
|
2955
|
-
}
|
|
2956
|
-
function assertJsonValue(value, ancestors) {
|
|
2957
|
-
if (value === null || typeof value === "boolean" || typeof value === "string") {
|
|
2958
|
-
return;
|
|
2959
|
-
}
|
|
2960
|
-
if (typeof value === "number") {
|
|
2961
|
-
if (Number.isFinite(value)) return;
|
|
2962
|
-
throw new TypeError("Public client configuration numbers must be finite.");
|
|
3194
|
+
// src/plugin/plugin_lifecycle.ts
|
|
3195
|
+
var PluginHookError = class extends Error {
|
|
3196
|
+
path;
|
|
3197
|
+
constructor(pluginName, hookName, cause) {
|
|
3198
|
+
super(`Plugin "${pluginName}" failed during "${hookName}"`, { cause });
|
|
3199
|
+
this.name = "PluginHookError";
|
|
3200
|
+
const path6 = cause?.path;
|
|
3201
|
+
if (typeof path6 === "string") this.path = path6;
|
|
2963
3202
|
}
|
|
2964
|
-
|
|
3203
|
+
};
|
|
3204
|
+
async function runPluginHook(plugin, hookName, context, hook) {
|
|
3205
|
+
try {
|
|
3206
|
+
return await context.tracer.span(
|
|
3207
|
+
`plugin.${hookName}`,
|
|
3208
|
+
{
|
|
3209
|
+
plugin: plugin.name,
|
|
3210
|
+
hook: hookName
|
|
3211
|
+
},
|
|
3212
|
+
() => hook(context)
|
|
3213
|
+
);
|
|
3214
|
+
} catch (error) {
|
|
3215
|
+
throw new PluginHookError(plugin.name, hookName, error);
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3218
|
+
async function runSetup(plugins, createContext) {
|
|
3219
|
+
await runLifecycleHook(
|
|
3220
|
+
plugins,
|
|
3221
|
+
"setup",
|
|
3222
|
+
(plugin) => plugin.setup,
|
|
3223
|
+
createContext
|
|
3224
|
+
);
|
|
3225
|
+
}
|
|
3226
|
+
async function runBuildStart(plugins, createContext) {
|
|
3227
|
+
await runLifecycleHook(
|
|
3228
|
+
plugins,
|
|
3229
|
+
"buildStart",
|
|
3230
|
+
(plugin) => plugin.buildStart,
|
|
3231
|
+
createContext
|
|
3232
|
+
);
|
|
3233
|
+
}
|
|
3234
|
+
async function runBuildEnd(plugins, createContext) {
|
|
3235
|
+
await runLifecycleHook(
|
|
3236
|
+
plugins,
|
|
3237
|
+
"buildEnd",
|
|
3238
|
+
(plugin) => plugin.buildEnd,
|
|
3239
|
+
createContext
|
|
3240
|
+
);
|
|
3241
|
+
}
|
|
3242
|
+
async function runDispose(plugins, createContext) {
|
|
3243
|
+
await runLifecycleHook(
|
|
3244
|
+
[...plugins].reverse(),
|
|
3245
|
+
"dispose",
|
|
3246
|
+
(plugin) => plugin.dispose,
|
|
3247
|
+
createContext
|
|
3248
|
+
);
|
|
3249
|
+
}
|
|
3250
|
+
async function runLifecycleHook(plugins, hookName, getHook, createContext) {
|
|
3251
|
+
for (const plugin of plugins) {
|
|
3252
|
+
const hook = getHook(plugin);
|
|
3253
|
+
if (!hook) continue;
|
|
3254
|
+
await runPluginHook(plugin, hookName, createContext(plugin), hook);
|
|
3255
|
+
}
|
|
3256
|
+
}
|
|
3257
|
+
|
|
3258
|
+
// src/plugin/manifest_hooks.ts
|
|
3259
|
+
var ManifestHookRunner = class {
|
|
3260
|
+
constructor(options) {
|
|
3261
|
+
this.options = options;
|
|
3262
|
+
}
|
|
3263
|
+
options;
|
|
3264
|
+
manifestHooksExecuted = 0;
|
|
3265
|
+
manifestHookDurationMs = 0;
|
|
3266
|
+
manifestHooksReused = 0;
|
|
3267
|
+
manifestHookCache = [];
|
|
3268
|
+
manifestHookMetricsByPlugin = /* @__PURE__ */ new Map();
|
|
3269
|
+
getMetrics() {
|
|
3270
|
+
return {
|
|
3271
|
+
manifestHooksExecuted: this.manifestHooksExecuted,
|
|
3272
|
+
manifestHooksReused: this.manifestHooksReused,
|
|
3273
|
+
manifestHookCacheHit: this.manifestHooksReused,
|
|
3274
|
+
manifestHookCacheMiss: this.manifestHooksExecuted,
|
|
3275
|
+
manifestHookDurationMs: this.manifestHookDurationMs,
|
|
3276
|
+
byPlugin: Object.fromEntries(this.manifestHookMetricsByPlugin)
|
|
3277
|
+
};
|
|
3278
|
+
}
|
|
3279
|
+
getNoChangeFastPathBlockReason(previousCache, pipelineFingerprint) {
|
|
3280
|
+
let cacheIndex = 0;
|
|
3281
|
+
for (const plugin of this.options.plugins()) {
|
|
3282
|
+
if (plugin.buildEnd) return "plugin-build-end";
|
|
3283
|
+
if (plugin.planGeneratedOutputs) return "plugin-generated-output-plan";
|
|
3284
|
+
if (!plugin.onManifestCreated) continue;
|
|
3285
|
+
const cached = previousCache[cacheIndex];
|
|
3286
|
+
cacheIndex += 1;
|
|
3287
|
+
if (cached?.pluginName !== plugin.name || !cached.cacheable || cached.pipelineFingerprint !== pipelineFingerprint)
|
|
3288
|
+
return "manifest-hook-cache-miss";
|
|
3289
|
+
}
|
|
3290
|
+
return void 0;
|
|
3291
|
+
}
|
|
3292
|
+
getCache() {
|
|
3293
|
+
return this.manifestHookCache;
|
|
3294
|
+
}
|
|
3295
|
+
async runManifestCreated(manifest, contentIndex, previousCache = [], pipelineFingerprint) {
|
|
3296
|
+
const context = {
|
|
3297
|
+
...this.options.createContext(contentIndex),
|
|
3298
|
+
manifest
|
|
3299
|
+
};
|
|
3300
|
+
let cacheIndex = 0;
|
|
3301
|
+
let entriesBeforeHook = serializeManifestEntries(manifest.entries);
|
|
3302
|
+
for (const plugin of this.options.plugins()) {
|
|
3303
|
+
const hook = plugin.onManifestCreated;
|
|
3304
|
+
if (!hook) continue;
|
|
3305
|
+
const inputFingerprint = fingerprintManifestHookInput(manifest);
|
|
3306
|
+
const cached = previousCache[cacheIndex];
|
|
3307
|
+
cacheIndex += 1;
|
|
3308
|
+
const reason = manifestHookCacheMissReason(
|
|
3309
|
+
cached,
|
|
3310
|
+
plugin.name,
|
|
3311
|
+
pipelineFingerprint,
|
|
3312
|
+
inputFingerprint
|
|
3313
|
+
);
|
|
3314
|
+
let restored = false;
|
|
3315
|
+
let restorePoint;
|
|
3316
|
+
if (cached !== void 0 && cached.pluginName === plugin.name && cached.cacheable && cached.pipelineFingerprint === pipelineFingerprint && cached.inputFingerprint === inputFingerprint && cached.snapshot !== void 0 && cached.outputFingerprint !== void 0) {
|
|
3317
|
+
restorePoint = captureManifestHookRestorePoint(manifest);
|
|
3318
|
+
restored = applyManifestHookSnapshot(manifest, cached.snapshot);
|
|
3319
|
+
if (restored && fingerprintManifestHookSnapshot(manifest) === cached.outputFingerprint) {
|
|
3320
|
+
this.manifestHooksReused += 1;
|
|
3321
|
+
this.manifestHookCache.push(cached);
|
|
3322
|
+
this.recordManifestHookMetric(plugin.name, "reused", "cache-hit", 0);
|
|
3323
|
+
entriesBeforeHook = serializeManifestEntries(manifest.entries);
|
|
3324
|
+
continue;
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
if (restored && restorePoint)
|
|
3328
|
+
restoreManifestHookEntries(manifest, restorePoint);
|
|
3329
|
+
const sideEffectsBefore = fingerprintManifestHookSideEffects(manifest);
|
|
3330
|
+
const outputCountBefore = this.options.generatedOutputCount();
|
|
3331
|
+
const diagnosticCountBefore = this.options.diagnosticCount();
|
|
3332
|
+
const manifestDiagnosticCountBefore = manifest.diagnostics.length;
|
|
3333
|
+
const started = performance.now();
|
|
3334
|
+
this.manifestHooksExecuted += 1;
|
|
3335
|
+
try {
|
|
3336
|
+
await runPluginHook(
|
|
3337
|
+
plugin,
|
|
3338
|
+
"onManifestCreated",
|
|
3339
|
+
this.options.createPluginContext(plugin, context),
|
|
3340
|
+
hook
|
|
3341
|
+
);
|
|
3342
|
+
} finally {
|
|
3343
|
+
const durationMs = performance.now() - started;
|
|
3344
|
+
this.manifestHookDurationMs += durationMs;
|
|
3345
|
+
this.recordManifestHookMetric(
|
|
3346
|
+
plugin.name,
|
|
3347
|
+
"executed",
|
|
3348
|
+
reason,
|
|
3349
|
+
durationMs
|
|
3350
|
+
);
|
|
3351
|
+
}
|
|
3352
|
+
const entriesAfterHook = serializeManifestEntries(manifest.entries);
|
|
3353
|
+
this.manifestHookCache.push(
|
|
3354
|
+
createManifestHookCacheEntry(
|
|
3355
|
+
plugin.name,
|
|
3356
|
+
plugin.resolveExternalDependencies === void 0 && sideEffectsBefore === fingerprintManifestHookSideEffects(manifest) && outputCountBefore === this.options.generatedOutputCount() && diagnosticCountBefore === this.options.diagnosticCount() && manifestDiagnosticCountBefore === manifest.diagnostics.length,
|
|
3357
|
+
pipelineFingerprint,
|
|
3358
|
+
inputFingerprint,
|
|
3359
|
+
manifest,
|
|
3360
|
+
entriesBeforeHook,
|
|
3361
|
+
entriesAfterHook
|
|
3362
|
+
)
|
|
3363
|
+
);
|
|
3364
|
+
entriesBeforeHook = entriesAfterHook;
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
recordManifestHookMetric(pluginName, outcome, reason, durationMs) {
|
|
3368
|
+
const previous = this.manifestHookMetricsByPlugin.get(pluginName) ?? {
|
|
3369
|
+
executed: 0,
|
|
3370
|
+
reused: 0,
|
|
3371
|
+
cacheHit: 0,
|
|
3372
|
+
cacheMiss: 0,
|
|
3373
|
+
durationMs: 0,
|
|
3374
|
+
reason
|
|
3375
|
+
};
|
|
3376
|
+
this.manifestHookMetricsByPlugin.set(pluginName, {
|
|
3377
|
+
executed: previous.executed + (outcome === "executed" ? 1 : 0),
|
|
3378
|
+
reused: previous.reused + (outcome === "reused" ? 1 : 0),
|
|
3379
|
+
cacheHit: previous.cacheHit + (outcome === "reused" ? 1 : 0),
|
|
3380
|
+
cacheMiss: previous.cacheMiss + (outcome === "executed" ? 1 : 0),
|
|
3381
|
+
durationMs: previous.durationMs + durationMs,
|
|
3382
|
+
reason
|
|
3383
|
+
});
|
|
3384
|
+
}
|
|
3385
|
+
};
|
|
3386
|
+
function manifestHookCacheMissReason(cached, pluginName, pipelineFingerprint, inputFingerprint) {
|
|
3387
|
+
if (!cached) return "cache-missing";
|
|
3388
|
+
if (cached.pluginName !== pluginName) return "plugin-order-changed";
|
|
3389
|
+
if (!cached.cacheable) return "uncacheable";
|
|
3390
|
+
if (cached.pipelineFingerprint !== pipelineFingerprint)
|
|
3391
|
+
return "pipeline-fingerprint-changed";
|
|
3392
|
+
if (cached.inputFingerprint !== inputFingerprint)
|
|
3393
|
+
return "manifest-input-changed";
|
|
3394
|
+
return "snapshot-invalid";
|
|
3395
|
+
}
|
|
3396
|
+
|
|
3397
|
+
// src/plugin/plugin_pages.ts
|
|
3398
|
+
var PluginPageResolver = class {
|
|
3399
|
+
constructor(options) {
|
|
3400
|
+
this.options = options;
|
|
3401
|
+
}
|
|
3402
|
+
options;
|
|
3403
|
+
pageDeclarations = /* @__PURE__ */ new WeakMap();
|
|
3404
|
+
async resolvePage(pathname, manifest, contentIndex) {
|
|
3405
|
+
const matches = [];
|
|
3406
|
+
const requestedPathname = normalizePagePath(pathname);
|
|
3407
|
+
for (const plugin of this.options.plugins()) {
|
|
3408
|
+
for (const pageType of plugin.pageTypes ?? []) {
|
|
3409
|
+
const pagePathname = normalizePageTypePath(pathname, pageType);
|
|
3410
|
+
const context = {
|
|
3411
|
+
...this.options.createContext(contentIndex),
|
|
3412
|
+
manifest,
|
|
3413
|
+
pathname: pagePathname
|
|
3414
|
+
};
|
|
3415
|
+
const page = await pageType.resolve(
|
|
3416
|
+
this.options.createPluginContext(plugin, context)
|
|
3417
|
+
);
|
|
3418
|
+
if (page) {
|
|
3419
|
+
matches.push({
|
|
3420
|
+
page: {
|
|
3421
|
+
...page,
|
|
3422
|
+
pathname: pageType.directoryIndex ? normalizeDirectoryPagePath(page.pathname) : page.pathname,
|
|
3423
|
+
type: pageType.id,
|
|
3424
|
+
pluginName: plugin.name
|
|
3425
|
+
},
|
|
3426
|
+
priority: pageType.priority ?? 0
|
|
3427
|
+
});
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3430
|
+
}
|
|
3431
|
+
if (matches.length === 0) return null;
|
|
3432
|
+
const highestPriority = Math.max(...matches.map((match) => match.priority));
|
|
3433
|
+
const highest = matches.filter(
|
|
3434
|
+
(match) => match.priority === highestPriority
|
|
3435
|
+
);
|
|
3436
|
+
if (highest.length !== 1) {
|
|
3437
|
+
throw new Error(
|
|
3438
|
+
`Multiple plugin page types match ${requestedPathname} at priority ${highestPriority}: ${highest.map((match) => match.page.type).join(", ")}`
|
|
3439
|
+
);
|
|
3440
|
+
}
|
|
3441
|
+
return highest[0].page;
|
|
3442
|
+
}
|
|
3443
|
+
async getPagePaths(manifest, contentIndex) {
|
|
3444
|
+
return [
|
|
3445
|
+
...new Set(
|
|
3446
|
+
(await this.getPageDeclarations(manifest, contentIndex)).map(
|
|
3447
|
+
(declaration) => declaration.pathname
|
|
3448
|
+
)
|
|
3449
|
+
)
|
|
3450
|
+
];
|
|
3451
|
+
}
|
|
3452
|
+
async getPageRoutes(manifest, contentIndex) {
|
|
3453
|
+
return (await this.getPageDeclarations(manifest, contentIndex)).map(
|
|
3454
|
+
({ pathname, plugin, pageType }) => ({
|
|
3455
|
+
pathname,
|
|
3456
|
+
pluginName: plugin.name,
|
|
3457
|
+
pageType: pageType.id
|
|
3458
|
+
})
|
|
3459
|
+
);
|
|
3460
|
+
}
|
|
3461
|
+
async getPageOutputs(manifest, contentIndex) {
|
|
3462
|
+
const outputs = await Promise.all(
|
|
3463
|
+
(await this.getPageDeclarations(manifest, contentIndex)).map(
|
|
3464
|
+
async ({ pathname, plugin, pageType }) => {
|
|
3465
|
+
const context = {
|
|
3466
|
+
...this.options.createContext(contentIndex),
|
|
3467
|
+
manifest,
|
|
3468
|
+
pathname
|
|
3469
|
+
};
|
|
3470
|
+
const dependencies = pageType.outputDependencies ? typeof pageType.outputDependencies === "function" ? await pageType.outputDependencies(
|
|
3471
|
+
this.options.createPluginContext(plugin, context)
|
|
3472
|
+
) : pageType.outputDependencies : [{ type: "unknown" }];
|
|
3473
|
+
return {
|
|
3474
|
+
output: {
|
|
3475
|
+
kind: "plugin-page",
|
|
3476
|
+
path: pathname,
|
|
3477
|
+
producer: `plugin:${plugin.name}:page:${pageType.id}`,
|
|
3478
|
+
dependencies
|
|
3479
|
+
},
|
|
3480
|
+
priority: pageType.priority ?? 0
|
|
3481
|
+
};
|
|
3482
|
+
}
|
|
3483
|
+
)
|
|
3484
|
+
);
|
|
3485
|
+
return uniqueOutputs(outputs);
|
|
3486
|
+
}
|
|
3487
|
+
async getPageDeclarations(manifest, contentIndex) {
|
|
3488
|
+
const existing = this.pageDeclarations.get(manifest);
|
|
3489
|
+
if (existing) return await existing;
|
|
3490
|
+
const declarations = this.resolvePageDeclarations(manifest, contentIndex);
|
|
3491
|
+
this.pageDeclarations.set(manifest, declarations);
|
|
3492
|
+
return await declarations;
|
|
3493
|
+
}
|
|
3494
|
+
async resolvePageDeclarations(manifest, contentIndex) {
|
|
3495
|
+
const declarations = [];
|
|
3496
|
+
const context = {
|
|
3497
|
+
...this.options.createContext(contentIndex),
|
|
3498
|
+
manifest
|
|
3499
|
+
};
|
|
3500
|
+
for (const plugin of this.options.plugins()) {
|
|
3501
|
+
for (const pageType of plugin.pageTypes ?? []) {
|
|
3502
|
+
const paths = pageType.paths;
|
|
3503
|
+
if (!paths) continue;
|
|
3504
|
+
const resolved = typeof paths === "function" ? await paths(this.options.createPluginContext(plugin, context)) : paths;
|
|
3505
|
+
for (const pathname of resolved) {
|
|
3506
|
+
declarations.push({
|
|
3507
|
+
pathname: normalizePageTypePath(pathname, pageType),
|
|
3508
|
+
plugin,
|
|
3509
|
+
pageType
|
|
3510
|
+
});
|
|
3511
|
+
}
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3514
|
+
return declarations;
|
|
3515
|
+
}
|
|
3516
|
+
};
|
|
3517
|
+
function collectPageTypeOwners(plugins) {
|
|
3518
|
+
const pageTypes = /* @__PURE__ */ new Map();
|
|
3519
|
+
for (const plugin of plugins) {
|
|
3520
|
+
const declaredPageTypes = plugin.pageTypes;
|
|
3521
|
+
if (declaredPageTypes !== void 0 && !Array.isArray(declaredPageTypes)) {
|
|
3522
|
+
throw new TypeError(`Plugin ${plugin.name} pageTypes must be an array`);
|
|
3523
|
+
}
|
|
3524
|
+
for (const pageType of declaredPageTypes ?? []) {
|
|
3525
|
+
validatePageType(plugin.name, pageType);
|
|
3526
|
+
const previous = pageTypes.get(pageType.id);
|
|
3527
|
+
if (previous) {
|
|
3528
|
+
throw new Error(
|
|
3529
|
+
`Plugin page type "${pageType.id}" is provided by both ${previous} and ${plugin.name}`
|
|
3530
|
+
);
|
|
3531
|
+
}
|
|
3532
|
+
pageTypes.set(pageType.id, plugin.name);
|
|
3533
|
+
}
|
|
3534
|
+
}
|
|
3535
|
+
}
|
|
3536
|
+
function validatePageType(pluginName, pageType) {
|
|
3537
|
+
if (typeof pageType !== "object" || pageType === null) {
|
|
3538
|
+
throw new TypeError(`Plugin ${pluginName} pageTypes must contain objects`);
|
|
3539
|
+
}
|
|
3540
|
+
const candidate = pageType;
|
|
3541
|
+
if (typeof candidate.id !== "string" || candidate.id.trim() === "") {
|
|
2965
3542
|
throw new TypeError(
|
|
2966
|
-
|
|
3543
|
+
`Plugin ${pluginName} page type id must be a non-empty string`
|
|
2967
3544
|
);
|
|
2968
3545
|
}
|
|
2969
|
-
if (
|
|
2970
|
-
throw new TypeError("Public client configuration must not contain cycles.");
|
|
2971
|
-
}
|
|
2972
|
-
const prototype = Object.getPrototypeOf(value);
|
|
2973
|
-
if (prototype !== Object.prototype && prototype !== null && !Array.isArray(value)) {
|
|
3546
|
+
if (typeof candidate.resolve !== "function") {
|
|
2974
3547
|
throw new TypeError(
|
|
2975
|
-
|
|
3548
|
+
`Plugin ${pluginName} page type "${candidate.id}" must provide a resolve function`
|
|
2976
3549
|
);
|
|
2977
3550
|
}
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
}
|
|
2982
|
-
ancestors.delete(value);
|
|
2983
|
-
}
|
|
2984
|
-
|
|
2985
|
-
// src/plugin/generated_output_registry.ts
|
|
2986
|
-
var GeneratedOutputRegistry = class {
|
|
2987
|
-
outputs = [];
|
|
2988
|
-
owners = /* @__PURE__ */ new Map();
|
|
2989
|
-
sinkFor(pluginName) {
|
|
2990
|
-
const register = (path6, output) => {
|
|
2991
|
-
const owner = this.owners.get(path6);
|
|
2992
|
-
if (owner) {
|
|
2993
|
-
throw new Error(
|
|
2994
|
-
`Duplicate generated output path "${path6}" declared by "${owner}" and "${pluginName}".`
|
|
2995
|
-
);
|
|
2996
|
-
}
|
|
2997
|
-
this.owners.set(path6, pluginName);
|
|
2998
|
-
this.outputs.push({
|
|
2999
|
-
path: path6,
|
|
3000
|
-
content: output.content,
|
|
3001
|
-
dependencies: output.dependencies,
|
|
3002
|
-
owner: pluginName
|
|
3003
|
-
});
|
|
3004
|
-
};
|
|
3005
|
-
return {
|
|
3006
|
-
emit: (output) => {
|
|
3007
|
-
register(normalizeGeneratedOutputPath(output.path), output);
|
|
3008
|
-
},
|
|
3009
|
-
emitAsset: (output) => {
|
|
3010
|
-
register(normalizeContentAssetOutputPath(output.path), output);
|
|
3011
|
-
}
|
|
3012
|
-
};
|
|
3013
|
-
}
|
|
3014
|
-
all() {
|
|
3015
|
-
return [...this.outputs].sort(
|
|
3016
|
-
(a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0
|
|
3551
|
+
if (candidate.paths !== void 0 && !Array.isArray(candidate.paths) && typeof candidate.paths !== "function") {
|
|
3552
|
+
throw new TypeError(
|
|
3553
|
+
`Plugin ${pluginName} page type "${candidate.id}" paths must be an array or function`
|
|
3017
3554
|
);
|
|
3018
3555
|
}
|
|
3019
|
-
|
|
3020
|
-
|
|
3556
|
+
if (candidate.priority !== void 0 && (typeof candidate.priority !== "number" || !Number.isFinite(candidate.priority))) {
|
|
3557
|
+
throw new TypeError(
|
|
3558
|
+
`Plugin ${pluginName} page type "${candidate.id}" priority must be a finite number`
|
|
3559
|
+
);
|
|
3021
3560
|
}
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
path;
|
|
3027
|
-
constructor(pluginName, hookName, cause) {
|
|
3028
|
-
super(`Plugin "${pluginName}" failed during "${hookName}"`, { cause });
|
|
3029
|
-
this.name = "PluginHookError";
|
|
3030
|
-
const path6 = cause?.path;
|
|
3031
|
-
if (typeof path6 === "string") this.path = path6;
|
|
3561
|
+
if (candidate.outputDependencies !== void 0 && !Array.isArray(candidate.outputDependencies) && typeof candidate.outputDependencies !== "function") {
|
|
3562
|
+
throw new TypeError(
|
|
3563
|
+
`Plugin ${pluginName} page type "${candidate.id}" outputDependencies must be an array or function`
|
|
3564
|
+
);
|
|
3032
3565
|
}
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
return await context.tracer.span(
|
|
3037
|
-
`plugin.${hookName}`,
|
|
3038
|
-
{
|
|
3039
|
-
plugin: plugin.name,
|
|
3040
|
-
hook: hookName
|
|
3041
|
-
},
|
|
3042
|
-
() => hook(context)
|
|
3566
|
+
if (candidate.directoryIndex !== void 0 && typeof candidate.directoryIndex !== "boolean") {
|
|
3567
|
+
throw new TypeError(
|
|
3568
|
+
`Plugin ${pluginName} page type "${candidate.id}" directoryIndex must be a boolean`
|
|
3043
3569
|
);
|
|
3044
|
-
} catch (error) {
|
|
3045
|
-
throw new PluginHookError(plugin.name, hookName, error);
|
|
3046
3570
|
}
|
|
3047
3571
|
}
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
plugins,
|
|
3051
|
-
"setup",
|
|
3052
|
-
(plugin) => plugin.setup,
|
|
3053
|
-
createContext
|
|
3054
|
-
);
|
|
3055
|
-
}
|
|
3056
|
-
async function runBuildStart(plugins, createContext) {
|
|
3057
|
-
await runLifecycleHook(
|
|
3058
|
-
plugins,
|
|
3059
|
-
"buildStart",
|
|
3060
|
-
(plugin) => plugin.buildStart,
|
|
3061
|
-
createContext
|
|
3062
|
-
);
|
|
3572
|
+
function normalizePageTypePath(pathname, pageType) {
|
|
3573
|
+
return pageType.directoryIndex ? normalizeDirectoryPagePath(pathname) : normalizePagePath(pathname);
|
|
3063
3574
|
}
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
"buildEnd",
|
|
3068
|
-
(plugin) => plugin.buildEnd,
|
|
3069
|
-
createContext
|
|
3070
|
-
);
|
|
3575
|
+
function normalizeDirectoryPagePath(pathname) {
|
|
3576
|
+
const path6 = normalizePagePath(pathname);
|
|
3577
|
+
return path6 === "/" ? path6 : `${path6}/`;
|
|
3071
3578
|
}
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
"dispose",
|
|
3076
|
-
(plugin) => plugin.dispose,
|
|
3077
|
-
createContext
|
|
3078
|
-
);
|
|
3579
|
+
function normalizePagePath(pathname) {
|
|
3580
|
+
const path6 = `/${pathname.split("/").filter(Boolean).join("/")}`;
|
|
3581
|
+
return path6 === "/" ? path6 : path6.replace(/\/$/, "");
|
|
3079
3582
|
}
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3583
|
+
function uniqueOutputs(outputs) {
|
|
3584
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
3585
|
+
for (const output of outputs) {
|
|
3586
|
+
const previous = byPath.get(output.output.path);
|
|
3587
|
+
if (!previous || output.priority > previous.priority) {
|
|
3588
|
+
byPath.set(output.output.path, output);
|
|
3589
|
+
continue;
|
|
3590
|
+
}
|
|
3591
|
+
if (output.output.producer === previous.output.producer) continue;
|
|
3592
|
+
if (output.priority === previous.priority) {
|
|
3593
|
+
throw new Error(
|
|
3594
|
+
`Multiple plugin page types declare ${output.output.path} at priority ${output.priority}: ${previous.output.producer}, ${output.output.producer}`
|
|
3595
|
+
);
|
|
3596
|
+
}
|
|
3085
3597
|
}
|
|
3598
|
+
return [...byPath.values()].map(({ output }) => output).sort((left, right) => left.path.localeCompare(right.path));
|
|
3086
3599
|
}
|
|
3087
3600
|
|
|
3088
3601
|
// src/plugin/plugin_runtime.ts
|
|
3089
3602
|
var PluginRuntime = class {
|
|
3090
3603
|
constructor(pipelineOptions = {}) {
|
|
3091
3604
|
this.pipelineOptions = pipelineOptions;
|
|
3605
|
+
this.pageResolver = new PluginPageResolver({
|
|
3606
|
+
plugins: () => this.plugins(),
|
|
3607
|
+
createContext: (contentIndex) => this.createContext(contentIndex),
|
|
3608
|
+
createPluginContext: (plugin, context) => this.createPluginContext(plugin, context)
|
|
3609
|
+
});
|
|
3610
|
+
this.manifestHooks = new ManifestHookRunner({
|
|
3611
|
+
plugins: () => this.plugins(),
|
|
3612
|
+
createContext: (contentIndex) => this.createContext(contentIndex),
|
|
3613
|
+
createPluginContext: (plugin, context) => this.createPluginContext(plugin, context),
|
|
3614
|
+
generatedOutputCount: () => this.generatedOutputs.count(),
|
|
3615
|
+
diagnosticCount: () => this.diagnostics.length
|
|
3616
|
+
});
|
|
3092
3617
|
}
|
|
3093
3618
|
pipelineOptions;
|
|
3094
3619
|
buildLifecycle = null;
|
|
3095
|
-
buildStage = "idle";
|
|
3096
3620
|
disposed = false;
|
|
3097
3621
|
diagnostics = [];
|
|
3098
3622
|
pluginCaches = /* @__PURE__ */ new Map();
|
|
3099
3623
|
generatedOutputs = new GeneratedOutputRegistry();
|
|
3100
3624
|
isBuildTime = false;
|
|
3101
3625
|
resolvedPlugins = null;
|
|
3102
|
-
pageDeclarations = /* @__PURE__ */ new WeakMap();
|
|
3103
3626
|
externalDependencies = /* @__PURE__ */ new Map();
|
|
3104
3627
|
externalDependencyFailures = [];
|
|
3105
3628
|
externalDependenciesResolved = false;
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
manifestHooksReused = 0;
|
|
3109
|
-
manifestHookCache = [];
|
|
3110
|
-
manifestHookMetricsByPlugin = /* @__PURE__ */ new Map();
|
|
3629
|
+
pageResolver;
|
|
3630
|
+
manifestHooks;
|
|
3111
3631
|
getDiagnostics() {
|
|
3112
3632
|
return this.diagnostics;
|
|
3113
3633
|
}
|
|
3114
|
-
getBuildStage() {
|
|
3115
|
-
return this.buildStage;
|
|
3116
|
-
}
|
|
3117
3634
|
getManifestHookMetrics() {
|
|
3118
|
-
return
|
|
3119
|
-
manifestHooksExecuted: this.manifestHooksExecuted,
|
|
3120
|
-
manifestHooksReused: this.manifestHooksReused,
|
|
3121
|
-
manifestHookCacheHit: this.manifestHooksReused,
|
|
3122
|
-
manifestHookCacheMiss: this.manifestHooksExecuted,
|
|
3123
|
-
manifestHookDurationMs: this.manifestHookDurationMs,
|
|
3124
|
-
byPlugin: Object.fromEntries(this.manifestHookMetricsByPlugin)
|
|
3125
|
-
};
|
|
3635
|
+
return this.manifestHooks.getMetrics();
|
|
3126
3636
|
}
|
|
3127
3637
|
getNoChangeFastPathBlockReason(previousCache, pipelineFingerprint) {
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
if (!plugin.onManifestCreated) continue;
|
|
3133
|
-
const cached = previousCache[cacheIndex];
|
|
3134
|
-
cacheIndex += 1;
|
|
3135
|
-
if (cached?.pluginName !== plugin.name || !cached.cacheable || cached.pipelineFingerprint !== pipelineFingerprint)
|
|
3136
|
-
return "manifest-hook-cache-miss";
|
|
3137
|
-
}
|
|
3138
|
-
return void 0;
|
|
3638
|
+
return this.manifestHooks.getNoChangeFastPathBlockReason(
|
|
3639
|
+
previousCache,
|
|
3640
|
+
pipelineFingerprint
|
|
3641
|
+
);
|
|
3139
3642
|
}
|
|
3140
3643
|
getManifestHookCache() {
|
|
3141
|
-
return this.
|
|
3644
|
+
return this.manifestHooks.getCache();
|
|
3142
3645
|
}
|
|
3143
3646
|
/**
|
|
3144
3647
|
* Starts the plugin build lifecycle exactly once, regardless of which public
|
|
@@ -3204,124 +3707,51 @@ var PluginRuntime = class {
|
|
|
3204
3707
|
const pluginContext = this.createPluginContext(plugin, context);
|
|
3205
3708
|
const resolved = await runPluginHook(
|
|
3206
3709
|
plugin,
|
|
3207
|
-
"resolveContentLocations",
|
|
3208
|
-
pluginContext,
|
|
3209
|
-
resolver
|
|
3210
|
-
);
|
|
3211
|
-
locations.push(...resolved);
|
|
3212
|
-
}
|
|
3213
|
-
return locations;
|
|
3214
|
-
}
|
|
3215
|
-
async extendContentLocations(entries, locations, contentIndex) {
|
|
3216
|
-
await this.runHook(
|
|
3217
|
-
"extendContentLocations",
|
|
3218
|
-
(plugin) => plugin.extendContentLocations,
|
|
3219
|
-
{
|
|
3220
|
-
...this.createContext(contentIndex),
|
|
3221
|
-
entries,
|
|
3222
|
-
locations
|
|
3223
|
-
}
|
|
3224
|
-
);
|
|
3225
|
-
}
|
|
3226
|
-
async runPostHook(hookName, slug, markdown, content, contentIndex) {
|
|
3227
|
-
await this.runHook(hookName, (plugin) => plugin[hookName], {
|
|
3228
|
-
...this.createContext(contentIndex),
|
|
3229
|
-
slug,
|
|
3230
|
-
markdown,
|
|
3231
|
-
content
|
|
3232
|
-
});
|
|
3233
|
-
}
|
|
3234
|
-
async runGraphHook(entries, contentIndex) {
|
|
3235
|
-
await this.runHook(
|
|
3236
|
-
"extendContentGraph",
|
|
3237
|
-
(plugin) => plugin.extendContentGraph,
|
|
3238
|
-
{
|
|
3239
|
-
...this.createContext(contentIndex),
|
|
3240
|
-
entries
|
|
3241
|
-
}
|
|
3242
|
-
);
|
|
3243
|
-
}
|
|
3244
|
-
async runManifestCreated(manifest, contentIndex, previousCache = [], pipelineFingerprint) {
|
|
3245
|
-
const context = { ...this.createContext(contentIndex), manifest };
|
|
3246
|
-
let cacheIndex = 0;
|
|
3247
|
-
for (const plugin of this.plugins()) {
|
|
3248
|
-
const hook = plugin.onManifestCreated;
|
|
3249
|
-
if (!hook) continue;
|
|
3250
|
-
const inputFingerprint = fingerprintManifestHookInput(manifest);
|
|
3251
|
-
const cached = previousCache[cacheIndex];
|
|
3252
|
-
cacheIndex += 1;
|
|
3253
|
-
const reason = manifestHookCacheMissReason(
|
|
3254
|
-
cached,
|
|
3255
|
-
plugin.name,
|
|
3256
|
-
pipelineFingerprint,
|
|
3257
|
-
inputFingerprint
|
|
3258
|
-
);
|
|
3259
|
-
const canRestore = cached?.pluginName === plugin.name && cached.cacheable && cached.pipelineFingerprint === pipelineFingerprint && cached.inputFingerprint === inputFingerprint;
|
|
3260
|
-
const manifestBeforeRestore = canRestore ? {
|
|
3261
|
-
entries: structuredClone(manifest.entries)
|
|
3262
|
-
} : void 0;
|
|
3263
|
-
const restored = canRestore && applyManifestHookSnapshot(manifest, cached.snapshot);
|
|
3264
|
-
const cacheHit = restored && fingerprintManifestHookSnapshot(cached.snapshot, manifest) === cached.outputFingerprint;
|
|
3265
|
-
if (cacheHit) {
|
|
3266
|
-
this.manifestHooksReused += 1;
|
|
3267
|
-
this.manifestHookCache.push(cached);
|
|
3268
|
-
this.recordManifestHookMetric(plugin.name, "reused", "cache-hit", 0);
|
|
3269
|
-
continue;
|
|
3270
|
-
}
|
|
3271
|
-
if (restored && manifestBeforeRestore)
|
|
3272
|
-
applyManifestHookSnapshot(manifest, manifestBeforeRestore);
|
|
3273
|
-
const sideEffectsBefore = fingerprintManifestHookSideEffects(manifest);
|
|
3274
|
-
const outputCountBefore = this.generatedOutputs.count();
|
|
3275
|
-
const diagnosticCountBefore = this.diagnostics.length;
|
|
3276
|
-
const manifestDiagnosticCountBefore = manifest.diagnostics.length;
|
|
3277
|
-
const started = performance.now();
|
|
3278
|
-
this.manifestHooksExecuted += 1;
|
|
3279
|
-
try {
|
|
3280
|
-
await runPluginHook(
|
|
3281
|
-
plugin,
|
|
3282
|
-
"onManifestCreated",
|
|
3283
|
-
this.createPluginContext(plugin, context),
|
|
3284
|
-
hook
|
|
3285
|
-
);
|
|
3286
|
-
} finally {
|
|
3287
|
-
const durationMs = performance.now() - started;
|
|
3288
|
-
this.manifestHookDurationMs += durationMs;
|
|
3289
|
-
this.recordManifestHookMetric(
|
|
3290
|
-
plugin.name,
|
|
3291
|
-
"executed",
|
|
3292
|
-
reason,
|
|
3293
|
-
durationMs
|
|
3294
|
-
);
|
|
3295
|
-
}
|
|
3296
|
-
this.manifestHookCache.push(
|
|
3297
|
-
createManifestHookCacheEntry(
|
|
3298
|
-
plugin.name,
|
|
3299
|
-
plugin.resolveExternalDependencies === void 0 && sideEffectsBefore === fingerprintManifestHookSideEffects(manifest) && outputCountBefore === this.generatedOutputs.count() && diagnosticCountBefore === this.diagnostics.length && manifestDiagnosticCountBefore === manifest.diagnostics.length,
|
|
3300
|
-
pipelineFingerprint,
|
|
3301
|
-
inputFingerprint,
|
|
3302
|
-
manifest
|
|
3303
|
-
)
|
|
3710
|
+
"resolveContentLocations",
|
|
3711
|
+
pluginContext,
|
|
3712
|
+
resolver
|
|
3304
3713
|
);
|
|
3714
|
+
locations.push(...resolved);
|
|
3305
3715
|
}
|
|
3716
|
+
return locations;
|
|
3306
3717
|
}
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3718
|
+
async extendContentLocations(entries, locations, contentIndex) {
|
|
3719
|
+
await this.runHook(
|
|
3720
|
+
"extendContentLocations",
|
|
3721
|
+
(plugin) => plugin.extendContentLocations,
|
|
3722
|
+
{
|
|
3723
|
+
...this.createContext(contentIndex),
|
|
3724
|
+
entries,
|
|
3725
|
+
locations
|
|
3726
|
+
}
|
|
3727
|
+
);
|
|
3728
|
+
}
|
|
3729
|
+
async runPostHook(hookName, slug, markdown, content, contentIndex) {
|
|
3730
|
+
await this.runHook(hookName, (plugin) => plugin[hookName], {
|
|
3731
|
+
...this.createContext(contentIndex),
|
|
3732
|
+
slug,
|
|
3733
|
+
markdown,
|
|
3734
|
+
content
|
|
3323
3735
|
});
|
|
3324
3736
|
}
|
|
3737
|
+
async runGraphHook(entries, contentIndex) {
|
|
3738
|
+
await this.runHook(
|
|
3739
|
+
"extendContentGraph",
|
|
3740
|
+
(plugin) => plugin.extendContentGraph,
|
|
3741
|
+
{
|
|
3742
|
+
...this.createContext(contentIndex),
|
|
3743
|
+
entries
|
|
3744
|
+
}
|
|
3745
|
+
);
|
|
3746
|
+
}
|
|
3747
|
+
async runManifestCreated(manifest, contentIndex, previousCache = [], pipelineFingerprint) {
|
|
3748
|
+
await this.manifestHooks.runManifestCreated(
|
|
3749
|
+
manifest,
|
|
3750
|
+
contentIndex,
|
|
3751
|
+
previousCache,
|
|
3752
|
+
pipelineFingerprint
|
|
3753
|
+
);
|
|
3754
|
+
}
|
|
3325
3755
|
async runBuildEnd(manifest, contentIndex) {
|
|
3326
3756
|
const context = {
|
|
3327
3757
|
...this.createContext(contentIndex),
|
|
@@ -3404,118 +3834,27 @@ var PluginRuntime = class {
|
|
|
3404
3834
|
});
|
|
3405
3835
|
}
|
|
3406
3836
|
async resolvePage(pathname, manifest, contentIndex) {
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
const pagePathname = normalizePageTypePath(pathname, pageType);
|
|
3412
|
-
const context = {
|
|
3413
|
-
...this.createContext(contentIndex),
|
|
3414
|
-
manifest,
|
|
3415
|
-
pathname: pagePathname
|
|
3416
|
-
};
|
|
3417
|
-
const page = await pageType.resolve(
|
|
3418
|
-
this.createPluginContext(plugin, context)
|
|
3419
|
-
);
|
|
3420
|
-
if (page) {
|
|
3421
|
-
matches.push({
|
|
3422
|
-
page: {
|
|
3423
|
-
...page,
|
|
3424
|
-
pathname: pageType.directoryIndex ? normalizeDirectoryPagePath(page.pathname) : page.pathname,
|
|
3425
|
-
type: pageType.id,
|
|
3426
|
-
pluginName: plugin.name
|
|
3427
|
-
},
|
|
3428
|
-
priority: pageType.priority ?? 0
|
|
3429
|
-
});
|
|
3430
|
-
}
|
|
3431
|
-
}
|
|
3432
|
-
}
|
|
3433
|
-
if (matches.length === 0) return null;
|
|
3434
|
-
const highestPriority = Math.max(...matches.map((match) => match.priority));
|
|
3435
|
-
const highest = matches.filter(
|
|
3436
|
-
(match) => match.priority === highestPriority
|
|
3837
|
+
return await this.pageResolver.resolvePage(
|
|
3838
|
+
pathname,
|
|
3839
|
+
manifest,
|
|
3840
|
+
contentIndex
|
|
3437
3841
|
);
|
|
3438
|
-
if (highest.length !== 1) {
|
|
3439
|
-
throw new Error(
|
|
3440
|
-
`Multiple plugin page types match ${requestedPathname} at priority ${highestPriority}: ${highest.map((match) => match.page.type).join(", ")}`
|
|
3441
|
-
);
|
|
3442
|
-
}
|
|
3443
|
-
return highest[0].page;
|
|
3444
3842
|
}
|
|
3445
3843
|
async getPagePaths(manifest, contentIndex) {
|
|
3446
|
-
return
|
|
3447
|
-
...new Set(
|
|
3448
|
-
(await this.getPageDeclarations(manifest, contentIndex)).map(
|
|
3449
|
-
(declaration) => declaration.pathname
|
|
3450
|
-
)
|
|
3451
|
-
)
|
|
3452
|
-
];
|
|
3844
|
+
return await this.pageResolver.getPagePaths(manifest, contentIndex);
|
|
3453
3845
|
}
|
|
3454
3846
|
async getPageRoutes(manifest, contentIndex) {
|
|
3455
|
-
return
|
|
3456
|
-
({ pathname, plugin, pageType }) => ({
|
|
3457
|
-
pathname,
|
|
3458
|
-
pluginName: plugin.name,
|
|
3459
|
-
pageType: pageType.id
|
|
3460
|
-
})
|
|
3461
|
-
);
|
|
3847
|
+
return await this.pageResolver.getPageRoutes(manifest, contentIndex);
|
|
3462
3848
|
}
|
|
3463
3849
|
async getPageOutputs(manifest, contentIndex) {
|
|
3464
|
-
|
|
3465
|
-
(await this.getPageDeclarations(manifest, contentIndex)).map(
|
|
3466
|
-
async ({ pathname, plugin, pageType }) => {
|
|
3467
|
-
const context = {
|
|
3468
|
-
...this.createContext(contentIndex),
|
|
3469
|
-
manifest,
|
|
3470
|
-
pathname
|
|
3471
|
-
};
|
|
3472
|
-
const dependencies = pageType.outputDependencies ? typeof pageType.outputDependencies === "function" ? await pageType.outputDependencies(
|
|
3473
|
-
this.createPluginContext(plugin, context)
|
|
3474
|
-
) : pageType.outputDependencies : [{ type: "unknown" }];
|
|
3475
|
-
return {
|
|
3476
|
-
output: {
|
|
3477
|
-
kind: "plugin-page",
|
|
3478
|
-
path: pathname,
|
|
3479
|
-
producer: `plugin:${plugin.name}:page:${pageType.id}`,
|
|
3480
|
-
dependencies
|
|
3481
|
-
},
|
|
3482
|
-
priority: pageType.priority ?? 0
|
|
3483
|
-
};
|
|
3484
|
-
}
|
|
3485
|
-
)
|
|
3486
|
-
);
|
|
3487
|
-
return uniqueOutputs(outputs);
|
|
3488
|
-
}
|
|
3489
|
-
async getPageDeclarations(manifest, contentIndex) {
|
|
3490
|
-
const existing = this.pageDeclarations.get(manifest);
|
|
3491
|
-
if (existing) return await existing;
|
|
3492
|
-
const declarations = this.resolvePageDeclarations(manifest, contentIndex);
|
|
3493
|
-
this.pageDeclarations.set(manifest, declarations);
|
|
3494
|
-
return await declarations;
|
|
3495
|
-
}
|
|
3496
|
-
async resolvePageDeclarations(manifest, contentIndex) {
|
|
3497
|
-
const declarations = [];
|
|
3498
|
-
const context = { ...this.createContext(contentIndex), manifest };
|
|
3499
|
-
for (const plugin of this.plugins()) {
|
|
3500
|
-
for (const pageType of plugin.pageTypes ?? []) {
|
|
3501
|
-
const paths = pageType.paths;
|
|
3502
|
-
if (!paths) continue;
|
|
3503
|
-
const resolved = typeof paths === "function" ? await paths(this.createPluginContext(plugin, context)) : paths;
|
|
3504
|
-
for (const pathname of resolved) {
|
|
3505
|
-
declarations.push({
|
|
3506
|
-
pathname: normalizePageTypePath(pathname, pageType),
|
|
3507
|
-
plugin,
|
|
3508
|
-
pageType
|
|
3509
|
-
});
|
|
3510
|
-
}
|
|
3511
|
-
}
|
|
3512
|
-
}
|
|
3513
|
-
return declarations;
|
|
3850
|
+
return await this.pageResolver.getPageOutputs(manifest, contentIndex);
|
|
3514
3851
|
}
|
|
3515
3852
|
getContentOutputDependencies() {
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3853
|
+
const plugins = this.plugins();
|
|
3854
|
+
return ({ entry, manifest }) => plugins.flatMap((plugin) => [
|
|
3855
|
+
...plugin.contentOutputDependencies ?? [],
|
|
3856
|
+
...plugin.resolveContentOutputDependencies?.({ entry, manifest }) ?? []
|
|
3857
|
+
]);
|
|
3519
3858
|
}
|
|
3520
3859
|
async collectDiagnostics(contentIndex) {
|
|
3521
3860
|
const tracer = this.observability().tracer;
|
|
@@ -3550,28 +3889,17 @@ var PluginRuntime = class {
|
|
|
3550
3889
|
const context = this.createContext(contentIndex);
|
|
3551
3890
|
const createPluginContext = (plugin) => this.createPluginContext(plugin, context);
|
|
3552
3891
|
const stages = [
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
},
|
|
3561
|
-
{
|
|
3562
|
-
stage: "onConfigResolved",
|
|
3563
|
-
run: () => this.runHook(
|
|
3564
|
-
"onConfigResolved",
|
|
3565
|
-
(plugin) => plugin.onConfigResolved,
|
|
3566
|
-
context
|
|
3567
|
-
)
|
|
3568
|
-
}
|
|
3892
|
+
() => runSetup(this.plugins(), createPluginContext),
|
|
3893
|
+
() => runBuildStart(this.plugins(), createPluginContext),
|
|
3894
|
+
() => this.runHook(
|
|
3895
|
+
"onConfigResolved",
|
|
3896
|
+
(plugin) => plugin.onConfigResolved,
|
|
3897
|
+
context
|
|
3898
|
+
)
|
|
3569
3899
|
];
|
|
3570
|
-
for (const
|
|
3571
|
-
this.buildStage = stage;
|
|
3900
|
+
for (const run of stages) {
|
|
3572
3901
|
await run();
|
|
3573
3902
|
}
|
|
3574
|
-
this.buildStage = "started";
|
|
3575
3903
|
}
|
|
3576
3904
|
createContext(contentIndex) {
|
|
3577
3905
|
return {
|
|
@@ -3639,101 +3967,8 @@ function deepFreeze(value) {
|
|
|
3639
3967
|
for (const property of Object.values(value)) deepFreeze(property);
|
|
3640
3968
|
return value;
|
|
3641
3969
|
}
|
|
3642
|
-
function
|
|
3643
|
-
|
|
3644
|
-
for (const plugin of plugins) {
|
|
3645
|
-
const declaredPageTypes = plugin.pageTypes;
|
|
3646
|
-
if (declaredPageTypes !== void 0 && !Array.isArray(declaredPageTypes)) {
|
|
3647
|
-
throw new TypeError(`Plugin ${plugin.name} pageTypes must be an array`);
|
|
3648
|
-
}
|
|
3649
|
-
for (const pageType of declaredPageTypes ?? []) {
|
|
3650
|
-
validatePageType(plugin.name, pageType);
|
|
3651
|
-
const previous = pageTypes.get(pageType.id);
|
|
3652
|
-
if (previous) {
|
|
3653
|
-
throw new Error(
|
|
3654
|
-
`Plugin page type "${pageType.id}" is provided by both ${previous} and ${plugin.name}`
|
|
3655
|
-
);
|
|
3656
|
-
}
|
|
3657
|
-
pageTypes.set(pageType.id, plugin.name);
|
|
3658
|
-
}
|
|
3659
|
-
}
|
|
3660
|
-
}
|
|
3661
|
-
function validatePageType(pluginName, pageType) {
|
|
3662
|
-
if (typeof pageType !== "object" || pageType === null) {
|
|
3663
|
-
throw new TypeError(`Plugin ${pluginName} pageTypes must contain objects`);
|
|
3664
|
-
}
|
|
3665
|
-
const candidate = pageType;
|
|
3666
|
-
if (typeof candidate.id !== "string" || candidate.id.trim() === "") {
|
|
3667
|
-
throw new TypeError(
|
|
3668
|
-
`Plugin ${pluginName} page type id must be a non-empty string`
|
|
3669
|
-
);
|
|
3670
|
-
}
|
|
3671
|
-
if (typeof candidate.resolve !== "function") {
|
|
3672
|
-
throw new TypeError(
|
|
3673
|
-
`Plugin ${pluginName} page type "${candidate.id}" must provide a resolve function`
|
|
3674
|
-
);
|
|
3675
|
-
}
|
|
3676
|
-
if (candidate.paths !== void 0 && !Array.isArray(candidate.paths) && typeof candidate.paths !== "function") {
|
|
3677
|
-
throw new TypeError(
|
|
3678
|
-
`Plugin ${pluginName} page type "${candidate.id}" paths must be an array or function`
|
|
3679
|
-
);
|
|
3680
|
-
}
|
|
3681
|
-
if (candidate.priority !== void 0 && (typeof candidate.priority !== "number" || !Number.isFinite(candidate.priority))) {
|
|
3682
|
-
throw new TypeError(
|
|
3683
|
-
`Plugin ${pluginName} page type "${candidate.id}" priority must be a finite number`
|
|
3684
|
-
);
|
|
3685
|
-
}
|
|
3686
|
-
if (candidate.outputDependencies !== void 0 && !Array.isArray(candidate.outputDependencies) && typeof candidate.outputDependencies !== "function") {
|
|
3687
|
-
throw new TypeError(
|
|
3688
|
-
`Plugin ${pluginName} page type "${candidate.id}" outputDependencies must be an array or function`
|
|
3689
|
-
);
|
|
3690
|
-
}
|
|
3691
|
-
if (candidate.directoryIndex !== void 0 && typeof candidate.directoryIndex !== "boolean") {
|
|
3692
|
-
throw new TypeError(
|
|
3693
|
-
`Plugin ${pluginName} page type "${candidate.id}" directoryIndex must be a boolean`
|
|
3694
|
-
);
|
|
3695
|
-
}
|
|
3696
|
-
}
|
|
3697
|
-
function normalizePageTypePath(pathname, pageType) {
|
|
3698
|
-
return pageType.directoryIndex ? normalizeDirectoryPagePath(pathname) : normalizePagePath(pathname);
|
|
3699
|
-
}
|
|
3700
|
-
function normalizeDirectoryPagePath(pathname) {
|
|
3701
|
-
const path6 = normalizePagePath(pathname);
|
|
3702
|
-
return path6 === "/" ? path6 : `${path6}/`;
|
|
3703
|
-
}
|
|
3704
|
-
function manifestHookCacheMissReason(cached, pluginName, pipelineFingerprint, inputFingerprint) {
|
|
3705
|
-
if (!cached) return "cache-missing";
|
|
3706
|
-
if (cached.pluginName !== pluginName) return "plugin-order-changed";
|
|
3707
|
-
if (!cached.cacheable) return "uncacheable";
|
|
3708
|
-
if (cached.pipelineFingerprint !== pipelineFingerprint)
|
|
3709
|
-
return "pipeline-fingerprint-changed";
|
|
3710
|
-
if (cached.inputFingerprint !== inputFingerprint)
|
|
3711
|
-
return "manifest-input-changed";
|
|
3712
|
-
return "snapshot-invalid";
|
|
3713
|
-
}
|
|
3714
|
-
function normalizePagePath(pathname) {
|
|
3715
|
-
const path6 = `/${pathname.split("/").filter(Boolean).join("/")}`;
|
|
3716
|
-
return path6 === "/" ? path6 : path6.replace(/\/$/, "");
|
|
3717
|
-
}
|
|
3718
|
-
function countSeverity(diagnostics, severity) {
|
|
3719
|
-
return diagnostics.filter((diagnostic) => diagnostic.severity === severity).length;
|
|
3720
|
-
}
|
|
3721
|
-
function uniqueOutputs(outputs) {
|
|
3722
|
-
const byPath = /* @__PURE__ */ new Map();
|
|
3723
|
-
for (const output of outputs) {
|
|
3724
|
-
const previous = byPath.get(output.output.path);
|
|
3725
|
-
if (!previous || output.priority > previous.priority) {
|
|
3726
|
-
byPath.set(output.output.path, output);
|
|
3727
|
-
continue;
|
|
3728
|
-
}
|
|
3729
|
-
if (output.output.producer === previous.output.producer) continue;
|
|
3730
|
-
if (output.priority === previous.priority) {
|
|
3731
|
-
throw new Error(
|
|
3732
|
-
`Multiple plugin page types declare ${output.output.path} at priority ${output.priority}: ${previous.output.producer}, ${output.output.producer}`
|
|
3733
|
-
);
|
|
3734
|
-
}
|
|
3735
|
-
}
|
|
3736
|
-
return [...byPath.values()].map(({ output }) => output).sort((left, right) => left.path.localeCompare(right.path));
|
|
3970
|
+
function countSeverity(diagnostics, severity) {
|
|
3971
|
+
return diagnostics.filter((diagnostic) => diagnostic.severity === severity).length;
|
|
3737
3972
|
}
|
|
3738
3973
|
|
|
3739
3974
|
// src/utils/error.ts
|
|
@@ -4106,7 +4341,8 @@ var ContentBuildCoordinator = class {
|
|
|
4106
4341
|
{},
|
|
4107
4342
|
() => fingerprintContentEntries(
|
|
4108
4343
|
entries,
|
|
4109
|
-
(entry) => this.dependencies.readEntry(entry)
|
|
4344
|
+
(entry) => this.dependencies.readEntry(entry),
|
|
4345
|
+
this.dependencies.includeFileMetadata
|
|
4110
4346
|
)
|
|
4111
4347
|
);
|
|
4112
4348
|
const previousState = await this.dependencies.observability.tracer.span(
|
|
@@ -4412,7 +4648,7 @@ var ContentLocationResolver = class {
|
|
|
4412
4648
|
this.dependencies.getContentIndex()
|
|
4413
4649
|
]);
|
|
4414
4650
|
await this.dependencies.pluginRuntime.startBuild(contentIndex);
|
|
4415
|
-
const inputs = await
|
|
4651
|
+
const inputs = await mapConcurrent(
|
|
4416
4652
|
entries.filter((entry) => entry.path.endsWith(".md")),
|
|
4417
4653
|
64,
|
|
4418
4654
|
async (entry) => ({
|
|
@@ -4473,24 +4709,52 @@ var ContentLocationResolver = class {
|
|
|
4473
4709
|
function toSlug2(path6) {
|
|
4474
4710
|
return path6.replace(/\.md$/, "");
|
|
4475
4711
|
}
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
const value = values[index];
|
|
4484
|
-
if (value !== void 0) results[index] = await map(value);
|
|
4485
|
-
}
|
|
4712
|
+
|
|
4713
|
+
// src/content/content_manifest_reuse.ts
|
|
4714
|
+
function reuseManifestEntry(input) {
|
|
4715
|
+
const { plan, slug } = input;
|
|
4716
|
+
if (!plan.previousState?.manifestEntries) return null;
|
|
4717
|
+
if (plan.previousState.pipelineFingerprint !== input.pipelineFingerprint) {
|
|
4718
|
+
return null;
|
|
4486
4719
|
}
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4720
|
+
if (plan.affectedContent.direct.has(slug)) return null;
|
|
4721
|
+
if (plan.affectedContent.dependent.has(slug)) return null;
|
|
4722
|
+
const contentCacheFingerprint = input.getContentCacheFingerprint();
|
|
4723
|
+
if (!contentCacheFingerprint) return null;
|
|
4724
|
+
if (plan.previousState.entries[`${slug}.md`]?.processedContentCacheFingerprint !== contentCacheFingerprint) {
|
|
4725
|
+
return null;
|
|
4726
|
+
}
|
|
4727
|
+
const entry = plan.previousManifestEntriesBySlug.get(slug);
|
|
4728
|
+
if (!entry) return null;
|
|
4729
|
+
return {
|
|
4730
|
+
entry: cloneManifestEntry(entry),
|
|
4731
|
+
contentCacheFingerprint
|
|
4732
|
+
};
|
|
4733
|
+
}
|
|
4734
|
+
function cloneManifestEntry(entry) {
|
|
4735
|
+
return {
|
|
4736
|
+
...entry,
|
|
4737
|
+
publicLocation: { ...entry.publicLocation },
|
|
4738
|
+
frontmatter: { ...entry.frontmatter },
|
|
4739
|
+
...entry.file ? {
|
|
4740
|
+
file: {
|
|
4741
|
+
...entry.file.size === void 0 ? {} : { size: entry.file.size },
|
|
4742
|
+
...reviveFileDate(entry.file.ctime, "ctime"),
|
|
4743
|
+
...reviveFileDate(entry.file.mtime, "mtime")
|
|
4744
|
+
}
|
|
4745
|
+
} : {},
|
|
4746
|
+
aliases: [...entry.aliases ?? []],
|
|
4747
|
+
tags: [...entry.tags],
|
|
4748
|
+
links: entry.links.map((link) => ({ ...link })),
|
|
4749
|
+
backlinks: [...entry.backlinks],
|
|
4750
|
+
assets: entry.assets.map((asset) => ({ ...asset })),
|
|
4751
|
+
...entry.headTags ? { headTags: [...entry.headTags] } : {}
|
|
4752
|
+
};
|
|
4753
|
+
}
|
|
4754
|
+
function reviveFileDate(value, key) {
|
|
4755
|
+
if (value === void 0) return {};
|
|
4756
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
4757
|
+
return Number.isNaN(date.getTime()) ? {} : { [key]: date };
|
|
4494
4758
|
}
|
|
4495
4759
|
|
|
4496
4760
|
// src/content/file_system_content_source.ts
|
|
@@ -4583,12 +4847,25 @@ var FileSystemContentSource = class {
|
|
|
4583
4847
|
continue;
|
|
4584
4848
|
}
|
|
4585
4849
|
if (!directoryEntry.isFile()) continue;
|
|
4586
|
-
|
|
4850
|
+
let stats;
|
|
4851
|
+
try {
|
|
4852
|
+
stats = await fs4.stat(filePath);
|
|
4853
|
+
} catch (error) {
|
|
4854
|
+
if (isFileNotFoundError(error)) continue;
|
|
4855
|
+
throw new Error(
|
|
4856
|
+
`Could not read content file metadata: ${logicalPath}`,
|
|
4857
|
+
{ cause: error }
|
|
4858
|
+
);
|
|
4859
|
+
}
|
|
4860
|
+
const createdAt = validTimestamp(stats.birthtimeMs, logicalPath, "ctime");
|
|
4861
|
+
const modifiedAt = validTimestamp(stats.mtimeMs, logicalPath, "mtime");
|
|
4862
|
+
const size = validSize(stats.size, logicalPath);
|
|
4587
4863
|
entries.push({
|
|
4588
4864
|
path: logicalPath,
|
|
4589
4865
|
metadata: {
|
|
4590
|
-
|
|
4591
|
-
|
|
4866
|
+
createdAt,
|
|
4867
|
+
modifiedAt,
|
|
4868
|
+
size
|
|
4592
4869
|
}
|
|
4593
4870
|
});
|
|
4594
4871
|
}
|
|
@@ -4661,6 +4938,14 @@ function isMissingDirectoryError(error, directory) {
|
|
|
4661
4938
|
function isFileNotFoundError(error) {
|
|
4662
4939
|
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
4663
4940
|
}
|
|
4941
|
+
function validTimestamp(value, logicalPath, name) {
|
|
4942
|
+
if (Number.isFinite(value)) return value;
|
|
4943
|
+
throw new Error(`Invalid ${name} metadata for content file: ${logicalPath}`);
|
|
4944
|
+
}
|
|
4945
|
+
function validSize(value, logicalPath) {
|
|
4946
|
+
if (Number.isSafeInteger(value) && value >= 0) return value;
|
|
4947
|
+
throw new Error(`Invalid size metadata for content file: ${logicalPath}`);
|
|
4948
|
+
}
|
|
4664
4949
|
function missingContentDirectoryError(directory) {
|
|
4665
4950
|
const relative = path4.relative(process.cwd(), directory);
|
|
4666
4951
|
const display = relative && !relative.startsWith("..") ? relative : directory;
|
|
@@ -4696,7 +4981,7 @@ function readOptionalStableId(value) {
|
|
|
4696
4981
|
|
|
4697
4982
|
// src/content/manifest_builder.ts
|
|
4698
4983
|
var ManifestBuilder = class {
|
|
4699
|
-
createEntry(slug, markdown, processed, contentIndex, location, publishing) {
|
|
4984
|
+
createEntry(slug, markdown, processed, contentIndex, location, publishing, sourceMetadata) {
|
|
4700
4985
|
const links = extractContentLinks(markdown, contentIndex);
|
|
4701
4986
|
const assets = [
|
|
4702
4987
|
...links.filter(
|
|
@@ -4713,6 +4998,7 @@ var ManifestBuilder = class {
|
|
|
4713
4998
|
permalink: location.permalink,
|
|
4714
4999
|
publicLocation: location,
|
|
4715
5000
|
title: getManifestTitle(slug, processed.frontmatter.title),
|
|
5001
|
+
file: toFileMetadata(sourceMetadata),
|
|
4716
5002
|
aliases: extractFrontmatterAliases(markdown),
|
|
4717
5003
|
frontmatter: processed.frontmatter,
|
|
4718
5004
|
publishing,
|
|
@@ -4779,6 +5065,21 @@ var ManifestBuilder = class {
|
|
|
4779
5065
|
return { ...manifest, graph: createContentGraph(manifest) };
|
|
4780
5066
|
}
|
|
4781
5067
|
};
|
|
5068
|
+
function toFileMetadata(metadata) {
|
|
5069
|
+
return {
|
|
5070
|
+
...validSize2(metadata?.size) ? { size: metadata.size } : {},
|
|
5071
|
+
...toDate(metadata?.createdAt, "ctime"),
|
|
5072
|
+
...toDate(metadata?.modifiedAt, "mtime")
|
|
5073
|
+
};
|
|
5074
|
+
}
|
|
5075
|
+
function validSize2(value) {
|
|
5076
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
5077
|
+
}
|
|
5078
|
+
function toDate(value, key) {
|
|
5079
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return {};
|
|
5080
|
+
const date = new Date(value);
|
|
5081
|
+
return Number.isNaN(date.getTime()) ? {} : { [key]: date };
|
|
5082
|
+
}
|
|
4782
5083
|
function createAliasIndex(entries) {
|
|
4783
5084
|
const byAlias = /* @__PURE__ */ new Map();
|
|
4784
5085
|
for (const entry of entries) {
|
|
@@ -4823,6 +5124,64 @@ function appendUniqueToMap(map, key, value) {
|
|
|
4823
5124
|
map.set(key, values);
|
|
4824
5125
|
}
|
|
4825
5126
|
|
|
5127
|
+
// src/content/manifest_assembler.ts
|
|
5128
|
+
var ManifestAssembler = class {
|
|
5129
|
+
constructor(contentCache) {
|
|
5130
|
+
this.contentCache = contentCache;
|
|
5131
|
+
}
|
|
5132
|
+
contentCache;
|
|
5133
|
+
builder = new ManifestBuilder();
|
|
5134
|
+
createEntry(...args) {
|
|
5135
|
+
return this.builder.createEntry(...args);
|
|
5136
|
+
}
|
|
5137
|
+
build(...args) {
|
|
5138
|
+
return this.builder.build(...args);
|
|
5139
|
+
}
|
|
5140
|
+
applyPublicView(manifest, strategy, publishingBuildTime) {
|
|
5141
|
+
for (const entry of manifest.entries) {
|
|
5142
|
+
entry.publishing = resolvePublishingState(entry.frontmatter, {
|
|
5143
|
+
strategy,
|
|
5144
|
+
buildTime: publishingBuildTime
|
|
5145
|
+
});
|
|
5146
|
+
}
|
|
5147
|
+
manifest.publicEntries = manifest.entries.filter(
|
|
5148
|
+
(entry) => entry.publishing.routable
|
|
5149
|
+
);
|
|
5150
|
+
manifest.discoverableEntries = manifest.entries.filter(
|
|
5151
|
+
(entry) => entry.publishing.discoverable
|
|
5152
|
+
);
|
|
5153
|
+
const publicSlugs = new Set(
|
|
5154
|
+
manifest.publicEntries.map((entry) => entry.slug)
|
|
5155
|
+
);
|
|
5156
|
+
const publicPermalinks = new Map(
|
|
5157
|
+
manifest.publicEntries.map((entry) => [entry.permalink, entry])
|
|
5158
|
+
);
|
|
5159
|
+
manifest.byRoutablePermalink = publicPermalinks;
|
|
5160
|
+
manifest.publicRedirects = new Map(
|
|
5161
|
+
[...manifest.redirects].filter(
|
|
5162
|
+
([, redirect]) => publicSlugs.has(redirect.slug)
|
|
5163
|
+
)
|
|
5164
|
+
);
|
|
5165
|
+
}
|
|
5166
|
+
synchronizeHtml(manifest, preHookEntries) {
|
|
5167
|
+
const preHookHtml = new Map(
|
|
5168
|
+
preHookEntries.map((entry) => [entry.slug, entry.html])
|
|
5169
|
+
);
|
|
5170
|
+
for (const entry of manifest.entries) {
|
|
5171
|
+
const changed = preHookHtml.get(entry.slug) !== entry.html;
|
|
5172
|
+
const cached = this.contentCache.get(entry.slug);
|
|
5173
|
+
if (cached) {
|
|
5174
|
+
if (changed) cached.html = entry.html;
|
|
5175
|
+
continue;
|
|
5176
|
+
}
|
|
5177
|
+
this.contentCache.set(entry.slug, {
|
|
5178
|
+
frontmatter: entry.frontmatter,
|
|
5179
|
+
html: entry.html
|
|
5180
|
+
});
|
|
5181
|
+
}
|
|
5182
|
+
}
|
|
5183
|
+
};
|
|
5184
|
+
|
|
4826
5185
|
// src/content/output_dependency.ts
|
|
4827
5186
|
function determineOutputChanges(input) {
|
|
4828
5187
|
const current = buildOutputInventory(
|
|
@@ -4833,7 +5192,16 @@ function determineOutputChanges(input) {
|
|
|
4833
5192
|
const previous = new Map(
|
|
4834
5193
|
(input.previousState?.outputs ?? []).map((output) => [output.path, output])
|
|
4835
5194
|
);
|
|
5195
|
+
const currentPublicLocations = publicLocationOwners(
|
|
5196
|
+
input.manifest.publicEntries
|
|
5197
|
+
);
|
|
5198
|
+
const previousPublicLocations = publicLocationOwners(
|
|
5199
|
+
(input.previousState?.manifestEntries ?? []).filter(
|
|
5200
|
+
(entry) => entry.publishing.routable
|
|
5201
|
+
)
|
|
5202
|
+
);
|
|
4836
5203
|
const changedContent = collectChangedContent(input);
|
|
5204
|
+
const refreshedContent = collectRefreshedContent(input);
|
|
4837
5205
|
const changedFiles = /* @__PURE__ */ new Set([
|
|
4838
5206
|
...input.changeSet.added,
|
|
4839
5207
|
...input.changeSet.changed,
|
|
@@ -4855,13 +5223,18 @@ function determineOutputChanges(input) {
|
|
|
4855
5223
|
const unchanged = [];
|
|
4856
5224
|
for (const output of current) {
|
|
4857
5225
|
if (isOutputAffected(
|
|
4858
|
-
output,
|
|
5226
|
+
withPreviousDependencies(output, previous.get(output.path)),
|
|
4859
5227
|
changedContent,
|
|
5228
|
+
refreshedContent,
|
|
4860
5229
|
changedTags,
|
|
4861
5230
|
changedFolders,
|
|
4862
5231
|
changedFiles,
|
|
4863
5232
|
changedExternal,
|
|
4864
|
-
changedExternalScopes
|
|
5233
|
+
changedExternalScopes,
|
|
5234
|
+
currentPublicLocations,
|
|
5235
|
+
previousPublicLocations,
|
|
5236
|
+
input.previousState?.manifestEntries ?? [],
|
|
5237
|
+
input.manifest.entries
|
|
4865
5238
|
)) {
|
|
4866
5239
|
affected.set(output.path, output);
|
|
4867
5240
|
} else {
|
|
@@ -4897,7 +5270,10 @@ function determineOutputChanges(input) {
|
|
|
4897
5270
|
function buildOutputInventory(manifest, pluginPageOutputs = [], contentOutputDependencies = []) {
|
|
4898
5271
|
const outputs = /* @__PURE__ */ new Map();
|
|
4899
5272
|
for (const entry of manifest.publicEntries) {
|
|
4900
|
-
addOutput(
|
|
5273
|
+
addOutput(
|
|
5274
|
+
outputs,
|
|
5275
|
+
contentOutput(entry, manifest, contentOutputDependencies)
|
|
5276
|
+
);
|
|
4901
5277
|
}
|
|
4902
5278
|
for (const [path6, redirect] of manifest.publicRedirects) {
|
|
4903
5279
|
addOutput(outputs, {
|
|
@@ -4930,21 +5306,26 @@ function htmlOutputPath(pathname) {
|
|
|
4930
5306
|
if (pathname.endsWith("/")) return `${withoutSlash}/index.html`;
|
|
4931
5307
|
return `${withoutSlash}.html`;
|
|
4932
5308
|
}
|
|
4933
|
-
function contentOutput(entry, dependencies) {
|
|
5309
|
+
function contentOutput(entry, manifest, dependencies) {
|
|
4934
5310
|
return {
|
|
4935
5311
|
kind: "content",
|
|
4936
5312
|
path: htmlOutputPath(entry.permalink),
|
|
4937
5313
|
producer: `content:${entry.slug}`,
|
|
4938
|
-
dependencies: [
|
|
5314
|
+
dependencies: [
|
|
5315
|
+
{ type: "content", slug: entry.slug },
|
|
5316
|
+
...resolveContentOutputDependencies(dependencies, entry, manifest)
|
|
5317
|
+
]
|
|
4939
5318
|
};
|
|
4940
5319
|
}
|
|
4941
|
-
function isOutputAffected(output, changedContent, changedTags, changedFolders, changedFiles, changedExternal, changedExternalScopes) {
|
|
5320
|
+
function isOutputAffected(output, changedContent, refreshedContent, changedTags, changedFolders, changedFiles, changedExternal, changedExternalScopes, currentPublicLocations, previousPublicLocations, previousEntries, currentEntries) {
|
|
4942
5321
|
if (isAffectedByExternalScope(output, changedExternalScopes)) return true;
|
|
4943
5322
|
for (const dependency of output.dependencies) {
|
|
4944
5323
|
if (dependency.type === "unknown") return true;
|
|
4945
5324
|
if (dependency.type === "external" && changedExternal.has(dependency.id))
|
|
4946
5325
|
return true;
|
|
4947
5326
|
if (dependency.type === "global") return changedContent.size > 0;
|
|
5327
|
+
if (dependency.type === "content-query" && isContentQueryAffected(dependency, previousEntries, currentEntries))
|
|
5328
|
+
return true;
|
|
4948
5329
|
if (dependency.type === "projection" && (dependency.projection === "navigation" || dependency.projection === "recent-posts")) {
|
|
4949
5330
|
return changedContent.size > 0;
|
|
4950
5331
|
}
|
|
@@ -4954,7 +5335,7 @@ function isOutputAffected(output, changedContent, changedTags, changedFolders, c
|
|
|
4954
5335
|
if (dependency.type === "projection" && dependency.projection === "taxonomy-folders") {
|
|
4955
5336
|
return changedFolders.size > 0;
|
|
4956
5337
|
}
|
|
4957
|
-
if (dependency.type === "content" && changedContent.has(dependency.slug)) {
|
|
5338
|
+
if (dependency.type === "content" && (changedContent.has(dependency.slug) || refreshedContent.has(dependency.slug))) {
|
|
4958
5339
|
return true;
|
|
4959
5340
|
}
|
|
4960
5341
|
if (dependency.type === "tag" && changedTags.has(dependency.tag))
|
|
@@ -4965,9 +5346,98 @@ function isOutputAffected(output, changedContent, changedTags, changedFolders, c
|
|
|
4965
5346
|
if (dependency.type === "file" && changedFiles.has(dependency.path)) {
|
|
4966
5347
|
return true;
|
|
4967
5348
|
}
|
|
5349
|
+
if (dependency.type === "public-location") {
|
|
5350
|
+
const currentOwner = currentPublicLocations.get(
|
|
5351
|
+
normalizePublicPath(dependency.pathname)
|
|
5352
|
+
);
|
|
5353
|
+
const previousOwner = previousPublicLocations.get(
|
|
5354
|
+
normalizePublicPath(dependency.pathname)
|
|
5355
|
+
);
|
|
5356
|
+
if (currentOwner?.slug !== previousOwner?.slug) return true;
|
|
5357
|
+
if (currentOwner && (changedContent.has(currentOwner.slug) || refreshedContent.has(currentOwner.slug)) || previousOwner && (changedContent.has(previousOwner.slug) || refreshedContent.has(previousOwner.slug))) {
|
|
5358
|
+
return true;
|
|
5359
|
+
}
|
|
5360
|
+
}
|
|
5361
|
+
}
|
|
5362
|
+
return false;
|
|
5363
|
+
}
|
|
5364
|
+
function isContentQueryAffected(dependency, previousEntries, currentEntries) {
|
|
5365
|
+
const previous = new Map(previousEntries.map((entry) => [entry.slug, entry]));
|
|
5366
|
+
const current = new Map(currentEntries.map((entry) => [entry.slug, entry]));
|
|
5367
|
+
const slugs = /* @__PURE__ */ new Set([...previous.keys(), ...current.keys()]);
|
|
5368
|
+
for (const slug of slugs) {
|
|
5369
|
+
const before = previous.get(slug);
|
|
5370
|
+
const after = current.get(slug);
|
|
5371
|
+
if (!before || !after) {
|
|
5372
|
+
if ((before ?? after)?.publishing.discoverable) return true;
|
|
5373
|
+
continue;
|
|
5374
|
+
}
|
|
5375
|
+
if (before.publishing.discoverable !== after.publishing.discoverable)
|
|
5376
|
+
return true;
|
|
5377
|
+
if (!before.publishing.discoverable && !after.publishing.discoverable)
|
|
5378
|
+
continue;
|
|
5379
|
+
if (dependency.unknown || dependency.properties.some(
|
|
5380
|
+
(property) => propertyChanged(before, after, property)
|
|
5381
|
+
))
|
|
5382
|
+
return true;
|
|
4968
5383
|
}
|
|
4969
5384
|
return false;
|
|
4970
5385
|
}
|
|
5386
|
+
function propertyChanged(before, after, property) {
|
|
5387
|
+
if (property === "*")
|
|
5388
|
+
return JSON.stringify(before.frontmatter) !== JSON.stringify(after.frontmatter);
|
|
5389
|
+
return JSON.stringify(contentQueryValue(before, property)) !== JSON.stringify(contentQueryValue(after, property));
|
|
5390
|
+
}
|
|
5391
|
+
function contentQueryValue(entry, property) {
|
|
5392
|
+
switch (property) {
|
|
5393
|
+
case "file.name":
|
|
5394
|
+
return `${entry.slug.split("/").at(-1) ?? entry.slug}.md`;
|
|
5395
|
+
case "file.path":
|
|
5396
|
+
return `${entry.slug}.md`;
|
|
5397
|
+
case "file.slug":
|
|
5398
|
+
return entry.slug;
|
|
5399
|
+
case "file.folder":
|
|
5400
|
+
return entry.slug.split("/").slice(0, -1).join("/");
|
|
5401
|
+
case "file.title":
|
|
5402
|
+
return entry.title;
|
|
5403
|
+
case "file.link":
|
|
5404
|
+
case "file.permalink":
|
|
5405
|
+
return entry.permalink;
|
|
5406
|
+
case "file.tags":
|
|
5407
|
+
return entry.tags;
|
|
5408
|
+
case "file.size":
|
|
5409
|
+
return entry.file?.size;
|
|
5410
|
+
case "file.ctime":
|
|
5411
|
+
return entry.file?.ctime;
|
|
5412
|
+
case "file.mtime":
|
|
5413
|
+
return entry.file?.mtime;
|
|
5414
|
+
case "file.links":
|
|
5415
|
+
return entry.links;
|
|
5416
|
+
default:
|
|
5417
|
+
return property.split(".").reduce(
|
|
5418
|
+
(value, key) => value && typeof value === "object" && Object.hasOwn(value, key) ? value[key] : void 0,
|
|
5419
|
+
entry.frontmatter
|
|
5420
|
+
);
|
|
5421
|
+
}
|
|
5422
|
+
}
|
|
5423
|
+
function resolveContentOutputDependencies(dependencies, entry, manifest) {
|
|
5424
|
+
return typeof dependencies === "function" ? dependencies({ entry, manifest }) : dependencies;
|
|
5425
|
+
}
|
|
5426
|
+
function withPreviousDependencies(output, previous) {
|
|
5427
|
+
if (!previous || previous.kind !== output.kind) return output;
|
|
5428
|
+
return {
|
|
5429
|
+
...output,
|
|
5430
|
+
dependencies: [...output.dependencies, ...previous.dependencies]
|
|
5431
|
+
};
|
|
5432
|
+
}
|
|
5433
|
+
function publicLocationOwners(entries) {
|
|
5434
|
+
return new Map(
|
|
5435
|
+
entries.map((entry) => [
|
|
5436
|
+
normalizePublicPath(entry.publicLocation.permalink),
|
|
5437
|
+
entry
|
|
5438
|
+
])
|
|
5439
|
+
);
|
|
5440
|
+
}
|
|
4971
5441
|
function isAffectedByExternalScope(output, changedExternalScopes) {
|
|
4972
5442
|
for (const scope of changedExternalScopes.values()) {
|
|
4973
5443
|
if (scope.type === "all") return true;
|
|
@@ -5014,6 +5484,21 @@ function changedExternalDependencyScopes(input) {
|
|
|
5014
5484
|
}
|
|
5015
5485
|
return changed;
|
|
5016
5486
|
}
|
|
5487
|
+
function collectRefreshedContent(input) {
|
|
5488
|
+
const refreshed = /* @__PURE__ */ new Set();
|
|
5489
|
+
const fingerprints = input.contentFingerprints;
|
|
5490
|
+
const previousEntries = input.previousState?.entries;
|
|
5491
|
+
if (!fingerprints || !previousEntries) return refreshed;
|
|
5492
|
+
for (const entry of input.manifest.entries) {
|
|
5493
|
+
const fingerprint = fingerprints.get(entry.slug);
|
|
5494
|
+
if (fingerprint === void 0) continue;
|
|
5495
|
+
if (previousEntries[`${entry.slug}.md`]?.processedContentCacheFingerprint === fingerprint) {
|
|
5496
|
+
continue;
|
|
5497
|
+
}
|
|
5498
|
+
refreshed.add(entry.slug);
|
|
5499
|
+
}
|
|
5500
|
+
return refreshed;
|
|
5501
|
+
}
|
|
5017
5502
|
function collectChangedContent(input) {
|
|
5018
5503
|
const changed = /* @__PURE__ */ new Set([
|
|
5019
5504
|
...input.affectedContent.direct,
|
|
@@ -5141,6 +5626,7 @@ var ContentManager = class {
|
|
|
5141
5626
|
}
|
|
5142
5627
|
};
|
|
5143
5628
|
this.contentIndexBuilder = new ContentIndexBuilder(this.source);
|
|
5629
|
+
this.manifestAssembler = new ManifestAssembler(this.contentCache);
|
|
5144
5630
|
this.pluginRuntime = new PluginRuntime(this.pipelineOptions);
|
|
5145
5631
|
const buildStatePath = resolveContentBuildStatePath(
|
|
5146
5632
|
pipelineOptions.config,
|
|
@@ -5159,13 +5645,16 @@ var ContentManager = class {
|
|
|
5159
5645
|
buildStatePath,
|
|
5160
5646
|
getEntries: () => this.entryReader.getEntries(),
|
|
5161
5647
|
readEntry: (entry) => this.entryReader.read(entry),
|
|
5162
|
-
observability
|
|
5648
|
+
observability,
|
|
5649
|
+
includeFileMetadata: this.pipelineOptions.plugins?.some(
|
|
5650
|
+
(plugin) => plugin && plugin.contentFileMetadata === true
|
|
5651
|
+
) ?? false
|
|
5163
5652
|
});
|
|
5164
5653
|
}
|
|
5165
5654
|
pipelineOptions;
|
|
5166
5655
|
source;
|
|
5167
5656
|
contentIndexBuilder;
|
|
5168
|
-
|
|
5657
|
+
manifestAssembler;
|
|
5169
5658
|
pluginRuntime;
|
|
5170
5659
|
contentIndex = null;
|
|
5171
5660
|
contentIndexAmbiguities = /* @__PURE__ */ new Map();
|
|
@@ -5327,10 +5816,13 @@ var ContentManager = class {
|
|
|
5327
5816
|
this.getAllPosts(),
|
|
5328
5817
|
this.getContentIndex(plan)
|
|
5329
5818
|
]);
|
|
5819
|
+
const entriesByPath = new Map(
|
|
5820
|
+
(await this.scan()).map((entry) => [entry.path, entry])
|
|
5821
|
+
);
|
|
5330
5822
|
const entries = await this.observability().tracer.span(
|
|
5331
5823
|
"content.manifest.entries",
|
|
5332
5824
|
{},
|
|
5333
|
-
() =>
|
|
5825
|
+
() => mapConcurrent(
|
|
5334
5826
|
posts,
|
|
5335
5827
|
CONTENT_PROCESSING_CONCURRENCY,
|
|
5336
5828
|
async (post) => {
|
|
@@ -5348,7 +5840,7 @@ var ContentManager = class {
|
|
|
5348
5840
|
`Content public location was not resolved: ${post.slug}`
|
|
5349
5841
|
);
|
|
5350
5842
|
}
|
|
5351
|
-
return this.
|
|
5843
|
+
return this.manifestAssembler.createEntry(
|
|
5352
5844
|
post.slug,
|
|
5353
5845
|
rawPost,
|
|
5354
5846
|
processed,
|
|
@@ -5357,7 +5849,8 @@ var ContentManager = class {
|
|
|
5357
5849
|
resolvePublishingState(processed.frontmatter, {
|
|
5358
5850
|
strategy: this.publishStrategy(),
|
|
5359
5851
|
buildTime: this.publishingBuildTime
|
|
5360
|
-
})
|
|
5852
|
+
}),
|
|
5853
|
+
entriesByPath.get(`${post.slug}.md`)?.metadata
|
|
5361
5854
|
);
|
|
5362
5855
|
}
|
|
5363
5856
|
)
|
|
@@ -5366,7 +5859,7 @@ var ContentManager = class {
|
|
|
5366
5859
|
const manifest = await this.observability().tracer.span(
|
|
5367
5860
|
"content.graph",
|
|
5368
5861
|
{},
|
|
5369
|
-
() => this.
|
|
5862
|
+
() => this.manifestAssembler.build(
|
|
5370
5863
|
entries,
|
|
5371
5864
|
contentIndex,
|
|
5372
5865
|
this.contentIndexAmbiguities
|
|
@@ -5374,7 +5867,11 @@ var ContentManager = class {
|
|
|
5374
5867
|
);
|
|
5375
5868
|
const cacheManifestEntries = structuredClone(manifest.entries);
|
|
5376
5869
|
this.locationResolver.populateRedirects(manifest, locations);
|
|
5377
|
-
this.applyPublicView(
|
|
5870
|
+
this.manifestAssembler.applyPublicView(
|
|
5871
|
+
manifest,
|
|
5872
|
+
this.publishStrategy(),
|
|
5873
|
+
this.publishingBuildTime
|
|
5874
|
+
);
|
|
5378
5875
|
manifest.pagePaths = [
|
|
5379
5876
|
...await this.pluginRuntime.getPagePaths(manifest, contentIndex)
|
|
5380
5877
|
];
|
|
@@ -5387,7 +5884,7 @@ var ContentManager = class {
|
|
|
5387
5884
|
plan.previousState?.manifestHookCache,
|
|
5388
5885
|
this.getPipelineFingerprint()
|
|
5389
5886
|
);
|
|
5390
|
-
this.
|
|
5887
|
+
this.manifestAssembler.synchronizeHtml(manifest, cacheManifestEntries);
|
|
5391
5888
|
manifest.assets = this.pluginRuntime.collectAssets();
|
|
5392
5889
|
manifest.clientEntries = this.pluginRuntime.collectClientEntries();
|
|
5393
5890
|
manifest.diagnostics = [
|
|
@@ -5414,7 +5911,8 @@ var ContentManager = class {
|
|
|
5414
5911
|
pluginPageOutputs,
|
|
5415
5912
|
contentOutputDependencies: this.pluginRuntime.getContentOutputDependencies(),
|
|
5416
5913
|
externalDependencies: plan.externalDependencies,
|
|
5417
|
-
externalDependencyFailures: plan.externalDependencyFailures
|
|
5914
|
+
externalDependencyFailures: plan.externalDependencyFailures,
|
|
5915
|
+
contentFingerprints: this.processedContentCacheFingerprints
|
|
5418
5916
|
})
|
|
5419
5917
|
);
|
|
5420
5918
|
if (this.outputChangeSet) {
|
|
@@ -5565,74 +6063,24 @@ var ContentManager = class {
|
|
|
5565
6063
|
async getContentLocations() {
|
|
5566
6064
|
return await this.locationResolver.getLocations();
|
|
5567
6065
|
}
|
|
5568
|
-
applyPublicView(manifest) {
|
|
5569
|
-
const strategy = this.publishStrategy();
|
|
5570
|
-
for (const entry of manifest.entries) {
|
|
5571
|
-
entry.publishing = resolvePublishingState(entry.frontmatter, {
|
|
5572
|
-
strategy,
|
|
5573
|
-
buildTime: this.publishingBuildTime
|
|
5574
|
-
});
|
|
5575
|
-
}
|
|
5576
|
-
manifest.publicEntries = manifest.entries.filter(
|
|
5577
|
-
(entry) => entry.publishing.routable
|
|
5578
|
-
);
|
|
5579
|
-
manifest.discoverableEntries = manifest.entries.filter(
|
|
5580
|
-
(entry) => entry.publishing.discoverable
|
|
5581
|
-
);
|
|
5582
|
-
const publicSlugs = new Set(
|
|
5583
|
-
manifest.publicEntries.map((entry) => entry.slug)
|
|
5584
|
-
);
|
|
5585
|
-
const publicPermalinks = new Map(
|
|
5586
|
-
manifest.publicEntries.map((entry) => [entry.permalink, entry])
|
|
5587
|
-
);
|
|
5588
|
-
manifest.byRoutablePermalink = publicPermalinks;
|
|
5589
|
-
manifest.publicRedirects = new Map(
|
|
5590
|
-
[...manifest.redirects].filter(
|
|
5591
|
-
([, redirect]) => publicSlugs.has(redirect.slug)
|
|
5592
|
-
)
|
|
5593
|
-
);
|
|
5594
|
-
}
|
|
5595
|
-
synchronizeManifestHtml(manifest, preHookEntries) {
|
|
5596
|
-
const preHookHtml = new Map(
|
|
5597
|
-
preHookEntries.map((entry) => [entry.slug, entry.html])
|
|
5598
|
-
);
|
|
5599
|
-
for (const entry of manifest.entries) {
|
|
5600
|
-
const changed = preHookHtml.get(entry.slug) !== entry.html;
|
|
5601
|
-
const cached = this.contentCache.get(entry.slug);
|
|
5602
|
-
if (cached) {
|
|
5603
|
-
if (changed) cached.html = entry.html;
|
|
5604
|
-
continue;
|
|
5605
|
-
}
|
|
5606
|
-
this.contentCache.set(entry.slug, {
|
|
5607
|
-
frontmatter: entry.frontmatter,
|
|
5608
|
-
html: entry.html
|
|
5609
|
-
});
|
|
5610
|
-
}
|
|
5611
|
-
}
|
|
5612
6066
|
publishStrategy() {
|
|
5613
6067
|
return this.pipelineOptions.config?.content.filters.publishStrategy ?? "explicit";
|
|
5614
6068
|
}
|
|
5615
6069
|
reuseManifestEntry(slug, source, plan) {
|
|
5616
|
-
|
|
5617
|
-
if (plan.previousState.pipelineFingerprint !== this.getPipelineFingerprint()) {
|
|
5618
|
-
return null;
|
|
5619
|
-
}
|
|
5620
|
-
if (plan.affectedContent.direct.has(slug)) return null;
|
|
5621
|
-
if (plan.affectedContent.dependent.has(slug)) return null;
|
|
5622
|
-
const contentCacheFingerprint = this.getProcessedContentCacheFingerprint(
|
|
6070
|
+
const reused = reuseManifestEntry({
|
|
5623
6071
|
slug,
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
}
|
|
5630
|
-
const entry = plan.previousManifestEntriesBySlug.get(slug);
|
|
5631
|
-
if (!entry) return null;
|
|
6072
|
+
plan,
|
|
6073
|
+
pipelineFingerprint: this.getPipelineFingerprint(),
|
|
6074
|
+
getContentCacheFingerprint: () => this.getProcessedContentCacheFingerprint(slug, source)
|
|
6075
|
+
});
|
|
6076
|
+
if (!reused) return null;
|
|
5632
6077
|
this.reusedContentCount += 1;
|
|
5633
|
-
this.processedContentCacheFingerprints.set(
|
|
6078
|
+
this.processedContentCacheFingerprints.set(
|
|
6079
|
+
slug,
|
|
6080
|
+
reused.contentCacheFingerprint
|
|
6081
|
+
);
|
|
5634
6082
|
this.observability().tracer.event("content.reuse", { slug });
|
|
5635
|
-
return
|
|
6083
|
+
return reused.entry;
|
|
5636
6084
|
}
|
|
5637
6085
|
getPipelineFingerprint() {
|
|
5638
6086
|
if (!this.pipelineOptions.config) return void 0;
|
|
@@ -5679,6 +6127,7 @@ var ContentManager = class {
|
|
|
5679
6127
|
return plan;
|
|
5680
6128
|
}
|
|
5681
6129
|
async createBuildPlan(options) {
|
|
6130
|
+
this.enableBuildTime();
|
|
5682
6131
|
const config = this.pipelineOptions.config;
|
|
5683
6132
|
if (config && hasGlobalContentDependency(config)) {
|
|
5684
6133
|
this.pipelineFingerprint = this.computePipelineFingerprint(
|
|
@@ -5741,7 +6190,7 @@ var ContentManager = class {
|
|
|
5741
6190
|
const strategy = this.publishStrategy();
|
|
5742
6191
|
const posts = await this.getAllPosts();
|
|
5743
6192
|
const routableSlugs = /* @__PURE__ */ new Set();
|
|
5744
|
-
await
|
|
6193
|
+
await mapConcurrent(posts, CONTENT_PROCESSING_CONCURRENCY, async (post) => {
|
|
5745
6194
|
const markdown = await this.getPost(post.slug);
|
|
5746
6195
|
let frontmatter;
|
|
5747
6196
|
try {
|
|
@@ -5785,38 +6234,6 @@ function readFrontmatter(markdown) {
|
|
|
5785
6234
|
matter3(file);
|
|
5786
6235
|
return file.data.matter ?? {};
|
|
5787
6236
|
}
|
|
5788
|
-
function cloneManifestEntry(entry) {
|
|
5789
|
-
return {
|
|
5790
|
-
...entry,
|
|
5791
|
-
publicLocation: { ...entry.publicLocation },
|
|
5792
|
-
frontmatter: { ...entry.frontmatter },
|
|
5793
|
-
aliases: [...entry.aliases ?? []],
|
|
5794
|
-
tags: [...entry.tags],
|
|
5795
|
-
links: entry.links.map((link) => ({ ...link })),
|
|
5796
|
-
backlinks: [...entry.backlinks],
|
|
5797
|
-
assets: entry.assets.map((asset) => ({ ...asset })),
|
|
5798
|
-
...entry.headTags ? { headTags: [...entry.headTags] } : {}
|
|
5799
|
-
};
|
|
5800
|
-
}
|
|
5801
|
-
async function mapConcurrent3(values, concurrency, map) {
|
|
5802
|
-
const results = new Array(values.length);
|
|
5803
|
-
let nextIndex = 0;
|
|
5804
|
-
async function worker() {
|
|
5805
|
-
while (nextIndex < values.length) {
|
|
5806
|
-
const index = nextIndex;
|
|
5807
|
-
nextIndex += 1;
|
|
5808
|
-
const value = values[index];
|
|
5809
|
-
if (value !== void 0) results[index] = await map(value);
|
|
5810
|
-
}
|
|
5811
|
-
}
|
|
5812
|
-
await Promise.all(
|
|
5813
|
-
Array.from(
|
|
5814
|
-
{ length: Math.min(concurrency, values.length) },
|
|
5815
|
-
() => worker()
|
|
5816
|
-
)
|
|
5817
|
-
);
|
|
5818
|
-
return results;
|
|
5819
|
-
}
|
|
5820
6237
|
|
|
5821
6238
|
// src/content/content_source.ts
|
|
5822
6239
|
async function readContentSourceEntry(source, logicalPath) {
|
|
@@ -6288,44 +6705,6 @@ function escapeHtmlAttribute(value) {
|
|
|
6288
6705
|
function escapeScriptJson(value) {
|
|
6289
6706
|
return value.replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
6290
6707
|
}
|
|
6291
|
-
|
|
6292
|
-
// src/utils/markdown.ts
|
|
6293
|
-
var FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})(.*)$/;
|
|
6294
|
-
var FENCE_CLOSE = /^ {0,3}(`{3,}|~{3,})\s*$/;
|
|
6295
|
-
function scanMarkdownFenceLines(source) {
|
|
6296
|
-
const lines = [];
|
|
6297
|
-
let fence = null;
|
|
6298
|
-
for (const value of source.split("\n")) {
|
|
6299
|
-
if (fence === null) {
|
|
6300
|
-
const open = FENCE_OPEN.exec(value);
|
|
6301
|
-
if (open && !(open[1][0] === "`" && open[2].includes("`"))) {
|
|
6302
|
-
fence = { marker: open[1][0], length: open[1].length };
|
|
6303
|
-
}
|
|
6304
|
-
lines.push({ value, inFence: false });
|
|
6305
|
-
continue;
|
|
6306
|
-
}
|
|
6307
|
-
const close = FENCE_CLOSE.exec(value);
|
|
6308
|
-
if (close && close[1][0] === fence.marker && close[1].length >= fence.length) {
|
|
6309
|
-
fence = null;
|
|
6310
|
-
lines.push({ value, inFence: false });
|
|
6311
|
-
continue;
|
|
6312
|
-
}
|
|
6313
|
-
lines.push({ value, inFence: true });
|
|
6314
|
-
}
|
|
6315
|
-
return lines;
|
|
6316
|
-
}
|
|
6317
|
-
|
|
6318
|
-
// src/utils/text.ts
|
|
6319
|
-
function stripHtml(html) {
|
|
6320
|
-
return html.replace(/<script\b[\s\S]*?<\/script>/gi, " ").replace(/<style\b[\s\S]*?<\/style>/gi, " ").replace(/<[^>]+>/g, " ");
|
|
6321
|
-
}
|
|
6322
|
-
function calculateReadingTime(html) {
|
|
6323
|
-
const text = stripHtml(html).trim();
|
|
6324
|
-
if (!text) return 1;
|
|
6325
|
-
const cjkCharacters = text.match(/\p{Script=Han}|\p{Script=Hiragana}|\p{Script=Katakana}/gu)?.length ?? 0;
|
|
6326
|
-
const latinWords = text.replace(/\p{Script=Han}|\p{Script=Hiragana}|\p{Script=Katakana}/gu, " ").match(/[\p{L}\p{N}]+/gu)?.length ?? 0;
|
|
6327
|
-
return Math.max(1, Math.ceil(cjkCharacters / 500 + latinWords / 220));
|
|
6328
|
-
}
|
|
6329
6708
|
export {
|
|
6330
6709
|
ATTACHMENTS_BASE_PATH,
|
|
6331
6710
|
CONTENT_BUILD_STATE_VERSION,
|
|
@@ -6362,8 +6741,11 @@ export {
|
|
|
6362
6741
|
determineOutputChanges,
|
|
6363
6742
|
escapeHtml,
|
|
6364
6743
|
escapeHtmlAttribute,
|
|
6744
|
+
escapeRegExp,
|
|
6365
6745
|
escapeScriptJson,
|
|
6746
|
+
extractContentLinks,
|
|
6366
6747
|
extractFrontmatterAliases,
|
|
6748
|
+
extractMarkdownLinkUrls,
|
|
6367
6749
|
fingerprintContentEntries,
|
|
6368
6750
|
fingerprintExternalValue,
|
|
6369
6751
|
getContentSourceEntry,
|