@markless/router 0.7.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-yboa-2m6.js → create-server-entry-BsYTWdxi.js} +24 -5
- package/dist/debug-channel-dB6CJyHa.js +1 -0
- package/dist/index.js +3 -3
- 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.js +67 -15
- package/package.json +8 -7
- package/src/vite/entries/fragment-entry.ts +67 -6
- package/dist/debug-channel-CewnC9fH.js +0 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
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-
|
|
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,7 +876,7 @@ 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
|
};
|
|
867
|
-
const headStart = (documentLoads && fragmentNavigation && !fragment ? servedHtmlAttributesScript() : "") + (options.importMap ?? "");
|
|
879
|
+
const headStart = (documentLoads && fragmentNavigation && !fragment ? servedHtmlAttributesScript() : "") + (fragment ? "" : options.importMap ?? "");
|
|
868
880
|
if (options.render === "blocking") {
|
|
869
881
|
const pageHtml = splitPageHtml(await renderToString(routedArtifact, renderOptions));
|
|
870
882
|
privateToVisitor();
|
|
@@ -1104,8 +1116,15 @@ function wholeDocument(shell, page, fragment, documentLoads) {
|
|
|
1104
1116
|
async function renderDocumentModule(documentModule, props, htmlAttributes) {
|
|
1105
1117
|
const output = await documentModule?.default?.renderSsr?.(props, { [HTML_ATTRIBUTES_SLOT]: htmlAttributes });
|
|
1106
1118
|
assertDocumentStorageFree(output);
|
|
1119
|
+
assertDocumentHandlerFree(output);
|
|
1107
1120
|
return output?.html;
|
|
1108
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
|
+
}
|
|
1109
1128
|
function assertDocumentStorageFree(output) {
|
|
1110
1129
|
const storage = documentStorageRecords(output?.state);
|
|
1111
1130
|
if (storage.length === 0) return;
|
|
@@ -1330,4 +1349,4 @@ function insertImportMap(html, importMap) {
|
|
|
1330
1349
|
return `${html.slice(0, at)}${importMap}${html.slice(at)}`;
|
|
1331
1350
|
}
|
|
1332
1351
|
//#endregion
|
|
1333
|
-
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
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-
|
|
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;
|
|
@@ -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 {};
|
package/dist/vite.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
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-
|
|
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
7
|
import { decodePath, joinURL, parsePath, parseQuery, parseURL, stringifyQuery, withLeadingSlash, withQuery, withoutBase, withoutLeadingSlash } from "ufo";
|
|
8
8
|
import { existsSync, readdirSync } from "node:fs";
|
|
@@ -11,10 +11,20 @@ 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;
|
|
@@ -2048,9 +2098,11 @@ function virtualModulesPlugin(resumeEntry = resumeEntryState(), prerenderWakeEnt
|
|
|
2048
2098
|
}
|
|
2049
2099
|
async function routerOptionsSource(options, documentFile) {
|
|
2050
2100
|
const documentSource = await readFile(documentFile, "utf8").catch(() => void 0);
|
|
2101
|
+
const handlerError = documentHandlerError(documentSource, documentFile);
|
|
2102
|
+
if (handlerError) throw new Error(handlerError);
|
|
2051
2103
|
return [
|
|
2052
2104
|
"export const routerMode = \"path\";",
|
|
2053
|
-
`export const documentNavigation = ${JSON.stringify(documentNavigationFor(documentSource
|
|
2105
|
+
`export const documentNavigation = ${JSON.stringify(documentNavigationFor(documentSource))};`,
|
|
2054
2106
|
linkIntentEnabled(options) ? `export const startLinkIntentPreloading = (root, preload) => (${listenForLinkIntent.toString()})(root, ${JSON.stringify(LINK_ATTRIBUTE)}, preload, ${JSON.stringify(PREFETCH_ATTRIBUTE)});` : "export const startLinkIntentPreloading = () => () => {};"
|
|
2055
2107
|
].join("\n");
|
|
2056
2108
|
}
|
|
@@ -2098,7 +2150,7 @@ function linkBridgeFilesPlugin(navigationEntry, fragmentEntry, options) {
|
|
|
2098
2150
|
fragmentNavigationSource: minifiedFragmentNavigationSource(),
|
|
2099
2151
|
fragmentNavigationRunSource: minifiedFragmentNavigationSource(runFragmentNavigation),
|
|
2100
2152
|
prefetch: options.prefetch,
|
|
2101
|
-
documentNavigation: documentNavigationFor(documentSource
|
|
2153
|
+
documentNavigation: documentNavigationFor(documentSource),
|
|
2102
2154
|
routeFileIds: bundledPageFileIds(chunks, root),
|
|
2103
2155
|
debug
|
|
2104
2156
|
});
|
|
@@ -2132,13 +2184,13 @@ function serverEntrySource(root, options) {
|
|
|
2132
2184
|
const query = rootScopeQuery(root);
|
|
2133
2185
|
return [
|
|
2134
2186
|
`import { createServerEntry } from '@markless/router/vite/runtime/create-server-entry';`,
|
|
2135
|
-
`import { resumeEntryPath } from
|
|
2136
|
-
`import { prerenderWakeEntryPath } from
|
|
2137
|
-
`import { navigationEntryPath } from
|
|
2138
|
-
`import { fragmentEntryPath } from
|
|
2139
|
-
`import { documentStylesheets, routeModulePreloads, routeSsrModulePreloads, routeStylesheets, documentImportMap } from
|
|
2140
|
-
`import { pageModuleLoaders, routeFileIds } from
|
|
2141
|
-
`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}`)};`,
|
|
2142
2194
|
`const documentModuleLoaders = import.meta.glob(['/document.tsrx']);`,
|
|
2143
2195
|
`const linkBridgePathsJson = globalThis.__marklessRouterLinkBridgePathsJson ?? ${JSON.stringify(LINK_BRIDGE_PATHS_PLACEHOLDER)};`,
|
|
2144
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/
|
|
68
|
-
"@markless/
|
|
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": {
|
|
@@ -401,7 +401,7 @@ async function mergeHead(d: Document, next: Document) {
|
|
|
401
401
|
const rel = element.localName === 'link' ? (element as HTMLLinkElement).rel : '';
|
|
402
402
|
if (rel === 'modulepreload' || rel === 'preload') continue;
|
|
403
403
|
if (rel === 'stylesheet' || element.localName === 'style') stale.push(element);
|
|
404
|
-
else element.remove();
|
|
404
|
+
else if ((element as HTMLScriptElement).type !== 'importmap') element.remove();
|
|
405
405
|
}
|
|
406
406
|
await loaded(d.defaultView!, stylesheets);
|
|
407
407
|
return { stale, scripts };
|
|
@@ -621,6 +621,53 @@ async function streamAppends(
|
|
|
621
621
|
return signal.aborted ? undefined : all;
|
|
622
622
|
}
|
|
623
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
|
+
|
|
624
671
|
export async function navigate(
|
|
625
672
|
d: Document,
|
|
626
673
|
url: URL,
|
|
@@ -647,13 +694,16 @@ export async function navigate(
|
|
|
647
694
|
return;
|
|
648
695
|
}
|
|
649
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;
|
|
650
700
|
const next = fetched.doc;
|
|
651
701
|
const documentLoads = state.config.documentLoads;
|
|
652
702
|
const incoming = documentLoads ? next.body : regionOf(state, next);
|
|
653
703
|
const outgoing = documentLoads ? d.body : regionOf(state, d);
|
|
654
704
|
const scope = documentLoads && !isContainer(d.body) ? d.body : outgoing?.parentElement;
|
|
655
705
|
if (!incoming || !outgoing || !scope) {
|
|
656
|
-
fallback(
|
|
706
|
+
fallback(landed.href);
|
|
657
707
|
return;
|
|
658
708
|
}
|
|
659
709
|
// The destination shows once its own code has arrived, so its first interaction never waits on a fetch.
|
|
@@ -686,8 +736,8 @@ export async function navigate(
|
|
|
686
736
|
w.history.scrollRestoration = 'manual';
|
|
687
737
|
session.current = entryId();
|
|
688
738
|
const entry = { __marklessFragment: { id: session.current, x: 0, y: 0 } };
|
|
689
|
-
if (mode === 'push') w.history.pushState(entry, '',
|
|
690
|
-
else w.history.replaceState(entry, '',
|
|
739
|
+
if (mode === 'push') w.history.pushState(entry, '', landed.href);
|
|
740
|
+
else w.history.replaceState(entry, '', landed.href);
|
|
691
741
|
}
|
|
692
742
|
const offsets: ScrolledBox[] = [];
|
|
693
743
|
for (const element of state.scrolled) {
|
|
@@ -709,7 +759,9 @@ export async function navigate(
|
|
|
709
759
|
if (documentLoads) syncHtmlAttributes(d, next, state.config.servedHtmlAttributesProperty);
|
|
710
760
|
let region: Element;
|
|
711
761
|
let end: Comment;
|
|
762
|
+
let start: Comment | undefined;
|
|
712
763
|
let scripts: Element[];
|
|
764
|
+
let held: HeldPage | undefined;
|
|
713
765
|
if (documentLoads && !isContainer(d.body)) {
|
|
714
766
|
const fresh: Element[] = [];
|
|
715
767
|
region = d.body;
|
|
@@ -731,7 +783,7 @@ export async function navigate(
|
|
|
731
783
|
const fresh: Element[] = [];
|
|
732
784
|
if (documentLoads) morphChildren(d, kept, arriving, fresh);
|
|
733
785
|
const leavingStart = pageStart(outgoing, state.config.pageStartComment);
|
|
734
|
-
|
|
786
|
+
start = pageStart(region, state.config.pageStartComment);
|
|
735
787
|
if (leavingStart || start) {
|
|
736
788
|
start ??= region.insertBefore(
|
|
737
789
|
d.createComment(state.config.pageStartComment),
|
|
@@ -746,16 +798,25 @@ export async function navigate(
|
|
|
746
798
|
}
|
|
747
799
|
scripts = [...scriptsIn(region), ...fresh.flatMap(scriptsIn)];
|
|
748
800
|
region.append(kept);
|
|
801
|
+
held = holdPage(region, start, end);
|
|
749
802
|
outgoing.replaceWith(region);
|
|
750
803
|
// The document's own nodes moved to the new element; focus inside them stays put.
|
|
751
804
|
if (focused && focused !== d.activeElement && region.contains(focused))
|
|
752
805
|
focused.focus({ preventScroll: true });
|
|
753
806
|
} else {
|
|
754
807
|
scripts = scriptsIn(region);
|
|
808
|
+
held = holdPage(region, undefined, null);
|
|
755
809
|
outgoing.replaceWith(region);
|
|
756
810
|
region.after((end = d.createComment('')));
|
|
757
811
|
}
|
|
758
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
|
+
}
|
|
759
820
|
// 'instant': a `scroll-behavior: smooth` page would otherwise animate every landing.
|
|
760
821
|
for (const box of offsets)
|
|
761
822
|
sameBox(scope, box)?.scrollTo({ top: box.top, left: box.left, behavior: 'instant' });
|
|
@@ -773,7 +834,7 @@ export async function navigate(
|
|
|
773
834
|
if (restore === 'reset') {
|
|
774
835
|
const target = url.hash ? d.getElementById(decodeURIComponent(url.hash.slice(1))) : null;
|
|
775
836
|
if (target) target.scrollIntoView({ behavior: 'instant' });
|
|
776
|
-
else w.scrollTo({ top: 0, left: 0, behavior: 'instant' });
|
|
837
|
+
else if (!(filled instanceof Promise)) w.scrollTo({ top: 0, left: 0, behavior: 'instant' });
|
|
777
838
|
} else if (restore !== 'manual')
|
|
778
839
|
w.scrollTo({ top: restore.y, left: restore.x, behavior: 'instant' });
|
|
779
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 };
|