@markless/router 0.6.0 → 0.8.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/dist/{create-server-entry-DaYssO9x.js → create-server-entry-BsYTWdxi.js} +28 -7
- package/dist/debug-channel-dB6CJyHa.js +1 -0
- package/dist/index.js +4 -4
- package/dist/{request-locals-z3rJTizs.js → request-locals-BDAhNpEa.js} +5 -1
- package/dist/{route-manifest-DMizh42B.js → route-manifest-DmoxXanW.js} +2 -2
- package/dist/vite/route-href.js +1 -2
- package/dist/vite/runtime/create-route-discovery.js +1 -1
- package/dist/vite/runtime/create-server-entry.js +1 -2
- package/dist/vite/runtime/mdx-route.d.ts +2 -1
- package/dist/vite/runtime/mdx-route.js +60 -1
- package/dist/vite.js +102 -18
- package/package.json +8 -7
- package/src/vite/entries/fragment-entry.ts +74 -9
- package/dist/debug-channel-CewnC9fH.js +0 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as ROUTE_SCRIPT_TYPE, c as
|
|
2
|
-
import { i as matchRouteManifest, n as buildRouteManifestFromFileIds, o as normalizeRouteFileId } from "./route-manifest-
|
|
1
|
+
import { a as ROUTE_SCRIPT_TYPE, c as servedHtmlAttributesScript, d as REPLACE_ATTRIBUTE, f as SCROLL_ATTRIBUTE, i as HTML_ATTRIBUTES_SLOT, l as LINK_ATTRIBUTE, n as requestLocals, o as SERVED_HTML_ATTRIBUTES_PROPERTY, r as takeMiddlewareHeaders, u as PREFETCH_ATTRIBUTE } from "./request-locals-BDAhNpEa.js";
|
|
2
|
+
import { i as matchRouteManifest, n as buildRouteManifestFromFileIds, o as normalizeRouteFileId } from "./route-manifest-DmoxXanW.js";
|
|
3
3
|
import { n as __marklessDebugBootstrapSource } from "./debug-channel-Di6SppZv.js";
|
|
4
4
|
import { CONTAINER_ATTRIBUTE, PROTOCOL_CONTAINER_END_COMMENT, PROTOCOL_CONTAINER_START_COMMENT, PROTOCOL_LOCATOR_PATHS_ATTRIBUTE } from "@markless/web/fns/container-region";
|
|
5
5
|
import { CONTAINER_RETIRE_PROPERTY, renderToString, splitSsrContainer } from "@markless/web/render-to-string";
|
|
@@ -49,6 +49,11 @@ function appendModulePreloads(document, items) {
|
|
|
49
49
|
return appended;
|
|
50
50
|
}
|
|
51
51
|
//#endregion
|
|
52
|
+
//#region src/vite/runtime/document-handlers.ts
|
|
53
|
+
function documentHandlersMessage(what) {
|
|
54
|
+
return `MARKLESS_ROUTER_DOCUMENT_HANDLERS_UNSUPPORTED: ${what}, but the router serves only the document's HTML and never wires it in the browser, so the handler would never run. Put the handler on an element a page renders, for example in a layout component each page wraps its content in.`;
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
52
57
|
//#region src/vite/fragment-navigation.ts
|
|
53
58
|
function startFragmentNavigation(d, linkAttributeOf, routes, importModule, loadRun, intentOf) {
|
|
54
59
|
const w = d.defaultView;
|
|
@@ -192,7 +197,14 @@ function runFragmentNavigation(d, config, routes, importModule, scrolled, arrivi
|
|
|
192
197
|
if (endpoint && !under) return false;
|
|
193
198
|
const rest = page.indexOf("**");
|
|
194
199
|
if (rest === -1 ? page.length !== parts.length : parts.length <= rest) return false;
|
|
195
|
-
return page.every((segment, index) =>
|
|
200
|
+
return page.every((segment, index) => {
|
|
201
|
+
if (segment === "**" || segment === ":") return true;
|
|
202
|
+
try {
|
|
203
|
+
return segment === decodeURIComponent(parts[index]);
|
|
204
|
+
} catch {
|
|
205
|
+
return segment === parts[index];
|
|
206
|
+
}
|
|
207
|
+
});
|
|
196
208
|
});
|
|
197
209
|
};
|
|
198
210
|
const eligible = (anchor, forClick = false) => {
|
|
@@ -836,7 +848,7 @@ function createServerEntry(options) {
|
|
|
836
848
|
const renderSsr = baseArtifact?.renderSsr ?? pageModule.marklessRenderSsr;
|
|
837
849
|
if (!renderSsr) {
|
|
838
850
|
const shell = await renderDocumentShell(documentModule, pageProps, "");
|
|
839
|
-
const message = `Page module must export
|
|
851
|
+
const message = `Page module must export a Markless compiled artifact: ${escapeHtml(file)}`;
|
|
840
852
|
return new Response(fillDocumentChildren(shell, message), http.commit(status, headers));
|
|
841
853
|
}
|
|
842
854
|
const documentLoads = options.documentNavigation === "document";
|
|
@@ -864,16 +876,17 @@ function createServerEntry(options) {
|
|
|
864
876
|
resumeModuleUrl: options.resumeEntryPath ?? baseArtifact?.resumeModuleUrl,
|
|
865
877
|
modulePreloads: (html, resumes) => landingModulePreloads(resumes ? options.routeSsrModulePreloads?.[file] : void 0, linkBridges?.[html.includes("href=\"#/") ? 1 : 0])
|
|
866
878
|
};
|
|
879
|
+
const headStart = (documentLoads && fragmentNavigation && !fragment ? servedHtmlAttributesScript() : "") + (fragment ? "" : options.importMap ?? "");
|
|
867
880
|
if (options.render === "blocking") {
|
|
868
881
|
const pageHtml = splitPageHtml(await renderToString(routedArtifact, renderOptions));
|
|
869
882
|
privateToVisitor();
|
|
870
|
-
const shell = await renderDocumentShell(documentModule, pageProps, pageHtml.headHtml,
|
|
883
|
+
const shell = await renderDocumentShell(documentModule, pageProps, pageHtml.headHtml, headStart);
|
|
871
884
|
return new Response(wholeDocument(shell, pageHtml, fragment, documentLoads), http.commit(status, headers));
|
|
872
885
|
}
|
|
873
886
|
const stream = await renderToStream(routedArtifact, renderOptions);
|
|
874
887
|
privateToVisitor();
|
|
875
888
|
const pageHtml = splitPageHtml(stream.shell);
|
|
876
|
-
const shell = await renderDocumentShell(documentModule, pageProps, pageHtml.headHtml,
|
|
889
|
+
const shell = await renderDocumentShell(documentModule, pageProps, pageHtml.headHtml, headStart);
|
|
877
890
|
if (stream.pendingArmCount === 0) return new Response(wholeDocument(shell, pageHtml, fragment, documentLoads), http.commit(status, headers));
|
|
878
891
|
const placed = placePage(shell, DOCUMENT_CHILDREN_PLACEHOLDER, pageHtml);
|
|
879
892
|
const prefix = fragment ? fragmentDocument(shell, pageHtml, documentLoads) + FRAGMENT_REGION_END : placed.prefix;
|
|
@@ -1103,8 +1116,15 @@ function wholeDocument(shell, page, fragment, documentLoads) {
|
|
|
1103
1116
|
async function renderDocumentModule(documentModule, props, htmlAttributes) {
|
|
1104
1117
|
const output = await documentModule?.default?.renderSsr?.(props, { [HTML_ATTRIBUTES_SLOT]: htmlAttributes });
|
|
1105
1118
|
assertDocumentStorageFree(output);
|
|
1119
|
+
assertDocumentHandlerFree(output);
|
|
1106
1120
|
return output?.html;
|
|
1107
1121
|
}
|
|
1122
|
+
function assertDocumentHandlerFree(output) {
|
|
1123
|
+
const view = output?.view;
|
|
1124
|
+
const names = [...new Set((view?.events ?? []).map((event) => event.eventName))];
|
|
1125
|
+
if (names.length === 0) return;
|
|
1126
|
+
throw new Error(documentHandlersMessage(`the document renders ${names.join(", ")} handlers`));
|
|
1127
|
+
}
|
|
1108
1128
|
function assertDocumentStorageFree(output) {
|
|
1109
1129
|
const storage = documentStorageRecords(output?.state);
|
|
1110
1130
|
if (storage.length === 0) return;
|
|
@@ -1269,6 +1289,7 @@ function fragmentConfig(fragment, documentLoads) {
|
|
|
1269
1289
|
replaceAttribute: REPLACE_ATTRIBUTE,
|
|
1270
1290
|
scrollAttribute: SCROLL_ATTRIBUTE,
|
|
1271
1291
|
routeScriptType: ROUTE_SCRIPT_TYPE,
|
|
1292
|
+
servedHtmlAttributesProperty: SERVED_HTML_ATTRIBUTES_PROPERTY,
|
|
1272
1293
|
retireProperty: CONTAINER_RETIRE_PROPERTY,
|
|
1273
1294
|
streamExecutorGlobal: STREAM_ARM_EXECUTOR_GLOBAL,
|
|
1274
1295
|
streamedArmSelector: STREAMED_ARM_SELECTOR,
|
|
@@ -1328,4 +1349,4 @@ function insertImportMap(html, importMap) {
|
|
|
1328
1349
|
return `${html.slice(0, at)}${importMap}${html.slice(at)}`;
|
|
1329
1350
|
}
|
|
1330
1351
|
//#endregion
|
|
1331
|
-
export { runFragmentNavigation as a, FRAGMENT_PATH_PREFIX as i, routerLinkBridgeModules as n, startFragmentNavigation as o, LINK_BRIDGE_DIR as r,
|
|
1352
|
+
export { runFragmentNavigation as a, listenForLinkIntent as c, FRAGMENT_PATH_PREFIX as i, routerLinkBridgeModules as n, startFragmentNavigation as o, LINK_BRIDGE_DIR as r, documentHandlersMessage as s, createServerEntry as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { t as __marklessDebugActivateContainer, n as __marklessDebugBootstrapSource, r as __marklessDebugChannelForTest, i as __marklessDebugDisposeContainer, a as __marklessDebugInvalidateElement, o as __marklessDebugRecordInteraction, s as __marklessDebugRecordViolation, c as __marklessDebugRegisterDelegatedEvents, l as __marklessDebugRegisterRouter, u as __marklessDebugResetForTest, d as __marklessDebugSetBoundaries, f as __marklessDebugStartContainer } from "./debug-channel-Di6SppZv.js";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as normalizeRequestPathname, c as parseRequestFile, i as matchRouteManifest, l as transformRequestFileSource, n as buildRouteManifestFromFileIds, o as normalizeRouteFileId, s as normalizeRequestFileId } from "./route-manifest-
|
|
1
|
+
import { d as REPLACE_ATTRIBUTE, f as SCROLL_ATTRIBUTE, i as HTML_ATTRIBUTES_SLOT, l as LINK_ATTRIBUTE, s as routeMountTarget, t as rememberRequestLocals, u as PREFETCH_ATTRIBUTE } from "./request-locals-BDAhNpEa.js";
|
|
2
|
+
import { a as normalizeRequestPathname, c as parseRequestFile, i as matchRouteManifest, l as transformRequestFileSource, n as buildRouteManifestFromFileIds, o as normalizeRouteFileId, s as normalizeRequestFileId } from "./route-manifest-DmoxXanW.js";
|
|
3
3
|
import { createExternalDelegateStructureTokens, createExternalDelegateView } from "@markless/web/fns/external-delegate";
|
|
4
4
|
import { render } from "@markless/web/render";
|
|
5
5
|
import { MARKLESS_PENDING_MIN_VISIBLE_MS as MARKLESS_NAV_PENDING_MIN_MS, settleOrPendingDeadline } from "@markless/web/fns/pending-timing";
|
|
@@ -152,8 +152,8 @@ async function __marklessRouterStartSpaNavigation(options) {
|
|
|
152
152
|
navigation.addEventListener("navigate", (event) => {
|
|
153
153
|
handleNavigateEvent(event, context);
|
|
154
154
|
});
|
|
155
|
-
if (typeof __MARKLESS_DEBUG_ENABLED__ !== "undefined" && __MARKLESS_DEBUG_ENABLED__) await import("./debug-channel-
|
|
156
|
-
if (typeof __MARKLESS_DEBUG_ENABLED__ !== "undefined" && __MARKLESS_DEBUG_ENABLED__) await import("./debug-channel-
|
|
155
|
+
if (typeof __MARKLESS_DEBUG_ENABLED__ !== "undefined" && __MARKLESS_DEBUG_ENABLED__) await import("./debug-channel-dB6CJyHa.js").then((debug) => debug.__marklessDebugRegisterRouter(void 0, "spa-click-listener")).catch(() => {});
|
|
156
|
+
if (typeof __MARKLESS_DEBUG_ENABLED__ !== "undefined" && __MARKLESS_DEBUG_ENABLED__) await import("./debug-channel-dB6CJyHa.js").then((debug) => debug.__marklessDebugRegisterRouter(void 0, "navigation-event")).catch(() => {});
|
|
157
157
|
{
|
|
158
158
|
const bootUrl = parseSameOriginUrl(runtimeWindow.location.href, runtimeWindow.location.href);
|
|
159
159
|
const bootHashPath = bootUrl ? hashRoutePath(bootUrl) : void 0;
|
|
@@ -8,6 +8,10 @@ const PREFETCH_ATTRIBUTE = "data-markless-router-prefetch";
|
|
|
8
8
|
//#region src/route-dom.ts
|
|
9
9
|
const ROUTE_SCRIPT_TYPE = "@markless/core/route";
|
|
10
10
|
const HTML_ATTRIBUTES_SLOT = "__marklessRouterHtmlAttributes";
|
|
11
|
+
const SERVED_HTML_ATTRIBUTES_PROPERTY = "__marklessServedHtmlAttributes";
|
|
12
|
+
function servedHtmlAttributesScript() {
|
|
13
|
+
return `<script>document.documentElement.${SERVED_HTML_ATTRIBUTES_PROPERTY}=document.documentElement.getAttributeNames()<\/script>`;
|
|
14
|
+
}
|
|
11
15
|
function routeMountTarget(document, currentRoot) {
|
|
12
16
|
const root = currentRoot ?? document.querySelector(`script[type="@markless/core/route"]`)?.parentElement;
|
|
13
17
|
if (!root) return document.body;
|
|
@@ -87,4 +91,4 @@ function takeMiddlewareHeaders(request) {
|
|
|
87
91
|
return record.taken;
|
|
88
92
|
}
|
|
89
93
|
//#endregion
|
|
90
|
-
export { ROUTE_SCRIPT_TYPE as a,
|
|
94
|
+
export { ROUTE_SCRIPT_TYPE as a, servedHtmlAttributesScript as c, REPLACE_ATTRIBUTE as d, SCROLL_ATTRIBUTE as f, HTML_ATTRIBUTES_SLOT as i, LINK_ATTRIBUTE as l, requestLocals as n, SERVED_HTML_ATTRIBUTES_PROPERTY as o, takeMiddlewareHeaders as r, routeMountTarget as s, rememberRequestLocals as t, PREFETCH_ATTRIBUTE as u };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { basename, extname, join, normalize, relative } from "pathe";
|
|
2
|
-
import { joinURL, withLeadingSlash, withoutLeadingSlash, withoutTrailingSlash } from "ufo";
|
|
2
|
+
import { decode, joinURL, withLeadingSlash, withoutLeadingSlash, withoutTrailingSlash } from "ufo";
|
|
3
3
|
const MIDDLEWARE_DIR = "middleware";
|
|
4
4
|
const HTTP_METHODS = /* @__PURE__ */ new Set([
|
|
5
5
|
"connect",
|
|
@@ -432,7 +432,7 @@ function matchRoutePathname(route, requestPathname) {
|
|
|
432
432
|
params[routeSegment.slice(1)] = requestSegment;
|
|
433
433
|
continue;
|
|
434
434
|
}
|
|
435
|
-
if (routeSegment !== requestSegment) return;
|
|
435
|
+
if (routeSegment !== decode(requestSegment)) return;
|
|
436
436
|
}
|
|
437
437
|
return params;
|
|
438
438
|
}
|
package/dist/vite/route-href.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { __marklessRouteHref };
|
|
1
|
+
export { t as __marklessRouteHref } from "../route-href-CGooO_g6.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { o as normalizeRouteFileId } from "../../route-manifest-
|
|
1
|
+
import { o as normalizeRouteFileId } from "../../route-manifest-DmoxXanW.js";
|
|
2
2
|
//#region src/vite/runtime/create-route-discovery.ts
|
|
3
3
|
function createRouteDiscovery(discoveredPageModuleLoaders) {
|
|
4
4
|
const pageModuleLoaders = Object.fromEntries(Object.entries(discoveredPageModuleLoaders).map(([file, loader]) => [normalizeRouteFileId(file), loader]));
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { createServerEntry, routerLinkBridgeModules };
|
|
1
|
+
export { t as createServerEntry, n as routerLinkBridgeModules } from "../../create-server-entry-BsYTWdxi.js";
|
|
@@ -1186,4 +1186,5 @@ export declare function renderMdxChild(children: MdxChild[], component: MdxCompo
|
|
|
1186
1186
|
export declare function composeMdxState(children: readonly MdxChild[]): MdxStatePayload | undefined;
|
|
1187
1187
|
export declare function composeMdxView(parts: readonly MdxRoutePart[], children: readonly MdxChild[], initialElementOffset: number, initialCommentOffset?: number): MdxViewPayload | undefined;
|
|
1188
1188
|
export declare function loadMdxSymbol(symbolId: string, children: readonly MdxChild[], loaders: readonly MdxSymbolLoader[]): unknown;
|
|
1189
|
-
//#endregion
|
|
1189
|
+
//#endregion
|
|
1190
|
+
export {};
|
|
@@ -1,5 +1,64 @@
|
|
|
1
1
|
import { i as protocolStateVersion, n as protocolInstanceQualifies, t as protocolInstancePath } from "../../protocol-constants-B4WxrZAS.js";
|
|
2
2
|
//#endregion
|
|
3
|
+
//#region ../serializer/src/protocol-record-keys.ts
|
|
4
|
+
/** The field names each templated record list may spell, in the order its templates index them. */
|
|
5
|
+
const PROTOCOL_RECORD_KEYS = {
|
|
6
|
+
"markless/state": {
|
|
7
|
+
computed: "graphNodeId name async deriveSymbolId value version root $type $ref source flags records id type fields items entries values bytes arrayType buffer byteOffset length byteLength directValue dependencies path snapshot status key error",
|
|
8
|
+
sharedSeeds: "graphNodeId deriveSymbolId dependencies path reads",
|
|
9
|
+
sharedDefinitions: "id name exportedName scope version graphNodeIds projectionIds dependencies definitionId definitionName returnProperties kind graphNodeId path"
|
|
10
|
+
},
|
|
11
|
+
"markless/view": {
|
|
12
|
+
domUpdates: "hostNodeId source graphNodeId path target kind prefix suffix trueValue falseValue textNode name constantClass leadingClass symbolId",
|
|
13
|
+
behaviors: "hostNodeId source functionSource inputSources inputValues inputGraphReads inputIndex graphNodeId path symbolId usesGraph",
|
|
14
|
+
elementHandles: "hostNodeId handleId name plural"
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region ../serializer/src/protocol-templated-lists.ts
|
|
19
|
+
/**
|
|
20
|
+
* The record lists a payload script may serve as record templates instead of a plain array. The
|
|
21
|
+
* inline resumer reads the others at page load, before any decoder has loaded, so they stay plain.
|
|
22
|
+
*/
|
|
23
|
+
const PROTOCOL_TEMPLATED_LISTS = {
|
|
24
|
+
"markless/state": [
|
|
25
|
+
"computed",
|
|
26
|
+
"sharedSeeds",
|
|
27
|
+
"sharedDefinitions"
|
|
28
|
+
],
|
|
29
|
+
"markless/view": [
|
|
30
|
+
"domUpdates",
|
|
31
|
+
"behaviors",
|
|
32
|
+
"elementHandles"
|
|
33
|
+
]
|
|
34
|
+
};
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region ../serializer/src/protocol-record-templates.ts
|
|
37
|
+
function expandRecordTemplates(list, keys = "") {
|
|
38
|
+
if (Array.isArray(list)) return list;
|
|
39
|
+
const { t, r } = list;
|
|
40
|
+
const names = keys.split(" ");
|
|
41
|
+
const parts = t.map((text) => text.replace(/~(\d+):/g, (_, at) => `"${names[+at]}":`).split(/\^(\d+)/));
|
|
42
|
+
return r.map((ref) => {
|
|
43
|
+
if (typeof ref === "number") return JSON.parse(parts[ref][0]);
|
|
44
|
+
if (!Array.isArray(ref)) return ref;
|
|
45
|
+
const part = parts[ref[0]];
|
|
46
|
+
let text = part[0];
|
|
47
|
+
for (let at = 1; at < part.length; at += 2) text += ref[+part[at] + 1] + part[at + 1];
|
|
48
|
+
return JSON.parse(text);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/** Expands, in place, every templated list of a freshly parsed payload script. */
|
|
52
|
+
function decodeProtocolPayloadLists(type, payload) {
|
|
53
|
+
const record = payload;
|
|
54
|
+
const keys = PROTOCOL_RECORD_KEYS[type];
|
|
55
|
+
if (record) for (const key of PROTOCOL_TEMPLATED_LISTS[type]) {
|
|
56
|
+
const list = record[key];
|
|
57
|
+
if (list && !Array.isArray(list)) record[key] = expandRecordTemplates(list, keys[key]);
|
|
58
|
+
}
|
|
59
|
+
return payload;
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
3
62
|
//#region ../serializer/src/storage-cookie.ts
|
|
4
63
|
const STORAGE_RENDER_CONTEXT_READER = "storageValue";
|
|
5
64
|
//#endregion
|
|
@@ -747,7 +806,7 @@ function locate(root, view, host) {
|
|
|
747
806
|
}
|
|
748
807
|
function readScript(root, type) {
|
|
749
808
|
const text = root.querySelector(`script[type="${type}"]`)?.textContent;
|
|
750
|
-
return text ? JSON.parse(text) : void 0;
|
|
809
|
+
return text ? decodeProtocolPayloadLists(type, JSON.parse(text)) : void 0;
|
|
751
810
|
}
|
|
752
811
|
function references(value, id) {
|
|
753
812
|
return value === id || !!value && typeof value === "object" && Object.values(value).some((child) => references(child, id));
|
package/dist/vite.js
CHANGED
|
@@ -1,20 +1,30 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { l as transformRequestFileSource, n as buildRouteManifestFromFileIds, o as normalizeRouteFileId, r as isPageModuleFile, t as assertNoEndpointConflicts } from "./route-manifest-
|
|
1
|
+
import { d as REPLACE_ATTRIBUTE, f as SCROLL_ATTRIBUTE, l as LINK_ATTRIBUTE, u as PREFETCH_ATTRIBUTE } from "./request-locals-BDAhNpEa.js";
|
|
2
|
+
import { i as matchRouteManifest, l as transformRequestFileSource, n as buildRouteManifestFromFileIds, o as normalizeRouteFileId, r as isPageModuleFile, t as assertNoEndpointConflicts } from "./route-manifest-DmoxXanW.js";
|
|
3
3
|
import { t as __marklessRouteHref } from "./route-href-CGooO_g6.js";
|
|
4
4
|
import { r as protocolIslandSegment } from "./protocol-constants-B4WxrZAS.js";
|
|
5
|
-
import { a as runFragmentNavigation, i as FRAGMENT_PATH_PREFIX, n as routerLinkBridgeModules, o as startFragmentNavigation, s as
|
|
5
|
+
import { a as runFragmentNavigation, c as listenForLinkIntent, i as FRAGMENT_PATH_PREFIX, n as routerLinkBridgeModules, o as startFragmentNavigation, s as documentHandlersMessage } from "./create-server-entry-BsYTWdxi.js";
|
|
6
6
|
import { dirname, extname, isAbsolute, join, normalize, relative, resolve } from "pathe";
|
|
7
|
-
import { decodePath, joinURL, parsePath, parseQuery, parseURL, stringifyQuery, withLeadingSlash, withQuery, withoutLeadingSlash } from "ufo";
|
|
8
|
-
import { existsSync } from "node:fs";
|
|
7
|
+
import { decodePath, joinURL, parsePath, parseQuery, parseURL, stringifyQuery, withLeadingSlash, withQuery, withoutBase, withoutLeadingSlash } from "ufo";
|
|
8
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
9
9
|
import { mkdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
10
10
|
import { nitro } from "nitro/vite";
|
|
11
11
|
import { minifySync, sortUserPlugins } from "vite";
|
|
12
12
|
import { isClientPrimarySourceRequest, isExecutionLogChunk, isMarklessNavigationPack, isRenderDataSourceRequest, isResumeSourceRequest, isRouteNavigationSourceRequest, isSymbolOnlySourceRequest, lazyStartupPackName, marklessDeferredChunkFileNames, optimizedDepsToInclude, plannedLandingFiles, resolverVirtualModuleSourceFile, startupPackOfLazy, symbolVirtualModuleSourceFile, warmEvaluatedImports } from "@markless/bundler/preload";
|
|
13
13
|
import { MARKLESS_BUILD_PREFIX, MARKLESS_CHUNK_SPECIFIER_PREFIX, MARKLESS_IMPORT_MAP_ASSET, MARKLESS_PUBLIC_BUILD_METADATA_FILES, importMapScript, marklessBuildManifestDir } from "@markless/bundler/rolldown";
|
|
14
|
-
import {
|
|
14
|
+
import { parse } from "@tsrx/yuku";
|
|
15
15
|
import { toHtml } from "hast-util-to-html";
|
|
16
16
|
import { HastReader, createMdxHastHandle, defineHastPlugin, dropHandle, materializeHastTree, resolveHastSubscriptions, serializeHandle, visitHastHandle } from "satteri";
|
|
17
17
|
import "@markless/web/render-to-string";
|
|
18
|
+
import { isEventAttribute } from "@markless/compiler";
|
|
19
|
+
//#region src/vite/tsrx-ast.ts
|
|
20
|
+
/** A `.tsrx` module's tree; throws a `SyntaxError` naming the first error, as a module that does not parse has no tree to read. */
|
|
21
|
+
function parseTsrxModule(source) {
|
|
22
|
+
const { program, diagnostics } = parse(source, { preserveParens: true });
|
|
23
|
+
const error = diagnostics.find((diagnostic) => diagnostic.severity === "error");
|
|
24
|
+
if (error) throw new SyntaxError(`${error.message} (${error.start}:${error.end})`);
|
|
25
|
+
return program;
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
18
28
|
//#region src/route-types.ts
|
|
19
29
|
const routeTypesEnvPath = "markless-router-env.d.ts";
|
|
20
30
|
const routeTypesOutputPath = ".output/markless/router/types/routes.d.ts";
|
|
@@ -300,7 +310,7 @@ function lowerRouterLinks(code, ast) {
|
|
|
300
310
|
return applyEdits(code, edits);
|
|
301
311
|
}
|
|
302
312
|
function parseTransformAst(code, id, parse) {
|
|
303
|
-
if (id.includes(".tsrx")) return
|
|
313
|
+
if (id.includes(".tsrx")) return parseTsrxModule(code);
|
|
304
314
|
return parse(code, {
|
|
305
315
|
astType: "ts",
|
|
306
316
|
lang: id.includes(".jsx") ? "jsx" : "tsx",
|
|
@@ -825,6 +835,8 @@ function scalarPlanSourceReference(source) {
|
|
|
825
835
|
[MARKLESS_SCALAR_PLAN_SOURCE_QUERY]: null
|
|
826
836
|
});
|
|
827
837
|
}
|
|
838
|
+
//#endregion
|
|
839
|
+
//#region ../bundler/src/source-module.ts
|
|
828
840
|
/**
|
|
829
841
|
* The same one line for the live roster, gated on the same fact the reconcile
|
|
830
842
|
* plane is gated on: a payload with no computed nodes can hold no roster
|
|
@@ -844,6 +856,7 @@ function emitQueuedResumeContainerEvent(source, { censusRepair = true } = {}) {
|
|
|
844
856
|
implementation,
|
|
845
857
|
"export function resumeContainerEvent(input) {",
|
|
846
858
|
" const root = input.root;",
|
|
859
|
+
" if (input.eventIndex >= 0) input = { ...input, eventRecord: (root.__mE ||= JSON.parse(root.querySelector('script[type=\"markless/view\"]').textContent).events)[input.eventIndex] };",
|
|
847
860
|
censusRepair ? " if (root.__marklessShifted) return import('@markless/web/fns/served-census-repair').then((census) => (census.marklessRepairCensus(root), resumeContainerEvent(input)));" : "",
|
|
848
861
|
" if (!root.__marklessDispatch) {",
|
|
849
862
|
" let handler = marklessResumeContainerEvent, tail = Promise.resolve(), done = tail, lastEvent = null, lastEventTime = undefined, seen = new WeakMap();",
|
|
@@ -1389,11 +1402,11 @@ const NAVIGATION_POLYFILL_MODULE = "@virtualstate/navigation";
|
|
|
1389
1402
|
//#endregion
|
|
1390
1403
|
//#region src/vite/document-navigation.ts
|
|
1391
1404
|
const ROUTE_INVARIANT_DOCUMENT_PROPS = /* @__PURE__ */ new Set(["children"]);
|
|
1392
|
-
function documentNavigationFor(source
|
|
1405
|
+
function documentNavigationFor(source) {
|
|
1393
1406
|
if (source === void 0) return "client";
|
|
1394
1407
|
let program;
|
|
1395
1408
|
try {
|
|
1396
|
-
program =
|
|
1409
|
+
program = parseTsrxModule(source);
|
|
1397
1410
|
} catch {
|
|
1398
1411
|
return "document";
|
|
1399
1412
|
}
|
|
@@ -1403,6 +1416,43 @@ function documentNavigationFor(source, id) {
|
|
|
1403
1416
|
if (!component) return "document";
|
|
1404
1417
|
return readsOnlyRouteInvariantProps(nodes(component.params)) ? "client" : "document";
|
|
1405
1418
|
}
|
|
1419
|
+
function documentHandlerError(source, id) {
|
|
1420
|
+
if (source === void 0) return void 0;
|
|
1421
|
+
let program;
|
|
1422
|
+
try {
|
|
1423
|
+
program = parseTsrxModule(source);
|
|
1424
|
+
} catch {
|
|
1425
|
+
return;
|
|
1426
|
+
}
|
|
1427
|
+
const found = firstHandlerAttribute(program);
|
|
1428
|
+
if (!found) return void 0;
|
|
1429
|
+
return documentHandlersMessage(`${id} sets ${found.attribute} on <${found.tag}>`);
|
|
1430
|
+
}
|
|
1431
|
+
function firstHandlerAttribute(value, tag = "") {
|
|
1432
|
+
if (Array.isArray(value)) {
|
|
1433
|
+
for (const item of value) {
|
|
1434
|
+
const found = firstHandlerAttribute(item, tag);
|
|
1435
|
+
if (found) return found;
|
|
1436
|
+
}
|
|
1437
|
+
return;
|
|
1438
|
+
}
|
|
1439
|
+
const current = node(value);
|
|
1440
|
+
if (!current) return void 0;
|
|
1441
|
+
if (current.type === "JSXAttribute") {
|
|
1442
|
+
const name = node(current.name)?.name;
|
|
1443
|
+
return typeof name === "string" && (isEventAttribute(name) || name === "attach") ? {
|
|
1444
|
+
attribute: name,
|
|
1445
|
+
tag
|
|
1446
|
+
} : void 0;
|
|
1447
|
+
}
|
|
1448
|
+
const elementName = node(node(current.openingElement)?.name)?.name;
|
|
1449
|
+
const nextTag = typeof elementName === "string" ? elementName : tag;
|
|
1450
|
+
for (const [key, child] of Object.entries(current)) {
|
|
1451
|
+
if (key === "loc" || key === "range" || key === "metadata") continue;
|
|
1452
|
+
const found = firstHandlerAttribute(child, nextTag);
|
|
1453
|
+
if (found) return found;
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1406
1456
|
function readsOnlyRouteInvariantProps(params) {
|
|
1407
1457
|
if (params.length === 0) return true;
|
|
1408
1458
|
if (params.length > 1) return false;
|
|
@@ -1574,6 +1624,7 @@ function router(options = {}) {
|
|
|
1574
1624
|
return [
|
|
1575
1625
|
routerConfigPlugin(nitroPlugins, resumeEntry, prerenderWakeEntry, navigationEntry, routePreloads, fragmentEntry, serverBuildState()),
|
|
1576
1626
|
devSourceModuleRequestPlugin(),
|
|
1627
|
+
devPageForUrlPlugin(options),
|
|
1577
1628
|
pageServerOnlyPropsPlugin(),
|
|
1578
1629
|
mdxTransformPlugin(),
|
|
1579
1630
|
requestFileTransformPlugin(),
|
|
@@ -1595,6 +1646,37 @@ function pageServerOnlyPropsPlugin() {
|
|
|
1595
1646
|
}
|
|
1596
1647
|
};
|
|
1597
1648
|
}
|
|
1649
|
+
function devPageForUrlPlugin(options) {
|
|
1650
|
+
return {
|
|
1651
|
+
name: "markless-router:dev-page-for-url",
|
|
1652
|
+
apply: "serve",
|
|
1653
|
+
configResolved(config) {
|
|
1654
|
+
config.plugins.find((plugin) => typeof plugin.api?.declarePageForUrl === "function")?.api.declarePageForUrl((href) => devPageForUrl(config.root, config.base, options.mode, href));
|
|
1655
|
+
}
|
|
1656
|
+
};
|
|
1657
|
+
}
|
|
1658
|
+
function devPageForUrl(root, base, mode, href) {
|
|
1659
|
+
let url;
|
|
1660
|
+
try {
|
|
1661
|
+
url = new URL(href);
|
|
1662
|
+
} catch {
|
|
1663
|
+
return;
|
|
1664
|
+
}
|
|
1665
|
+
const pagesDir = join(root, "pages");
|
|
1666
|
+
if (!existsSync(pagesDir)) return void 0;
|
|
1667
|
+
const fileIds = readdirSync(pagesDir, {
|
|
1668
|
+
recursive: true,
|
|
1669
|
+
encoding: "utf8"
|
|
1670
|
+
}).map((file) => join("pages", file));
|
|
1671
|
+
let manifest;
|
|
1672
|
+
try {
|
|
1673
|
+
manifest = buildRouteManifestFromFileIds(fileIds);
|
|
1674
|
+
} catch {
|
|
1675
|
+
return;
|
|
1676
|
+
}
|
|
1677
|
+
const pathname = mode === "hash" && url.hash.startsWith("#/") ? url.hash.slice(1) : withoutBase(url.pathname, base);
|
|
1678
|
+
return matchRouteManifest(pathname, manifest)?.route.file ?? manifest.statusPages.notFound;
|
|
1679
|
+
}
|
|
1598
1680
|
function devSourceModuleRequestPlugin() {
|
|
1599
1681
|
let root = "";
|
|
1600
1682
|
return {
|
|
@@ -2016,9 +2098,11 @@ function virtualModulesPlugin(resumeEntry = resumeEntryState(), prerenderWakeEnt
|
|
|
2016
2098
|
}
|
|
2017
2099
|
async function routerOptionsSource(options, documentFile) {
|
|
2018
2100
|
const documentSource = await readFile(documentFile, "utf8").catch(() => void 0);
|
|
2101
|
+
const handlerError = documentHandlerError(documentSource, documentFile);
|
|
2102
|
+
if (handlerError) throw new Error(handlerError);
|
|
2019
2103
|
return [
|
|
2020
2104
|
"export const routerMode = \"path\";",
|
|
2021
|
-
`export const documentNavigation = ${JSON.stringify(documentNavigationFor(documentSource
|
|
2105
|
+
`export const documentNavigation = ${JSON.stringify(documentNavigationFor(documentSource))};`,
|
|
2022
2106
|
linkIntentEnabled(options) ? `export const startLinkIntentPreloading = (root, preload) => (${listenForLinkIntent.toString()})(root, ${JSON.stringify(LINK_ATTRIBUTE)}, preload, ${JSON.stringify(PREFETCH_ATTRIBUTE)});` : "export const startLinkIntentPreloading = () => () => {};"
|
|
2023
2107
|
].join("\n");
|
|
2024
2108
|
}
|
|
@@ -2066,7 +2150,7 @@ function linkBridgeFilesPlugin(navigationEntry, fragmentEntry, options) {
|
|
|
2066
2150
|
fragmentNavigationSource: minifiedFragmentNavigationSource(),
|
|
2067
2151
|
fragmentNavigationRunSource: minifiedFragmentNavigationSource(runFragmentNavigation),
|
|
2068
2152
|
prefetch: options.prefetch,
|
|
2069
|
-
documentNavigation: documentNavigationFor(documentSource
|
|
2153
|
+
documentNavigation: documentNavigationFor(documentSource),
|
|
2070
2154
|
routeFileIds: bundledPageFileIds(chunks, root),
|
|
2071
2155
|
debug
|
|
2072
2156
|
});
|
|
@@ -2100,13 +2184,13 @@ function serverEntrySource(root, options) {
|
|
|
2100
2184
|
const query = rootScopeQuery(root);
|
|
2101
2185
|
return [
|
|
2102
2186
|
`import { createServerEntry } from '@markless/router/vite/runtime/create-server-entry';`,
|
|
2103
|
-
`import { resumeEntryPath } from
|
|
2104
|
-
`import { prerenderWakeEntryPath } from
|
|
2105
|
-
`import { navigationEntryPath } from
|
|
2106
|
-
`import { fragmentEntryPath } from
|
|
2107
|
-
`import { documentStylesheets, routeModulePreloads, routeSsrModulePreloads, routeStylesheets, documentImportMap } from
|
|
2108
|
-
`import { pageModuleLoaders, routeFileIds } from
|
|
2109
|
-
`import { documentNavigation } from
|
|
2187
|
+
`import { resumeEntryPath } from ${JSON.stringify(`${RESUME_ENTRY_PATH_ID}${query}`)};`,
|
|
2188
|
+
`import { prerenderWakeEntryPath } from ${JSON.stringify(`${PRERENDER_WAKE_ENTRY_PATH_ID}${query}`)};`,
|
|
2189
|
+
`import { navigationEntryPath } from ${JSON.stringify(`${NAVIGATION_ENTRY_PATH_ID}${query}`)};`,
|
|
2190
|
+
`import { fragmentEntryPath } from ${JSON.stringify(`${FRAGMENT_ENTRY_PATH_ID}${query}`)};`,
|
|
2191
|
+
`import { documentStylesheets, routeModulePreloads, routeSsrModulePreloads, routeStylesheets, documentImportMap } from ${JSON.stringify(`${ROUTE_PRELOADS_ID}${query}`)};`,
|
|
2192
|
+
`import { pageModuleLoaders, routeFileIds } from ${JSON.stringify(`${ROUTE_DISCOVERY_ID}${query}`)};`,
|
|
2193
|
+
`import { documentNavigation } from ${JSON.stringify(`${ROUTER_OPTIONS_ID}${query}`)};`,
|
|
2110
2194
|
`const documentModuleLoaders = import.meta.glob(['/document.tsrx']);`,
|
|
2111
2195
|
`const linkBridgePathsJson = globalThis.__marklessRouterLinkBridgePathsJson ?? ${JSON.stringify(LINK_BRIDGE_PATHS_PLACEHOLDER)};`,
|
|
2112
2196
|
`const entry = createServerEntry({`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markless/router",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Markless router: client-side navigation with SSR streaming and build-time preload maps.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -57,22 +57,23 @@
|
|
|
57
57
|
"provenance": true
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@tsrx/yuku": "0.
|
|
60
|
+
"@tsrx/yuku": "0.8.0",
|
|
61
61
|
"@virtualstate/navigation": "1.0.1-alpha.212",
|
|
62
62
|
"hast-util-to-html": "^9.0.5",
|
|
63
|
-
"nitro": "3.0.
|
|
63
|
+
"nitro": "3.0.260903-beta",
|
|
64
64
|
"pathe": "^2.0.3",
|
|
65
65
|
"satteri": "0.6.3",
|
|
66
66
|
"ufo": "^1.6.4",
|
|
67
|
-
"@markless/bundler": "0.
|
|
68
|
-
"@markless/web": "0.
|
|
67
|
+
"@markless/bundler": "0.8.0",
|
|
68
|
+
"@markless/web": "0.8.0",
|
|
69
|
+
"@markless/compiler": "0.8.0"
|
|
69
70
|
},
|
|
70
71
|
"devDependencies": {
|
|
71
|
-
"@markless/analyzer": "0.
|
|
72
|
+
"@markless/analyzer": "0.8.0"
|
|
72
73
|
},
|
|
73
74
|
"peerDependencies": {
|
|
74
75
|
"typescript": "^5.9.3",
|
|
75
|
-
"vite": "8.
|
|
76
|
+
"vite": "8.3.4"
|
|
76
77
|
},
|
|
77
78
|
"peerDependenciesMeta": {
|
|
78
79
|
"vite": {
|
|
@@ -9,6 +9,7 @@ export interface FragmentNavigationConfig {
|
|
|
9
9
|
readonly replaceAttribute: string;
|
|
10
10
|
readonly scrollAttribute: string;
|
|
11
11
|
readonly routeScriptType: string;
|
|
12
|
+
readonly servedHtmlAttributesProperty: string;
|
|
12
13
|
readonly retireProperty: string;
|
|
13
14
|
readonly streamExecutorGlobal: string;
|
|
14
15
|
readonly streamedArmSelector: string;
|
|
@@ -400,7 +401,7 @@ async function mergeHead(d: Document, next: Document) {
|
|
|
400
401
|
const rel = element.localName === 'link' ? (element as HTMLLinkElement).rel : '';
|
|
401
402
|
if (rel === 'modulepreload' || rel === 'preload') continue;
|
|
402
403
|
if (rel === 'stylesheet' || element.localName === 'style') stale.push(element);
|
|
403
|
-
else element.remove();
|
|
404
|
+
else if ((element as HTMLScriptElement).type !== 'importmap') element.remove();
|
|
404
405
|
}
|
|
405
406
|
await loaded(d.defaultView!, stylesheets);
|
|
406
407
|
return { stale, scripts };
|
|
@@ -409,10 +410,13 @@ async function mergeHead(d: Document, next: Document) {
|
|
|
409
410
|
// Only attributes a server document set are the server's to remove: scripts own the rest (a theme class).
|
|
410
411
|
const serverHtmlAttributes = new WeakMap<Document, Set<string>>();
|
|
411
412
|
|
|
412
|
-
function syncHtmlAttributes(d: Document, next: Document): void {
|
|
413
|
+
function syncHtmlAttributes(d: Document, next: Document, servedProperty: string): void {
|
|
413
414
|
const root = d.documentElement;
|
|
414
415
|
const incoming = next.documentElement;
|
|
415
|
-
|
|
416
|
+
const served =
|
|
417
|
+
serverHtmlAttributes.get(d) ??
|
|
418
|
+
((root as unknown as Record<string, readonly string[] | undefined>)[servedProperty] || []);
|
|
419
|
+
for (const name of served)
|
|
416
420
|
if (!incoming.hasAttribute(name)) root.removeAttribute(name);
|
|
417
421
|
for (const attribute of incoming.attributes)
|
|
418
422
|
if (root.getAttribute(attribute.name) !== attribute.value)
|
|
@@ -617,6 +621,53 @@ async function streamAppends(
|
|
|
617
621
|
return signal.aborted ? undefined : all;
|
|
618
622
|
}
|
|
619
623
|
|
|
624
|
+
// Inserting a big page whole lays it out in one long frame; the HTML parser yields while it streams a document, and the swap does the same.
|
|
625
|
+
const FRAME_ELEMENTS = 800;
|
|
626
|
+
|
|
627
|
+
type HeldPage = [parent: Node, before: Node | null, nodes: ChildNode[]][];
|
|
628
|
+
|
|
629
|
+
function holdPage(parent: Element, after: Node | undefined, before: Node | null): HeldPage {
|
|
630
|
+
const nodes: ChildNode[] = [];
|
|
631
|
+
for (let node = after ? after.nextSibling : parent.firstChild; node && node !== before; ) {
|
|
632
|
+
const next: ChildNode | null = node.nextSibling;
|
|
633
|
+
nodes.push(node);
|
|
634
|
+
node.remove();
|
|
635
|
+
node = next;
|
|
636
|
+
}
|
|
637
|
+
return [[parent, before, nodes]];
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
const elementsIn = (node: Node) =>
|
|
641
|
+
node.nodeType === 1 ? (node as Element).getElementsByTagName('*').length + 1 : 0;
|
|
642
|
+
|
|
643
|
+
// Places a frame's worth synchronously, so the first paint shows the top of the page; true when everything is in.
|
|
644
|
+
function fillPage(
|
|
645
|
+
w: Window,
|
|
646
|
+
held: HeldPage,
|
|
647
|
+
budget: number,
|
|
648
|
+
current: () => boolean,
|
|
649
|
+
): true | Promise<boolean> {
|
|
650
|
+
let used = 0;
|
|
651
|
+
while (held.length) {
|
|
652
|
+
const [parent, before, nodes] = held[0]!;
|
|
653
|
+
const node = nodes.shift()!;
|
|
654
|
+
if (!nodes.length) held.shift();
|
|
655
|
+
const size = elementsIn(node);
|
|
656
|
+
if (used + size > budget && node.firstChild) {
|
|
657
|
+
const children = [...node.childNodes];
|
|
658
|
+
(node as Element).replaceChildren();
|
|
659
|
+
held.unshift([node, null, children]);
|
|
660
|
+
used += 1;
|
|
661
|
+
} else used += size;
|
|
662
|
+
parent.insertBefore(node, before);
|
|
663
|
+
if (used >= budget && held.length)
|
|
664
|
+
return new Promise<void>((resolve) =>
|
|
665
|
+
w.requestAnimationFrame(() => w.setTimeout(resolve, 0)),
|
|
666
|
+
).then(() => current() && fillPage(w, held, budget, current));
|
|
667
|
+
}
|
|
668
|
+
return true;
|
|
669
|
+
}
|
|
670
|
+
|
|
620
671
|
export async function navigate(
|
|
621
672
|
d: Document,
|
|
622
673
|
url: URL,
|
|
@@ -643,13 +694,16 @@ export async function navigate(
|
|
|
643
694
|
return;
|
|
644
695
|
}
|
|
645
696
|
if (id !== session.navigation) return taken.abort();
|
|
697
|
+
// A prefetch is shared by every link to the page, so the address takes the clicked link's #fragment.
|
|
698
|
+
const landed = new URL(fetched.url);
|
|
699
|
+
landed.hash = url.hash;
|
|
646
700
|
const next = fetched.doc;
|
|
647
701
|
const documentLoads = state.config.documentLoads;
|
|
648
702
|
const incoming = documentLoads ? next.body : regionOf(state, next);
|
|
649
703
|
const outgoing = documentLoads ? d.body : regionOf(state, d);
|
|
650
704
|
const scope = documentLoads && !isContainer(d.body) ? d.body : outgoing?.parentElement;
|
|
651
705
|
if (!incoming || !outgoing || !scope) {
|
|
652
|
-
fallback(
|
|
706
|
+
fallback(landed.href);
|
|
653
707
|
return;
|
|
654
708
|
}
|
|
655
709
|
// The destination shows once its own code has arrived, so its first interaction never waits on a fetch.
|
|
@@ -682,8 +736,8 @@ export async function navigate(
|
|
|
682
736
|
w.history.scrollRestoration = 'manual';
|
|
683
737
|
session.current = entryId();
|
|
684
738
|
const entry = { __marklessFragment: { id: session.current, x: 0, y: 0 } };
|
|
685
|
-
if (mode === 'push') w.history.pushState(entry, '',
|
|
686
|
-
else w.history.replaceState(entry, '',
|
|
739
|
+
if (mode === 'push') w.history.pushState(entry, '', landed.href);
|
|
740
|
+
else w.history.replaceState(entry, '', landed.href);
|
|
687
741
|
}
|
|
688
742
|
const offsets: ScrolledBox[] = [];
|
|
689
743
|
for (const element of state.scrolled) {
|
|
@@ -702,10 +756,12 @@ export async function navigate(
|
|
|
702
756
|
(w as unknown as Record<string, unknown>)[state.config.streamExecutorGlobal] = undefined;
|
|
703
757
|
for (const element of d.querySelectorAll(state.config.streamedArmSelector)) element.remove();
|
|
704
758
|
state.shown = pageKey(new URL(w.location.href));
|
|
705
|
-
if (documentLoads) syncHtmlAttributes(d, next);
|
|
759
|
+
if (documentLoads) syncHtmlAttributes(d, next, state.config.servedHtmlAttributesProperty);
|
|
706
760
|
let region: Element;
|
|
707
761
|
let end: Comment;
|
|
762
|
+
let start: Comment | undefined;
|
|
708
763
|
let scripts: Element[];
|
|
764
|
+
let held: HeldPage | undefined;
|
|
709
765
|
if (documentLoads && !isContainer(d.body)) {
|
|
710
766
|
const fresh: Element[] = [];
|
|
711
767
|
region = d.body;
|
|
@@ -727,7 +783,7 @@ export async function navigate(
|
|
|
727
783
|
const fresh: Element[] = [];
|
|
728
784
|
if (documentLoads) morphChildren(d, kept, arriving, fresh);
|
|
729
785
|
const leavingStart = pageStart(outgoing, state.config.pageStartComment);
|
|
730
|
-
|
|
786
|
+
start = pageStart(region, state.config.pageStartComment);
|
|
731
787
|
if (leavingStart || start) {
|
|
732
788
|
start ??= region.insertBefore(
|
|
733
789
|
d.createComment(state.config.pageStartComment),
|
|
@@ -742,16 +798,25 @@ export async function navigate(
|
|
|
742
798
|
}
|
|
743
799
|
scripts = [...scriptsIn(region), ...fresh.flatMap(scriptsIn)];
|
|
744
800
|
region.append(kept);
|
|
801
|
+
held = holdPage(region, start, end);
|
|
745
802
|
outgoing.replaceWith(region);
|
|
746
803
|
// The document's own nodes moved to the new element; focus inside them stays put.
|
|
747
804
|
if (focused && focused !== d.activeElement && region.contains(focused))
|
|
748
805
|
focused.focus({ preventScroll: true });
|
|
749
806
|
} else {
|
|
750
807
|
scripts = scriptsIn(region);
|
|
808
|
+
held = holdPage(region, undefined, null);
|
|
751
809
|
outgoing.replaceWith(region);
|
|
752
810
|
region.after((end = d.createComment('')));
|
|
753
811
|
}
|
|
754
812
|
}
|
|
813
|
+
// A scroll target or a restored position needs the whole page in place before it is applied.
|
|
814
|
+
const progressive = restore === 'reset' && !url.hash && d.visibilityState !== 'hidden';
|
|
815
|
+
const filled = held && fillPage(w, held, progressive ? FRAME_ELEMENTS : Infinity, () => id === session.navigation);
|
|
816
|
+
if (filled instanceof Promise) {
|
|
817
|
+
w.scrollTo({ top: 0, left: 0, behavior: 'instant' });
|
|
818
|
+
if (!(await filled)) return;
|
|
819
|
+
}
|
|
755
820
|
// 'instant': a `scroll-behavior: smooth` page would otherwise animate every landing.
|
|
756
821
|
for (const box of offsets)
|
|
757
822
|
sameBox(scope, box)?.scrollTo({ top: box.top, left: box.left, behavior: 'instant' });
|
|
@@ -769,7 +834,7 @@ export async function navigate(
|
|
|
769
834
|
if (restore === 'reset') {
|
|
770
835
|
const target = url.hash ? d.getElementById(decodeURIComponent(url.hash.slice(1))) : null;
|
|
771
836
|
if (target) target.scrollIntoView({ behavior: 'instant' });
|
|
772
|
-
else w.scrollTo({ top: 0, left: 0, behavior: 'instant' });
|
|
837
|
+
else if (!(filled instanceof Promise)) w.scrollTo({ top: 0, left: 0, behavior: 'instant' });
|
|
773
838
|
} else if (restore !== 'manual')
|
|
774
839
|
w.scrollTo({ top: restore.y, left: restore.x, behavior: 'instant' });
|
|
775
840
|
d.dispatchEvent(new CustomEvent('marklessrouterrendered'));
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import { a as __marklessDebugInvalidateElement, c as __marklessDebugRegisterDelegatedEvents, d as __marklessDebugSetBoundaries, f as __marklessDebugStartContainer, i as __marklessDebugDisposeContainer, l as __marklessDebugRegisterRouter, n as __marklessDebugBootstrapSource, o as __marklessDebugRecordInteraction, r as __marklessDebugChannelForTest, s as __marklessDebugRecordViolation, t as __marklessDebugActivateContainer, u as __marklessDebugResetForTest } from "./debug-channel-Di6SppZv.js";
|
|
2
|
-
export { __marklessDebugActivateContainer, __marklessDebugBootstrapSource, __marklessDebugChannelForTest, __marklessDebugDisposeContainer, __marklessDebugInvalidateElement, __marklessDebugRecordInteraction, __marklessDebugRecordViolation, __marklessDebugRegisterDelegatedEvents, __marklessDebugRegisterRouter, __marklessDebugResetForTest, __marklessDebugSetBoundaries, __marklessDebugStartContainer };
|