@takazudo/zfb-runtime 2.22.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -31
- package/dist/client-router/router.js +1 -1
- package/dist/client-router/router.js.map +1 -1
- package/dist/client-router/swap-functions.js +32 -19
- package/dist/client-router/swap-functions.js.map +1 -1
- package/dist/client-router-component.d.ts +3 -10
- package/dist/client-router-component.js +8 -51
- package/dist/client-router-component.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/router.d.ts +3 -6
- package/dist/router.js +7 -6
- package/dist/router.js.map +1 -1
- package/package.json +3 -13
- package/dist/framework.d.ts +0 -16
- package/dist/framework.js +0 -17
- package/dist/framework.js.map +0 -1
|
@@ -160,25 +160,38 @@ export function swapBodyElement(newElement, oldElement) {
|
|
|
160
160
|
else {
|
|
161
161
|
newTarget.replaceWith(el);
|
|
162
162
|
}
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const
|
|
178
|
-
if (
|
|
179
|
-
el.setAttribute("
|
|
180
|
-
|
|
181
|
-
|
|
163
|
+
// Compare effective identity and props on the lifted wrapper. The surviving
|
|
164
|
+
// node carries incoming metadata before the runtime recreates its root.
|
|
165
|
+
const islandAttrs = [
|
|
166
|
+
"data-zfb-island",
|
|
167
|
+
"data-zfb-island-skip-ssr",
|
|
168
|
+
"data-zfb-transport",
|
|
169
|
+
"data-zfb-protocol",
|
|
170
|
+
"data-zfb-build",
|
|
171
|
+
];
|
|
172
|
+
const isIsland = islandAttrs
|
|
173
|
+
.slice(0, 2)
|
|
174
|
+
.some((attr) => el.hasAttribute(attr) || newTarget.hasAttribute(attr));
|
|
175
|
+
if (isIsland) {
|
|
176
|
+
const identityChanged = islandAttrs.some((attr) => el.getAttribute(attr) !== newTarget.getAttribute(attr));
|
|
177
|
+
const copyProps = shouldCopyProps(el);
|
|
178
|
+
if (identityChanged || (copyProps && !isSameProps(el, newTarget))) {
|
|
179
|
+
el.setAttribute(ISLAND_REMOUNT_ATTR, "");
|
|
180
|
+
for (const attr of islandAttrs) {
|
|
181
|
+
const value = newTarget.getAttribute(attr);
|
|
182
|
+
if (value === null)
|
|
183
|
+
el.removeAttribute(attr);
|
|
184
|
+
else
|
|
185
|
+
el.setAttribute(attr, value);
|
|
186
|
+
}
|
|
187
|
+
if (copyProps) {
|
|
188
|
+
const props = newTarget.getAttribute("data-props");
|
|
189
|
+
if (props === null)
|
|
190
|
+
el.removeAttribute("data-props");
|
|
191
|
+
else
|
|
192
|
+
el.setAttribute("data-props", props);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
182
195
|
}
|
|
183
196
|
}
|
|
184
197
|
// This will upgrade any Declarative Shadow DOM in the new body.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swap-functions.js","sourceRoot":"","sources":["../../src/client-router/swap-functions.ts"],"names":[],"mappings":"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","sourcesContent":["/// <reference lib=\"dom\" />\n/// <reference lib=\"dom.iterable\" />\n// Ported verbatim from Astro transitions/swap-functions.ts. Mechanical renames\n// applied per W1B §13.4 (data-astro-* → data-zfb-*, dataset.astro* → dataset.zfb*,\n// astro-island element-name branch → data-zfb-island marker per W1B §12.3 island-lifecycle\n// contract). vueScopedStyleId is intentionally KEPT verbatim per W2A §2.8 (the W3B\n// issue body's \"DELETE\" instruction is overridden by W2A's reclassification).\n\nexport type SavedFocus = {\n activeElement: HTMLElement | null;\n start?: number | null;\n end?: number | null;\n};\n\nconst PERSIST_ATTR = \"data-zfb-transition-persist\";\n\n// Cross-package island attributes must use the same literal strings here and in\n// @takazudo/zfb's runtime: PERSIST_ATTR is read by both packages;\n// ISLAND_REMOUNT_ATTR below is written here and consumed there; that runtime's\n// public ISLAND_MOUNTED_ATTR (\"data-zfb-island-mounted\") is preserved on lifted\n// islands and cleared/re-written there during a props-changed remount.\n//\n// ISLAND_REMOUNT_ATTR is written onto a persisted island whose props changed\n// across the swap. @takazudo/zfb's mountNewIslands consumes it — the island\n// mounted-map lives in that separate package, so a DOM attribute is the only\n// channel the two packages share. Namespaced under data-zfb-* like every other\n// zfb marker so it can't collide with a bare attribute a consumer sets on the\n// same island root. Replaces the opaque `ssr` attribute the original Astro port\n// set here but nothing consumed — zfb islands are marker divs, not <astro-island>\n// custom elements. See client-router/port-spec.md §12.3 / #1389.\nconst ISLAND_REMOUNT_ATTR = \"data-zfb-island-remount\";\n\nconst NON_OVERRIDABLE_ZFB_ATTRS = [\"data-zfb-transition\", \"data-zfb-transition-fallback\"];\n\n// Consumers extend the preserve-set via <meta name=\"zfb-preserve-html-attrs\"\n// content=\"data-theme data-sidebar-hidden …\">, emitted by\n// <ClientRouter preserveHtmlAttrs={[…]} />. Without it, a runtime <html>\n// attribute set from a persisted island is wiped on every swap. See\n// client-router/port-spec.md deviation #11 and Takazudo/zudo-front-builder#1103.\nconst PRESERVE_ATTRS_META_NAME = \"zfb-preserve-html-attrs\";\n\nconst knownVueScopedStyles = new Map<string, HTMLStyleElement>();\n\nconst scriptsAlreadyRan = new Set<string>();\nexport function detectScriptExecuted(script: HTMLScriptElement) {\n const key = script.src ? new URL(script.src, location.href).href : script.textContent!;\n if (scriptsAlreadyRan.has(key)) return true;\n scriptsAlreadyRan.add(key);\n return false;\n}\n\n/*\n * \tMark new scripts that should not execute\n */\nexport function deselectScripts(doc: Document) {\n for (const s2 of doc.scripts) {\n if (\n // Check if the script should be rerun regardless of it being the same\n !s2.hasAttribute(\"data-zfb-rerun\") &&\n // Check if the script has already been executed\n detectScriptExecuted(s2)\n ) {\n // the old script is in the new document and doesn't have the rerun attribute\n // we mark it as executed to prevent re-execution\n s2.dataset[\"zfbExec\"] = \"\";\n }\n }\n}\n\n/*\n * Read the consumer-configured preserve-list from the live document's\n * <meta name=\"zfb-preserve-html-attrs\"> tag. The list is a site-wide, static\n * contract (the tag is rendered on every page, so it is present on both the\n * current and incoming documents); reading the live document is sufficient.\n * Returns [] when the tag is absent.\n */\nfunction consumerPreservedAttrs(): string[] {\n const content = document\n .querySelector(`meta[name=\"${PRESERVE_ATTRS_META_NAME}\"]`)\n ?.getAttribute(\"content\");\n // Attribute names are ASCII-case-insensitive and the DOM exposes them\n // lowercased, so normalize the list to match (NON_OVERRIDABLE_ZFB_ATTRS is\n // already lowercase). Without this, a mixed-case entry would silently never\n // match the live attribute name.\n return content ? content.toLowerCase().split(/\\s+/).filter(Boolean) : [];\n}\n\nconst attrValueSelector = (attr: string, value: string): string =>\n value === \"\" ? `[${attr}=\"\"]` : `[${attr}=${CSS.escape(value)}]`;\n\nconst querySelectorWithAttrValue = (\n root: ParentNode,\n selectorPrefix: string,\n attr: string,\n value: string,\n): Element | null => {\n const escapedMatch = root.querySelector(`${selectorPrefix}${attrValueSelector(attr, value)}`);\n if (escapedMatch) return escapedMatch;\n return (\n Array.from(root.querySelectorAll(`${selectorPrefix}[${attr}]`)).find(\n (el) => el.getAttribute(attr) === value,\n ) ?? null\n );\n};\n\n/*\n * swap attributes of the html element\n * delete all attributes from the current document\n * insert all attributes from doc\n * reinsert all original attributes whose name is in the preserve-set:\n * NON_OVERRIDABLE_ZFB_ATTRS (transition-internal) ∪ the consumer preserve-list.\n * Preserved attributes are re-applied last, so a runtime value on the current\n * root wins over the incoming document's default (e.g. a localStorage-driven\n * data-theme). See client-router/port-spec.md deviation #11 / #1103.\n */\nexport function swapRootAttributes(newDoc: Document) {\n const currentRoot = document.documentElement;\n const preserve = new Set([...NON_OVERRIDABLE_ZFB_ATTRS, ...consumerPreservedAttrs()]);\n const preservedAttributes = [...currentRoot.attributes].filter(\n ({ name }) => (currentRoot.removeAttribute(name), preserve.has(name)),\n );\n [...newDoc.documentElement.attributes, ...preservedAttributes].forEach(({ name, value }) =>\n currentRoot.setAttribute(name, value),\n );\n}\n\n/*\n * make the old head look like the new one\n */\nexport function swapHeadElements(doc: Document) {\n for (const el of Array.from(document.head.children)) {\n const newEl = persistedHeadElement(el as HTMLElement, doc);\n // If the element exists in the document already, remove it\n // from the new document and leave the current node alone\n if (newEl) {\n newEl.remove();\n } else {\n if ((import.meta as any).env?.DEV && el instanceof HTMLStyleElement) {\n // In DEV mode, keep updated Vue scoped styles for later reuse\n const viteDevId = vueScopedStyleId(el);\n viteDevId && knownVueScopedStyles.set(viteDevId, el);\n }\n // If the element does not exist in the new document, remove the element from current the head.\n el.remove();\n }\n }\n\n // Everything left in the new head is new, append it all.\n if ((import.meta as any).env?.DEV) {\n // In DEV mode, replace known Vue scoped styles with the versions we remembered\n [...doc.head.children].forEach((child) => {\n document.head.append(knownVueScopedStyles.get((child as any).dataset?.viteDevId) || child);\n });\n } else {\n document.head.append(...doc.head.children);\n }\n}\n\nexport function swapBodyElement(newElement: Element, oldElement: Element) {\n // Lift persist elements to <html> before the body swap so they stay in the DOM\n // throughout replaceWith(). This prevents Safari from losing WebGL context on\n // <canvas> elements due to brief DOM detachment. Uses moveBefore() where available\n // (Chrome 133+) for zero-detachment atomic moves.\n const persistPairs: { old: Element; newTarget: Element }[] = [];\n const docEl = oldElement.ownerDocument.documentElement;\n\n // moveBefore() is not yet in TypeScript's DOM lib, feature-detect and wrap.\n const moveBefore: ((parent: Node, node: Node, child: Node | null) => void) | null =\n typeof (docEl as any).moveBefore === \"function\"\n ? (parent, node, child) => (parent as any).moveBefore(node, child)\n : null;\n\n for (const el of oldElement.querySelectorAll(`[${PERSIST_ATTR}]`)) {\n const id = el.getAttribute(PERSIST_ATTR);\n const newEl = id !== null ? querySelectorWithAttrValue(newElement, \"\", PERSIST_ATTR, id) : null;\n if (!newEl) continue; // no matching target — leave in old body to be discarded\n persistPairs.push({ old: el, newTarget: newEl });\n if (moveBefore) {\n moveBefore(docEl, el, null);\n } else {\n docEl.appendChild(el);\n }\n }\n\n // this will reset scroll Position\n oldElement.replaceWith(newElement);\n\n // Move persist elements into the new body at the position of their targets\n for (const { old: el, newTarget } of persistPairs) {\n if (moveBefore) {\n moveBefore(newTarget.parentNode!, el, newTarget);\n newTarget.remove();\n } else {\n newTarget.replaceWith(el);\n }\n // Persist-props hybrid path (port-spec §12.3.1 hybrid case / §12.3.2): the\n // persisted island survived the lift with its live component instance, but\n // the incoming page carries different props. Refresh data-props on the\n // surviving node and flag it with ISLAND_REMOUNT_ATTR. That flag is the\n // cross-package \"needs-remount\" signal consumed by @takazudo/zfb's\n // mountNewIslands (clearMountedForRemount): it can't be an in-memory queue\n // because the islands runtime's mounted-map lives in a different package, so\n // the flagged DOM node itself is the queue. mountNewIslands unmounts the\n // stale instance and re-mounts against this node with the new data-props.\n if (\n newTarget.matches(\"[data-zfb-island]\") &&\n shouldCopyProps(el as HTMLElement) &&\n !isSameProps(el, newTarget)\n ) {\n el.setAttribute(ISLAND_REMOUNT_ATTR, \"\");\n // zfb island wrapper writes SSR props to data-props, not props (different attribute, not just renamed).\n const np = newTarget.getAttribute(\"data-props\");\n if (np !== null) el.setAttribute(\"data-props\", np);\n else el.removeAttribute(\"data-props\");\n }\n }\n\n // This will upgrade any Declarative Shadow DOM in the new body.\n attachShadowRoots(newElement);\n}\n\n/**\n * Attach Shadow DOM roots for templates with the declarative `shadowrootmode` attribute.\n * @see https://web.dev/articles/declarative-shadow-dom#polyfill\n * @param root DOM subtree to attach shadow roots within.\n */\nfunction attachShadowRoots(root: Element | ShadowRoot) {\n root.querySelectorAll<HTMLTemplateElement>(\"template[shadowrootmode]\").forEach((template) => {\n const mode = template.getAttribute(\"shadowrootmode\");\n const parent = template.parentNode;\n if ((mode === \"closed\" || mode === \"open\") && parent instanceof HTMLElement) {\n // Skip if shadow root already exists (e.g., from transition-persisted elements)\n if (parent.shadowRoot) {\n template.remove();\n return;\n }\n const shadowRoot = parent.attachShadow({ mode });\n shadowRoot.appendChild(template.content);\n template.remove();\n attachShadowRoots(shadowRoot);\n }\n });\n}\n\nexport const saveFocus = (): (() => void) => {\n const activeElement = document.activeElement as HTMLElement;\n // The element that currently has the focus is part of a DOM tree\n // that will survive the transition to the new document.\n // Save the element and the cursor position\n if (activeElement?.closest(`[${PERSIST_ATTR}]`)) {\n if (activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement) {\n const start = activeElement.selectionStart;\n const end = activeElement.selectionEnd;\n return () => restoreFocus({ activeElement, start, end });\n }\n return () => restoreFocus({ activeElement });\n } else {\n return () => restoreFocus({ activeElement: null });\n }\n};\n\nexport const restoreFocus = ({ activeElement, start, end }: SavedFocus) => {\n if (activeElement) {\n activeElement.focus();\n if (activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement) {\n if (typeof start === \"number\") activeElement.selectionStart = start;\n if (typeof end === \"number\") activeElement.selectionEnd = end;\n }\n }\n};\n\nexport const vueScopedStyleId = (el: HTMLStyleElement): string => {\n const viteDevId = el.dataset[\"viteDevId\"] || \"\";\n\n const url = new URL(viteDevId, location.href);\n return url.searchParams.get(\"vue\") !== null &&\n url.searchParams.get(\"type\") === \"style\" &&\n url.searchParams.has(\"scoped\")\n ? viteDevId\n : \"\";\n};\n\n// Check for a head element that should persist and returns it,\n// either because it has the data attribute or because replacing it would cause avoidable FOUC.\nconst persistedHeadElement = (el: HTMLElement, newDoc: Document): Element | null => {\n const id = el.getAttribute(PERSIST_ATTR);\n const newEl = id !== null ? querySelectorWithAttrValue(newDoc.head, \"\", PERSIST_ATTR, id) : null;\n if (newEl) {\n return newEl;\n }\n if (el.matches(\"link[rel=stylesheet]\")) {\n const href = el.getAttribute(\"href\");\n return href !== null\n ? querySelectorWithAttrValue(newDoc.head, \"link[rel=stylesheet]\", \"href\", href)\n : null;\n }\n // In dev mode, Vite injects <style data-vite-dev-id=\"...\"> elements whose\n // textContent may later be transformed (especially Vue's `:deep()` → `[data-v-xxx]`).\n // Match these by their stable dev ID so the already-transformed style is preserved\n // across ClientRouter soft navigations instead of being replaced by the raw version.\n // There are other ids that can't be preserved and need a refresh, like Uno's /__uno.css,\n // which keeps the same id, but with different contents.\n // To avoid enumerating all exceptions, we only apply the auto-persist logic to elements\n // that look like Vue's dev styles.\n if ((import.meta as any).env?.DEV && el instanceof HTMLStyleElement) {\n const viteDevId = vueScopedStyleId(el);\n if (viteDevId) {\n return querySelectorWithAttrValue(newDoc.head, \"style\", \"data-vite-dev-id\", viteDevId);\n }\n }\n // Preserve inline <style> elements with identical content across navigations.\n // This prevents unnecessary removal and re-insertion of styles (e.g. @font-face\n // declarations from <Font>), which would cause the browser to re-evaluate them\n // and trigger a flash of unstyled text (FOUT).\n if (el.tagName === \"STYLE\" && el.textContent) {\n const styles = newDoc.head.querySelectorAll(\"style\");\n for (const s of styles) {\n if (s.textContent === el.textContent) {\n return s;\n }\n }\n }\n // Preserve font preload links across navigations to avoid re-fetching cached fonts.\n if (el.matches(\"link[rel=preload][as=font]\")) {\n const href = el.getAttribute(\"href\");\n return href !== null\n ? querySelectorWithAttrValue(newDoc.head, \"link[rel=preload][as=font]\", \"href\", href)\n : null;\n }\n return null;\n};\n\nconst shouldCopyProps = (el: HTMLElement): boolean => {\n const persistProps = el.dataset[\"zfbTransitionPersistProps\"];\n return persistProps == null || persistProps === \"false\";\n};\n\nconst isSameProps = (oldEl: Element, newEl: Element) => {\n // zfb island wrapper writes SSR props to data-props, not props (different attribute, not just renamed).\n return oldEl.getAttribute(\"data-props\") === newEl.getAttribute(\"data-props\");\n};\n\nexport const swapFunctions = {\n deselectScripts,\n swapRootAttributes,\n swapHeadElements,\n swapBodyElement,\n saveFocus,\n};\n\nexport const swap = (doc: Document) => {\n deselectScripts(doc);\n swapRootAttributes(doc);\n swapHeadElements(doc);\n const restoreFocusFunction = saveFocus();\n swapBodyElement(doc.body, document.body);\n restoreFocusFunction();\n};\n"]}
|
|
1
|
+
{"version":3,"file":"swap-functions.js","sourceRoot":"","sources":["../../src/client-router/swap-functions.ts"],"names":[],"mappings":"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","sourcesContent":["/// <reference lib=\"dom\" />\n/// <reference lib=\"dom.iterable\" />\n// Ported verbatim from Astro transitions/swap-functions.ts. Mechanical renames\n// applied per W1B §13.4 (data-astro-* → data-zfb-*, dataset.astro* → dataset.zfb*,\n// astro-island element-name branch → data-zfb-island marker per W1B §12.3 island-lifecycle\n// contract). vueScopedStyleId is intentionally KEPT verbatim per W2A §2.8 (the W3B\n// issue body's \"DELETE\" instruction is overridden by W2A's reclassification).\n\nexport type SavedFocus = {\n activeElement: HTMLElement | null;\n start?: number | null;\n end?: number | null;\n};\n\nconst PERSIST_ATTR = \"data-zfb-transition-persist\";\n\n// Cross-package island attributes must use the same literal strings here and in\n// @takazudo/zfb's runtime: PERSIST_ATTR is read by both packages;\n// ISLAND_REMOUNT_ATTR below is written here and consumed there; that runtime's\n// public ISLAND_MOUNTED_ATTR (\"data-zfb-island-mounted\") is preserved on lifted\n// islands and cleared/re-written there during a props-changed remount.\n//\n// ISLAND_REMOUNT_ATTR is written onto a persisted island whose props changed\n// across the swap. @takazudo/zfb's mountNewIslands consumes it — the island\n// mounted-map lives in that separate package, so a DOM attribute is the only\n// channel the two packages share. Namespaced under data-zfb-* like every other\n// zfb marker so it can't collide with a bare attribute a consumer sets on the\n// same island root. Replaces the opaque `ssr` attribute the original Astro port\n// set here but nothing consumed — zfb islands are marker divs, not <astro-island>\n// custom elements. See client-router/port-spec.md §12.3 / #1389.\nconst ISLAND_REMOUNT_ATTR = \"data-zfb-island-remount\";\n\nconst NON_OVERRIDABLE_ZFB_ATTRS = [\"data-zfb-transition\", \"data-zfb-transition-fallback\"];\n\n// Consumers extend the preserve-set via <meta name=\"zfb-preserve-html-attrs\"\n// content=\"data-theme data-sidebar-hidden …\">, emitted by\n// <ClientRouter preserveHtmlAttrs={[…]} />. Without it, a runtime <html>\n// attribute set from a persisted island is wiped on every swap. See\n// client-router/port-spec.md deviation #11 and Takazudo/zudo-front-builder#1103.\nconst PRESERVE_ATTRS_META_NAME = \"zfb-preserve-html-attrs\";\n\nconst knownVueScopedStyles = new Map<string, HTMLStyleElement>();\n\nconst scriptsAlreadyRan = new Set<string>();\nexport function detectScriptExecuted(script: HTMLScriptElement) {\n const key = script.src ? new URL(script.src, location.href).href : script.textContent!;\n if (scriptsAlreadyRan.has(key)) return true;\n scriptsAlreadyRan.add(key);\n return false;\n}\n\n/*\n * \tMark new scripts that should not execute\n */\nexport function deselectScripts(doc: Document) {\n for (const s2 of doc.scripts) {\n if (\n // Check if the script should be rerun regardless of it being the same\n !s2.hasAttribute(\"data-zfb-rerun\") &&\n // Check if the script has already been executed\n detectScriptExecuted(s2)\n ) {\n // the old script is in the new document and doesn't have the rerun attribute\n // we mark it as executed to prevent re-execution\n s2.dataset[\"zfbExec\"] = \"\";\n }\n }\n}\n\n/*\n * Read the consumer-configured preserve-list from the live document's\n * <meta name=\"zfb-preserve-html-attrs\"> tag. The list is a site-wide, static\n * contract (the tag is rendered on every page, so it is present on both the\n * current and incoming documents); reading the live document is sufficient.\n * Returns [] when the tag is absent.\n */\nfunction consumerPreservedAttrs(): string[] {\n const content = document\n .querySelector(`meta[name=\"${PRESERVE_ATTRS_META_NAME}\"]`)\n ?.getAttribute(\"content\");\n // Attribute names are ASCII-case-insensitive and the DOM exposes them\n // lowercased, so normalize the list to match (NON_OVERRIDABLE_ZFB_ATTRS is\n // already lowercase). Without this, a mixed-case entry would silently never\n // match the live attribute name.\n return content ? content.toLowerCase().split(/\\s+/).filter(Boolean) : [];\n}\n\nconst attrValueSelector = (attr: string, value: string): string =>\n value === \"\" ? `[${attr}=\"\"]` : `[${attr}=${CSS.escape(value)}]`;\n\nconst querySelectorWithAttrValue = (\n root: ParentNode,\n selectorPrefix: string,\n attr: string,\n value: string,\n): Element | null => {\n const escapedMatch = root.querySelector(`${selectorPrefix}${attrValueSelector(attr, value)}`);\n if (escapedMatch) return escapedMatch;\n return (\n Array.from(root.querySelectorAll(`${selectorPrefix}[${attr}]`)).find(\n (el) => el.getAttribute(attr) === value,\n ) ?? null\n );\n};\n\n/*\n * swap attributes of the html element\n * delete all attributes from the current document\n * insert all attributes from doc\n * reinsert all original attributes whose name is in the preserve-set:\n * NON_OVERRIDABLE_ZFB_ATTRS (transition-internal) ∪ the consumer preserve-list.\n * Preserved attributes are re-applied last, so a runtime value on the current\n * root wins over the incoming document's default (e.g. a localStorage-driven\n * data-theme). See client-router/port-spec.md deviation #11 / #1103.\n */\nexport function swapRootAttributes(newDoc: Document) {\n const currentRoot = document.documentElement;\n const preserve = new Set([...NON_OVERRIDABLE_ZFB_ATTRS, ...consumerPreservedAttrs()]);\n const preservedAttributes = [...currentRoot.attributes].filter(\n ({ name }) => (currentRoot.removeAttribute(name), preserve.has(name)),\n );\n [...newDoc.documentElement.attributes, ...preservedAttributes].forEach(({ name, value }) =>\n currentRoot.setAttribute(name, value),\n );\n}\n\n/*\n * make the old head look like the new one\n */\nexport function swapHeadElements(doc: Document) {\n for (const el of Array.from(document.head.children)) {\n const newEl = persistedHeadElement(el as HTMLElement, doc);\n // If the element exists in the document already, remove it\n // from the new document and leave the current node alone\n if (newEl) {\n newEl.remove();\n } else {\n if ((import.meta as any).env?.DEV && el instanceof HTMLStyleElement) {\n // In DEV mode, keep updated Vue scoped styles for later reuse\n const viteDevId = vueScopedStyleId(el);\n viteDevId && knownVueScopedStyles.set(viteDevId, el);\n }\n // If the element does not exist in the new document, remove the element from current the head.\n el.remove();\n }\n }\n\n // Everything left in the new head is new, append it all.\n if ((import.meta as any).env?.DEV) {\n // In DEV mode, replace known Vue scoped styles with the versions we remembered\n [...doc.head.children].forEach((child) => {\n document.head.append(knownVueScopedStyles.get((child as any).dataset?.viteDevId) || child);\n });\n } else {\n document.head.append(...doc.head.children);\n }\n}\n\nexport function swapBodyElement(newElement: Element, oldElement: Element) {\n // Lift persist elements to <html> before the body swap so they stay in the DOM\n // throughout replaceWith(). This prevents Safari from losing WebGL context on\n // <canvas> elements due to brief DOM detachment. Uses moveBefore() where available\n // (Chrome 133+) for zero-detachment atomic moves.\n const persistPairs: { old: Element; newTarget: Element }[] = [];\n const docEl = oldElement.ownerDocument.documentElement;\n\n // moveBefore() is not yet in TypeScript's DOM lib, feature-detect and wrap.\n const moveBefore: ((parent: Node, node: Node, child: Node | null) => void) | null =\n typeof (docEl as any).moveBefore === \"function\"\n ? (parent, node, child) => (parent as any).moveBefore(node, child)\n : null;\n\n for (const el of oldElement.querySelectorAll(`[${PERSIST_ATTR}]`)) {\n const id = el.getAttribute(PERSIST_ATTR);\n const newEl = id !== null ? querySelectorWithAttrValue(newElement, \"\", PERSIST_ATTR, id) : null;\n if (!newEl) continue; // no matching target — leave in old body to be discarded\n persistPairs.push({ old: el, newTarget: newEl });\n if (moveBefore) {\n moveBefore(docEl, el, null);\n } else {\n docEl.appendChild(el);\n }\n }\n\n // this will reset scroll Position\n oldElement.replaceWith(newElement);\n\n // Move persist elements into the new body at the position of their targets\n for (const { old: el, newTarget } of persistPairs) {\n if (moveBefore) {\n moveBefore(newTarget.parentNode!, el, newTarget);\n newTarget.remove();\n } else {\n newTarget.replaceWith(el);\n }\n // Compare effective identity and props on the lifted wrapper. The surviving\n // node carries incoming metadata before the runtime recreates its root.\n const islandAttrs = [\n \"data-zfb-island\",\n \"data-zfb-island-skip-ssr\",\n \"data-zfb-transport\",\n \"data-zfb-protocol\",\n \"data-zfb-build\",\n ];\n const isIsland = islandAttrs\n .slice(0, 2)\n .some((attr) => el.hasAttribute(attr) || newTarget.hasAttribute(attr));\n if (isIsland) {\n const identityChanged = islandAttrs.some(\n (attr) => el.getAttribute(attr) !== newTarget.getAttribute(attr),\n );\n const copyProps = shouldCopyProps(el as HTMLElement);\n if (identityChanged || (copyProps && !isSameProps(el, newTarget))) {\n el.setAttribute(ISLAND_REMOUNT_ATTR, \"\");\n for (const attr of islandAttrs) {\n const value = newTarget.getAttribute(attr);\n if (value === null) el.removeAttribute(attr);\n else el.setAttribute(attr, value);\n }\n if (copyProps) {\n const props = newTarget.getAttribute(\"data-props\");\n if (props === null) el.removeAttribute(\"data-props\");\n else el.setAttribute(\"data-props\", props);\n }\n }\n }\n }\n\n // This will upgrade any Declarative Shadow DOM in the new body.\n attachShadowRoots(newElement);\n}\n\n/**\n * Attach Shadow DOM roots for templates with the declarative `shadowrootmode` attribute.\n * @see https://web.dev/articles/declarative-shadow-dom#polyfill\n * @param root DOM subtree to attach shadow roots within.\n */\nfunction attachShadowRoots(root: Element | ShadowRoot) {\n root.querySelectorAll<HTMLTemplateElement>(\"template[shadowrootmode]\").forEach((template) => {\n const mode = template.getAttribute(\"shadowrootmode\");\n const parent = template.parentNode;\n if ((mode === \"closed\" || mode === \"open\") && parent instanceof HTMLElement) {\n // Skip if shadow root already exists (e.g., from transition-persisted elements)\n if (parent.shadowRoot) {\n template.remove();\n return;\n }\n const shadowRoot = parent.attachShadow({ mode });\n shadowRoot.appendChild(template.content);\n template.remove();\n attachShadowRoots(shadowRoot);\n }\n });\n}\n\nexport const saveFocus = (): (() => void) => {\n const activeElement = document.activeElement as HTMLElement;\n // The element that currently has the focus is part of a DOM tree\n // that will survive the transition to the new document.\n // Save the element and the cursor position\n if (activeElement?.closest(`[${PERSIST_ATTR}]`)) {\n if (activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement) {\n const start = activeElement.selectionStart;\n const end = activeElement.selectionEnd;\n return () => restoreFocus({ activeElement, start, end });\n }\n return () => restoreFocus({ activeElement });\n } else {\n return () => restoreFocus({ activeElement: null });\n }\n};\n\nexport const restoreFocus = ({ activeElement, start, end }: SavedFocus) => {\n if (activeElement) {\n activeElement.focus();\n if (activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement) {\n if (typeof start === \"number\") activeElement.selectionStart = start;\n if (typeof end === \"number\") activeElement.selectionEnd = end;\n }\n }\n};\n\nexport const vueScopedStyleId = (el: HTMLStyleElement): string => {\n const viteDevId = el.dataset[\"viteDevId\"] || \"\";\n\n const url = new URL(viteDevId, location.href);\n return url.searchParams.get(\"vue\") !== null &&\n url.searchParams.get(\"type\") === \"style\" &&\n url.searchParams.has(\"scoped\")\n ? viteDevId\n : \"\";\n};\n\n// Check for a head element that should persist and returns it,\n// either because it has the data attribute or because replacing it would cause avoidable FOUC.\nconst persistedHeadElement = (el: HTMLElement, newDoc: Document): Element | null => {\n const id = el.getAttribute(PERSIST_ATTR);\n const newEl = id !== null ? querySelectorWithAttrValue(newDoc.head, \"\", PERSIST_ATTR, id) : null;\n if (newEl) {\n return newEl;\n }\n if (el.matches(\"link[rel=stylesheet]\")) {\n const href = el.getAttribute(\"href\");\n return href !== null\n ? querySelectorWithAttrValue(newDoc.head, \"link[rel=stylesheet]\", \"href\", href)\n : null;\n }\n // In dev mode, Vite injects <style data-vite-dev-id=\"...\"> elements whose\n // textContent may later be transformed (especially Vue's `:deep()` → `[data-v-xxx]`).\n // Match these by their stable dev ID so the already-transformed style is preserved\n // across ClientRouter soft navigations instead of being replaced by the raw version.\n // There are other ids that can't be preserved and need a refresh, like Uno's /__uno.css,\n // which keeps the same id, but with different contents.\n // To avoid enumerating all exceptions, we only apply the auto-persist logic to elements\n // that look like Vue's dev styles.\n if ((import.meta as any).env?.DEV && el instanceof HTMLStyleElement) {\n const viteDevId = vueScopedStyleId(el);\n if (viteDevId) {\n return querySelectorWithAttrValue(newDoc.head, \"style\", \"data-vite-dev-id\", viteDevId);\n }\n }\n // Preserve inline <style> elements with identical content across navigations.\n // This prevents unnecessary removal and re-insertion of styles (e.g. @font-face\n // declarations from <Font>), which would cause the browser to re-evaluate them\n // and trigger a flash of unstyled text (FOUT).\n if (el.tagName === \"STYLE\" && el.textContent) {\n const styles = newDoc.head.querySelectorAll(\"style\");\n for (const s of styles) {\n if (s.textContent === el.textContent) {\n return s;\n }\n }\n }\n // Preserve font preload links across navigations to avoid re-fetching cached fonts.\n if (el.matches(\"link[rel=preload][as=font]\")) {\n const href = el.getAttribute(\"href\");\n return href !== null\n ? querySelectorWithAttrValue(newDoc.head, \"link[rel=preload][as=font]\", \"href\", href)\n : null;\n }\n return null;\n};\n\nconst shouldCopyProps = (el: HTMLElement): boolean => {\n const persistProps = el.dataset[\"zfbTransitionPersistProps\"];\n return persistProps == null || persistProps === \"false\";\n};\n\nconst isSameProps = (oldEl: Element, newEl: Element) => {\n // zfb island wrapper writes SSR props to data-props, not props (different attribute, not just renamed).\n return oldEl.getAttribute(\"data-props\") === newEl.getAttribute(\"data-props\");\n};\n\nexport const swapFunctions = {\n deselectScripts,\n swapRootAttributes,\n swapHeadElements,\n swapBodyElement,\n saveFocus,\n};\n\nexport const swap = (doc: Document) => {\n deselectScripts(doc);\n swapRootAttributes(doc);\n swapHeadElements(doc);\n const restoreFocusFunction = saveFocus();\n swapBodyElement(doc.body, document.body);\n restoreFocusFunction();\n};\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Description } from "@takazudo/zfb/zudo-react";
|
|
1
2
|
export interface ClientRouterProps {
|
|
2
3
|
/** Fallback animation strategy when native View Transitions are not supported. */
|
|
3
4
|
fallback?: "none" | "animate" | "swap";
|
|
@@ -44,16 +45,8 @@ export interface ClientRouterProps {
|
|
|
44
45
|
*/
|
|
45
46
|
traverseRefetch?: boolean;
|
|
46
47
|
}
|
|
47
|
-
/**
|
|
48
|
-
|
|
49
|
-
* Structural type — intentionally matches the Preact/React VNode object shape
|
|
50
|
-
* so consumers do not type-infer through the internal representation.
|
|
51
|
-
*/
|
|
52
|
-
export type ClientRouterElement = {
|
|
53
|
-
readonly type: string;
|
|
54
|
-
readonly props: Readonly<Record<string, unknown>>;
|
|
55
|
-
readonly key: unknown;
|
|
56
|
-
};
|
|
48
|
+
/** Head descriptions returned by ClientRouter. */
|
|
49
|
+
export type ClientRouterElement = Description;
|
|
57
50
|
/**
|
|
58
51
|
* `<ClientRouter />` — SPA soft-swap navigation with View Transition animations.
|
|
59
52
|
*
|
|
@@ -11,67 +11,24 @@
|
|
|
11
11
|
// click/form intercepts live in `client-router/router.ts` and are registered
|
|
12
12
|
// via `init()`. No inline <script> dangerouslySetInnerHTML is emitted.
|
|
13
13
|
//
|
|
14
|
-
// Component/activation split (#2437): this module
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
// access. Importing it — including via the root barrel `@takazudo/zfb-runtime`
|
|
19
|
-
// — registers no listeners and writes no history. `./client-router.ts` is the
|
|
20
|
-
// activation shim: it re-exports this module's surface and additionally runs
|
|
21
|
-
// `init()` as a side effect on import, which `import "@takazudo/zfb-runtime/client-router"`
|
|
22
|
-
// (auto-injected by the islands bundler) relies on for byte-compatible activation.
|
|
23
|
-
//
|
|
24
|
-
// Framework-agnostic element minting (no JSX syntax):
|
|
25
|
-
// `@takazudo/zfb-runtime` does not depend on a framework runtime. Head nodes
|
|
26
|
-
// are minted by calling `jsx` from `react/jsx-runtime` directly — NOT JSX
|
|
27
|
-
// syntax, so this stays a plain `.ts` file with no tsconfig JSX changes. The
|
|
28
|
-
// engine alias-rewrites `react/jsx-runtime` → `preact/jsx-runtime` in Preact
|
|
29
|
-
// mode (bundler.rs ~2886) and resolves it natively in React mode, so the same
|
|
30
|
-
// call mints a real element for whichever framework the project configured.
|
|
31
|
-
// The previous approach (a hand-rolled `{ type, props, key, constructor:
|
|
32
|
-
// undefined }` object literal — the Preact diff-path sentinel) only worked for
|
|
33
|
-
// Preact: React's renderer rejects such an object as a child with React error
|
|
34
|
-
// #31 ("Objects are not valid as a React child"), because a real React element
|
|
35
|
-
// carries `$$typeof: Symbol.for("react.element")` a literal cannot fake. Same
|
|
36
|
-
// migration as `Island` in @takazudo/zfb.
|
|
37
|
-
//
|
|
38
|
-
// The component renders three base sibling elements to <head> (plus optional
|
|
39
|
-
// metas emitted conditionally — see `prefetchDisabled` and `preserveHtmlAttrs`):
|
|
40
|
-
// 1. A <style> tag with the `.zfb-route-announcer` ARIA helper class.
|
|
41
|
-
// 2. <meta name="zfb-view-transitions-enabled" content="true" />
|
|
42
|
-
// 3. <meta name="zfb-view-transitions-fallback" content={fallback} />
|
|
43
|
-
//
|
|
44
|
-
// The route-announcer <div> is injected into <body> by `announce()` in
|
|
45
|
-
// `client-router/router.ts` on every navigation.
|
|
46
|
-
import { jsx } from "react/jsx-runtime";
|
|
14
|
+
// Component/activation split (#2437): this pure module imports the owned
|
|
15
|
+
// JSX factory and prefetch helpers without activating the client router.
|
|
16
|
+
// The router activation shim remains at the client-router subpath.
|
|
17
|
+
import { jsx } from "@takazudo/zfb/zudo-react/jsx-runtime";
|
|
47
18
|
import { init as prefetchInit } from "./client-router/prefetch.js";
|
|
48
19
|
// Module-level guard: prefetch bootstrap is triggered at most once per module
|
|
49
20
|
// lifetime even if <ClientRouter prefetchAll /> is mounted multiple times (#276).
|
|
50
21
|
let prefetchBootstrapped = false;
|
|
51
|
-
/**
|
|
52
|
-
* Mint a head element through the per-project JSX runtime.
|
|
53
|
-
*
|
|
54
|
-
* Calls `jsx` from `react/jsx-runtime` (alias-rewritten to
|
|
55
|
-
* `preact/jsx-runtime` in Preact mode by the engine, native in React mode)
|
|
56
|
-
* so the returned value is a real element for whichever framework the
|
|
57
|
-
* project configured — NOT a hand-rolled `{ type, props, key }` literal,
|
|
58
|
-
* which only Preact accepts and which makes React throw error #31. A stable
|
|
59
|
-
* `key` is passed because `ClientRouter()` returns these nodes in a plain
|
|
60
|
-
* array (React warns about keyless list children otherwise). Mirrors the
|
|
61
|
-
* `Island` migration in `@takazudo/zfb`.
|
|
62
|
-
*/
|
|
22
|
+
/** Mint a keyed owned head description without JSX syntax in this .ts file. */
|
|
63
23
|
function makeVNode(type, props, key) {
|
|
64
|
-
//
|
|
65
|
-
// tags or component types), which rejects an arbitrary runtime `string`.
|
|
66
|
-
// The tag is dynamic here, so cast to the factory's own first-param type —
|
|
67
|
-
// robust whether the engine aliases `jsx` to react or preact at build time.
|
|
24
|
+
// The static tag choices below are passed through this string parameter.
|
|
68
25
|
return jsx(type, props, key);
|
|
69
26
|
}
|
|
70
27
|
// CSS for the route-announcer element. Ported verbatim from Astro's
|
|
71
28
|
// `<style is:global>` block in ClientRouter.astro (lines 11–23), renaming
|
|
72
29
|
// `.astro-route-announcer` → `.zfb-route-announcer` per W1B §5.
|
|
73
30
|
// This is a global (non-scoped) <style> because the announcer <div> is
|
|
74
|
-
// appended to document.body at runtime, outside
|
|
31
|
+
// appended to document.body at runtime, outside the rendered head.
|
|
75
32
|
const announcerCss = `
|
|
76
33
|
.zfb-route-announcer {
|
|
77
34
|
position: absolute;
|
|
@@ -112,7 +69,7 @@ export function ClientRouter({ fallback = "animate", prefetchAll: prefetchAllPro
|
|
|
112
69
|
}
|
|
113
70
|
const nodes = [
|
|
114
71
|
// Global styles for the ARIA route-announcer div injected into <body>.
|
|
115
|
-
makeVNode("style", {
|
|
72
|
+
makeVNode("style", { rawHtml: announcerCss }, "zfb-vt-style"),
|
|
116
73
|
// Opt-in meta tag: router checks for this to decide whether to intercept navigations.
|
|
117
74
|
makeVNode("meta", { name: "zfb-view-transitions-enabled", content: "true" }, "zfb-vt-enabled"),
|
|
118
75
|
// Fallback strategy meta tag: read by getFallback() in router.ts.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-router-component.js","sourceRoot":"","sources":["../src/client-router-component.ts"],"names":[],"mappings":"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","sourcesContent":["// `@takazudo/zfb-runtime` — `<ClientRouter />` component (pure module).\n//\n// Ported from Astro's `ClientRouter.astro` (155 lines).\n// Source: packages/astro/components/ClientRouter.astro\n// Issue: zudolab/zudo-doc#1519 (W3D), parent epic zudolab/zudo-doc#1510.\n//\n// Named-cause deviation — inline script split (W1B §13.6):\n// Astro's <script> block is processed by Vite at build time as a bundled\n// module with virtual-module imports (`astro:transitions/client`). The zfb\n// port emits meta tags + global styles from the JSX component, while the\n// click/form intercepts live in `client-router/router.ts` and are registered\n// via `init()`. No inline <script> dangerouslySetInnerHTML is emitted.\n//\n// Component/activation split (#2437): this module is the pure component —\n// it imports ONLY `react/jsx-runtime` and `./client-router/prefetch.js`\n// (`prefetch.ts` has zero module-scope side effects), never\n// `./client-router/router.js`, and performs no top-level `document`/`window`\n// access. Importing it — including via the root barrel `@takazudo/zfb-runtime`\n// — registers no listeners and writes no history. `./client-router.ts` is the\n// activation shim: it re-exports this module's surface and additionally runs\n// `init()` as a side effect on import, which `import \"@takazudo/zfb-runtime/client-router\"`\n// (auto-injected by the islands bundler) relies on for byte-compatible activation.\n//\n// Framework-agnostic element minting (no JSX syntax):\n// `@takazudo/zfb-runtime` does not depend on a framework runtime. Head nodes\n// are minted by calling `jsx` from `react/jsx-runtime` directly — NOT JSX\n// syntax, so this stays a plain `.ts` file with no tsconfig JSX changes. The\n// engine alias-rewrites `react/jsx-runtime` → `preact/jsx-runtime` in Preact\n// mode (bundler.rs ~2886) and resolves it natively in React mode, so the same\n// call mints a real element for whichever framework the project configured.\n// The previous approach (a hand-rolled `{ type, props, key, constructor:\n// undefined }` object literal — the Preact diff-path sentinel) only worked for\n// Preact: React's renderer rejects such an object as a child with React error\n// #31 (\"Objects are not valid as a React child\"), because a real React element\n// carries `$$typeof: Symbol.for(\"react.element\")` a literal cannot fake. Same\n// migration as `Island` in @takazudo/zfb.\n//\n// The component renders three base sibling elements to <head> (plus optional\n// metas emitted conditionally — see `prefetchDisabled` and `preserveHtmlAttrs`):\n// 1. A <style> tag with the `.zfb-route-announcer` ARIA helper class.\n// 2. <meta name=\"zfb-view-transitions-enabled\" content=\"true\" />\n// 3. <meta name=\"zfb-view-transitions-fallback\" content={fallback} />\n//\n// The route-announcer <div> is injected into <body> by `announce()` in\n// `client-router/router.ts` on every navigation.\n\nimport { jsx } from \"react/jsx-runtime\";\n\nimport { init as prefetchInit } from \"./client-router/prefetch.js\";\n\n// Module-level guard: prefetch bootstrap is triggered at most once per module\n// lifetime even if <ClientRouter prefetchAll /> is mounted multiple times (#276).\nlet prefetchBootstrapped = false;\n\nexport interface ClientRouterProps {\n /** Fallback animation strategy when native View Transitions are not supported. */\n fallback?: \"none\" | \"animate\" | \"swap\";\n /**\n * When true, opts every same-origin link into the default prefetch strategy\n * (hover) by calling prefetchInit({ prefetchAll: true }) once on the client.\n */\n prefetchAll?: boolean;\n /**\n * Extra `<html>` attribute names to preserve across SPA swaps. By default the\n * client router copies the incoming server-rendered document's `<html>`\n * attributes onto the live root, dropping any current attribute that isn't\n * internal to the transition machinery — so a *runtime* attribute a consumer\n * sets from a persisted island (e.g. `data-theme` or `data-sidebar-hidden`\n * driven from `localStorage`) is lost on every navigation. List those names\n * here and the router re-applies their current value after each swap. Emitted\n * as a `<meta name=\"zfb-preserve-html-attrs\">` tag that `swapRootAttributes`\n * reads at swap time.\n *\n * Mount with the **same list on every page** that participates in SPA\n * navigation: the preserve-list is read from the *current* (outgoing) page's\n * meta at swap time, so a page that omits an entry drops that attribute when\n * navigating away from it. Names are matched case-insensitively (DOM attribute\n * names are lowercased). For dynamic/computed cases, mutate\n * `event.newDocument.documentElement` in a `zfb:before-swap` listener instead.\n * @see https://github.com/Takazudo/zudo-front-builder/issues/1103\n */\n preserveHtmlAttrs?: string[];\n /**\n * Opt this page OUT of the same-page traverse fast-path (default `false` —\n * fast-path ON). By default a Back/Forward traversal between two history\n * entries sharing the same `pathname + search` is served instantly from the\n * live DOM — no re-fetch, no re-swap, so island/client state is preserved.\n *\n * Set this on a **per-request SSR page** (`prerender = false`) whose\n * server-rendered content can legitimately differ between two visits to the\n * same URL: with the fast-path skipped such a traverse would pin the stale\n * first-render content. Emitted as\n * `<meta name=\"zfb-traverse-refetch\" content=\"true\">`, which the router reads\n * on the current (target) page to force the fetch back on. Mount with the\n * **same value on every page** that participates in SPA navigation, mirroring\n * the `preserveHtmlAttrs` guidance.\n * @see https://github.com/Takazudo/zudo-front-builder/issues/1376\n */\n traverseRefetch?: boolean;\n}\n\n/**\n * Public element shape for each node returned by `<ClientRouter />`.\n * Structural type — intentionally matches the Preact/React VNode object shape\n * so consumers do not type-infer through the internal representation.\n */\nexport type ClientRouterElement = {\n readonly type: string;\n readonly props: Readonly<Record<string, unknown>>;\n readonly key: unknown;\n};\n\n/**\n * Mint a head element through the per-project JSX runtime.\n *\n * Calls `jsx` from `react/jsx-runtime` (alias-rewritten to\n * `preact/jsx-runtime` in Preact mode by the engine, native in React mode)\n * so the returned value is a real element for whichever framework the\n * project configured — NOT a hand-rolled `{ type, props, key }` literal,\n * which only Preact accepts and which makes React throw error #31. A stable\n * `key` is passed because `ClientRouter()` returns these nodes in a plain\n * array (React warns about keyless list children otherwise). Mirrors the\n * `Island` migration in `@takazudo/zfb`.\n */\nfunction makeVNode(type: string, props: Record<string, unknown>, key: string): ClientRouterElement {\n // `jsx`'s `type` param is typed `ElementType` (string-literal intrinsic\n // tags or component types), which rejects an arbitrary runtime `string`.\n // The tag is dynamic here, so cast to the factory's own first-param type —\n // robust whether the engine aliases `jsx` to react or preact at build time.\n return jsx(type as Parameters<typeof jsx>[0], props, key) as unknown as ClientRouterElement;\n}\n\n// CSS for the route-announcer element. Ported verbatim from Astro's\n// `<style is:global>` block in ClientRouter.astro (lines 11–23), renaming\n// `.astro-route-announcer` → `.zfb-route-announcer` per W1B §5.\n// This is a global (non-scoped) <style> because the announcer <div> is\n// appended to document.body at runtime, outside any Preact-controlled subtree.\nconst announcerCss = `\n.zfb-route-announcer {\n\tposition: absolute;\n\tleft: 0;\n\ttop: 0;\n\tclip: rect(0 0 0 0);\n\tclip-path: inset(50%);\n\toverflow: hidden;\n\twhite-space: nowrap;\n\twidth: 1px;\n\theight: 1px;\n}\n`;\n\n/**\n * `<ClientRouter />` — SPA soft-swap navigation with View Transition animations.\n *\n * Mount once in your page `<head>`. Emits the opt-in meta tags and the global\n * `.zfb-route-announcer` stylesheet that the route-announcer ARIA div needs.\n * Importing this module performs no side effects — click/form-submit\n * intercepts are wired only when `./client-router.js` (the activation shim,\n * imported directly or via the `@takazudo/zfb-runtime/client-router` subpath)\n * is evaluated.\n *\n * @example\n * ```tsx\n * import { ClientRouter } from \"@takazudo/zfb-runtime\";\n * // In your page <head>:\n * <ClientRouter fallback=\"animate\" />\n * ```\n */\nexport function ClientRouter({\n fallback = \"animate\",\n prefetchAll: prefetchAllProp = false,\n preserveHtmlAttrs = [],\n traverseRefetch = false,\n}: ClientRouterProps = {}): readonly ClientRouterElement[] {\n // Bootstrap prefetch exactly once on the client when prefetchAll is true.\n // The initialized flag inside prefetchInit() provides a second safety layer\n // in case of concurrent hydration or manual callers (#276).\n if (typeof document !== \"undefined\" && prefetchAllProp && !prefetchBootstrapped) {\n prefetchBootstrapped = true;\n prefetchInit({ prefetchAll: true });\n }\n\n const nodes: ClientRouterElement[] = [\n // Global styles for the ARIA route-announcer div injected into <body>.\n makeVNode(\"style\", { dangerouslySetInnerHTML: { __html: announcerCss } }, \"zfb-vt-style\"),\n // Opt-in meta tag: router checks for this to decide whether to intercept navigations.\n makeVNode(\"meta\", { name: \"zfb-view-transitions-enabled\", content: \"true\" }, \"zfb-vt-enabled\"),\n // Fallback strategy meta tag: read by getFallback() in router.ts.\n makeVNode(\n \"meta\",\n { name: \"zfb-view-transitions-fallback\", content: fallback },\n \"zfb-vt-fallback\",\n ),\n ];\n\n // Prefetch-disabled meta tag (#277): emitted when the bundler set\n // `globalThis.__zfb.prefetchDisabled = true` (from `zfb.config.ts`\n // `prefetch: { disabled: true }`). The sibling prefetch-core module reads\n // `document.querySelector('meta[name=\"zfb-prefetch-disabled\"][content=\"true\"]')`\n // at `init()` time and short-circuits if found.\n //\n // The flag is site-wide and static — set once at bundle-emit time, never\n // per-page. This meta tag appears on every page that mounts `<ClientRouter />`\n // or not at all.\n //\n // Pin the contract verbatim — the attribute names and content value are\n // shared with the sibling prefetch-core sub-issue (#276).\n if ((globalThis as { __zfb?: { prefetchDisabled?: boolean } }).__zfb?.prefetchDisabled === true) {\n nodes.push(\n makeVNode(\n \"meta\",\n { name: \"zfb-prefetch-disabled\", content: \"true\" },\n \"zfb-prefetch-disabled\",\n ),\n );\n }\n\n // Consumer-extensible <html> attribute preserve-list (#1103). swapRootAttributes\n // reads this meta and re-applies the listed attributes' runtime values after each\n // swap, so persisted-island state on <html> (data-theme, data-sidebar-hidden, …)\n // survives navigation. Emitted only when non-empty, so non-opt-in output stays\n // byte-identical. Same conditional-emission shape as the prefetch-disabled meta.\n const preserveList = preserveHtmlAttrs.filter(Boolean);\n if (preserveList.length > 0) {\n nodes.push(\n makeVNode(\n \"meta\",\n { name: \"zfb-preserve-html-attrs\", content: preserveList.join(\" \") },\n \"zfb-preserve-html-attrs\",\n ),\n );\n }\n\n // Traverse-refetch opt-out meta (#1376). A same-page Back/Forward traversal\n // skips the re-fetch/re-swap by default (the router's traverse fast-path);\n // this meta opts a per-request SSR page (`prerender = false`) back INTO the\n // fetch, since skipping it would pin stale server-rendered content. Emitted\n // only when opted in, so non-opt-in output stays byte-identical. Same\n // conditional-emission shape as the prefetch-disabled / preserve-html-attrs\n // metas; the router reads meta[name=\"zfb-traverse-refetch\"][content=\"true\"].\n if (traverseRefetch) {\n nodes.push(\n makeVNode(\"meta\", { name: \"zfb-traverse-refetch\", content: \"true\" }, \"zfb-traverse-refetch\"),\n );\n }\n\n return nodes;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"client-router-component.js","sourceRoot":"","sources":["../src/client-router-component.ts"],"names":[],"mappings":"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","sourcesContent":["// `@takazudo/zfb-runtime` — `<ClientRouter />` component (pure module).\n//\n// Ported from Astro's `ClientRouter.astro` (155 lines).\n// Source: packages/astro/components/ClientRouter.astro\n// Issue: zudolab/zudo-doc#1519 (W3D), parent epic zudolab/zudo-doc#1510.\n//\n// Named-cause deviation — inline script split (W1B §13.6):\n// Astro's <script> block is processed by Vite at build time as a bundled\n// module with virtual-module imports (`astro:transitions/client`). The zfb\n// port emits meta tags + global styles from the JSX component, while the\n// click/form intercepts live in `client-router/router.ts` and are registered\n// via `init()`. No inline <script> dangerouslySetInnerHTML is emitted.\n//\n// Component/activation split (#2437): this pure module imports the owned\n// JSX factory and prefetch helpers without activating the client router.\n// The router activation shim remains at the client-router subpath.\n\nimport { jsx } from \"@takazudo/zfb/zudo-react/jsx-runtime\";\nimport type { Description } from \"@takazudo/zfb/zudo-react\";\n\nimport { init as prefetchInit } from \"./client-router/prefetch.js\";\n\n// Module-level guard: prefetch bootstrap is triggered at most once per module\n// lifetime even if <ClientRouter prefetchAll /> is mounted multiple times (#276).\nlet prefetchBootstrapped = false;\n\nexport interface ClientRouterProps {\n /** Fallback animation strategy when native View Transitions are not supported. */\n fallback?: \"none\" | \"animate\" | \"swap\";\n /**\n * When true, opts every same-origin link into the default prefetch strategy\n * (hover) by calling prefetchInit({ prefetchAll: true }) once on the client.\n */\n prefetchAll?: boolean;\n /**\n * Extra `<html>` attribute names to preserve across SPA swaps. By default the\n * client router copies the incoming server-rendered document's `<html>`\n * attributes onto the live root, dropping any current attribute that isn't\n * internal to the transition machinery — so a *runtime* attribute a consumer\n * sets from a persisted island (e.g. `data-theme` or `data-sidebar-hidden`\n * driven from `localStorage`) is lost on every navigation. List those names\n * here and the router re-applies their current value after each swap. Emitted\n * as a `<meta name=\"zfb-preserve-html-attrs\">` tag that `swapRootAttributes`\n * reads at swap time.\n *\n * Mount with the **same list on every page** that participates in SPA\n * navigation: the preserve-list is read from the *current* (outgoing) page's\n * meta at swap time, so a page that omits an entry drops that attribute when\n * navigating away from it. Names are matched case-insensitively (DOM attribute\n * names are lowercased). For dynamic/computed cases, mutate\n * `event.newDocument.documentElement` in a `zfb:before-swap` listener instead.\n * @see https://github.com/Takazudo/zudo-front-builder/issues/1103\n */\n preserveHtmlAttrs?: string[];\n /**\n * Opt this page OUT of the same-page traverse fast-path (default `false` —\n * fast-path ON). By default a Back/Forward traversal between two history\n * entries sharing the same `pathname + search` is served instantly from the\n * live DOM — no re-fetch, no re-swap, so island/client state is preserved.\n *\n * Set this on a **per-request SSR page** (`prerender = false`) whose\n * server-rendered content can legitimately differ between two visits to the\n * same URL: with the fast-path skipped such a traverse would pin the stale\n * first-render content. Emitted as\n * `<meta name=\"zfb-traverse-refetch\" content=\"true\">`, which the router reads\n * on the current (target) page to force the fetch back on. Mount with the\n * **same value on every page** that participates in SPA navigation, mirroring\n * the `preserveHtmlAttrs` guidance.\n * @see https://github.com/Takazudo/zudo-front-builder/issues/1376\n */\n traverseRefetch?: boolean;\n}\n\n/** Head descriptions returned by ClientRouter. */\nexport type ClientRouterElement = Description;\n\n/** Mint a keyed owned head description without JSX syntax in this .ts file. */\nfunction makeVNode(type: string, props: Record<string, unknown>, key: string): ClientRouterElement {\n // The static tag choices below are passed through this string parameter.\n return jsx(type as Parameters<typeof jsx>[0], props, key);\n}\n\n// CSS for the route-announcer element. Ported verbatim from Astro's\n// `<style is:global>` block in ClientRouter.astro (lines 11–23), renaming\n// `.astro-route-announcer` → `.zfb-route-announcer` per W1B §5.\n// This is a global (non-scoped) <style> because the announcer <div> is\n// appended to document.body at runtime, outside the rendered head.\nconst announcerCss = `\n.zfb-route-announcer {\n\tposition: absolute;\n\tleft: 0;\n\ttop: 0;\n\tclip: rect(0 0 0 0);\n\tclip-path: inset(50%);\n\toverflow: hidden;\n\twhite-space: nowrap;\n\twidth: 1px;\n\theight: 1px;\n}\n`;\n\n/**\n * `<ClientRouter />` — SPA soft-swap navigation with View Transition animations.\n *\n * Mount once in your page `<head>`. Emits the opt-in meta tags and the global\n * `.zfb-route-announcer` stylesheet that the route-announcer ARIA div needs.\n * Importing this module performs no side effects — click/form-submit\n * intercepts are wired only when `./client-router.js` (the activation shim,\n * imported directly or via the `@takazudo/zfb-runtime/client-router` subpath)\n * is evaluated.\n *\n * @example\n * ```tsx\n * import { ClientRouter } from \"@takazudo/zfb-runtime\";\n * // In your page <head>:\n * <ClientRouter fallback=\"animate\" />\n * ```\n */\nexport function ClientRouter({\n fallback = \"animate\",\n prefetchAll: prefetchAllProp = false,\n preserveHtmlAttrs = [],\n traverseRefetch = false,\n}: ClientRouterProps = {}): readonly ClientRouterElement[] {\n // Bootstrap prefetch exactly once on the client when prefetchAll is true.\n // The initialized flag inside prefetchInit() provides a second safety layer\n // in case of concurrent hydration or manual callers (#276).\n if (typeof document !== \"undefined\" && prefetchAllProp && !prefetchBootstrapped) {\n prefetchBootstrapped = true;\n prefetchInit({ prefetchAll: true });\n }\n\n const nodes: ClientRouterElement[] = [\n // Global styles for the ARIA route-announcer div injected into <body>.\n makeVNode(\"style\", { rawHtml: announcerCss }, \"zfb-vt-style\"),\n // Opt-in meta tag: router checks for this to decide whether to intercept navigations.\n makeVNode(\"meta\", { name: \"zfb-view-transitions-enabled\", content: \"true\" }, \"zfb-vt-enabled\"),\n // Fallback strategy meta tag: read by getFallback() in router.ts.\n makeVNode(\n \"meta\",\n { name: \"zfb-view-transitions-fallback\", content: fallback },\n \"zfb-vt-fallback\",\n ),\n ];\n\n // Prefetch-disabled meta tag (#277): emitted when the bundler set\n // `globalThis.__zfb.prefetchDisabled = true` (from `zfb.config.ts`\n // `prefetch: { disabled: true }`). The sibling prefetch-core module reads\n // `document.querySelector('meta[name=\"zfb-prefetch-disabled\"][content=\"true\"]')`\n // at `init()` time and short-circuits if found.\n //\n // The flag is site-wide and static — set once at bundle-emit time, never\n // per-page. This meta tag appears on every page that mounts `<ClientRouter />`\n // or not at all.\n //\n // Pin the contract verbatim — the attribute names and content value are\n // shared with the sibling prefetch-core sub-issue (#276).\n if ((globalThis as { __zfb?: { prefetchDisabled?: boolean } }).__zfb?.prefetchDisabled === true) {\n nodes.push(\n makeVNode(\n \"meta\",\n { name: \"zfb-prefetch-disabled\", content: \"true\" },\n \"zfb-prefetch-disabled\",\n ),\n );\n }\n\n // Consumer-extensible <html> attribute preserve-list (#1103). swapRootAttributes\n // reads this meta and re-applies the listed attributes' runtime values after each\n // swap, so persisted-island state on <html> (data-theme, data-sidebar-hidden, …)\n // survives navigation. Emitted only when non-empty, so non-opt-in output stays\n // byte-identical. Same conditional-emission shape as the prefetch-disabled meta.\n const preserveList = preserveHtmlAttrs.filter(Boolean);\n if (preserveList.length > 0) {\n nodes.push(\n makeVNode(\n \"meta\",\n { name: \"zfb-preserve-html-attrs\", content: preserveList.join(\" \") },\n \"zfb-preserve-html-attrs\",\n ),\n );\n }\n\n // Traverse-refetch opt-out meta (#1376). A same-page Back/Forward traversal\n // skips the re-fetch/re-swap by default (the router's traverse fast-path);\n // this meta opts a per-request SSR page (`prerender = false`) back INTO the\n // fetch, since skipping it would pin stale server-rendered content. Emitted\n // only when opted in, so non-opt-in output stays byte-identical. Same\n // conditional-emission shape as the prefetch-disabled / preserve-html-attrs\n // metas; the router reads meta[name=\"zfb-traverse-refetch\"][content=\"true\"].\n if (traverseRefetch) {\n nodes.push(\n makeVNode(\"meta\", { name: \"zfb-traverse-refetch\", content: \"true\" }, \"zfb-traverse-refetch\"),\n );\n }\n\n return nodes;\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export type { CreatePageRouterOptions, PageDefinition, PageHeading, PageModule, PageRouter, } from "./router.js";
|
|
2
|
-
export type { FrameworkAdapter } from "./framework.js";
|
|
3
2
|
export type { ContentSnapshot, EntrySnapshot } from "./snapshot.js";
|
|
4
3
|
export { ViewTransitions, type ViewTransitionsElement } from "./view-transitions.js";
|
|
5
4
|
export { ClientRouter, type ClientRouterProps } from "./client-router-component.js";
|
package/dist/index.js
CHANGED
|
@@ -18,8 +18,7 @@
|
|
|
18
18
|
// const router = createPageRouter({
|
|
19
19
|
// pages: [...],
|
|
20
20
|
// contentSnapshot: __ZFB_CONTENT_SNAPSHOT__, // embedded by the bundler
|
|
21
|
-
|
|
22
|
-
// });
|
|
21
|
+
//// });
|
|
23
22
|
//
|
|
24
23
|
// export default { fetch: router };
|
|
25
24
|
//
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,EAAE;AACF,0EAA0E;AAC1E,oEAAoE;AACpE,2EAA2E;AAC3E,qEAAqE;AACrE,2EAA2E;AAC3E,mFAAmF;AACnF,sEAAsE;AACtE,0EAA0E;AAC1E,qEAAqE;AACrE,EAAE;AACF,qEAAqE;AACrE,uEAAuE;AACvE,EAAE;AACF,qEAAqE;AACrE,EAAE;AACF,sCAAsC;AACtC,oBAAoB;AACpB,4EAA4E;AAC5E,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,EAAE;AACF,0EAA0E;AAC1E,oEAAoE;AACpE,2EAA2E;AAC3E,qEAAqE;AACrE,2EAA2E;AAC3E,mFAAmF;AACnF,sEAAsE;AACtE,0EAA0E;AAC1E,qEAAqE;AACrE,EAAE;AACF,qEAAqE;AACrE,uEAAuE;AACvE,EAAE;AACF,qEAAqE;AACrE,EAAE;AACF,sCAAsC;AACtC,oBAAoB;AACpB,4EAA4E;AAC5E,UAAU;AACV,EAAE;AACF,sCAAsC;AACtC,EAAE;AACF,wEAAwE;AAUxE,OAAO,EAAE,eAAe,EAA+B,MAAM,uBAAuB,CAAC;AAErF,2FAA2F;AAC3F,2CAA2C;AAC3C,EAAE;AACF,mEAAmE;AACnE,4EAA4E;AAC5E,2EAA2E;AAC3E,yEAAyE;AACzE,yEAAyE;AACzE,8EAA8E;AAC9E,qEAAqE;AACrE,OAAO,EAAE,YAAY,EAA0B,MAAM,8BAA8B,CAAC;AACpF,OAAO,EACL,QAAQ,EACR,uBAAuB,EACvB,2BAA2B,EAC3B,gBAAgB,GACjB,MAAM,2BAA2B,CAAC;AAEnC,kCAAkC;AAClC,8EAA8E;AAC9E,4BAA4B;AAC5B,OAAO,EAAE,QAAQ,EAAE,IAAI,IAAI,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAM7E,OAAO,EACL,6BAA6B,EAC7B,4BAA4B,EAC5B,sBAAsB,EACtB,qBAAqB,EACrB,oBAAoB,EACpB,6BAA6B,EAC7B,gCAAgC,EAChC,yBAAyB,EACzB,kCAAkC,EAClC,2BAA2B,GAC5B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,mCAAmC,CAAC","sourcesContent":["// Public entry point for `@takazudo/zfb-runtime`.\n//\n// This is the **client-safe** barrel: everything re-exported here must be\n// bundleable for `--platform=browser` without pulling a server-only\n// dependency. In particular `createPageRouter` (and the Hono server router\n// it builds) is intentionally NOT re-exported here — it lives at the\n// server-only subpath `@takazudo/zfb-runtime/server`. Re-exporting it from\n// this barrel makes any island that does `import ... from \"@takazudo/zfb-runtime\"`\n// drag `hono` into the browser graph, which esbuild must then resolve\n// (issue #1298). The page-router *types* below are `export type` only, so\n// esbuild strips them and no runtime edge to `./router.js` survives.\n//\n// Worker bundles produced by T3 import the server subpath and invoke\n// `createPageRouter` once, at module top, to obtain the fetch handler:\n//\n// import { createPageRouter } from \"@takazudo/zfb-runtime/server\";\n//\n// const router = createPageRouter({\n// pages: [...],\n// contentSnapshot: __ZFB_CONTENT_SNAPSHOT__, // embedded by the bundler\n//// });\n//\n// export default { fetch: router };\n//\n// The README documents the bundle shape T6 (embedded V8 host) consumes.\n\nexport type {\n CreatePageRouterOptions,\n PageDefinition,\n PageHeading,\n PageModule,\n PageRouter,\n} from \"./router.js\";\nexport type { ContentSnapshot, EntrySnapshot } from \"./snapshot.js\";\nexport { ViewTransitions, type ViewTransitionsElement } from \"./view-transitions.js\";\n\n// Client-router public surface (W3D — mirrors @takazudo/zfb-runtime/client-router barrel).\n// See W1B §2 for the full public API spec.\n//\n// ClientRouter is imported from the pure component module, NOT the\n// `./client-router.js` activation shim (#2437): the shim runs `init()` as a\n// module-scope side effect, which would make `import { ClientRouter } from\n// \"@takazudo/zfb-runtime\"` silently activate the router. The pure module\n// performs zero side effects, so the root barrel stays side-effect-free.\n// `import \"@takazudo/zfb-runtime/client-router\"` (the shim's subpath) remains\n// the byte-compatible activation entry point — see client-router.ts.\nexport { ClientRouter, type ClientRouterProps } from \"./client-router-component.js\";\nexport {\n navigate,\n supportsViewTransitions,\n transitionEnabledOnThisPage,\n syncHistoryEntry,\n} from \"./client-router/router.js\";\n\n// Prefetch public surface (#276).\n// `init` from prefetch.ts is re-exported as `prefetchInit` to avoid collision\n// with the router's `init`.\nexport { prefetch, init as prefetchInit } from \"./client-router/prefetch.js\";\nexport type {\n PrefetchStrategy,\n PrefetchInitOptions,\n PrefetchOptions,\n} from \"./client-router/prefetch.js\";\nexport {\n TRANSITION_BEFORE_PREPARATION,\n TRANSITION_AFTER_PREPARATION,\n TRANSITION_BEFORE_SWAP,\n TRANSITION_AFTER_SWAP,\n TRANSITION_PAGE_LOAD,\n TRANSITION_NAVIGATION_ABORTED,\n TransitionBeforePreparationEvent,\n TransitionBeforeSwapEvent,\n isTransitionBeforePreparationEvent,\n isTransitionBeforeSwapEvent,\n} from \"./client-router/events.js\";\nexport { swapFunctions, swap } from \"./client-router/swap-functions.js\";\nexport type {\n Direction,\n Fallback,\n NavigationTypeString,\n Options,\n SyncHistoryEntryOptions,\n} from \"./client-router/types.js\";\n\n// Plugin lifecycle types (#255). The runtime package re-exports the\n// `@takazudo/zfb/plugins` surface so consumers writing plugins from a\n// project that only depends on `@takazudo/zfb-runtime` can still\n// import the canonical types (e.g. `import type { ZfbPlugin } from\n// \"@takazudo/zfb-runtime\"`).\nexport type {\n ZfbPlugin,\n ZfbPluginLogger,\n ZfbBuildHookContext,\n ZfbDevMiddlewareContext,\n ZfbDevMiddlewareHandler,\n ZfbDevMiddlewareRequest,\n ZfbDevMiddlewareResponse,\n ZfbRouteEntry,\n ZfbRouteManifest,\n ZfbSetupContext,\n ZfbVirtualModuleLoader,\n} from \"@takazudo/zfb/plugins\";\n"]}
|
package/dist/router.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { FrameworkAdapter } from "./framework.js";
|
|
2
1
|
import type { ContentSnapshot } from "./snapshot.js";
|
|
3
2
|
/**
|
|
4
3
|
* Heading metadata emitted by the MDX `headings` export (T4). Cross-ref:
|
|
@@ -16,7 +15,7 @@ export interface PageHeading {
|
|
|
16
15
|
* - `default`: the JSX page component. Called with the props returned by
|
|
17
16
|
* `getStaticProps` (if exported) or the `props` from the matching
|
|
18
17
|
* `paths()` entry (for dynamic routes). The return value is fed straight
|
|
19
|
-
* to the
|
|
18
|
+
* to the owned server renderer's `renderToString`. A dynamic route with no
|
|
20
19
|
* `paths()` export is called with `{ params: urlParams }` instead of
|
|
21
20
|
* `{}` — see `createPageRouter`'s JSDoc for the full componentInput
|
|
22
21
|
* derivation table.
|
|
@@ -76,8 +75,6 @@ export interface CreatePageRouterOptions {
|
|
|
76
75
|
* `getCollection(...)` resolve synchronously from memory.
|
|
77
76
|
*/
|
|
78
77
|
readonly contentSnapshot: ContentSnapshot;
|
|
79
|
-
/** Framework adapter pinning the SSR call. */
|
|
80
|
-
readonly framework: FrameworkAdapter;
|
|
81
78
|
/**
|
|
82
79
|
* When `true`, the 500 body for render errors includes the full JS stack
|
|
83
80
|
* trace. When `false`, only the message + route are included. When
|
|
@@ -93,7 +90,7 @@ export interface CreatePageRouterOptions {
|
|
|
93
90
|
/**
|
|
94
91
|
* Fetch-handler shape returned by [`createPageRouter`]. Shaped as a plain
|
|
95
92
|
* function (not a Hono `app`) so the consumer's contract is exactly
|
|
96
|
-
* "Worker-style fetch handler" with no leaked
|
|
93
|
+
* "Worker-style fetch handler" with no leaked renderer types.
|
|
97
94
|
*/
|
|
98
95
|
export type PageRouter = (request: Request) => Promise<Response>;
|
|
99
96
|
/**
|
|
@@ -110,7 +107,7 @@ export type PageRouter = (request: Request) => Promise<Response>;
|
|
|
110
107
|
* under `pages/api/` — actually reaches its handler instead of being
|
|
111
108
|
* 404'd by the inner router. See the comment at the `app.all` call
|
|
112
109
|
* site below. The handler imports the page module, calls
|
|
113
|
-
* `
|
|
110
|
+
* `renderToString(module.default(componentInput))`, and
|
|
114
111
|
* returns the string in a `Response` with the appropriate
|
|
115
112
|
* `Content-Type`. (`componentInput` is the page's props object — see
|
|
116
113
|
* the derivation table below; it is never the incoming `Request`.)
|
package/dist/router.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// user pages/ + content/ + layouts/ + components/
|
|
7
7
|
// → esbuild bundle (T3) // single ESM file
|
|
8
8
|
// → embedded V8 host (T6) // same WinterCG surface as CF Workers
|
|
9
|
-
// → createPageRouter({ pages, contentSnapshot
|
|
9
|
+
// → createPageRouter({ pages, contentSnapshot })
|
|
10
10
|
// → (request) => Promise<Response>
|
|
11
11
|
//
|
|
12
12
|
// The bundle's Worker entry point shape is documented in the package
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
// that hand-build `pages` and therefore bypass the scanner.
|
|
40
40
|
import { Hono } from "hono";
|
|
41
41
|
import { setContentSnapshot } from "@takazudo/zfb/content";
|
|
42
|
+
import { renderToString } from "@takazudo/zfb/zudo-react/server";
|
|
42
43
|
// ---------------------------------------------------------------------------
|
|
43
44
|
// Implementation
|
|
44
45
|
// ---------------------------------------------------------------------------
|
|
@@ -113,7 +114,7 @@ async function getOrEvalPaths(memo, routeKey, pathsFn) {
|
|
|
113
114
|
* under `pages/api/` — actually reaches its handler instead of being
|
|
114
115
|
* 404'd by the inner router. See the comment at the `app.all` call
|
|
115
116
|
* site below. The handler imports the page module, calls
|
|
116
|
-
* `
|
|
117
|
+
* `renderToString(module.default(componentInput))`, and
|
|
117
118
|
* returns the string in a `Response` with the appropriate
|
|
118
119
|
* `Content-Type`. (`componentInput` is the page's props object — see
|
|
119
120
|
* the derivation table below; it is never the incoming `Request`.)
|
|
@@ -317,7 +318,7 @@ export function createPageRouter(opts) {
|
|
|
317
318
|
if (!match) {
|
|
318
319
|
// The URL params do not correspond to any paths() entry —
|
|
319
320
|
// this is the dev-mode equivalent of a build-time miss.
|
|
320
|
-
// Hono's `c.notFound()` returns
|
|
321
|
+
// Hono's `c.notFound()` returns Hono's default 404,
|
|
321
322
|
// which is cleaner than fabricating an empty-props render.
|
|
322
323
|
return c.notFound();
|
|
323
324
|
}
|
|
@@ -377,7 +378,7 @@ export function createPageRouter(opts) {
|
|
|
377
378
|
// directly (e.g. `pages/api/*.tsx` handlers that use Web Fetch
|
|
378
379
|
// primitives instead of returning JSX) is responsible for its own
|
|
379
380
|
// status, headers, and body — return it as-is rather than running
|
|
380
|
-
// it through the
|
|
381
|
+
// it through the owned SSR renderer. A `return` from inside a
|
|
381
382
|
// `try` does not trigger the `catch`, so this passes through
|
|
382
383
|
// correctly without special-casing.
|
|
383
384
|
if (result instanceof Response) {
|
|
@@ -385,11 +386,11 @@ export function createPageRouter(opts) {
|
|
|
385
386
|
}
|
|
386
387
|
// Non-HTML routes (e.g. `sitemap.xml.tsx`, `feed.xml.tsx`) commonly
|
|
387
388
|
// return their body as a pre-serialised `string` instead of a
|
|
388
|
-
// VNode. Routing those through `
|
|
389
|
+
// VNode. Routing those through `renderToString` would
|
|
389
390
|
// HTML-escape the angle brackets and ampersands, producing
|
|
390
391
|
// garbage XML. Pass strings through verbatim; only wrap actual
|
|
391
392
|
// VNodes.
|
|
392
|
-
const html = typeof result === "string" ? result :
|
|
393
|
+
const html = typeof result === "string" ? result : renderToString(result);
|
|
393
394
|
const contentType = mod.contentType ?? DEFAULT_CONTENT_TYPE;
|
|
394
395
|
return c.body(ensureHtml5Doctype(html, contentType), 200, { "Content-Type": contentType });
|
|
395
396
|
}
|