@alignable/bifrost 1.0.24 → 1.0.26
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.
|
@@ -5,20 +5,10 @@ import {
|
|
|
5
5
|
// renderer/wrapped/onBeforeRender.client.ts
|
|
6
6
|
import { redirect } from "vike/abort";
|
|
7
7
|
|
|
8
|
-
// lib/
|
|
9
|
-
async function
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
await new Promise(() => {
|
|
13
|
-
});
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
const parsedUrl = new URL(url);
|
|
17
|
-
if (window.location.origin === parsedUrl.origin) {
|
|
18
|
-
window.location.href = parsedUrl.pathname + parsedUrl.search + parsedUrl.hash;
|
|
19
|
-
} else {
|
|
20
|
-
window.location.href = url;
|
|
21
|
-
}
|
|
8
|
+
// lib/hardNavigate.ts
|
|
9
|
+
async function hardNavigate(url) {
|
|
10
|
+
history.pushState(null, "", url);
|
|
11
|
+
window.Turbolinks.controller.viewInvalidated();
|
|
22
12
|
await new Promise(() => {
|
|
23
13
|
});
|
|
24
14
|
}
|
|
@@ -31,7 +21,9 @@ async function wrappedOnBeforeRender(pageContext) {
|
|
|
31
21
|
}).catch(() => {
|
|
32
22
|
});
|
|
33
23
|
if (!resp) {
|
|
34
|
-
|
|
24
|
+
window.location.href = pageContext.urlParsed.href;
|
|
25
|
+
await new Promise(() => {
|
|
26
|
+
});
|
|
35
27
|
return;
|
|
36
28
|
}
|
|
37
29
|
if (resp.redirected) {
|
|
@@ -39,20 +31,18 @@ async function wrappedOnBeforeRender(pageContext) {
|
|
|
39
31
|
if (window.location.origin === parsedUrl.origin) {
|
|
40
32
|
throw redirect(parsedUrl.pathname + parsedUrl.search + parsedUrl.hash);
|
|
41
33
|
} else {
|
|
42
|
-
|
|
43
|
-
return;
|
|
34
|
+
throw redirect(resp.url);
|
|
44
35
|
}
|
|
45
36
|
}
|
|
46
37
|
if (!resp.ok) {
|
|
47
|
-
await
|
|
48
|
-
return;
|
|
38
|
+
await hardNavigate(resp.url);
|
|
49
39
|
}
|
|
50
40
|
const html = await resp.text();
|
|
51
41
|
const layoutInfo = pageContext.config.getLayout(
|
|
52
42
|
Object.fromEntries(resp.headers.entries())
|
|
53
43
|
);
|
|
54
44
|
if (!layoutInfo) {
|
|
55
|
-
await
|
|
45
|
+
await hardNavigate(resp.url);
|
|
56
46
|
return;
|
|
57
47
|
}
|
|
58
48
|
const parsed = document.createElement("html");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../renderer/wrapped/onBeforeRender.client.ts","../../../lib/
|
|
1
|
+
{"version":3,"sources":["../../../renderer/wrapped/onBeforeRender.client.ts","../../../lib/hardNavigate.ts"],"sourcesContent":["import \"../../lib/type\";\nimport type { PageContextClient } from \"vike/types\";\nimport { redirect } from \"vike/abort\";\nimport { getElementAttributes } from \"../../lib/elementUtils\";\nimport { hardNavigate } from \"../../lib/hardNavigate\";\n\n// onBeforeRender runs before changing the browser location, so `throw redirect` works\n// we wait for onBeforeRenderClient to call mergeHead, which runs after browser location change\n// Possibly could move this back into onBeforeRenderClient: https://github.com/vikejs/vike/pull/2820\nexport default async function wrappedOnBeforeRender(\n pageContext: PageContextClient\n) {\n if (\n pageContext.isClientSide &&\n !pageContext?._snapshot &&\n !pageContext.isHydration\n ) {\n /*\n Mermaid diagram of client side navigation logic:\n\n Vike Router --> Proxy Mode\n Proxy Mode -->|wrapped| Request Legacy Backend\n Request Legacy Backend -->|redirect| Vike Router\n Request Legacy Backend -->|html| Render Wrapped Page\n Proxy Mode -->|false| Render Vike Page\n Proxy Mode -->|passthru| Browser Navigation\n\n ┌─────────────┐ ┌────────────┐ ┌────────────────────────┐ ┌─────────────────────┐\n │ │ │ │ │ │ │ │\n │ Vike Router ├────►│ Proxy Mode ├─wrapped►│ Request Legacy Backend ├html─►│ Render Wrapped Page │\n │ │ │ │ │ │ │ │\n └─────────────┘ └──────┬─────┘ └────────────┬───────────┘ └─────────────────────┘\n ▲ │ redirect \n └───────────────────┼────────────────────────────┘ \n false \n │ ┌────────────────────────┐ \n │ │ │ \n ├──────────────►│ Render Vike Page │ \n passthru │ │ \n │ └────────────────────────┘ \n │ \n │ ┌────────────────────────┐ \n │ │ │ \n └──────────────►│ Browser Navigation │ \n │ │ \n └────────────────────────┘ \n\n The Vike router must run on every redirect, because the legacy backend could redirect to a Vike page.\n The browser follows redirects automatically, which hits the vike server, which will passthru if needed\n It would be more performant to run the Vike router on the client, but the browser does not expose redirect info.\n Optimization: use serviceworker to intercept redirects.\n */\n const resp = await fetch(pageContext.urlParsed.href, {\n headers: { ...pageContext.config.proxyHeaders, accept: \"text/html\" },\n }).catch(() => {});\n\n if (!resp) {\n // hard reload. can happen on cors errors when redirected to external page\n window.location.href = pageContext.urlParsed.href;\n // stop vike rendering to let navigation happen\n await new Promise(() => {});\n return;\n }\n\n if (resp.redirected) {\n const parsedUrl = new URL(resp.url);\n // Need to redirect to run vike router (in case redirect is not wrapped page)\n // Downside is we will make another network request\n // TODO: Can we prevent the double request? Move to server side and throw redirect on 3xx?\n if (window.location.origin === parsedUrl.origin) {\n // redirect needs to start with \"/\" or vike will do hard reload\n throw redirect(parsedUrl.pathname + parsedUrl.search + parsedUrl.hash);\n } else {\n // external redirect\n throw redirect(resp.url);\n }\n }\n if (!resp.ok) {\n await hardNavigate(resp.url);\n }\n const html = await resp.text();\n const layoutInfo = pageContext.config.getLayout!(\n Object.fromEntries(resp.headers.entries())\n );\n if (!layoutInfo) {\n // Fallback to full reload if layout not found\n await hardNavigate(resp.url);\n return;\n }\n\n const parsed = document.createElement(\"html\");\n parsed.innerHTML = html;\n const bodyEl = parsed.querySelector(\"body\")!;\n const headEl = parsed.querySelector(\"head\")!;\n pageContext.proxyLayoutInfo = layoutInfo;\n pageContext._turbolinksProxy = {\n body: bodyEl,\n head: headEl,\n bodyAttrs: getElementAttributes(bodyEl),\n };\n }\n}\n","/**\n * Hard navigate to a URL using `history.pushState` and `window.location.reload` instead of\n * `window.location.href`. This prevents the mobile apps from opening the URL in a browser tab.\n */\nexport async function hardNavigate(url: string): Promise<never> {\n history.pushState(null, \"\", url);\n window.Turbolinks.controller.viewInvalidated();\n // stop vike rendering to let navigation happen\n await new Promise(() => {});\n}\n"],"mappings":";;;;;AAEA,SAAS,gBAAgB;;;ACEzB,eAAsB,aAAa,KAA6B;AAC9D,UAAQ,UAAU,MAAM,IAAI,GAAG;AAC/B,SAAO,WAAW,WAAW,gBAAgB;AAE7C,QAAM,IAAI,QAAQ,MAAM;AAAA,EAAC,CAAC;AAC5B;;;ADAA,eAAO,sBACL,aACA;AACA,MACE,YAAY,gBACZ,CAAC,aAAa,aACd,CAAC,YAAY,aACb;AAoCA,UAAM,OAAO,MAAM,MAAM,YAAY,UAAU,MAAM;AAAA,MACnD,SAAS,EAAE,GAAG,YAAY,OAAO,cAAc,QAAQ,YAAY;AAAA,IACrE,CAAC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAEjB,QAAI,CAAC,MAAM;AAET,aAAO,SAAS,OAAO,YAAY,UAAU;AAE7C,YAAM,IAAI,QAAQ,MAAM;AAAA,MAAC,CAAC;AAC1B;AAAA,IACF;AAEA,QAAI,KAAK,YAAY;AACnB,YAAM,YAAY,IAAI,IAAI,KAAK,GAAG;AAIlC,UAAI,OAAO,SAAS,WAAW,UAAU,QAAQ;AAE/C,cAAM,SAAS,UAAU,WAAW,UAAU,SAAS,UAAU,IAAI;AAAA,MACvE,OAAO;AAEL,cAAM,SAAS,KAAK,GAAG;AAAA,MACzB;AAAA,IACF;AACA,QAAI,CAAC,KAAK,IAAI;AACZ,YAAM,aAAa,KAAK,GAAG;AAAA,IAC7B;AACA,UAAM,OAAO,MAAM,KAAK,KAAK;AAC7B,UAAM,aAAa,YAAY,OAAO;AAAA,MACpC,OAAO,YAAY,KAAK,QAAQ,QAAQ,CAAC;AAAA,IAC3C;AACA,QAAI,CAAC,YAAY;AAEf,YAAM,aAAa,KAAK,GAAG;AAC3B;AAAA,IACF;AAEA,UAAM,SAAS,SAAS,cAAc,MAAM;AAC5C,WAAO,YAAY;AACnB,UAAM,SAAS,OAAO,cAAc,MAAM;AAC1C,UAAM,SAAS,OAAO,cAAc,MAAM;AAC1C,gBAAY,kBAAkB;AAC9B,gBAAY,mBAAmB;AAAA,MAC7B,MAAM;AAAA,MACN,MAAM;AAAA,MACN,WAAW,qBAAqB,MAAM;AAAA,IACxC;AAAA,EACF;AACF;","names":[]}
|
|
@@ -18,6 +18,7 @@ function recordExistingHeadScripts(categorizedHead) {
|
|
|
18
18
|
tracked: elementIsTracked(element)
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
|
+
lastTrackedScriptSignature = lastTrackedScriptSignature || trackedElementSignature(categorizedHead);
|
|
21
22
|
firstMerge = false;
|
|
22
23
|
}
|
|
23
24
|
function mergeHead(head) {
|
|
@@ -36,12 +37,13 @@ function mergeHead(head) {
|
|
|
36
37
|
return reload();
|
|
37
38
|
}
|
|
38
39
|
if (!firstMerge) {
|
|
39
|
-
|
|
40
|
-
if (lastTrackedScriptSignature !==
|
|
40
|
+
const newTrackedScriptSignature = trackedElementSignature(newHead);
|
|
41
|
+
if (lastTrackedScriptSignature !== newTrackedScriptSignature) {
|
|
41
42
|
return reload();
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
|
-
|
|
45
|
+
firstMerge = false;
|
|
46
|
+
lastTrackedScriptSignature = lastTrackedScriptSignature || trackedElementSignature(newHead);
|
|
45
47
|
copyNewHeadStylesheetElements(newHead.stylesheets, oldHead.stylesheets);
|
|
46
48
|
removeCurrentHeadProvisionalElements(oldHead.provisional);
|
|
47
49
|
copyNewHeadProvisionalElements(newHead.provisional);
|
|
@@ -50,8 +52,8 @@ function mergeHead(head) {
|
|
|
50
52
|
waitForHeadScripts: copyNewHeadScriptElements(newHead.scripts)
|
|
51
53
|
};
|
|
52
54
|
}
|
|
53
|
-
function trackedElementSignature(
|
|
54
|
-
return scripts.filter(elementIsTracked).map((s) => s.outerHTML).join();
|
|
55
|
+
function trackedElementSignature(head) {
|
|
56
|
+
return [...head.scripts, ...head.stylesheets].filter(elementIsTracked).map((s) => s.outerHTML).join();
|
|
55
57
|
}
|
|
56
58
|
function copyNewHeadStylesheetElements(next, prev) {
|
|
57
59
|
const existing = prev.map((s) => s.outerHTML);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../lib/turbolinks/mergeHead.ts","../../../renderer/wrapped/onBeforeRenderClient.ts"],"sourcesContent":["import { createScriptElement } from \"./util\";\n\ninterface ElementDetails {\n tracked: boolean;\n}\nconst allHeadScriptsEverRun: { [outerHTML: string]: ElementDetails } = {};\nlet firstMerge = true;\nlet lastTrackedScriptSignature: string;\n\nexport function recordExistingHeadScripts(\n categorizedHead?: ReturnType<typeof categorizeHead>\n) {\n categorizedHead ||= categorizeHead(document.head);\n // record all existing head scripts as having been run, because they were run by browser, not mergeHead\n for (const element of categorizedHead.scripts) {\n allHeadScriptsEverRun[element.outerHTML] = {\n tracked: elementIsTracked(element),\n };\n }\n firstMerge = false;\n}\n\n// Returns function which resolves when all new blocking head scripts have loaded\nexport function mergeHead(head: HTMLHeadElement) {\n const newHead = categorizeHead(head);\n const oldHead = categorizeHead(document.head);\n const reload = () => {\n window.Turbolinks.controller.viewInvalidated();\n return {\n waitForReload: () => new Promise<void>(() => {}),\n waitForHeadScripts: () => new Promise<void>(() => {}),\n };\n };\n\n if (\n head\n .querySelector('meta[name=\"turbolinks-visit-control\"]')\n ?.getAttribute(\"content\") === \"reload\"\n ) {\n return reload();\n }\n\n if (!firstMerge) {\n // IMPORTANT: we allow first merge to always proceed without reload\n lastTrackedScriptSignature =\n lastTrackedScriptSignature ||\n trackedElementSignature([...oldHead.scripts, ...oldHead.stylesheets]);\n if (\n lastTrackedScriptSignature !==\n trackedElementSignature([...newHead.scripts, ...newHead.stylesheets])\n ) {\n return reload();\n }\n }\n recordExistingHeadScripts(oldHead);\n\n copyNewHeadStylesheetElements(newHead.stylesheets, oldHead.stylesheets);\n removeCurrentHeadProvisionalElements(oldHead.provisional);\n copyNewHeadProvisionalElements(newHead.provisional);\n\n return {\n waitForReload: () => Promise.resolve(),\n waitForHeadScripts: copyNewHeadScriptElements(newHead.scripts),\n };\n}\n\nfunction trackedElementSignature(scripts: Element[]) {\n return scripts\n .filter(elementIsTracked)\n .map((s) => s.outerHTML)\n .join();\n}\n\nfunction copyNewHeadStylesheetElements(next: Element[], prev: Element[]) {\n const existing = prev.map((s) => s.outerHTML);\n for (const element of next) {\n if (!existing.includes(element.outerHTML)) {\n document.head.appendChild(element);\n }\n }\n}\n\nfunction copyNewHeadScriptElements(next: Element[]): () => Promise<void> {\n const deferredScripts: Element[] = [];\n let blockingLoaded: boolean[] = [];\n const scriptsLoadedPromise = new Promise<void>((onScriptsLoaded) => {\n for (const element of next as HTMLScriptElement[]) {\n const runBefore = element.outerHTML in allHeadScriptsEverRun;\n if (!runBefore) {\n let cb;\n if (!element.defer && element.src) {\n const idx = blockingLoaded.length;\n cb = () => {\n blockingLoaded[idx] = true;\n if (blockingLoaded.every((v) => v)) {\n onScriptsLoaded();\n }\n };\n blockingLoaded.push(false);\n }\n const newElement = createScriptElement(element, cb);\n if (element.defer) {\n deferredScripts.push(newElement);\n } else {\n document.head.appendChild(newElement);\n }\n allHeadScriptsEverRun[element.outerHTML] = {\n tracked: elementIsTracked(element),\n };\n }\n }\n if (blockingLoaded.length === 0) {\n // raf waits for react to finish\n onScriptsLoaded();\n }\n });\n return () => {\n deferredScripts.forEach((s) => document.head.appendChild(s));\n return scriptsLoadedPromise;\n };\n}\n\nfunction removeCurrentHeadProvisionalElements(prev: Element[]) {\n for (const element of prev) {\n document.head.removeChild(element);\n }\n}\n\nfunction copyNewHeadProvisionalElements(next: Element[]) {\n for (const element of next) {\n document.head.appendChild(element);\n }\n}\n\nfunction elementIsTracked(element: Element) {\n return element.getAttribute(\"data-turbolinks-track\") == \"reload\";\n}\n\nfunction elementIsScript(element: Element) {\n const tagName = element.tagName.toLowerCase();\n return tagName == \"script\";\n}\n\nfunction elementIsStylesheet(element: Element) {\n const tagName = element.tagName.toLowerCase();\n return (\n tagName == \"style\" ||\n (tagName == \"link\" && element.getAttribute(\"rel\") == \"stylesheet\")\n );\n}\n\nfunction elementIsFavicon(element: Element) {\n const tagName = element.tagName.toLowerCase();\n\n return tagName == \"link\" && element.getAttribute(\"rel\") == \"icon\";\n}\n\nfunction categorizeHead(head: ParentNode) {\n const scripts = [];\n const stylesheets = [];\n const provisional = [];\n for (const element of head.children) {\n // we want to keep the same favicon on page transitions\n if (elementIsFavicon(element)) {\n continue;\n }\n\n if (elementIsScript(element)) {\n scripts.push(element);\n } else if (elementIsStylesheet(element)) {\n stylesheets.push(element);\n } else {\n provisional.push(element);\n }\n }\n return { scripts, stylesheets, provisional };\n}\n","import \"../../lib/type\";\nimport type { PageContextClient } from \"vike/types\";\nimport { Turbolinks } from \"../../lib/turbolinks\";\nimport {\n mergeHead,\n recordExistingHeadScripts,\n} from \"../../lib/turbolinks/mergeHead\";\nimport {\n setBodyAttributes,\n getElementAttributes,\n} from \"../../lib/elementUtils\";\n\nexport default async function wrappedOnBeforeRenderClient(\n pageContext: PageContextClient\n) {\n if (pageContext.isHydration) {\n // Vike scripts load async so can run before document.body exists. we need to delay rendering.\n // This is only an issue if user sets `injectScriptsAt: \"HTML_BEGIN\"` in +config.ts\n if (document.readyState === \"loading\") {\n await new Promise((resolve) =>\n document.addEventListener(\"DOMContentLoaded\", () => resolve(null))\n );\n }\n pageContext._turbolinksProxy = {\n body: document.getElementById(\"proxied-body\")!,\n };\n Turbolinks._vpsCachePageContext({\n proxyLayoutInfo: pageContext.proxyLayoutInfo,\n });\n recordExistingHeadScripts();\n return;\n }\n\n if (pageContext?._snapshot) {\n if (pageContext.isHydration) {\n throw new Error(\n \"restoration visit should never happen on initial render\"\n );\n }\n const { proxyLayoutInfo } = pageContext._snapshot.pageContext;\n const { bodyEl, headEl } = pageContext._snapshot;\n const proxyBodyEl = bodyEl.querySelector(\"#proxied-body\")!;\n if (!proxyBodyEl || !(proxyBodyEl instanceof HTMLElement)) {\n throw new Error(\"proxied body not found in cached snapshot\");\n }\n pageContext.proxyLayoutInfo = proxyLayoutInfo;\n pageContext._turbolinksProxy = {\n bodyAttrs: getElementAttributes(bodyEl),\n body: proxyBodyEl,\n head: headEl,\n };\n }\n const { head, bodyAttrs } = pageContext._turbolinksProxy!;\n pageContext._shouldEmitBeforeRender = true;\n\n await Turbolinks._vikeBeforeRender(pageContext._turbolinksVisit, {\n proxyLayoutInfo: pageContext.proxyLayoutInfo,\n });\n const { waitForReload, waitForHeadScripts } = mergeHead(head!);\n\n // If a full reload is required, wait for it here\n await waitForReload();\n pageContext._waitForHeadScripts = waitForHeadScripts;\n\n if (bodyAttrs) setBodyAttributes(bodyAttrs);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../lib/turbolinks/mergeHead.ts","../../../renderer/wrapped/onBeforeRenderClient.ts"],"sourcesContent":["import { createScriptElement } from \"./util\";\n\ninterface ElementDetails {\n tracked: boolean;\n}\nconst allHeadScriptsEverRun: { [outerHTML: string]: ElementDetails } = {};\nlet firstMerge = true;\nlet lastTrackedScriptSignature: string;\n\nexport function recordExistingHeadScripts(categorizedHead?: CategorizedHead) {\n categorizedHead ||= categorizeHead(document.head);\n // record all existing head scripts as having been run, because they were run by browser, not mergeHead\n for (const element of categorizedHead.scripts) {\n allHeadScriptsEverRun[element.outerHTML] = {\n tracked: elementIsTracked(element),\n };\n }\n lastTrackedScriptSignature =\n lastTrackedScriptSignature || trackedElementSignature(categorizedHead);\n firstMerge = false;\n}\n\n// Returns function which resolves when all new blocking head scripts have loaded\nexport function mergeHead(head: HTMLHeadElement) {\n const newHead = categorizeHead(head);\n const oldHead = categorizeHead(document.head);\n const reload = () => {\n window.Turbolinks.controller.viewInvalidated();\n return {\n waitForReload: () => new Promise<void>(() => {}),\n waitForHeadScripts: () => new Promise<void>(() => {}),\n };\n };\n\n if (\n head\n .querySelector('meta[name=\"turbolinks-visit-control\"]')\n ?.getAttribute(\"content\") === \"reload\"\n ) {\n return reload();\n }\n\n if (!firstMerge) {\n // IMPORTANT: we allow first merge to always proceed without reload\n const newTrackedScriptSignature = trackedElementSignature(newHead);\n if (lastTrackedScriptSignature !== newTrackedScriptSignature) {\n return reload();\n }\n }\n firstMerge = false;\n lastTrackedScriptSignature =\n lastTrackedScriptSignature || trackedElementSignature(newHead);\n\n copyNewHeadStylesheetElements(newHead.stylesheets, oldHead.stylesheets);\n removeCurrentHeadProvisionalElements(oldHead.provisional);\n copyNewHeadProvisionalElements(newHead.provisional);\n\n return {\n waitForReload: () => Promise.resolve(),\n waitForHeadScripts: copyNewHeadScriptElements(newHead.scripts),\n };\n}\n\nfunction trackedElementSignature(head: CategorizedHead) {\n return [...head.scripts, ...head.stylesheets]\n .filter(elementIsTracked)\n .map((s) => s.outerHTML)\n .join();\n}\n\nfunction copyNewHeadStylesheetElements(next: Element[], prev: Element[]) {\n const existing = prev.map((s) => s.outerHTML);\n for (const element of next) {\n if (!existing.includes(element.outerHTML)) {\n document.head.appendChild(element);\n }\n }\n}\n\nfunction copyNewHeadScriptElements(next: Element[]): () => Promise<void> {\n const deferredScripts: Element[] = [];\n let blockingLoaded: boolean[] = [];\n const scriptsLoadedPromise = new Promise<void>((onScriptsLoaded) => {\n for (const element of next as HTMLScriptElement[]) {\n const runBefore = element.outerHTML in allHeadScriptsEverRun;\n if (!runBefore) {\n let cb;\n if (!element.defer && element.src) {\n const idx = blockingLoaded.length;\n cb = () => {\n blockingLoaded[idx] = true;\n if (blockingLoaded.every((v) => v)) {\n onScriptsLoaded();\n }\n };\n blockingLoaded.push(false);\n }\n const newElement = createScriptElement(element, cb);\n if (element.defer) {\n deferredScripts.push(newElement);\n } else {\n document.head.appendChild(newElement);\n }\n allHeadScriptsEverRun[element.outerHTML] = {\n tracked: elementIsTracked(element),\n };\n }\n }\n if (blockingLoaded.length === 0) {\n // raf waits for react to finish\n onScriptsLoaded();\n }\n });\n return () => {\n deferredScripts.forEach((s) => document.head.appendChild(s));\n return scriptsLoadedPromise;\n };\n}\n\nfunction removeCurrentHeadProvisionalElements(prev: Element[]) {\n for (const element of prev) {\n document.head.removeChild(element);\n }\n}\n\nfunction copyNewHeadProvisionalElements(next: Element[]) {\n for (const element of next) {\n document.head.appendChild(element);\n }\n}\n\nfunction elementIsTracked(element: Element) {\n return element.getAttribute(\"data-turbolinks-track\") == \"reload\";\n}\n\nfunction elementIsScript(element: Element) {\n const tagName = element.tagName.toLowerCase();\n return tagName == \"script\";\n}\n\nfunction elementIsStylesheet(element: Element) {\n const tagName = element.tagName.toLowerCase();\n return (\n tagName == \"style\" ||\n (tagName == \"link\" && element.getAttribute(\"rel\") == \"stylesheet\")\n );\n}\n\nfunction elementIsFavicon(element: Element) {\n const tagName = element.tagName.toLowerCase();\n\n return tagName == \"link\" && element.getAttribute(\"rel\") == \"icon\";\n}\n\nfunction categorizeHead(head: ParentNode) {\n const scripts = [];\n const stylesheets = [];\n const provisional = [];\n for (const element of head.children) {\n // we want to keep the same favicon on page transitions\n if (elementIsFavicon(element)) {\n continue;\n }\n\n if (elementIsScript(element)) {\n scripts.push(element);\n } else if (elementIsStylesheet(element)) {\n stylesheets.push(element);\n } else {\n provisional.push(element);\n }\n }\n return { scripts, stylesheets, provisional };\n}\ntype CategorizedHead = ReturnType<typeof categorizeHead>;\n","import \"../../lib/type\";\nimport type { PageContextClient } from \"vike/types\";\nimport { Turbolinks } from \"../../lib/turbolinks\";\nimport {\n mergeHead,\n recordExistingHeadScripts,\n} from \"../../lib/turbolinks/mergeHead\";\nimport {\n setBodyAttributes,\n getElementAttributes,\n} from \"../../lib/elementUtils\";\n\nexport default async function wrappedOnBeforeRenderClient(\n pageContext: PageContextClient\n) {\n if (pageContext.isHydration) {\n // Vike scripts load async so can run before document.body exists. we need to delay rendering.\n // This is only an issue if user sets `injectScriptsAt: \"HTML_BEGIN\"` in +config.ts\n if (document.readyState === \"loading\") {\n await new Promise((resolve) =>\n document.addEventListener(\"DOMContentLoaded\", () => resolve(null))\n );\n }\n pageContext._turbolinksProxy = {\n body: document.getElementById(\"proxied-body\")!,\n };\n Turbolinks._vpsCachePageContext({\n proxyLayoutInfo: pageContext.proxyLayoutInfo,\n });\n recordExistingHeadScripts();\n return;\n }\n\n if (pageContext?._snapshot) {\n if (pageContext.isHydration) {\n throw new Error(\n \"restoration visit should never happen on initial render\"\n );\n }\n const { proxyLayoutInfo } = pageContext._snapshot.pageContext;\n const { bodyEl, headEl } = pageContext._snapshot;\n const proxyBodyEl = bodyEl.querySelector(\"#proxied-body\")!;\n if (!proxyBodyEl || !(proxyBodyEl instanceof HTMLElement)) {\n throw new Error(\"proxied body not found in cached snapshot\");\n }\n pageContext.proxyLayoutInfo = proxyLayoutInfo;\n pageContext._turbolinksProxy = {\n bodyAttrs: getElementAttributes(bodyEl),\n body: proxyBodyEl,\n head: headEl,\n };\n }\n const { head, bodyAttrs } = pageContext._turbolinksProxy!;\n pageContext._shouldEmitBeforeRender = true;\n\n await Turbolinks._vikeBeforeRender(pageContext._turbolinksVisit, {\n proxyLayoutInfo: pageContext.proxyLayoutInfo,\n });\n const { waitForReload, waitForHeadScripts } = mergeHead(head!);\n\n // If a full reload is required, wait for it here\n await waitForReload();\n pageContext._waitForHeadScripts = waitForHeadScripts;\n\n if (bodyAttrs) setBodyAttributes(bodyAttrs);\n}\n"],"mappings":";;;;;;;;;;AAKA,IAAM,wBAAiE,CAAC;AACxE,IAAI,aAAa;AACjB,IAAI;AAEG,SAAS,0BAA0B,iBAAmC;AAC3E,wCAAoB,eAAe,SAAS,IAAI;AAEhD,aAAW,WAAW,gBAAgB,SAAS;AAC7C,0BAAsB,QAAQ,SAAS,IAAI;AAAA,MACzC,SAAS,iBAAiB,OAAO;AAAA,IACnC;AAAA,EACF;AACA,+BACE,8BAA8B,wBAAwB,eAAe;AACvE,eAAa;AACf;AAGO,SAAS,UAAU,MAAuB;AAC/C,QAAM,UAAU,eAAe,IAAI;AACnC,QAAM,UAAU,eAAe,SAAS,IAAI;AAC5C,QAAM,SAAS,MAAM;AACnB,WAAO,WAAW,WAAW,gBAAgB;AAC7C,WAAO;AAAA,MACL,eAAe,MAAM,IAAI,QAAc,MAAM;AAAA,MAAC,CAAC;AAAA,MAC/C,oBAAoB,MAAM,IAAI,QAAc,MAAM;AAAA,MAAC,CAAC;AAAA,IACtD;AAAA,EACF;AAEA,MACE,KACG,cAAc,uCAAuC,GACpD,aAAa,SAAS,MAAM,UAChC;AACA,WAAO,OAAO;AAAA,EAChB;AAEA,MAAI,CAAC,YAAY;AAEf,UAAM,4BAA4B,wBAAwB,OAAO;AACjE,QAAI,+BAA+B,2BAA2B;AAC5D,aAAO,OAAO;AAAA,IAChB;AAAA,EACF;AACA,eAAa;AACb,+BACE,8BAA8B,wBAAwB,OAAO;AAE/D,gCAA8B,QAAQ,aAAa,QAAQ,WAAW;AACtE,uCAAqC,QAAQ,WAAW;AACxD,iCAA+B,QAAQ,WAAW;AAElD,SAAO;AAAA,IACL,eAAe,MAAM,QAAQ,QAAQ;AAAA,IACrC,oBAAoB,0BAA0B,QAAQ,OAAO;AAAA,EAC/D;AACF;AAEA,SAAS,wBAAwB,MAAuB;AACtD,SAAO,CAAC,GAAG,KAAK,SAAS,GAAG,KAAK,WAAW,EACzC,OAAO,gBAAgB,EACvB,IAAI,CAAC,MAAM,EAAE,SAAS,EACtB,KAAK;AACV;AAEA,SAAS,8BAA8B,MAAiB,MAAiB;AACvE,QAAM,WAAW,KAAK,IAAI,CAAC,MAAM,EAAE,SAAS;AAC5C,aAAW,WAAW,MAAM;AAC1B,QAAI,CAAC,SAAS,SAAS,QAAQ,SAAS,GAAG;AACzC,eAAS,KAAK,YAAY,OAAO;AAAA,IACnC;AAAA,EACF;AACF;AAEA,SAAS,0BAA0B,MAAsC;AACvE,QAAM,kBAA6B,CAAC;AACpC,MAAI,iBAA4B,CAAC;AACjC,QAAM,uBAAuB,IAAI,QAAc,CAAC,oBAAoB;AAClE,eAAW,WAAW,MAA6B;AACjD,YAAM,YAAY,QAAQ,aAAa;AACvC,UAAI,CAAC,WAAW;AACd,YAAI;AACJ,YAAI,CAAC,QAAQ,SAAS,QAAQ,KAAK;AACjC,gBAAM,MAAM,eAAe;AAC3B,eAAK,MAAM;AACT,2BAAe,GAAG,IAAI;AACtB,gBAAI,eAAe,MAAM,CAAC,MAAM,CAAC,GAAG;AAClC,8BAAgB;AAAA,YAClB;AAAA,UACF;AACA,yBAAe,KAAK,KAAK;AAAA,QAC3B;AACA,cAAM,aAAa,oBAAoB,SAAS,EAAE;AAClD,YAAI,QAAQ,OAAO;AACjB,0BAAgB,KAAK,UAAU;AAAA,QACjC,OAAO;AACL,mBAAS,KAAK,YAAY,UAAU;AAAA,QACtC;AACA,8BAAsB,QAAQ,SAAS,IAAI;AAAA,UACzC,SAAS,iBAAiB,OAAO;AAAA,QACnC;AAAA,MACF;AAAA,IACF;AACA,QAAI,eAAe,WAAW,GAAG;AAE/B,sBAAgB;AAAA,IAClB;AAAA,EACF,CAAC;AACD,SAAO,MAAM;AACX,oBAAgB,QAAQ,CAAC,MAAM,SAAS,KAAK,YAAY,CAAC,CAAC;AAC3D,WAAO;AAAA,EACT;AACF;AAEA,SAAS,qCAAqC,MAAiB;AAC7D,aAAW,WAAW,MAAM;AAC1B,aAAS,KAAK,YAAY,OAAO;AAAA,EACnC;AACF;AAEA,SAAS,+BAA+B,MAAiB;AACvD,aAAW,WAAW,MAAM;AAC1B,aAAS,KAAK,YAAY,OAAO;AAAA,EACnC;AACF;AAEA,SAAS,iBAAiB,SAAkB;AAC1C,SAAO,QAAQ,aAAa,uBAAuB,KAAK;AAC1D;AAEA,SAAS,gBAAgB,SAAkB;AACzC,QAAM,UAAU,QAAQ,QAAQ,YAAY;AAC5C,SAAO,WAAW;AACpB;AAEA,SAAS,oBAAoB,SAAkB;AAC7C,QAAM,UAAU,QAAQ,QAAQ,YAAY;AAC5C,SACE,WAAW,WACV,WAAW,UAAU,QAAQ,aAAa,KAAK,KAAK;AAEzD;AAEA,SAAS,iBAAiB,SAAkB;AAC1C,QAAM,UAAU,QAAQ,QAAQ,YAAY;AAE5C,SAAO,WAAW,UAAU,QAAQ,aAAa,KAAK,KAAK;AAC7D;AAEA,SAAS,eAAe,MAAkB;AACxC,QAAM,UAAU,CAAC;AACjB,QAAM,cAAc,CAAC;AACrB,QAAM,cAAc,CAAC;AACrB,aAAW,WAAW,KAAK,UAAU;AAEnC,QAAI,iBAAiB,OAAO,GAAG;AAC7B;AAAA,IACF;AAEA,QAAI,gBAAgB,OAAO,GAAG;AAC5B,cAAQ,KAAK,OAAO;AAAA,IACtB,WAAW,oBAAoB,OAAO,GAAG;AACvC,kBAAY,KAAK,OAAO;AAAA,IAC1B,OAAO;AACL,kBAAY,KAAK,OAAO;AAAA,IAC1B;AAAA,EACF;AACA,SAAO,EAAE,SAAS,aAAa,YAAY;AAC7C;;;ACjKA,eAAO,4BACL,aACA;AACA,MAAI,YAAY,aAAa;AAG3B,QAAI,SAAS,eAAe,WAAW;AACrC,YAAM,IAAI;AAAA,QAAQ,CAAC,YACjB,SAAS,iBAAiB,oBAAoB,MAAM,QAAQ,IAAI,CAAC;AAAA,MACnE;AAAA,IACF;AACA,gBAAY,mBAAmB;AAAA,MAC7B,MAAM,SAAS,eAAe,cAAc;AAAA,IAC9C;AACA,eAAW,qBAAqB;AAAA,MAC9B,iBAAiB,YAAY;AAAA,IAC/B,CAAC;AACD,8BAA0B;AAC1B;AAAA,EACF;AAEA,MAAI,aAAa,WAAW;AAC1B,QAAI,YAAY,aAAa;AAC3B,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,UAAM,EAAE,gBAAgB,IAAI,YAAY,UAAU;AAClD,UAAM,EAAE,QAAQ,OAAO,IAAI,YAAY;AACvC,UAAM,cAAc,OAAO,cAAc,eAAe;AACxD,QAAI,CAAC,eAAe,EAAE,uBAAuB,cAAc;AACzD,YAAM,IAAI,MAAM,2CAA2C;AAAA,IAC7D;AACA,gBAAY,kBAAkB;AAC9B,gBAAY,mBAAmB;AAAA,MAC7B,WAAW,qBAAqB,MAAM;AAAA,MACtC,MAAM;AAAA,MACN,MAAM;AAAA,IACR;AAAA,EACF;AACA,QAAM,EAAE,MAAM,UAAU,IAAI,YAAY;AACxC,cAAY,0BAA0B;AAEtC,QAAM,WAAW,kBAAkB,YAAY,kBAAkB;AAAA,IAC/D,iBAAiB,YAAY;AAAA,EAC/B,CAAC;AACD,QAAM,EAAE,eAAe,mBAAmB,IAAI,UAAU,IAAK;AAG7D,QAAM,cAAc;AACpB,cAAY,sBAAsB;AAElC,MAAI;AAAW,sBAAkB,SAAS;AAC5C;","names":[]}
|