@bagelink/blox 1.20.5 → 1.20.6
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/{shard-runner-0mHNuOav.js → shard-runner-BGMCtBmP.js} +4 -3
- package/dist/{shard-runner-DsFdfDLh.cjs → shard-runner-C6BtYRC2.cjs} +4 -3
- package/dist/ssg/cli.cjs +3 -2
- package/dist/ssg/cli.mjs +3 -2
- package/dist/ssg/index.cjs +1 -1
- package/dist/ssg/index.mjs +17 -17
- package/dist/ssg/prerender.d.ts.map +1 -1
- package/dist/ssg/preview.d.ts +9 -0
- package/dist/ssg/preview.d.ts.map +1 -0
- package/dist/ssg/shard-runner.d.ts.map +1 -1
- package/dist/ssg/validate-output.d.ts +1 -0
- package/dist/ssg/validate-output.d.ts.map +1 -1
- package/dist/{validate-output-CJhtHWFT.js → validate-output-BTvdILwE.js} +97 -36
- package/dist/{validate-output-Bwp0ediG.cjs → validate-output-CAcHbUBv.cjs} +75 -14
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ import { spawn } from "node:child_process";
|
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import process from "node:process";
|
|
5
|
-
import { f as fetchCmsSiteData, g as generateSitemapArtifacts,
|
|
5
|
+
import { f as fetchCmsSiteData, g as generateSitemapArtifacts, c as validatePreviewOutput, e as validateNoRedirectingLinks, i as isErrorPagePath, h as isPreviewPath } from "./validate-output-BTvdILwE.js";
|
|
6
6
|
const AUTO_SHARD_CAP = Number(process.env.BLOX_SSG_SHARD_CAP) || 6;
|
|
7
7
|
const PREFETCH_BUDGET = Number(process.env.BLOX_SSG_PREFETCH_BUDGET) || 24;
|
|
8
8
|
const SEQUENTIAL_SHARDS = process.env.BLOX_SSG_SEQUENTIAL === "1";
|
|
@@ -16,7 +16,7 @@ function matchesCmsPath(pathname, cmsPaths) {
|
|
|
16
16
|
return false;
|
|
17
17
|
}
|
|
18
18
|
function sitemapRoutesForWebsite(routes, siteNoindex) {
|
|
19
|
-
return routes.filter((route) => !route.noindex && !isErrorPagePath(route.url));
|
|
19
|
+
return routes.filter((route) => !route.noindex && !isErrorPagePath(route.url) && !isPreviewPath(route.url));
|
|
20
20
|
}
|
|
21
21
|
function resolveShardCount(shards) {
|
|
22
22
|
if (shards === "auto") {
|
|
@@ -145,7 +145,7 @@ async function runSharded(opts) {
|
|
|
145
145
|
}
|
|
146
146
|
if (process.env.BLOX_SSG_LLM !== "0" && !((_a = website == null ? void 0 : website.seo) == null ? void 0 : _a.noindex)) {
|
|
147
147
|
try {
|
|
148
|
-
const { mergeLlmParts } = await import("./validate-output-
|
|
148
|
+
const { mergeLlmParts } = await import("./validate-output-BTvdILwE.js").then((n) => n.G);
|
|
149
149
|
const mergeWebsite = website;
|
|
150
150
|
const partFiles = Array.from({ length: total }, (_, i) => path.join(llmPartsDir, `part.${i}.json`));
|
|
151
151
|
const { pages } = await mergeLlmParts({
|
|
@@ -191,6 +191,7 @@ async function runSharded(opts) {
|
|
|
191
191
|
await fs.rm(routePartsDir, { recursive: true, force: true });
|
|
192
192
|
await fs.rm(siteDataFile, { force: true }).catch(() => {
|
|
193
193
|
});
|
|
194
|
+
await validatePreviewOutput(path.resolve("dist"));
|
|
194
195
|
await validateNoRedirectingLinks(path.resolve("dist"));
|
|
195
196
|
console.log("[blox-ssg] validated internal links (no redirecting URLs)");
|
|
196
197
|
console.log(`[blox-ssg] sharded run done in ${((Date.now() - start) / 1e3).toFixed(1)}s`);
|
|
@@ -26,7 +26,7 @@ const node_child_process = require("node:child_process");
|
|
|
26
26
|
const os = require("node:os");
|
|
27
27
|
const path = require("node:path");
|
|
28
28
|
const process = require("node:process");
|
|
29
|
-
const llmArtifacts = require("./validate-output-
|
|
29
|
+
const llmArtifacts = require("./validate-output-CAcHbUBv.cjs");
|
|
30
30
|
const AUTO_SHARD_CAP = Number(process.env.BLOX_SSG_SHARD_CAP) || 6;
|
|
31
31
|
const PREFETCH_BUDGET = Number(process.env.BLOX_SSG_PREFETCH_BUDGET) || 24;
|
|
32
32
|
const SEQUENTIAL_SHARDS = process.env.BLOX_SSG_SEQUENTIAL === "1";
|
|
@@ -40,7 +40,7 @@ function matchesCmsPath(pathname, cmsPaths) {
|
|
|
40
40
|
return false;
|
|
41
41
|
}
|
|
42
42
|
function sitemapRoutesForWebsite(routes, siteNoindex) {
|
|
43
|
-
return routes.filter((route) => !route.noindex && !llmArtifacts.isErrorPagePath(route.url));
|
|
43
|
+
return routes.filter((route) => !route.noindex && !llmArtifacts.isErrorPagePath(route.url) && !llmArtifacts.isPreviewPath(route.url));
|
|
44
44
|
}
|
|
45
45
|
function resolveShardCount(shards) {
|
|
46
46
|
if (shards === "auto") {
|
|
@@ -169,7 +169,7 @@ async function runSharded(opts) {
|
|
|
169
169
|
}
|
|
170
170
|
if (process.env.BLOX_SSG_LLM !== "0" && !((_a = website == null ? void 0 : website.seo) == null ? void 0 : _a.noindex)) {
|
|
171
171
|
try {
|
|
172
|
-
const { mergeLlmParts } = await Promise.resolve().then(() => require("./validate-output-
|
|
172
|
+
const { mergeLlmParts } = await Promise.resolve().then(() => require("./validate-output-CAcHbUBv.cjs")).then((n) => n.llmArtifacts);
|
|
173
173
|
const mergeWebsite = website;
|
|
174
174
|
const partFiles = Array.from({ length: total }, (_, i) => path.join(llmPartsDir, `part.${i}.json`));
|
|
175
175
|
const { pages } = await mergeLlmParts({
|
|
@@ -215,6 +215,7 @@ async function runSharded(opts) {
|
|
|
215
215
|
await fs.rm(routePartsDir, { recursive: true, force: true });
|
|
216
216
|
await fs.rm(siteDataFile, { force: true }).catch(() => {
|
|
217
217
|
});
|
|
218
|
+
await llmArtifacts.validatePreviewOutput(path.resolve("dist"));
|
|
218
219
|
await llmArtifacts.validateNoRedirectingLinks(path.resolve("dist"));
|
|
219
220
|
console.log("[blox-ssg] validated internal links (no redirecting URLs)");
|
|
220
221
|
console.log(`[blox-ssg] sharded run done in ${((Date.now() - start) / 1e3).toFixed(1)}s`);
|
package/dist/ssg/cli.cjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const process = require("node:process");
|
|
4
4
|
const fs = require("node:fs/promises");
|
|
5
5
|
const ssg_buildGrant = require("./build-grant.cjs");
|
|
6
|
-
const llmArtifacts = require("../validate-output-
|
|
6
|
+
const llmArtifacts = require("../validate-output-CAcHbUBv.cjs");
|
|
7
7
|
const localeStrategy = require("../localeStrategy-OwRIn35C.cjs");
|
|
8
8
|
function installFetchTimeout() {
|
|
9
9
|
const ms = Number(process.env.BLOX_SSG_FETCH_TIMEOUT_MS) || 2e4;
|
|
@@ -157,7 +157,7 @@ Environment:
|
|
|
157
157
|
if (shardsOpt !== null || routesCmd) {
|
|
158
158
|
const { apiBase: apiBase2, websiteName: configWebsiteName2 } = await resolveApiBase(mode);
|
|
159
159
|
const websiteName2 = process.env.WEBSITE_NAME ?? configWebsiteName2 ?? "default";
|
|
160
|
-
const { runSharded } = await Promise.resolve().then(() => require("../shard-runner-
|
|
160
|
+
const { runSharded } = await Promise.resolve().then(() => require("../shard-runner-C6BtYRC2.cjs"));
|
|
161
161
|
const code = await runSharded({
|
|
162
162
|
shards: shardsOpt ?? "auto",
|
|
163
163
|
mode,
|
|
@@ -303,6 +303,7 @@ ${"=".repeat(60)}`);
|
|
|
303
303
|
}
|
|
304
304
|
}
|
|
305
305
|
if (validateOutput && shardTotal === 1) {
|
|
306
|
+
await llmArtifacts.validatePreviewOutput("dist");
|
|
306
307
|
await llmArtifacts.validateInternalLinks("dist");
|
|
307
308
|
await llmArtifacts.validateNoRedirectingLinks("dist");
|
|
308
309
|
console.log("[blox-ssg] validated internal links");
|
package/dist/ssg/cli.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import process from "node:process";
|
|
3
3
|
import fs from "node:fs/promises";
|
|
4
4
|
import { resolveBloxBuildGrant } from "./build-grant.mjs";
|
|
5
|
-
import { v as validateSsgOutput, u as uploadBloxManifest, p as polyfillBloxSsgGlobals, f as fetchCmsSiteData, b as buildCollectionResolver, a as prerender, c as
|
|
5
|
+
import { v as validateSsgOutput, u as uploadBloxManifest, p as polyfillBloxSsgGlobals, f as fetchCmsSiteData, b as buildCollectionResolver, a as prerender, c as validatePreviewOutput, d as validateInternalLinks, e as validateNoRedirectingLinks } from "../validate-output-BTvdILwE.js";
|
|
6
6
|
import { s as setPageSlugMap } from "../localeStrategy-hXlNof1E.js";
|
|
7
7
|
function installFetchTimeout() {
|
|
8
8
|
const ms = Number(process.env.BLOX_SSG_FETCH_TIMEOUT_MS) || 2e4;
|
|
@@ -156,7 +156,7 @@ Environment:
|
|
|
156
156
|
if (shardsOpt !== null || routesCmd) {
|
|
157
157
|
const { apiBase: apiBase2, websiteName: configWebsiteName2 } = await resolveApiBase(mode);
|
|
158
158
|
const websiteName2 = process.env.WEBSITE_NAME ?? configWebsiteName2 ?? "default";
|
|
159
|
-
const { runSharded } = await import("../shard-runner-
|
|
159
|
+
const { runSharded } = await import("../shard-runner-BGMCtBmP.js");
|
|
160
160
|
const code = await runSharded({
|
|
161
161
|
shards: shardsOpt ?? "auto",
|
|
162
162
|
mode,
|
|
@@ -302,6 +302,7 @@ ${"=".repeat(60)}`);
|
|
|
302
302
|
}
|
|
303
303
|
}
|
|
304
304
|
if (validateOutput && shardTotal === 1) {
|
|
305
|
+
await validatePreviewOutput("dist");
|
|
305
306
|
await validateInternalLinks("dist");
|
|
306
307
|
await validateNoRedirectingLinks("dist");
|
|
307
308
|
console.log("[blox-ssg] validated internal links");
|
package/dist/ssg/index.cjs
CHANGED
|
@@ -22,7 +22,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
22
22
|
mod
|
|
23
23
|
));
|
|
24
24
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
25
|
-
const llmArtifacts = require("../validate-output-
|
|
25
|
+
const llmArtifacts = require("../validate-output-CAcHbUBv.cjs");
|
|
26
26
|
const ssg_buildGrant = require("./build-grant.cjs");
|
|
27
27
|
const ssg_client = require("../client-AoDTJJXJ.cjs");
|
|
28
28
|
const vue = require("vue");
|
package/dist/ssg/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { j as buildPageHead, k as primaryContextEntry, l as buildJsonLd, m as extractPageText, B as BloxBuildResponseError, n as bloxBuildJson } from "../validate-output-BTvdILwE.js";
|
|
2
|
+
import { o, q, f, r, s, t, w, g, x, y, z, A, C, D, p, a, E, F, u, d, v } from "../validate-output-BTvdILwE.js";
|
|
3
3
|
import { parseBloxBuildGrantBody, resolveBloxBuildGrant } from "./build-grant.mjs";
|
|
4
4
|
import { a as BLOX_HYDRATION_WINDOW_KEY } from "../client-Ce9fRCGH.js";
|
|
5
5
|
import { createSSRApp, h, Suspense } from "vue";
|
|
@@ -443,35 +443,35 @@ export {
|
|
|
443
443
|
BLOX_HYDRATION_WINDOW_KEY,
|
|
444
444
|
BloxBuildResponseError,
|
|
445
445
|
bloxBuildJson,
|
|
446
|
-
|
|
446
|
+
o as bloxBuildRequest,
|
|
447
447
|
buildJsonLd,
|
|
448
448
|
buildPageHead,
|
|
449
|
-
|
|
449
|
+
q as buildSiteHead,
|
|
450
450
|
createBloxSSREntry,
|
|
451
451
|
extractPageText,
|
|
452
452
|
f as fetchCmsSiteData,
|
|
453
453
|
fetchDatastoreSnapshot,
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
454
|
+
r as generateLlmsFullTxt,
|
|
455
|
+
s as generateLlmsTxt,
|
|
456
|
+
t as generateNetlifyRedirects,
|
|
457
|
+
w as generateRobotsTxt,
|
|
458
458
|
g as generateSitemapArtifacts,
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
459
|
+
x as generateSitemapIndexXml,
|
|
460
|
+
y as generateSitemapXml,
|
|
461
|
+
z as getBloxBuildRequestOptions,
|
|
462
|
+
A as getSiteIconFlags,
|
|
463
|
+
C as localeSitemapPath,
|
|
464
|
+
D as mergeLlmParts,
|
|
465
465
|
parseBloxBuildGrantBody,
|
|
466
466
|
p as polyfillBloxSsgGlobals,
|
|
467
467
|
a as prerender,
|
|
468
468
|
querySnapshotRows,
|
|
469
469
|
renderBloxSsgPage,
|
|
470
470
|
resolveBloxBuildGrant,
|
|
471
|
-
|
|
471
|
+
E as resolveManifestProvenance,
|
|
472
472
|
snapshotRows,
|
|
473
|
-
|
|
473
|
+
F as splitSitemapPathsByLocale,
|
|
474
474
|
u as uploadBloxManifest,
|
|
475
|
-
|
|
475
|
+
d as validateInternalLinks,
|
|
476
476
|
v as validateSsgOutput
|
|
477
477
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prerender.d.ts","sourceRoot":"","sources":["../../src/ssg/prerender.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACvE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAA;
|
|
1
|
+
{"version":3,"file":"prerender.d.ts","sourceRoot":"","sources":["../../src/ssg/prerender.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const PREVIEW_PATH = "/_blox_preview";
|
|
2
|
+
export declare const PREVIEW_FILE = "_blox_preview.html";
|
|
3
|
+
export declare const PREVIEW_REWRITES = "/_blox_preview /_blox_preview.html 200!\n/_blox_preview/* /_blox_preview.html 200!\n";
|
|
4
|
+
export declare function isPreviewPath(value: string): boolean;
|
|
5
|
+
/** The client shell, never the prerendered homepage, boots the editor iframe. */
|
|
6
|
+
export declare function writePreviewShell(outDir: string, rawTemplate: string): Promise<void>;
|
|
7
|
+
/** Reserved rules always precede CMS and user catchalls; stale overrides lose. */
|
|
8
|
+
export declare function previewRedirects(existing: string, cms: string): string;
|
|
9
|
+
//# sourceMappingURL=preview.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../src/ssg/preview.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,YAAY,mBAAmB,CAAA;AAC5C,eAAO,MAAM,YAAY,uBAAuB,CAAA;AAChD,eAAO,MAAM,gBAAgB,yFAAqF,CAAA;AAElH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAGpD;AAED,iFAAiF;AACjF,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgB1F;AAED,kFAAkF;AAClF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAStE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shard-runner.d.ts","sourceRoot":"","sources":["../../src/ssg/shard-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAQH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"shard-runner.d.ts","sourceRoot":"","sources":["../../src/ssg/shard-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAQH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAA;AAO9D,MAAM,WAAW,kBAAkB;IAClC,qDAAqD;IACrD,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;IACvB,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAA;IACZ,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAA;IACf,sDAAsD;IACtD,WAAW,EAAE,MAAM,CAAA;IACnB,yEAAyE;IACzE,YAAY,CAAC,EAAE,uBAAuB,CAAA;IACtC,iFAAiF;IACjF,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,8FAA8F;IAC9F,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CAClB;AAWD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAQ/E;AAED,wBAAgB,uBAAuB,CAAC,CAAC,SAAS;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAClF,MAAM,EAAE,CAAC,EAAE,EACX,WAAW,EAAE,OAAO,GAAG,SAAS,GAC9B,CAAC,EAAE,CAOL;AAsED;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAiK1E"}
|
|
@@ -35,5 +35,6 @@ export interface ValidateSsgOutputOptions {
|
|
|
35
35
|
* remain for external/legacy URLs only.
|
|
36
36
|
*/
|
|
37
37
|
export declare function validateNoRedirectingLinks(outDir: string): Promise<void>;
|
|
38
|
+
export declare function validatePreviewOutput(outDir: string): Promise<void>;
|
|
38
39
|
export declare function validateSsgOutput(options: ValidateSsgOutputOptions): Promise<void>;
|
|
39
40
|
//# sourceMappingURL=validate-output.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-output.d.ts","sourceRoot":"","sources":["../../src/ssg/validate-output.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"validate-output.d.ts","sourceRoot":"","sources":["../../src/ssg/validate-output.ts"],"names":[],"mappings":"AAYA;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBzE;AAED,MAAM,WAAW,wBAAwB;IACxC,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B;;;;;;;OAOG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC5B;AAED;;;;;;;GAOG;AACH,wBAAsB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA4B9E;AAqED,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA6BzE;AAED,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6ExF"}
|
|
@@ -814,6 +814,38 @@ function absUrl(value) {
|
|
|
814
814
|
if (/^(https?:)?\/\//i.test(v) || v.startsWith("/")) return v;
|
|
815
815
|
return `${FILES_BASE_URL}/${v.replace(/^\/+/, "")}`;
|
|
816
816
|
}
|
|
817
|
+
const PREVIEW_PATH = "/_blox_preview";
|
|
818
|
+
const PREVIEW_FILE = "_blox_preview.html";
|
|
819
|
+
const PREVIEW_REWRITES = `${PREVIEW_PATH} /${PREVIEW_FILE} 200!
|
|
820
|
+
${PREVIEW_PATH}/* /${PREVIEW_FILE} 200!
|
|
821
|
+
`;
|
|
822
|
+
function isPreviewPath(value) {
|
|
823
|
+
const pathname = value.split(/[?#]/)[0].replace(/\/+$/, "");
|
|
824
|
+
return pathname === PREVIEW_PATH || pathname === `/${PREVIEW_FILE}` || pathname.startsWith(`${PREVIEW_PATH}/`);
|
|
825
|
+
}
|
|
826
|
+
async function writePreviewShell(outDir, rawTemplate) {
|
|
827
|
+
const target = path.join(outDir, PREVIEW_FILE);
|
|
828
|
+
try {
|
|
829
|
+
await fs.access(target);
|
|
830
|
+
return;
|
|
831
|
+
} catch {
|
|
832
|
+
}
|
|
833
|
+
if (/__BLOX_(?:HYDRATION|STATE|DATA)__|data-server-rendered/.test(rawTemplate)) {
|
|
834
|
+
throw new Error("Cannot create Blox preview shell from prerendered HTML; rebuild the Vite client first");
|
|
835
|
+
}
|
|
836
|
+
const shell = stripTemplateSeoTags(rawTemplate).replace(/<meta\b[^>]*\bname\s*=\s*["'](?:robots|googlebot|bingbot)["'][^>]*>/gi, "").replace(/<link\b[^>]*\bhreflang\s*=[^>]*>/gi, "").replace(/<\/head>/i, '<title>Blox editor preview</title>\n<meta name="robots" content="noindex, nofollow">\n</head>');
|
|
837
|
+
await fs.writeFile(target, shell, "utf8");
|
|
838
|
+
}
|
|
839
|
+
function previewRedirects(existing, cms) {
|
|
840
|
+
const clean = (content) => content.split("\n").filter((line) => {
|
|
841
|
+
const source = line.trim().split(/\s+/)[0];
|
|
842
|
+
return !source || source.startsWith("#") || !isPreviewPath(source.replace(/\*$/, ""));
|
|
843
|
+
}).join("\n");
|
|
844
|
+
const cmsRules = clean(cms);
|
|
845
|
+
const generated = new Set(cmsRules.split("\n").filter((line) => line.trim() && !line.trim().startsWith("#")));
|
|
846
|
+
const userRules = clean(existing).split("\n").filter((line) => !generated.has(line)).join("\n");
|
|
847
|
+
return PREVIEW_REWRITES + [cmsRules.trim(), userRules.trim()].filter(Boolean).join("\n") + "\n";
|
|
848
|
+
}
|
|
817
849
|
function typeFromContextKey(key) {
|
|
818
850
|
const k = key.replace(/^\$/, "").toLowerCase();
|
|
819
851
|
if (/(post|article|blog|story|news)/.test(k)) return "Article";
|
|
@@ -1075,6 +1107,7 @@ async function prerender({
|
|
|
1075
1107
|
const absServerEntry = path.resolve(absRoot, serverEntry);
|
|
1076
1108
|
const templatePath = path.join(absClient, "index.html");
|
|
1077
1109
|
const rawTemplate = await fs.readFile(templatePath, "utf8");
|
|
1110
|
+
if (writeSharedAssets) await writePreviewShell(absClient, rawTemplate);
|
|
1078
1111
|
let template = stripTemplateSeoTags(rawTemplate, getSiteIconFlags(website));
|
|
1079
1112
|
const siteHead = buildSiteHead(website);
|
|
1080
1113
|
if (siteHead) {
|
|
@@ -1257,33 +1290,30 @@ async function prerender({
|
|
|
1257
1290
|
console.log(" Generated robots.txt");
|
|
1258
1291
|
if ((_e = website == null ? void 0 : website.seo) == null ? void 0 : _e.noindex) {
|
|
1259
1292
|
const headersPath = path.join(absClient, "_headers");
|
|
1260
|
-
let
|
|
1293
|
+
let existing2 = "";
|
|
1261
1294
|
try {
|
|
1262
|
-
|
|
1295
|
+
existing2 = await fs.readFile(headersPath, "utf8");
|
|
1263
1296
|
} catch {
|
|
1264
1297
|
}
|
|
1265
|
-
if (!
|
|
1298
|
+
if (!existing2.includes("X-Robots-Tag")) {
|
|
1266
1299
|
await fs.writeFile(
|
|
1267
1300
|
headersPath,
|
|
1268
1301
|
`/*
|
|
1269
1302
|
X-Robots-Tag: noindex, nofollow
|
|
1270
|
-
${
|
|
1303
|
+
${existing2}`,
|
|
1271
1304
|
"utf8"
|
|
1272
1305
|
);
|
|
1273
1306
|
console.log(" Generated _headers (X-Robots-Tag: noindex)");
|
|
1274
1307
|
}
|
|
1275
1308
|
}
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
existing = await fs.readFile(redirectsPath, "utf8");
|
|
1282
|
-
} catch {
|
|
1283
|
-
}
|
|
1284
|
-
await fs.writeFile(redirectsPath, redirectsContent + existing, "utf8");
|
|
1285
|
-
console.log(` Generated _redirects (${redirects.length} rules)`);
|
|
1309
|
+
const redirectsPath = path.join(absClient, "_redirects");
|
|
1310
|
+
let existing = "";
|
|
1311
|
+
try {
|
|
1312
|
+
existing = await fs.readFile(redirectsPath, "utf8");
|
|
1313
|
+
} catch {
|
|
1286
1314
|
}
|
|
1315
|
+
await fs.writeFile(redirectsPath, previewRedirects(existing, generateNetlifyRedirects(redirects)), "utf8");
|
|
1316
|
+
console.log(` Generated _redirects (${redirects.length} CMS rules + editor preview)`);
|
|
1287
1317
|
if (!((_f = website == null ? void 0 : website.seo) == null ? void 0 : _f.noindex) && llmEnabled && llm.writeIndex !== false && llmPages.length > 0) {
|
|
1288
1318
|
const indexable = llmPages.filter((p) => !noindexPaths.has(p.url));
|
|
1289
1319
|
const llmsTxt = generateLlmsTxt({ website, pages: indexable, config: llm });
|
|
@@ -1338,6 +1368,7 @@ function normalizePath$1(p) {
|
|
|
1338
1368
|
}
|
|
1339
1369
|
function enqueue(p, queue, seen, isExcluded) {
|
|
1340
1370
|
const np = normalizePath$1(p);
|
|
1371
|
+
if (isPreviewPath(np)) return;
|
|
1341
1372
|
if (seen.has(np)) return;
|
|
1342
1373
|
if (isExcluded(np)) return;
|
|
1343
1374
|
seen.add(np);
|
|
@@ -1669,8 +1700,35 @@ async function readSiteNoindex(outDir) {
|
|
|
1669
1700
|
}
|
|
1670
1701
|
return false;
|
|
1671
1702
|
}
|
|
1703
|
+
async function validatePreviewOutput(outDir) {
|
|
1704
|
+
var _a, _b;
|
|
1705
|
+
const html = await fs.readFile(path.join(outDir, PREVIEW_FILE), "utf8");
|
|
1706
|
+
const redirects = await fs.readFile(path.join(outDir, "_redirects"), "utf8");
|
|
1707
|
+
const { Window } = await import("happy-dom");
|
|
1708
|
+
const window = new Window();
|
|
1709
|
+
try {
|
|
1710
|
+
const doc = window.document;
|
|
1711
|
+
doc.write(html);
|
|
1712
|
+
const robots = ((_a = doc.querySelector('meta[name="robots"]')) == null ? void 0 : _a.getAttribute("content")) ?? "";
|
|
1713
|
+
const app = doc.querySelector("#app");
|
|
1714
|
+
const scripts = [...doc.querySelectorAll('script[type="module"][src]')];
|
|
1715
|
+
if (!/\bnoindex\b/i.test(robots) || !/\bnofollow\b/i.test(robots) || doc.querySelector('link[rel="canonical"], link[hreflang]') || /__BLOX_(?:HYDRATION|STATE|DATA)__|data-server-rendered/.test(html) || (app ? app.children.length > 0 || !!((_b = app.textContent) == null ? void 0 : _b.trim()) : !html.includes("<!--app-html-->")) || !scripts.length || unhashedGeneratedAssets(html).length || !redirects.startsWith(PREVIEW_REWRITES)) {
|
|
1716
|
+
throw new Error("Invalid Blox preview output: expected a clean noindex client shell and first-priority preview rewrites");
|
|
1717
|
+
}
|
|
1718
|
+
for (const script of scripts) {
|
|
1719
|
+
const src = script.getAttribute("src") ?? "";
|
|
1720
|
+
if (!/^\/assets\/[A-Za-z0-9_./-]+$/.test(src) || src.split("/").includes("..") || !contentHash.test(src)) {
|
|
1721
|
+
throw new Error(`Invalid Blox preview client asset: ${src}`);
|
|
1722
|
+
}
|
|
1723
|
+
await fs.access(path.join(outDir, src));
|
|
1724
|
+
}
|
|
1725
|
+
} finally {
|
|
1726
|
+
await window.happyDOM.close();
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1672
1729
|
async function validateSsgOutput(options) {
|
|
1673
1730
|
const outDir = path.resolve(options.outDir);
|
|
1731
|
+
await validatePreviewOutput(outDir);
|
|
1674
1732
|
const failures = [];
|
|
1675
1733
|
const checkInternalLinks = options.checkInternalLinks ?? true;
|
|
1676
1734
|
const requireHashedAssets = options.requireHashedAssets ?? true;
|
|
@@ -1686,6 +1744,7 @@ async function validateSsgOutput(options) {
|
|
|
1686
1744
|
for (const file of htmlFiles) {
|
|
1687
1745
|
const name = path.relative(outDir, file);
|
|
1688
1746
|
const html = await fs.readFile(file, "utf8");
|
|
1747
|
+
if (name === PREVIEW_FILE) continue;
|
|
1689
1748
|
if (embeddedCmsState.test(html)) hydratedFiles += 1;
|
|
1690
1749
|
if (!/<title>[^<]+<\/title>/i.test(html)) failures.push(`${name}: missing page title`);
|
|
1691
1750
|
if (!/<link\s[^>]*rel=["']canonical["'][^>]*>/i.test(html)) {
|
|
@@ -1794,35 +1853,37 @@ async function validateSitemap(outDir, content) {
|
|
|
1794
1853
|
return failures;
|
|
1795
1854
|
}
|
|
1796
1855
|
export {
|
|
1797
|
-
|
|
1856
|
+
getSiteIconFlags as A,
|
|
1798
1857
|
BloxBuildResponseError as B,
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1858
|
+
localeSitemapPath as C,
|
|
1859
|
+
mergeLlmParts as D,
|
|
1860
|
+
resolveManifestProvenance as E,
|
|
1861
|
+
splitSitemapPathsByLocale as F,
|
|
1862
|
+
llmArtifacts as G,
|
|
1802
1863
|
prerender as a,
|
|
1803
1864
|
buildCollectionResolver as b,
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1865
|
+
validatePreviewOutput as c,
|
|
1866
|
+
validateInternalLinks as d,
|
|
1867
|
+
validateNoRedirectingLinks as e,
|
|
1807
1868
|
fetchCmsSiteData as f,
|
|
1808
1869
|
generateSitemapArtifacts as g,
|
|
1809
|
-
|
|
1870
|
+
isPreviewPath as h,
|
|
1810
1871
|
isErrorPagePath as i,
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1872
|
+
buildPageHead as j,
|
|
1873
|
+
primaryContextEntry as k,
|
|
1874
|
+
buildJsonLd as l,
|
|
1875
|
+
extractPageText as m,
|
|
1876
|
+
bloxBuildJson as n,
|
|
1877
|
+
bloxBuildRequest as o,
|
|
1817
1878
|
polyfillBloxSsgGlobals as p,
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1879
|
+
buildSiteHead as q,
|
|
1880
|
+
generateLlmsFullTxt as r,
|
|
1881
|
+
generateLlmsTxt as s,
|
|
1882
|
+
generateNetlifyRedirects as t,
|
|
1822
1883
|
uploadBloxManifest as u,
|
|
1823
1884
|
validateSsgOutput as v,
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1885
|
+
generateRobotsTxt as w,
|
|
1886
|
+
generateSitemapIndexXml as x,
|
|
1887
|
+
generateSitemapXml as y,
|
|
1888
|
+
getBloxBuildRequestOptions as z
|
|
1828
1889
|
};
|
|
@@ -837,6 +837,38 @@ function absUrl(value) {
|
|
|
837
837
|
if (/^(https?:)?\/\//i.test(v) || v.startsWith("/")) return v;
|
|
838
838
|
return `${FILES_BASE_URL}/${v.replace(/^\/+/, "")}`;
|
|
839
839
|
}
|
|
840
|
+
const PREVIEW_PATH = "/_blox_preview";
|
|
841
|
+
const PREVIEW_FILE = "_blox_preview.html";
|
|
842
|
+
const PREVIEW_REWRITES = `${PREVIEW_PATH} /${PREVIEW_FILE} 200!
|
|
843
|
+
${PREVIEW_PATH}/* /${PREVIEW_FILE} 200!
|
|
844
|
+
`;
|
|
845
|
+
function isPreviewPath(value) {
|
|
846
|
+
const pathname = value.split(/[?#]/)[0].replace(/\/+$/, "");
|
|
847
|
+
return pathname === PREVIEW_PATH || pathname === `/${PREVIEW_FILE}` || pathname.startsWith(`${PREVIEW_PATH}/`);
|
|
848
|
+
}
|
|
849
|
+
async function writePreviewShell(outDir, rawTemplate) {
|
|
850
|
+
const target = path.join(outDir, PREVIEW_FILE);
|
|
851
|
+
try {
|
|
852
|
+
await fs.access(target);
|
|
853
|
+
return;
|
|
854
|
+
} catch {
|
|
855
|
+
}
|
|
856
|
+
if (/__BLOX_(?:HYDRATION|STATE|DATA)__|data-server-rendered/.test(rawTemplate)) {
|
|
857
|
+
throw new Error("Cannot create Blox preview shell from prerendered HTML; rebuild the Vite client first");
|
|
858
|
+
}
|
|
859
|
+
const shell = stripTemplateSeoTags(rawTemplate).replace(/<meta\b[^>]*\bname\s*=\s*["'](?:robots|googlebot|bingbot)["'][^>]*>/gi, "").replace(/<link\b[^>]*\bhreflang\s*=[^>]*>/gi, "").replace(/<\/head>/i, '<title>Blox editor preview</title>\n<meta name="robots" content="noindex, nofollow">\n</head>');
|
|
860
|
+
await fs.writeFile(target, shell, "utf8");
|
|
861
|
+
}
|
|
862
|
+
function previewRedirects(existing, cms) {
|
|
863
|
+
const clean = (content) => content.split("\n").filter((line) => {
|
|
864
|
+
const source = line.trim().split(/\s+/)[0];
|
|
865
|
+
return !source || source.startsWith("#") || !isPreviewPath(source.replace(/\*$/, ""));
|
|
866
|
+
}).join("\n");
|
|
867
|
+
const cmsRules = clean(cms);
|
|
868
|
+
const generated = new Set(cmsRules.split("\n").filter((line) => line.trim() && !line.trim().startsWith("#")));
|
|
869
|
+
const userRules = clean(existing).split("\n").filter((line) => !generated.has(line)).join("\n");
|
|
870
|
+
return PREVIEW_REWRITES + [cmsRules.trim(), userRules.trim()].filter(Boolean).join("\n") + "\n";
|
|
871
|
+
}
|
|
840
872
|
function typeFromContextKey(key) {
|
|
841
873
|
const k = key.replace(/^\$/, "").toLowerCase();
|
|
842
874
|
if (/(post|article|blog|story|news)/.test(k)) return "Article";
|
|
@@ -1098,6 +1130,7 @@ async function prerender({
|
|
|
1098
1130
|
const absServerEntry = path.resolve(absRoot, serverEntry);
|
|
1099
1131
|
const templatePath = path.join(absClient, "index.html");
|
|
1100
1132
|
const rawTemplate = await fs.readFile(templatePath, "utf8");
|
|
1133
|
+
if (writeSharedAssets) await writePreviewShell(absClient, rawTemplate);
|
|
1101
1134
|
let template = stripTemplateSeoTags(rawTemplate, getSiteIconFlags(website));
|
|
1102
1135
|
const siteHead = buildSiteHead(website);
|
|
1103
1136
|
if (siteHead) {
|
|
@@ -1280,33 +1313,30 @@ async function prerender({
|
|
|
1280
1313
|
console.log(" Generated robots.txt");
|
|
1281
1314
|
if ((_e = website == null ? void 0 : website.seo) == null ? void 0 : _e.noindex) {
|
|
1282
1315
|
const headersPath = path.join(absClient, "_headers");
|
|
1283
|
-
let
|
|
1316
|
+
let existing2 = "";
|
|
1284
1317
|
try {
|
|
1285
|
-
|
|
1318
|
+
existing2 = await fs.readFile(headersPath, "utf8");
|
|
1286
1319
|
} catch {
|
|
1287
1320
|
}
|
|
1288
|
-
if (!
|
|
1321
|
+
if (!existing2.includes("X-Robots-Tag")) {
|
|
1289
1322
|
await fs.writeFile(
|
|
1290
1323
|
headersPath,
|
|
1291
1324
|
`/*
|
|
1292
1325
|
X-Robots-Tag: noindex, nofollow
|
|
1293
|
-
${
|
|
1326
|
+
${existing2}`,
|
|
1294
1327
|
"utf8"
|
|
1295
1328
|
);
|
|
1296
1329
|
console.log(" Generated _headers (X-Robots-Tag: noindex)");
|
|
1297
1330
|
}
|
|
1298
1331
|
}
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
existing = await fs.readFile(redirectsPath, "utf8");
|
|
1305
|
-
} catch {
|
|
1306
|
-
}
|
|
1307
|
-
await fs.writeFile(redirectsPath, redirectsContent + existing, "utf8");
|
|
1308
|
-
console.log(` Generated _redirects (${redirects.length} rules)`);
|
|
1332
|
+
const redirectsPath = path.join(absClient, "_redirects");
|
|
1333
|
+
let existing = "";
|
|
1334
|
+
try {
|
|
1335
|
+
existing = await fs.readFile(redirectsPath, "utf8");
|
|
1336
|
+
} catch {
|
|
1309
1337
|
}
|
|
1338
|
+
await fs.writeFile(redirectsPath, previewRedirects(existing, generateNetlifyRedirects(redirects)), "utf8");
|
|
1339
|
+
console.log(` Generated _redirects (${redirects.length} CMS rules + editor preview)`);
|
|
1310
1340
|
if (!((_f = website == null ? void 0 : website.seo) == null ? void 0 : _f.noindex) && llmEnabled && llm.writeIndex !== false && llmPages.length > 0) {
|
|
1311
1341
|
const indexable = llmPages.filter((p) => !noindexPaths.has(p.url));
|
|
1312
1342
|
const llmsTxt = generateLlmsTxt({ website, pages: indexable, config: llm });
|
|
@@ -1361,6 +1391,7 @@ function normalizePath$1(p) {
|
|
|
1361
1391
|
}
|
|
1362
1392
|
function enqueue(p, queue, seen, isExcluded) {
|
|
1363
1393
|
const np = normalizePath$1(p);
|
|
1394
|
+
if (isPreviewPath(np)) return;
|
|
1364
1395
|
if (seen.has(np)) return;
|
|
1365
1396
|
if (isExcluded(np)) return;
|
|
1366
1397
|
seen.add(np);
|
|
@@ -1692,8 +1723,35 @@ async function readSiteNoindex(outDir) {
|
|
|
1692
1723
|
}
|
|
1693
1724
|
return false;
|
|
1694
1725
|
}
|
|
1726
|
+
async function validatePreviewOutput(outDir) {
|
|
1727
|
+
var _a, _b;
|
|
1728
|
+
const html = await fs.readFile(path.join(outDir, PREVIEW_FILE), "utf8");
|
|
1729
|
+
const redirects = await fs.readFile(path.join(outDir, "_redirects"), "utf8");
|
|
1730
|
+
const { Window } = await import("happy-dom");
|
|
1731
|
+
const window = new Window();
|
|
1732
|
+
try {
|
|
1733
|
+
const doc = window.document;
|
|
1734
|
+
doc.write(html);
|
|
1735
|
+
const robots = ((_a = doc.querySelector('meta[name="robots"]')) == null ? void 0 : _a.getAttribute("content")) ?? "";
|
|
1736
|
+
const app = doc.querySelector("#app");
|
|
1737
|
+
const scripts = [...doc.querySelectorAll('script[type="module"][src]')];
|
|
1738
|
+
if (!/\bnoindex\b/i.test(robots) || !/\bnofollow\b/i.test(robots) || doc.querySelector('link[rel="canonical"], link[hreflang]') || /__BLOX_(?:HYDRATION|STATE|DATA)__|data-server-rendered/.test(html) || (app ? app.children.length > 0 || !!((_b = app.textContent) == null ? void 0 : _b.trim()) : !html.includes("<!--app-html-->")) || !scripts.length || unhashedGeneratedAssets(html).length || !redirects.startsWith(PREVIEW_REWRITES)) {
|
|
1739
|
+
throw new Error("Invalid Blox preview output: expected a clean noindex client shell and first-priority preview rewrites");
|
|
1740
|
+
}
|
|
1741
|
+
for (const script of scripts) {
|
|
1742
|
+
const src = script.getAttribute("src") ?? "";
|
|
1743
|
+
if (!/^\/assets\/[A-Za-z0-9_./-]+$/.test(src) || src.split("/").includes("..") || !contentHash.test(src)) {
|
|
1744
|
+
throw new Error(`Invalid Blox preview client asset: ${src}`);
|
|
1745
|
+
}
|
|
1746
|
+
await fs.access(path.join(outDir, src));
|
|
1747
|
+
}
|
|
1748
|
+
} finally {
|
|
1749
|
+
await window.happyDOM.close();
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1695
1752
|
async function validateSsgOutput(options) {
|
|
1696
1753
|
const outDir = path.resolve(options.outDir);
|
|
1754
|
+
await validatePreviewOutput(outDir);
|
|
1697
1755
|
const failures = [];
|
|
1698
1756
|
const checkInternalLinks = options.checkInternalLinks ?? true;
|
|
1699
1757
|
const requireHashedAssets = options.requireHashedAssets ?? true;
|
|
@@ -1709,6 +1767,7 @@ async function validateSsgOutput(options) {
|
|
|
1709
1767
|
for (const file of htmlFiles) {
|
|
1710
1768
|
const name = path.relative(outDir, file);
|
|
1711
1769
|
const html = await fs.readFile(file, "utf8");
|
|
1770
|
+
if (name === PREVIEW_FILE) continue;
|
|
1712
1771
|
if (embeddedCmsState.test(html)) hydratedFiles += 1;
|
|
1713
1772
|
if (!/<title>[^<]+<\/title>/i.test(html)) failures.push(`${name}: missing page title`);
|
|
1714
1773
|
if (!/<link\s[^>]*rel=["']canonical["'][^>]*>/i.test(html)) {
|
|
@@ -1835,6 +1894,7 @@ exports.generateSitemapXml = generateSitemapXml;
|
|
|
1835
1894
|
exports.getBloxBuildRequestOptions = getBloxBuildRequestOptions;
|
|
1836
1895
|
exports.getSiteIconFlags = getSiteIconFlags;
|
|
1837
1896
|
exports.isErrorPagePath = isErrorPagePath;
|
|
1897
|
+
exports.isPreviewPath = isPreviewPath;
|
|
1838
1898
|
exports.llmArtifacts = llmArtifacts;
|
|
1839
1899
|
exports.localeSitemapPath = localeSitemapPath;
|
|
1840
1900
|
exports.mergeLlmParts = mergeLlmParts;
|
|
@@ -1846,4 +1906,5 @@ exports.splitSitemapPathsByLocale = splitSitemapPathsByLocale;
|
|
|
1846
1906
|
exports.uploadBloxManifest = uploadBloxManifest;
|
|
1847
1907
|
exports.validateInternalLinks = validateInternalLinks;
|
|
1848
1908
|
exports.validateNoRedirectingLinks = validateNoRedirectingLinks;
|
|
1909
|
+
exports.validatePreviewOutput = validatePreviewOutput;
|
|
1849
1910
|
exports.validateSsgOutput = validateSsgOutput;
|
package/package.json
CHANGED