@bagelink/blox 1.18.3 → 1.18.7
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/{PreviewApp-BKrLRDBC.js → PreviewApp-9H9-jLVN.js} +1 -1
- package/dist/{PreviewApp-Kb3uTwtH.cjs → PreviewApp-D8ZBTG0j.cjs} +1 -1
- package/dist/{PreviewApp.vue_vue_type_style_index_0_lang-AC3W0NSJ.cjs → PreviewApp.vue_vue_type_style_index_0_lang-BYCayJs0.cjs} +1 -1
- package/dist/{PreviewApp.vue_vue_type_style_index_0_lang-BMGv1eSR.js → PreviewApp.vue_vue_type_style_index_0_lang-BwiLyRBw.js} +1 -1
- package/dist/{appRuntime-CWlPH_5r.js → appRuntime-DZSCSsnS.js} +39 -156
- package/dist/{appRuntime-BU2Ls-Y7.cjs → appRuntime-Z0AxTPxf.cjs} +17 -134
- package/dist/composables/useLocale.d.ts +3 -0
- package/dist/composables/useLocale.d.ts.map +1 -1
- package/dist/composables/useLocaleNav.d.ts.map +1 -1
- package/dist/createBloxApp.d.ts.map +1 -1
- package/dist/hydration.d.ts +2 -0
- package/dist/hydration.d.ts.map +1 -1
- package/dist/index.cjs +16 -9
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +44 -36
- package/dist/localeStrategy-OwRIn35C.cjs +207 -0
- package/dist/localeStrategy-hXlNof1E.js +208 -0
- package/dist/localeStrategy.d.ts +21 -0
- package/dist/localeStrategy.d.ts.map +1 -1
- package/dist/{shard-runner-zmTY7v3n.cjs → shard-runner-Bz46UqN8.cjs} +16 -7
- package/dist/{shard-runner-yZNy6pQy.js → shard-runner-CoFcUVGe.js} +17 -8
- package/dist/ssg/cli.cjs +10 -2
- package/dist/ssg/cli.mjs +10 -2
- package/dist/ssg/cms-routes.d.ts +7 -0
- package/dist/ssg/cms-routes.d.ts.map +1 -1
- package/dist/ssg/index.cjs +4 -2
- package/dist/ssg/index.mjs +19 -17
- package/dist/ssg/prerender.d.ts +5 -1
- package/dist/ssg/prerender.d.ts.map +1 -1
- package/dist/ssg/render-resolved-page.d.ts +2 -0
- package/dist/ssg/render-resolved-page.d.ts.map +1 -1
- package/dist/ssg/shard-runner.d.ts +3 -0
- package/dist/ssg/shard-runner.d.ts.map +1 -1
- package/dist/ssg/validate-output.d.ts +9 -0
- package/dist/ssg/validate-output.d.ts.map +1 -1
- package/dist/{validate-output-3fNPagvj.js → validate-output-B8pmE7hf.js} +115 -28
- package/dist/{validate-output-CrhXrC1l.cjs → validate-output-CbIQrCwa.cjs} +99 -12
- package/package.json +125 -124
- package/LICENSE +0 -21
- package/dist/pageTitle-B1mtDJKn.js +0 -59
- package/dist/pageTitle-wobGhtiZ.cjs +0 -58
|
@@ -3,7 +3,7 @@ import fs, { readFile } from "node:fs/promises";
|
|
|
3
3
|
import path, { resolve } from "node:path";
|
|
4
4
|
import process$1 from "node:process";
|
|
5
5
|
import { pathToFileURL } from "node:url";
|
|
6
|
-
import { r as resolvePageTitle, p as primaryPageContext, c as contextPageTitle } from "./
|
|
6
|
+
import { r as resolvePageTitle, p as primaryPageContext, c as contextPageTitle } from "./localeStrategy-hXlNof1E.js";
|
|
7
7
|
class BloxBuildResponseError extends Error {
|
|
8
8
|
constructor(status, url, statusText) {
|
|
9
9
|
super(`Build API ${status} ${statusText}: ${url}`);
|
|
@@ -72,6 +72,24 @@ async function bloxBuildJson(input, init = {}, options = {}) {
|
|
|
72
72
|
if (response.status === 204) return null;
|
|
73
73
|
return response.json();
|
|
74
74
|
}
|
|
75
|
+
function buildPageSlugMap(localizedBundles, defaultLocale) {
|
|
76
|
+
var _a;
|
|
77
|
+
const map = {};
|
|
78
|
+
const defaultPages = ((_a = localizedBundles.find((result) => result.locale === defaultLocale)) == null ? void 0 : _a.bundle.pages) ?? [];
|
|
79
|
+
const canonicalById = new Map(
|
|
80
|
+
defaultPages.flatMap((page) => page.id && page.slug && !page.slug.includes(":") ? [[page.id, page.slug]] : [])
|
|
81
|
+
);
|
|
82
|
+
for (const { locale, bundle } of localizedBundles) {
|
|
83
|
+
for (const page of bundle.pages) {
|
|
84
|
+
if (!page.slug || page.slug.includes(":") || page.status && page.status !== "published") continue;
|
|
85
|
+
const canonical = page.id ? canonicalById.get(page.id) : void 0;
|
|
86
|
+
if (!canonical) continue;
|
|
87
|
+
const entry = map[canonical] ?? (map[canonical] = { [defaultLocale]: canonical });
|
|
88
|
+
entry[locale] = page.slug;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return map;
|
|
92
|
+
}
|
|
75
93
|
function localizePath(slug, locale, defaultLocale) {
|
|
76
94
|
if (locale === defaultLocale) return slug === "" ? "/" : slug;
|
|
77
95
|
const s = slug === "" || slug === "/" ? "" : slug;
|
|
@@ -118,7 +136,7 @@ async function fetchCmsSiteData(apiBase, websiteName, requestOptions = {}) {
|
|
|
118
136
|
return [{
|
|
119
137
|
from_path: fromPath,
|
|
120
138
|
to_path: localizePath(page.slug, locale, defaultLocale2),
|
|
121
|
-
status_code:
|
|
139
|
+
status_code: 301
|
|
122
140
|
}];
|
|
123
141
|
});
|
|
124
142
|
});
|
|
@@ -132,6 +150,7 @@ async function fetchCmsSiteData(apiBase, websiteName, requestOptions = {}) {
|
|
|
132
150
|
website: website2,
|
|
133
151
|
paths: paths2,
|
|
134
152
|
pageSlugs: [...new Set(localizedBundles.flatMap((result) => result.bundle.pages.flatMap((page) => page.slug ? [localizePath(page.slug, result.locale, defaultLocale2)] : [])))],
|
|
153
|
+
pageSlugMap: buildPageSlugMap(localizedBundles, defaultLocale2),
|
|
135
154
|
redirects: redirects2,
|
|
136
155
|
collections: extractCollectionRefs(localizedBundles.flatMap((result) => result.bundle.pages)),
|
|
137
156
|
collectionItems
|
|
@@ -175,7 +194,7 @@ async function fetchCmsSiteData(apiBase, websiteName, requestOptions = {}) {
|
|
|
175
194
|
return [{
|
|
176
195
|
from_path: fromPath,
|
|
177
196
|
to_path: localizePath(page.slug, locale, defaultLocale),
|
|
178
|
-
status_code:
|
|
197
|
+
status_code: 301
|
|
179
198
|
}];
|
|
180
199
|
});
|
|
181
200
|
});
|
|
@@ -191,7 +210,18 @@ async function fetchCmsSiteData(apiBase, websiteName, requestOptions = {}) {
|
|
|
191
210
|
const pages = localizedPages.flatMap((result) => result.pages);
|
|
192
211
|
const collections = extractCollectionRefs(pages);
|
|
193
212
|
const pageSlugs = [...new Set(localizedPages.flatMap((result) => result.pages.flatMap((page) => page.slug ? [localizePath(page.slug, result.locale, defaultLocale)] : [])))];
|
|
194
|
-
return {
|
|
213
|
+
return {
|
|
214
|
+
websiteId: site.id,
|
|
215
|
+
website,
|
|
216
|
+
paths,
|
|
217
|
+
pageSlugs,
|
|
218
|
+
pageSlugMap: buildPageSlugMap(
|
|
219
|
+
localizedPages.map((result) => ({ locale: result.locale, bundle: { pages: result.pages } })),
|
|
220
|
+
defaultLocale
|
|
221
|
+
),
|
|
222
|
+
redirects,
|
|
223
|
+
collections
|
|
224
|
+
};
|
|
195
225
|
}
|
|
196
226
|
async function expandRoutes(apiBase, pages, requestOptions, locale, collectItems) {
|
|
197
227
|
const routes = [];
|
|
@@ -992,6 +1022,7 @@ async function prerender({
|
|
|
992
1022
|
website = null,
|
|
993
1023
|
websiteId = "",
|
|
994
1024
|
redirects = [],
|
|
1025
|
+
pageSlugMap = {},
|
|
995
1026
|
llm = {}
|
|
996
1027
|
} = {}) {
|
|
997
1028
|
var _a, _b, _c, _d, _e, _f;
|
|
@@ -1099,6 +1130,7 @@ async function prerender({
|
|
|
1099
1130
|
template,
|
|
1100
1131
|
website,
|
|
1101
1132
|
websiteId,
|
|
1133
|
+
pageSlugMap,
|
|
1102
1134
|
resolveCache,
|
|
1103
1135
|
llm
|
|
1104
1136
|
});
|
|
@@ -1242,7 +1274,7 @@ ${existing}`,
|
|
|
1242
1274
|
totalDiscovered: seen.size
|
|
1243
1275
|
};
|
|
1244
1276
|
}
|
|
1245
|
-
function normalizePath(p) {
|
|
1277
|
+
function normalizePath$1(p) {
|
|
1246
1278
|
if (!p) return "/";
|
|
1247
1279
|
try {
|
|
1248
1280
|
if (p.startsWith("http://") || p.startsWith("https://")) {
|
|
@@ -1259,7 +1291,7 @@ function normalizePath(p) {
|
|
|
1259
1291
|
return p || "/";
|
|
1260
1292
|
}
|
|
1261
1293
|
function enqueue(p, queue, seen, isExcluded) {
|
|
1262
|
-
const np = normalizePath(p);
|
|
1294
|
+
const np = normalizePath$1(p);
|
|
1263
1295
|
if (seen.has(np)) return;
|
|
1264
1296
|
if (isExcluded(np)) return;
|
|
1265
1297
|
seen.add(np);
|
|
@@ -1268,7 +1300,7 @@ function enqueue(p, queue, seen, isExcluded) {
|
|
|
1268
1300
|
function makeExcludeChecker(excludePaths) {
|
|
1269
1301
|
const rules = Array.isArray(excludePaths) ? excludePaths : [excludePaths];
|
|
1270
1302
|
return (p) => {
|
|
1271
|
-
const np = normalizePath(p);
|
|
1303
|
+
const np = normalizePath$1(p);
|
|
1272
1304
|
for (const r of rules) {
|
|
1273
1305
|
if (typeof r === "string" && r === "") continue;
|
|
1274
1306
|
if (typeof r === "function") {
|
|
@@ -1276,7 +1308,7 @@ function makeExcludeChecker(excludePaths) {
|
|
|
1276
1308
|
} else if (r instanceof RegExp) {
|
|
1277
1309
|
if (r.test(np)) return true;
|
|
1278
1310
|
} else if (typeof r === "string") {
|
|
1279
|
-
const prefix = normalizePath(r);
|
|
1311
|
+
const prefix = normalizePath$1(r);
|
|
1280
1312
|
if (np === prefix || np.startsWith(`${prefix}/`)) return true;
|
|
1281
1313
|
}
|
|
1282
1314
|
}
|
|
@@ -1514,6 +1546,60 @@ async function validateInternalLinks(outDir) {
|
|
|
1514
1546
|
${failures.map((item) => ` - ${item}`).join("\n")}`);
|
|
1515
1547
|
}
|
|
1516
1548
|
}
|
|
1549
|
+
async function validateNoRedirectingLinks(outDir) {
|
|
1550
|
+
const root = path.resolve(outDir);
|
|
1551
|
+
const redirectSources = await readRedirectSources(root, "301", "302", "307", "308");
|
|
1552
|
+
if (!redirectSources.size) return;
|
|
1553
|
+
const failures = [];
|
|
1554
|
+
for (const file of await collectHtmlFiles(root)) {
|
|
1555
|
+
const name = path.relative(root, file);
|
|
1556
|
+
const html = await fs.readFile(file, "utf8");
|
|
1557
|
+
for (const href of internalHrefs(html)) {
|
|
1558
|
+
const [withoutSuffix] = href.split(/[?#]/);
|
|
1559
|
+
const normalized = normalizePath(withoutSuffix || "/");
|
|
1560
|
+
if (redirectSources.has(normalized)) {
|
|
1561
|
+
failures.push(`${name}: links to redirecting URL "${href}"`);
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
if (failures.length) {
|
|
1566
|
+
throw new Error(
|
|
1567
|
+
`Invalid Blox SSG output:
|
|
1568
|
+
${failures.slice(0, 25).map((item) => ` - ${item}`).join("\n")}`
|
|
1569
|
+
);
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
async function readRedirectSources(root, ...statuses) {
|
|
1573
|
+
const sources = /* @__PURE__ */ new Set();
|
|
1574
|
+
let content = "";
|
|
1575
|
+
try {
|
|
1576
|
+
content = await fs.readFile(path.join(root, "_redirects"), "utf8");
|
|
1577
|
+
} catch {
|
|
1578
|
+
return sources;
|
|
1579
|
+
}
|
|
1580
|
+
for (const line of content.split("\n")) {
|
|
1581
|
+
const trimmed = line.trim();
|
|
1582
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
1583
|
+
const parts = trimmed.split(/\s+/);
|
|
1584
|
+
if (parts.length < 3) continue;
|
|
1585
|
+
const status = parts[2];
|
|
1586
|
+
if (!statuses.includes(status)) continue;
|
|
1587
|
+
sources.add(normalizePath(parts[0]));
|
|
1588
|
+
}
|
|
1589
|
+
return sources;
|
|
1590
|
+
}
|
|
1591
|
+
function normalizePath(value) {
|
|
1592
|
+
const clean = value.split(/[?#]/)[0] || "/";
|
|
1593
|
+
return clean !== "/" ? clean.replace(/\/+$/, "") : "/";
|
|
1594
|
+
}
|
|
1595
|
+
function* internalHrefs(html) {
|
|
1596
|
+
const attr = /(?:<a\b[^>]*\shref=["']([^"']+)["'])|(?:data-to=["']([^"']+)["'])/gi;
|
|
1597
|
+
let match;
|
|
1598
|
+
while (match = attr.exec(html)) {
|
|
1599
|
+
const value = match[1] ?? match[2] ?? "";
|
|
1600
|
+
if (value.startsWith("/") && !value.startsWith("//")) yield value;
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1517
1603
|
const anchorHref = /<a\b[^>]*\shref=["']([^"']*)["']/gi;
|
|
1518
1604
|
const ABSOLUTE_OR_SPECIAL = /^(?:[a-z][a-z0-9+.-]*:|\/\/|\/|#|\?|$)/i;
|
|
1519
1605
|
async function readSiteNoindex(outDir) {
|
|
@@ -1654,33 +1740,34 @@ async function validateSitemap(outDir, content) {
|
|
|
1654
1740
|
return failures;
|
|
1655
1741
|
}
|
|
1656
1742
|
export {
|
|
1657
|
-
|
|
1743
|
+
resolveManifestProvenance as A,
|
|
1658
1744
|
BloxBuildResponseError as B,
|
|
1659
|
-
|
|
1745
|
+
splitSitemapPathsByLocale as C,
|
|
1746
|
+
llmArtifacts as D,
|
|
1660
1747
|
prerender as a,
|
|
1661
1748
|
buildCollectionResolver as b,
|
|
1662
1749
|
validateInternalLinks as c,
|
|
1663
|
-
|
|
1664
|
-
|
|
1750
|
+
validateNoRedirectingLinks as d,
|
|
1751
|
+
buildPageHead as e,
|
|
1665
1752
|
fetchCmsSiteData as f,
|
|
1666
1753
|
generateSitemapArtifacts as g,
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1754
|
+
primaryContextEntry as h,
|
|
1755
|
+
buildJsonLd as i,
|
|
1756
|
+
extractPageText as j,
|
|
1757
|
+
bloxBuildJson as k,
|
|
1758
|
+
bloxBuildRequest as l,
|
|
1759
|
+
buildSiteHead as m,
|
|
1760
|
+
generateLlmsFullTxt as n,
|
|
1761
|
+
generateLlmsTxt as o,
|
|
1675
1762
|
polyfillBloxSsgGlobals as p,
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1763
|
+
generateNetlifyRedirects as q,
|
|
1764
|
+
generateRobotsTxt as r,
|
|
1765
|
+
generateSitemapIndexXml as s,
|
|
1766
|
+
generateSitemapXml as t,
|
|
1680
1767
|
uploadBloxManifest as u,
|
|
1681
1768
|
validateSsgOutput as v,
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1769
|
+
getBloxBuildRequestOptions as w,
|
|
1770
|
+
getSiteIconFlags as x,
|
|
1771
|
+
localeSitemapPath as y,
|
|
1772
|
+
mergeLlmParts as z
|
|
1686
1773
|
};
|
|
@@ -26,7 +26,7 @@ const fs = require("node:fs/promises");
|
|
|
26
26
|
const path = require("node:path");
|
|
27
27
|
const process$1 = require("node:process");
|
|
28
28
|
const node_url = require("node:url");
|
|
29
|
-
const
|
|
29
|
+
const localeStrategy = require("./localeStrategy-OwRIn35C.cjs");
|
|
30
30
|
class BloxBuildResponseError extends Error {
|
|
31
31
|
constructor(status, url, statusText) {
|
|
32
32
|
super(`Build API ${status} ${statusText}: ${url}`);
|
|
@@ -95,6 +95,24 @@ async function bloxBuildJson(input, init = {}, options = {}) {
|
|
|
95
95
|
if (response.status === 204) return null;
|
|
96
96
|
return response.json();
|
|
97
97
|
}
|
|
98
|
+
function buildPageSlugMap(localizedBundles, defaultLocale) {
|
|
99
|
+
var _a;
|
|
100
|
+
const map = {};
|
|
101
|
+
const defaultPages = ((_a = localizedBundles.find((result) => result.locale === defaultLocale)) == null ? void 0 : _a.bundle.pages) ?? [];
|
|
102
|
+
const canonicalById = new Map(
|
|
103
|
+
defaultPages.flatMap((page) => page.id && page.slug && !page.slug.includes(":") ? [[page.id, page.slug]] : [])
|
|
104
|
+
);
|
|
105
|
+
for (const { locale, bundle } of localizedBundles) {
|
|
106
|
+
for (const page of bundle.pages) {
|
|
107
|
+
if (!page.slug || page.slug.includes(":") || page.status && page.status !== "published") continue;
|
|
108
|
+
const canonical = page.id ? canonicalById.get(page.id) : void 0;
|
|
109
|
+
if (!canonical) continue;
|
|
110
|
+
const entry = map[canonical] ?? (map[canonical] = { [defaultLocale]: canonical });
|
|
111
|
+
entry[locale] = page.slug;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return map;
|
|
115
|
+
}
|
|
98
116
|
function localizePath(slug, locale, defaultLocale) {
|
|
99
117
|
if (locale === defaultLocale) return slug === "" ? "/" : slug;
|
|
100
118
|
const s = slug === "" || slug === "/" ? "" : slug;
|
|
@@ -141,7 +159,7 @@ async function fetchCmsSiteData(apiBase, websiteName, requestOptions = {}) {
|
|
|
141
159
|
return [{
|
|
142
160
|
from_path: fromPath,
|
|
143
161
|
to_path: localizePath(page.slug, locale, defaultLocale2),
|
|
144
|
-
status_code:
|
|
162
|
+
status_code: 301
|
|
145
163
|
}];
|
|
146
164
|
});
|
|
147
165
|
});
|
|
@@ -155,6 +173,7 @@ async function fetchCmsSiteData(apiBase, websiteName, requestOptions = {}) {
|
|
|
155
173
|
website: website2,
|
|
156
174
|
paths: paths2,
|
|
157
175
|
pageSlugs: [...new Set(localizedBundles.flatMap((result) => result.bundle.pages.flatMap((page) => page.slug ? [localizePath(page.slug, result.locale, defaultLocale2)] : [])))],
|
|
176
|
+
pageSlugMap: buildPageSlugMap(localizedBundles, defaultLocale2),
|
|
158
177
|
redirects: redirects2,
|
|
159
178
|
collections: extractCollectionRefs(localizedBundles.flatMap((result) => result.bundle.pages)),
|
|
160
179
|
collectionItems
|
|
@@ -198,7 +217,7 @@ async function fetchCmsSiteData(apiBase, websiteName, requestOptions = {}) {
|
|
|
198
217
|
return [{
|
|
199
218
|
from_path: fromPath,
|
|
200
219
|
to_path: localizePath(page.slug, locale, defaultLocale),
|
|
201
|
-
status_code:
|
|
220
|
+
status_code: 301
|
|
202
221
|
}];
|
|
203
222
|
});
|
|
204
223
|
});
|
|
@@ -214,7 +233,18 @@ async function fetchCmsSiteData(apiBase, websiteName, requestOptions = {}) {
|
|
|
214
233
|
const pages = localizedPages.flatMap((result) => result.pages);
|
|
215
234
|
const collections = extractCollectionRefs(pages);
|
|
216
235
|
const pageSlugs = [...new Set(localizedPages.flatMap((result) => result.pages.flatMap((page) => page.slug ? [localizePath(page.slug, result.locale, defaultLocale)] : [])))];
|
|
217
|
-
return {
|
|
236
|
+
return {
|
|
237
|
+
websiteId: site.id,
|
|
238
|
+
website,
|
|
239
|
+
paths,
|
|
240
|
+
pageSlugs,
|
|
241
|
+
pageSlugMap: buildPageSlugMap(
|
|
242
|
+
localizedPages.map((result) => ({ locale: result.locale, bundle: { pages: result.pages } })),
|
|
243
|
+
defaultLocale
|
|
244
|
+
),
|
|
245
|
+
redirects,
|
|
246
|
+
collections
|
|
247
|
+
};
|
|
218
248
|
}
|
|
219
249
|
async function expandRoutes(apiBase, pages, requestOptions, locale, collectItems) {
|
|
220
250
|
const routes = [];
|
|
@@ -444,7 +474,7 @@ function buildPageHead(options) {
|
|
|
444
474
|
const page = resolvedData == null ? void 0 : resolvedData.page;
|
|
445
475
|
const ctx = extractPrimaryContext(resolvedData == null ? void 0 : resolvedData.contexts);
|
|
446
476
|
const siteName = seo.site_name || void 0;
|
|
447
|
-
const title =
|
|
477
|
+
const title = localeStrategy.resolvePageTitle(resolvedData ?? {}, {
|
|
448
478
|
override: override == null ? void 0 : override.title,
|
|
449
479
|
siteName,
|
|
450
480
|
defaultTitle: seo.default_meta_title
|
|
@@ -738,20 +768,20 @@ function getSiteIconFlags(website) {
|
|
|
738
768
|
function extractPrimaryContext(contexts) {
|
|
739
769
|
var _a;
|
|
740
770
|
const empty = { title: null, description: null, image: null };
|
|
741
|
-
const ctx = (_a =
|
|
771
|
+
const ctx = (_a = localeStrategy.primaryPageContext(contexts)) == null ? void 0 : _a.ctx;
|
|
742
772
|
if (!ctx) return empty;
|
|
743
773
|
const str2 = (key) => {
|
|
744
774
|
const v = ctx[key];
|
|
745
775
|
return typeof v === "string" && v.trim() ? v.trim() : null;
|
|
746
776
|
};
|
|
747
777
|
return {
|
|
748
|
-
title:
|
|
778
|
+
title: localeStrategy.contextPageTitle(ctx),
|
|
749
779
|
description: str2("meta_description") || str2("excerpt") || str2("description") || str2("blurb") || str2("summary"),
|
|
750
780
|
image: str2("og_image") || str2("cover_image_url") || str2("image_url") || str2("image")
|
|
751
781
|
};
|
|
752
782
|
}
|
|
753
783
|
function primaryContextEntry(contexts) {
|
|
754
|
-
return
|
|
784
|
+
return localeStrategy.primaryPageContext(contexts);
|
|
755
785
|
}
|
|
756
786
|
function esc(s) {
|
|
757
787
|
return s.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
@@ -1015,6 +1045,7 @@ async function prerender({
|
|
|
1015
1045
|
website = null,
|
|
1016
1046
|
websiteId = "",
|
|
1017
1047
|
redirects = [],
|
|
1048
|
+
pageSlugMap = {},
|
|
1018
1049
|
llm = {}
|
|
1019
1050
|
} = {}) {
|
|
1020
1051
|
var _a, _b, _c, _d, _e, _f;
|
|
@@ -1122,6 +1153,7 @@ async function prerender({
|
|
|
1122
1153
|
template,
|
|
1123
1154
|
website,
|
|
1124
1155
|
websiteId,
|
|
1156
|
+
pageSlugMap,
|
|
1125
1157
|
resolveCache,
|
|
1126
1158
|
llm
|
|
1127
1159
|
});
|
|
@@ -1265,7 +1297,7 @@ ${existing}`,
|
|
|
1265
1297
|
totalDiscovered: seen.size
|
|
1266
1298
|
};
|
|
1267
1299
|
}
|
|
1268
|
-
function normalizePath(p) {
|
|
1300
|
+
function normalizePath$1(p) {
|
|
1269
1301
|
if (!p) return "/";
|
|
1270
1302
|
try {
|
|
1271
1303
|
if (p.startsWith("http://") || p.startsWith("https://")) {
|
|
@@ -1282,7 +1314,7 @@ function normalizePath(p) {
|
|
|
1282
1314
|
return p || "/";
|
|
1283
1315
|
}
|
|
1284
1316
|
function enqueue(p, queue, seen, isExcluded) {
|
|
1285
|
-
const np = normalizePath(p);
|
|
1317
|
+
const np = normalizePath$1(p);
|
|
1286
1318
|
if (seen.has(np)) return;
|
|
1287
1319
|
if (isExcluded(np)) return;
|
|
1288
1320
|
seen.add(np);
|
|
@@ -1291,7 +1323,7 @@ function enqueue(p, queue, seen, isExcluded) {
|
|
|
1291
1323
|
function makeExcludeChecker(excludePaths) {
|
|
1292
1324
|
const rules = Array.isArray(excludePaths) ? excludePaths : [excludePaths];
|
|
1293
1325
|
return (p) => {
|
|
1294
|
-
const np = normalizePath(p);
|
|
1326
|
+
const np = normalizePath$1(p);
|
|
1295
1327
|
for (const r of rules) {
|
|
1296
1328
|
if (typeof r === "string" && r === "") continue;
|
|
1297
1329
|
if (typeof r === "function") {
|
|
@@ -1299,7 +1331,7 @@ function makeExcludeChecker(excludePaths) {
|
|
|
1299
1331
|
} else if (r instanceof RegExp) {
|
|
1300
1332
|
if (r.test(np)) return true;
|
|
1301
1333
|
} else if (typeof r === "string") {
|
|
1302
|
-
const prefix = normalizePath(r);
|
|
1334
|
+
const prefix = normalizePath$1(r);
|
|
1303
1335
|
if (np === prefix || np.startsWith(`${prefix}/`)) return true;
|
|
1304
1336
|
}
|
|
1305
1337
|
}
|
|
@@ -1537,6 +1569,60 @@ async function validateInternalLinks(outDir) {
|
|
|
1537
1569
|
${failures.map((item) => ` - ${item}`).join("\n")}`);
|
|
1538
1570
|
}
|
|
1539
1571
|
}
|
|
1572
|
+
async function validateNoRedirectingLinks(outDir) {
|
|
1573
|
+
const root = path.resolve(outDir);
|
|
1574
|
+
const redirectSources = await readRedirectSources(root, "301", "302", "307", "308");
|
|
1575
|
+
if (!redirectSources.size) return;
|
|
1576
|
+
const failures = [];
|
|
1577
|
+
for (const file of await collectHtmlFiles(root)) {
|
|
1578
|
+
const name = path.relative(root, file);
|
|
1579
|
+
const html = await fs.readFile(file, "utf8");
|
|
1580
|
+
for (const href of internalHrefs(html)) {
|
|
1581
|
+
const [withoutSuffix] = href.split(/[?#]/);
|
|
1582
|
+
const normalized = normalizePath(withoutSuffix || "/");
|
|
1583
|
+
if (redirectSources.has(normalized)) {
|
|
1584
|
+
failures.push(`${name}: links to redirecting URL "${href}"`);
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
if (failures.length) {
|
|
1589
|
+
throw new Error(
|
|
1590
|
+
`Invalid Blox SSG output:
|
|
1591
|
+
${failures.slice(0, 25).map((item) => ` - ${item}`).join("\n")}`
|
|
1592
|
+
);
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
async function readRedirectSources(root, ...statuses) {
|
|
1596
|
+
const sources = /* @__PURE__ */ new Set();
|
|
1597
|
+
let content = "";
|
|
1598
|
+
try {
|
|
1599
|
+
content = await fs.readFile(path.join(root, "_redirects"), "utf8");
|
|
1600
|
+
} catch {
|
|
1601
|
+
return sources;
|
|
1602
|
+
}
|
|
1603
|
+
for (const line of content.split("\n")) {
|
|
1604
|
+
const trimmed = line.trim();
|
|
1605
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
1606
|
+
const parts = trimmed.split(/\s+/);
|
|
1607
|
+
if (parts.length < 3) continue;
|
|
1608
|
+
const status = parts[2];
|
|
1609
|
+
if (!statuses.includes(status)) continue;
|
|
1610
|
+
sources.add(normalizePath(parts[0]));
|
|
1611
|
+
}
|
|
1612
|
+
return sources;
|
|
1613
|
+
}
|
|
1614
|
+
function normalizePath(value) {
|
|
1615
|
+
const clean = value.split(/[?#]/)[0] || "/";
|
|
1616
|
+
return clean !== "/" ? clean.replace(/\/+$/, "") : "/";
|
|
1617
|
+
}
|
|
1618
|
+
function* internalHrefs(html) {
|
|
1619
|
+
const attr = /(?:<a\b[^>]*\shref=["']([^"']+)["'])|(?:data-to=["']([^"']+)["'])/gi;
|
|
1620
|
+
let match;
|
|
1621
|
+
while (match = attr.exec(html)) {
|
|
1622
|
+
const value = match[1] ?? match[2] ?? "";
|
|
1623
|
+
if (value.startsWith("/") && !value.startsWith("//")) yield value;
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1540
1626
|
const anchorHref = /<a\b[^>]*\shref=["']([^"']*)["']/gi;
|
|
1541
1627
|
const ABSOLUTE_OR_SPECIAL = /^(?:[a-z][a-z0-9+.-]*:|\/\/|\/|#|\?|$)/i;
|
|
1542
1628
|
async function readSiteNoindex(outDir) {
|
|
@@ -1704,4 +1790,5 @@ exports.resolveManifestProvenance = resolveManifestProvenance;
|
|
|
1704
1790
|
exports.splitSitemapPathsByLocale = splitSitemapPathsByLocale;
|
|
1705
1791
|
exports.uploadBloxManifest = uploadBloxManifest;
|
|
1706
1792
|
exports.validateInternalLinks = validateInternalLinks;
|
|
1793
|
+
exports.validateNoRedirectingLinks = validateNoRedirectingLinks;
|
|
1707
1794
|
exports.validateSsgOutput = validateSsgOutput;
|