From 50fbf2e63c5d0ff865fdd7b53cad3e1e5d244d95 Mon Sep 17 00:00:00 2001
From: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Date: Tue, 15 Sep 2026 19:17:43 +0800
Subject: [PATCH] fix(frontstage): classify exported page links as pages or
shipped assets
The editorial blog pages link their architecture diagrams with relative
targets. The share-bundle smoke
treated every relative as a page directory and asserted
/index.html, so main has failed the Frontstage Pages build since the
diagrams shipped: it demanded position-en.svg/index.html.
Classify relative references instead: asset references must resolve to the
asset file itself, and page references must own an index.html. Local
diagram sources are now checked the same way, so a dropped diagram fails the
smoke instead of passing silently.
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
---
examples/frontstage-share-bundle-smoke.mjs | 27 ++++++++++++++++++++--
1 file changed, 25 insertions(+), 2 deletions(-)
diff --git a/examples/frontstage-share-bundle-smoke.mjs b/examples/frontstage-share-bundle-smoke.mjs
index d281870ded..45c2b4a8e2 100644
--- a/examples/frontstage-share-bundle-smoke.mjs
+++ b/examples/frontstage-share-bundle-smoke.mjs
@@ -35,6 +35,21 @@ function assertExists(path) {
}
}
+// Relative references in the exported editorial pages point at either another
+// exported page directory or a shipped static asset. Page references must own
+// an index.html; asset references must resolve to the asset file itself.
+const relativeAssetReferencePattern =
+ /\.(?:avif|css|csv|gif|ico|jpe?g|js|json|mjs|mp4|pdf|png|svg|txt|webm|webp|woff2?|xml|zip)$/i;
+const nonBundleReferencePattern = /^(?:[a-z][a-z0-9+.-]*:|\/\/|#)/i;
+
+function assertRelativeReferenceExists(pagePath, target) {
+ if (relativeAssetReferencePattern.test(target)) {
+ assertExists(resolve(dirname(pagePath), target));
+ return;
+ }
+ assertExists(resolve(dirname(pagePath), target, "index.html"));
+}
+
function assertNoLeak(text, label) {
const forbidden = [
/\/Users\//,
@@ -123,12 +138,20 @@ for (const locale of ["", "zh/"]) {
assertExists(resolve(dirname(pagePath), stylesheet[1]));
for (const match of html.matchAll(/]+href="([^"]+)"/g)) {
const href = match[1];
- if (/^(https?:|#)/.test(href)) continue;
+ if (nonBundleReferencePattern.test(href)) continue;
if (href.startsWith("/")) throw new Error("Blog navigation must preserve the hosting base");
const target = href.split(/[?#]/)[0];
// MkDocs pages are built later by the publication workflow.
if (target.includes("docs/")) continue;
- assertExists(resolve(dirname(pagePath), target, "index.html"));
+ assertRelativeReferenceExists(pagePath, target);
+ }
+ // Embedded diagrams are the primary content of these pages, so their local
+ // sources must ship in the bundle too.
+ for (const match of html.matchAll(/
]+src="([^"]+)"/g)) {
+ const src = match[1];
+ if (nonBundleReferencePattern.test(src)) continue;
+ if (src.startsWith("/")) throw new Error("Blog images must preserve the hosting base");
+ assertRelativeReferenceExists(pagePath, src.split(/[?#]/)[0]);
}
}
}