@markless/router 0.4.0 → 0.6.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 +1331 -0
- package/dist/debug-channel-CewnC9fH.js +2 -0
- package/dist/{debug-channel-CSzo-Zj8.js → debug-channel-Di6SppZv.js} +36 -17
- package/dist/index-BjxpxRL7.d.ts +248 -0
- package/dist/index.d.ts +2 -212
- package/dist/index.js +108 -40
- package/dist/protocol-constants-B4WxrZAS.js +26 -0
- package/dist/request-locals-z3rJTizs.js +90 -0
- package/dist/route-href-CGooO_g6.js +18 -0
- package/dist/route-manifest-DMizh42B.js +456 -0
- package/dist/typescript-plugin.cjs +9 -6
- package/dist/typescript-plugin.d.ts +3 -11194
- package/dist/typescript-plugin.js +9 -6
- package/dist/vite/route-href.d.ts +2 -3
- package/dist/vite/route-href.js +1 -17
- package/dist/vite/runtime/create-route-discovery.d.ts +3 -4
- package/dist/vite/runtime/create-route-discovery.js +1 -1
- package/dist/vite/runtime/create-server-entry.d.ts +27 -5
- package/dist/vite/runtime/create-server-entry.js +2 -400
- package/dist/vite/runtime/mdx-route.d.ts +1116 -17
- package/dist/vite/runtime/mdx-route.js +1097 -31
- package/dist/vite.d.ts +10 -6
- package/dist/vite.js +965 -203
- package/package.json +7 -6
- package/src/vite/entries/client-entry.ts +10 -1
- package/src/vite/entries/fragment-entry.ts +797 -0
- package/src/vite/entries/resume-entry.ts +41 -7
- package/dist/request-files-C-0LLSsx.js +0 -232
- package/dist/route-manifest-DPgGIyrS.js +0 -186
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { extname, isAbsolute, normalize, relative, resolve } from "pathe";
|
|
2
2
|
//#region src/typescript-plugin/index.ts
|
|
3
|
-
const PAGE_EXTENSIONS = new Set([".tsrx", ".mdx"]);
|
|
3
|
+
const PAGE_EXTENSIONS = /* @__PURE__ */ new Set([".tsrx", ".mdx"]);
|
|
4
4
|
function init(modules) {
|
|
5
5
|
const typeScript = modules.typescript;
|
|
6
6
|
return { create(info) {
|
|
@@ -15,7 +15,8 @@ function init(modules) {
|
|
|
15
15
|
const projectRoot = getProjectRoot(typeScript, info);
|
|
16
16
|
const pagesDir = resolvePagesDir(getConfiguredProjectRoot(info) ?? projectRoot, pluginConfig);
|
|
17
17
|
proxy.getCompletionsAtPosition = (fileName, position, options, formattingSettings) => {
|
|
18
|
-
|
|
18
|
+
const completions = languageService.getCompletionsAtPosition(fileName, position, options, formattingSettings);
|
|
19
|
+
return withRouteHrefCompletions(typeScript, info, pagesDir, fileName, position, completions);
|
|
19
20
|
};
|
|
20
21
|
return proxy;
|
|
21
22
|
} };
|
|
@@ -57,7 +58,7 @@ function isRouteHrefCompletionContext(typeScript, info, fileName, position) {
|
|
|
57
58
|
}
|
|
58
59
|
function isInsideStringLikeToken(typeScript, source, position) {
|
|
59
60
|
const scanner = typeScript.createScanner(typeScript.ScriptTarget.Latest, false, typeScript.LanguageVariant.JSX, source);
|
|
60
|
-
const stringLikeTokens = new Set([
|
|
61
|
+
const stringLikeTokens = /* @__PURE__ */ new Set([
|
|
61
62
|
typeScript.SyntaxKind.StringLiteral,
|
|
62
63
|
typeScript.SyntaxKind.NoSubstitutionTemplateLiteral,
|
|
63
64
|
typeScript.SyntaxKind.TemplateHead,
|
|
@@ -164,13 +165,15 @@ function routeHrefCompletion(fileId) {
|
|
|
164
165
|
}
|
|
165
166
|
function isReservedOrUnsupportedPage(fileId) {
|
|
166
167
|
const extension = extname(fileId);
|
|
167
|
-
const
|
|
168
|
+
const withoutExtension = fileId.slice(0, -extension.length);
|
|
169
|
+
const routeFile = normalize(withoutExtension);
|
|
168
170
|
const segments = routeFile === "." ? [] : routeFile.split("/").filter(Boolean);
|
|
169
|
-
return segments
|
|
171
|
+
return segments.length === 1 && (segments[0] === "404" || segments[0] === "500");
|
|
170
172
|
}
|
|
171
173
|
function routeFromFileId(fileId) {
|
|
172
174
|
const extension = extname(fileId);
|
|
173
|
-
const
|
|
175
|
+
const withoutExtension = fileId.slice(0, -extension.length);
|
|
176
|
+
const routeFile = normalize(withoutExtension);
|
|
174
177
|
const segments = routeFile === "." ? [] : routeFile.split("/").filter(Boolean);
|
|
175
178
|
if (segments.at(-1) === "index") segments.pop();
|
|
176
179
|
const params = [];
|
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
//#region src/vite/entries/route-href.d.ts
|
|
2
|
-
declare function __marklessRouteHref(pattern: string, params: Readonly<Record<string, unknown>>): string;
|
|
3
|
-
//#endregion
|
|
4
|
-
export { __marklessRouteHref };
|
|
2
|
+
export declare function __marklessRouteHref(pattern: string, params: Readonly<Record<string, unknown>>): string;
|
|
3
|
+
//#endregion
|
package/dist/vite/route-href.js
CHANGED
|
@@ -1,18 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
const routeParamPattern = /\[\.\.\.([A-Za-z_$][\w$]*)\]|\[([A-Za-z_$][\w$]*)\]/g;
|
|
3
|
-
function __marklessRouteHref(pattern, params) {
|
|
4
|
-
return pattern.replace(routeParamPattern, (_match, catchAll, dynamic) => {
|
|
5
|
-
const name = catchAll || dynamic;
|
|
6
|
-
const value = params?.[name];
|
|
7
|
-
if (catchAll) return encodeCatchAllParam(pattern, value);
|
|
8
|
-
if (value === null || value === void 0) throw new Error(`Typed route error: ${pattern} requires params:\n- ${name}`);
|
|
9
|
-
return encodeURIComponent(String(value));
|
|
10
|
-
});
|
|
11
|
-
}
|
|
12
|
-
function encodeCatchAllParam(pattern, value) {
|
|
13
|
-
const segments = Array.isArray(value) ? value : value === null || value === void 0 ? [] : String(value).split("/");
|
|
14
|
-
if (segments.length === 0 || segments.some((segment) => String(segment) === "")) throw new Error(`Typed route error: ${pattern} requires a non-empty catch-all param.`);
|
|
15
|
-
return segments.map((segment) => encodeURIComponent(String(segment))).join("/");
|
|
16
|
-
}
|
|
17
|
-
//#endregion
|
|
1
|
+
import { t as __marklessRouteHref } from "../route-href-CGooO_g6.js";
|
|
18
2
|
export { __marklessRouteHref };
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
//#region src/vite/runtime/create-route-discovery.d.ts
|
|
2
|
-
interface RouteDiscovery {
|
|
2
|
+
export interface RouteDiscovery {
|
|
3
3
|
readonly pageModuleLoaders: Record<string, () => Promise<unknown>>;
|
|
4
4
|
readonly routeFileIds: readonly string[];
|
|
5
5
|
}
|
|
6
|
-
declare function createRouteDiscovery(discoveredPageModuleLoaders: Record<string, () => Promise<unknown>>): RouteDiscovery;
|
|
7
|
-
//#endregion
|
|
8
|
-
export { RouteDiscovery, createRouteDiscovery };
|
|
6
|
+
export declare function createRouteDiscovery(discoveredPageModuleLoaders: Record<string, () => Promise<unknown>>): RouteDiscovery;
|
|
7
|
+
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { o as normalizeRouteFileId } from "../../route-manifest-DMizh42B.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,21 +1,43 @@
|
|
|
1
|
+
import { z as RouteManifest } from "../../index-BjxpxRL7.js";
|
|
1
2
|
import { ModulePreloadInput } from "@markless/web/render-to-string";
|
|
2
|
-
|
|
3
3
|
//#region src/vite/runtime/create-server-entry.d.ts
|
|
4
|
-
interface ServerEntryOptions {
|
|
4
|
+
export interface ServerEntryOptions {
|
|
5
5
|
readonly dev?: boolean;
|
|
6
|
+
readonly base?: string;
|
|
6
7
|
readonly navigationEntryPath?: string;
|
|
8
|
+
readonly prefetch?: boolean;
|
|
7
9
|
readonly resumeEntryPath?: string;
|
|
8
10
|
readonly prerenderWakeEntryPath?: string;
|
|
9
11
|
readonly routeModulePreloads?: Record<string, readonly ModulePreloadInput[]>;
|
|
10
12
|
readonly routeSsrModulePreloads?: Record<string, readonly ModulePreloadInput[]>;
|
|
11
13
|
readonly routeStylesheets?: Record<string, readonly string[]>;
|
|
14
|
+
readonly documentStylesheets?: readonly string[];
|
|
15
|
+
readonly importMap?: string;
|
|
12
16
|
readonly documentModuleLoader: (() => Promise<unknown>) | undefined;
|
|
17
|
+
readonly documentNavigation?: 'client' | 'document';
|
|
13
18
|
readonly pageModuleLoaders: Record<string, () => Promise<unknown>>;
|
|
14
19
|
readonly routeFileIds: readonly string[];
|
|
15
20
|
readonly render?: 'streaming' | 'blocking';
|
|
21
|
+
readonly navigation?: 'fragment' | 'render';
|
|
22
|
+
readonly fragmentEntryPath?: string;
|
|
23
|
+
readonly fragmentEntryImports?: readonly string[];
|
|
24
|
+
readonly fragmentNavigationSource?: string;
|
|
25
|
+
readonly fragmentNavigationRunSource?: string;
|
|
26
|
+
readonly linkBridgePaths?: readonly [withoutHashLinks: string, withHashLinks: string];
|
|
16
27
|
}
|
|
17
|
-
|
|
28
|
+
export type LinkBridgeOptions = Pick<ServerEntryOptions, 'base' | 'navigationEntryPath' | 'prefetch' | 'documentNavigation' | 'navigation' | 'fragmentEntryPath' | 'fragmentEntryImports' | 'fragmentNavigationSource' | 'fragmentNavigationRunSource' | 'routeFileIds'> & {
|
|
29
|
+
readonly debug?: boolean;
|
|
30
|
+
};
|
|
31
|
+
/** The link bridge modules a built app's pages import; the build writes them as static client files. */
|
|
32
|
+
export declare function routerLinkBridgeModules(options: LinkBridgeOptions, manifest?: RouteManifest): LinkBridgeFiles | undefined;
|
|
33
|
+
export declare function createServerEntry(options: ServerEntryOptions): {
|
|
18
34
|
fetch: (request: Request) => Promise<Response>;
|
|
35
|
+
linkBridgeModules: LinkBridgeFiles | undefined;
|
|
19
36
|
};
|
|
20
|
-
|
|
21
|
-
|
|
37
|
+
export interface LinkBridgeModule {
|
|
38
|
+
readonly path: string;
|
|
39
|
+
readonly source: string;
|
|
40
|
+
}
|
|
41
|
+
/** The two page-imported bridges (without and with '#/' links), then any file they load later. */
|
|
42
|
+
export type LinkBridgeFiles = readonly [withoutHashLinks: LinkBridgeModule, withHashLinks: LinkBridgeModule, ...later: LinkBridgeModule[]];
|
|
43
|
+
//#endregion
|
|
@@ -1,400 +1,2 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
|
|
3
|
-
import { renderToString } from "@markless/web/render-to-string";
|
|
4
|
-
import { renderToStream } from "@markless/web/render-to-stream";
|
|
5
|
-
import { normalizeMarklessDevError, renderMarklessDevErrorDocument } from "@markless/bundler/dev-error";
|
|
6
|
-
//#region src/vite/runtime/create-server-entry.ts
|
|
7
|
-
const DOCUMENT_CHILDREN_PLACEHOLDER = "__markless_router_document_children__";
|
|
8
|
-
function createServerEntry(options) {
|
|
9
|
-
const manifest = buildRouteManifestFromFileIds(options.routeFileIds);
|
|
10
|
-
assertCurrentRouteAssets(options);
|
|
11
|
-
async function fetch(request) {
|
|
12
|
-
const url = new URL(request.url);
|
|
13
|
-
if (isNitroApiPathname(url.pathname)) return new Response("Not found", { status: 404 });
|
|
14
|
-
const match = matchRouteManifest(url.pathname, manifest);
|
|
15
|
-
if (!match) return renderStatusPage(url, manifest.statusPages.notFound, 404, "Not found");
|
|
16
|
-
try {
|
|
17
|
-
return await renderPage(url, match.route.file, match.params, 200);
|
|
18
|
-
} catch (error) {
|
|
19
|
-
console.error("[markless-router] page render failed:", error);
|
|
20
|
-
if (options.dev) return renderDevelopmentError(url, error);
|
|
21
|
-
try {
|
|
22
|
-
return await renderStatusPage(url, manifest.statusPages.error, 500, "Internal Server Error");
|
|
23
|
-
} catch (errorPageError) {
|
|
24
|
-
console.error("[markless-router] error page render failed:", errorPageError);
|
|
25
|
-
return new Response("Internal Server Error", {
|
|
26
|
-
status: 500,
|
|
27
|
-
headers: { "content-type": "text/plain;charset=utf-8" }
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
function renderDevelopmentError(url, error) {
|
|
33
|
-
const headers = {
|
|
34
|
-
"cache-control": "no-store",
|
|
35
|
-
"content-type": "text/html;charset=utf-8"
|
|
36
|
-
};
|
|
37
|
-
try {
|
|
38
|
-
const payload = normalizeMarklessDevError(error, { id: `navigation:${url.pathname}` });
|
|
39
|
-
return new Response(renderMarklessDevErrorDocument(payload), {
|
|
40
|
-
status: 500,
|
|
41
|
-
headers
|
|
42
|
-
});
|
|
43
|
-
} catch {
|
|
44
|
-
return new Response(escapeHtml(flattenError(error)), {
|
|
45
|
-
status: 500,
|
|
46
|
-
headers: {
|
|
47
|
-
...headers,
|
|
48
|
-
"content-type": "text/plain;charset=utf-8"
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
async function renderStatusPage(url, file, status, fallbackText) {
|
|
54
|
-
if (!file) return new Response(fallbackText, { status });
|
|
55
|
-
return renderPage(url, file, {}, status);
|
|
56
|
-
}
|
|
57
|
-
async function renderPage(url, file, params, status) {
|
|
58
|
-
const loadPageModule = options.pageModuleLoaders[file];
|
|
59
|
-
if (!loadPageModule) return new Response(`Page module not found: ${file}`, { status: 500 });
|
|
60
|
-
const pageModule = await loadPageModule();
|
|
61
|
-
const pageProps = {
|
|
62
|
-
params,
|
|
63
|
-
url: {
|
|
64
|
-
href: url.href,
|
|
65
|
-
pathname: url.pathname,
|
|
66
|
-
search: url.search
|
|
67
|
-
},
|
|
68
|
-
status
|
|
69
|
-
};
|
|
70
|
-
const documentModule = options.documentModuleLoader ? await options.documentModuleLoader() : void 0;
|
|
71
|
-
const headers = { "content-type": "text/html;charset=utf-8" };
|
|
72
|
-
const baseArtifact = pageModule.default;
|
|
73
|
-
const renderSsr = baseArtifact?.renderSsr ?? pageModule.marklessRenderSsr;
|
|
74
|
-
if (!renderSsr) {
|
|
75
|
-
const shell = await renderDocumentShell(documentModule, pageProps, "");
|
|
76
|
-
const message = `Page module must export an Markless compiled artifact: ${escapeHtml(file)}`;
|
|
77
|
-
return new Response(fillDocumentChildren(shell, message), {
|
|
78
|
-
status,
|
|
79
|
-
headers
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
const pageArtifact = routedPageArtifact(renderSsr, baseArtifact, pageProps, file, options.navigationEntryPath, options.routeStylesheets?.[file]);
|
|
83
|
-
const routedArtifact = options.prerenderWakeEntryPath ? {
|
|
84
|
-
...pageArtifact,
|
|
85
|
-
prerenderWakeModuleUrl: options.prerenderWakeEntryPath
|
|
86
|
-
} : pageArtifact;
|
|
87
|
-
const renderOptions = {
|
|
88
|
-
props: pageProps,
|
|
89
|
-
resumeModuleUrl: options.resumeEntryPath ?? baseArtifact?.resumeModuleUrl,
|
|
90
|
-
modulePreloads: (html) => modulePreloadsForPage(file, html, pageProps.url.href, manifest, options.routeModulePreloads, options.routeSsrModulePreloads)
|
|
91
|
-
};
|
|
92
|
-
if (options.render === "blocking") {
|
|
93
|
-
const pageHtml = splitLeadingHeadHtml(await renderToString(routedArtifact, renderOptions));
|
|
94
|
-
const shell = await renderDocumentShell(documentModule, pageProps, pageHtml.headHtml);
|
|
95
|
-
return new Response(fillDocumentChildren(shell, pageHtml.bodyHtml), {
|
|
96
|
-
status,
|
|
97
|
-
headers
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
const stream = await renderToStream(routedArtifact, renderOptions);
|
|
101
|
-
const pageHtml = splitLeadingHeadHtml(stream.shell);
|
|
102
|
-
const shell = await renderDocumentShell(documentModule, pageProps, pageHtml.headHtml);
|
|
103
|
-
if (stream.pendingArmCount === 0) return new Response(fillDocumentChildren(shell, pageHtml.bodyHtml), {
|
|
104
|
-
status,
|
|
105
|
-
headers
|
|
106
|
-
});
|
|
107
|
-
const placeholderAt = shell.indexOf(DOCUMENT_CHILDREN_PLACEHOLDER);
|
|
108
|
-
const prefix = placeholderAt === -1 ? shell : shell.slice(0, placeholderAt) + pageHtml.bodyHtml;
|
|
109
|
-
const suffix = placeholderAt === -1 ? "" : shell.slice(placeholderAt + 37);
|
|
110
|
-
const encoder = new TextEncoder();
|
|
111
|
-
let cancelled = false;
|
|
112
|
-
let completed = false;
|
|
113
|
-
const body = new ReadableStream({
|
|
114
|
-
async start(controller) {
|
|
115
|
-
controller.enqueue(encoder.encode(prefix));
|
|
116
|
-
try {
|
|
117
|
-
for await (const chunk of stream.appends()) {
|
|
118
|
-
if (cancelled) return;
|
|
119
|
-
controller.enqueue(encoder.encode(chunk));
|
|
120
|
-
}
|
|
121
|
-
} catch (error) {
|
|
122
|
-
if (cancelled) return;
|
|
123
|
-
console.error("[markless-router] streaming settle failed:", error);
|
|
124
|
-
controller.error(error);
|
|
125
|
-
completed = true;
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
if (cancelled) return;
|
|
129
|
-
controller.enqueue(encoder.encode(suffix));
|
|
130
|
-
controller.close();
|
|
131
|
-
completed = true;
|
|
132
|
-
},
|
|
133
|
-
cancel(reason) {
|
|
134
|
-
if (completed) return;
|
|
135
|
-
cancelled = true;
|
|
136
|
-
stream.cancel(reason);
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
return new Response(body, {
|
|
140
|
-
status,
|
|
141
|
-
headers
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
return { fetch };
|
|
145
|
-
}
|
|
146
|
-
function flattenError(error) {
|
|
147
|
-
if (error instanceof Error) return error.message;
|
|
148
|
-
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") return error.message;
|
|
149
|
-
return String(error);
|
|
150
|
-
}
|
|
151
|
-
function assertCurrentRouteAssets(options) {
|
|
152
|
-
if (options.routeStylesheets === void 0) return;
|
|
153
|
-
const routeMaps = [
|
|
154
|
-
["navigation", options.routeModulePreloads ?? {}],
|
|
155
|
-
["SSR", options.routeSsrModulePreloads ?? {}],
|
|
156
|
-
["style", options.routeStylesheets]
|
|
157
|
-
];
|
|
158
|
-
const expected = [...new Set(options.routeFileIds.map(normalizeRouteFileId))].filter((file) => /^pages\/.+\.(?:tsrx|mdx)$/.test(file)).toSorted();
|
|
159
|
-
for (const [label, routes] of routeMaps) {
|
|
160
|
-
const actual = Object.keys(routes).toSorted();
|
|
161
|
-
if (JSON.stringify(actual) !== JSON.stringify(expected)) throw new Error(`Markless Router ${label} client-asset routes are stale; expected ${JSON.stringify(expected)}, received ${JSON.stringify(actual)}. Rebuild the client environment before the server environment.`);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
function routedPageArtifact(renderSsr, baseArtifact, pageProps, file, navigationEntryPath, stylesheetHrefs) {
|
|
165
|
-
const headInjections = [...baseArtifact?.headInjections ?? [], ...(stylesheetHrefs ?? []).map((href) => ({
|
|
166
|
-
tag: "link",
|
|
167
|
-
location: "head",
|
|
168
|
-
attributes: {
|
|
169
|
-
rel: "stylesheet",
|
|
170
|
-
href
|
|
171
|
-
}
|
|
172
|
-
}))];
|
|
173
|
-
return {
|
|
174
|
-
resumeModuleUrl: baseArtifact?.resumeModuleUrl,
|
|
175
|
-
inlineResumerSources: baseArtifact?.inlineResumerSources,
|
|
176
|
-
storageSeeds: baseArtifact?.storageSeeds,
|
|
177
|
-
...headInjections.length > 0 ? { headInjections } : {},
|
|
178
|
-
async renderSsr(renderProps, renderContext) {
|
|
179
|
-
const output = await renderSsr(renderProps ?? pageProps, renderContext);
|
|
180
|
-
if (!output) return { html: "" };
|
|
181
|
-
const routeScript = output.state || output.view ? renderRouteScript(file) : "";
|
|
182
|
-
const linkBridge = navigationEntryPath ? renderLinkBridgeScript(navigationEntryPath) : "";
|
|
183
|
-
const stateWithProps = withPagePropsCell(output.state, pageProps);
|
|
184
|
-
return routeScript || linkBridge || stateWithProps !== output.state ? {
|
|
185
|
-
...output,
|
|
186
|
-
state: stateWithProps,
|
|
187
|
-
html: `${output.html}${routeScript}${linkBridge}`
|
|
188
|
-
} : output;
|
|
189
|
-
}
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
function withPagePropsCell(state, props) {
|
|
193
|
-
if (!state || typeof state !== "object") return state;
|
|
194
|
-
const cells = state.cells;
|
|
195
|
-
if (!Array.isArray(cells) || cells.some((cell) => cell?.graphNodeId === "prop:props")) return state;
|
|
196
|
-
return {
|
|
197
|
-
...state,
|
|
198
|
-
cells: [...cells, {
|
|
199
|
-
graphNodeId: "prop:props",
|
|
200
|
-
name: "props",
|
|
201
|
-
valueKind: "object",
|
|
202
|
-
directValue: props
|
|
203
|
-
}]
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
function modulePreloadsForPage(file, html, baseHref, manifest, routeModulePreloads, routeSsrModulePreloads) {
|
|
207
|
-
const preloads = [];
|
|
208
|
-
const seen = /* @__PURE__ */ new Set();
|
|
209
|
-
addModulePreloads(preloads, seen, routeSsrModulePreloads?.[file]);
|
|
210
|
-
if (!routeModulePreloads || !html.includes("data-markless-router-link")) return preloads.length > 0 ? preloads : void 0;
|
|
211
|
-
for (const href of routerLinkHrefs(html)) {
|
|
212
|
-
const url = parseSameOriginUrl(href, baseHref);
|
|
213
|
-
const match = url && matchRouteManifest(url.pathname, manifest);
|
|
214
|
-
addModulePreloads(preloads, seen, match && routeModulePreloads[match.route.file]);
|
|
215
|
-
}
|
|
216
|
-
return preloads.length > 0 ? preloads : void 0;
|
|
217
|
-
}
|
|
218
|
-
function routerLinkHrefs(html) {
|
|
219
|
-
return [...html.matchAll(/<a\b(?=[^>]*\bdata-markless-router-link(?:[\s=>]|$))(?=[^>]*\bhref="([^"]*)")[^>]*>/g)].map((match) => unescapeHtmlAttribute(match[1] ?? ""));
|
|
220
|
-
}
|
|
221
|
-
function parseSameOriginUrl(href, base) {
|
|
222
|
-
try {
|
|
223
|
-
const current = new URL(base);
|
|
224
|
-
const url = new URL(href, current);
|
|
225
|
-
return url.origin === current.origin ? url : void 0;
|
|
226
|
-
} catch {
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
function unescapeHtmlAttribute(value) {
|
|
231
|
-
return value.replaceAll(""", "\"").replaceAll(">", ">").replaceAll("<", "<").replaceAll("&", "&");
|
|
232
|
-
}
|
|
233
|
-
function addModulePreloads(preloads, seen, items) {
|
|
234
|
-
for (const preload of items ?? []) {
|
|
235
|
-
const preloadHref = typeof preload === "string" ? preload : preload.href;
|
|
236
|
-
if (!preloadHref || seen.has(preloadHref)) continue;
|
|
237
|
-
seen.add(preloadHref);
|
|
238
|
-
preloads.push(preload);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
function splitLeadingHeadHtml(html) {
|
|
242
|
-
const containerStart = html.indexOf("<div data-async-container");
|
|
243
|
-
if (containerStart <= 0) return {
|
|
244
|
-
bodyHtml: html,
|
|
245
|
-
headHtml: ""
|
|
246
|
-
};
|
|
247
|
-
return {
|
|
248
|
-
bodyHtml: html.slice(containerStart),
|
|
249
|
-
headHtml: html.slice(0, containerStart)
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
async function renderDocumentShell(documentModule, pageProps, headHtml) {
|
|
253
|
-
const attributes = htmlAttributes(documentModule, pageProps);
|
|
254
|
-
const documentHtml = await renderDocumentModule(documentModule, {
|
|
255
|
-
...pageProps,
|
|
256
|
-
children: DOCUMENT_CHILDREN_PLACEHOLDER
|
|
257
|
-
});
|
|
258
|
-
if (documentHtml !== void 0) return [
|
|
259
|
-
"<!doctype html>",
|
|
260
|
-
`<html${renderAttributes(attributes)}>`,
|
|
261
|
-
insertHeadHtml(documentHtml, headHtml),
|
|
262
|
-
"</html>"
|
|
263
|
-
].join("");
|
|
264
|
-
return [
|
|
265
|
-
"<!doctype html>",
|
|
266
|
-
`<html${renderAttributes(attributes)}>`,
|
|
267
|
-
"<head>",
|
|
268
|
-
"<meta charset=\"utf-8\">",
|
|
269
|
-
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">",
|
|
270
|
-
headHtml,
|
|
271
|
-
"</head>",
|
|
272
|
-
"<body>",
|
|
273
|
-
DOCUMENT_CHILDREN_PLACEHOLDER,
|
|
274
|
-
"</body>",
|
|
275
|
-
"</html>"
|
|
276
|
-
].join("");
|
|
277
|
-
}
|
|
278
|
-
function fillDocumentChildren(documentShell, children) {
|
|
279
|
-
const at = documentShell.indexOf(DOCUMENT_CHILDREN_PLACEHOLDER);
|
|
280
|
-
if (at === -1) return documentShell;
|
|
281
|
-
return documentShell.slice(0, at) + children + documentShell.slice(at + 37);
|
|
282
|
-
}
|
|
283
|
-
async function renderDocumentModule(documentModule, props) {
|
|
284
|
-
const output = await documentModule?.default?.renderSsr?.(props);
|
|
285
|
-
assertDocumentStorageFree(output);
|
|
286
|
-
return output?.html;
|
|
287
|
-
}
|
|
288
|
-
function assertDocumentStorageFree(output) {
|
|
289
|
-
const storage = documentStorageRecords(output?.state);
|
|
290
|
-
if (storage.length === 0) return;
|
|
291
|
-
const keys = storage.map((record) => record.key).join(", ");
|
|
292
|
-
throw new Error(`MARKLESS_ROUTER_DOCUMENT_STORAGE_UNSUPPORTED: the document declares storage cells (${keys}), but the router serves only the document's HTML, so their state payload never reaches the browser and they can never resume. Declare storage() at module scope in a component the page renders instead.`);
|
|
293
|
-
}
|
|
294
|
-
function documentStorageRecords(state) {
|
|
295
|
-
if (!state || typeof state !== "object") return [];
|
|
296
|
-
const storage = state.storage;
|
|
297
|
-
return Array.isArray(storage) ? storage : [];
|
|
298
|
-
}
|
|
299
|
-
function insertHeadHtml(documentHtml, headHtml) {
|
|
300
|
-
if (!headHtml) return documentHtml;
|
|
301
|
-
const headEnd = documentHtml.indexOf("</head>");
|
|
302
|
-
if (headEnd === -1) return `${headHtml}${documentHtml}`;
|
|
303
|
-
return `${documentHtml.slice(0, headEnd)}${headHtml}${documentHtml.slice(headEnd)}`;
|
|
304
|
-
}
|
|
305
|
-
function renderRouteScript(file) {
|
|
306
|
-
return `<script type="@markless/core/route">${escapeScriptJson({ file })}<\/script>`;
|
|
307
|
-
}
|
|
308
|
-
function renderLinkBridgeScript(resumeEntryPath) {
|
|
309
|
-
const debugBootstrap = typeof __MARKLESS_DEBUG_ENABLED__ !== "undefined" && __MARKLESS_DEBUG_ENABLED__ ? `<script data-markless-router-debug-bootstrap>${escapeInlineScript(`(() => {
|
|
310
|
-
const d = document;
|
|
311
|
-
const s = d.currentScript;
|
|
312
|
-
const r = s && s.closest('[data-async-container]');
|
|
313
|
-
if (!r) return;
|
|
314
|
-
try { globalThis[Symbol.for('markless.debug.channel.v1.bootstrap')] = ${__marklessDebugBootstrapSource()}(r, 'ssr-inline', false); } catch {}
|
|
315
|
-
})();`)}<\/script>` : "";
|
|
316
|
-
const debugRegistration = typeof __MARKLESS_DEBUG_ENABLED__ !== "undefined" && __MARKLESS_DEBUG_ENABLED__ ? `
|
|
317
|
-
try {
|
|
318
|
-
const key = Symbol.for('markless.debug.channel.v1.bootstrap');
|
|
319
|
-
const md = globalThis[key];
|
|
320
|
-
if (md) { md.router('ssr-link-bridge'); md.activate(); }
|
|
321
|
-
} catch {}` : "";
|
|
322
|
-
return `${debugBootstrap}<script data-markless-router-link-resumer>${escapeInlineScript(`(() => {
|
|
323
|
-
const d = document;
|
|
324
|
-
const s = d.currentScript;
|
|
325
|
-
const r = s && s.closest('[data-async-container]');
|
|
326
|
-
if (!r || r.__marklessRouterLinkResumerStarted) return;
|
|
327
|
-
r.__marklessRouterLinkResumerStarted = true;
|
|
328
|
-
const linkAttr = 'data-markless-router-link';
|
|
329
|
-
const replaceAttr = 'data-markless-router-replace';
|
|
330
|
-
const scrollAttr = 'data-markless-router-scroll';
|
|
331
|
-
const anchorFrom = (event) => {
|
|
332
|
-
const target = event.composedPath && event.composedPath()[0] || event.target;
|
|
333
|
-
return target && target.closest ? target.closest('a[href]') : target && target.parentElement && target.parentElement.closest ? target.parentElement.closest('a[href]') : null;
|
|
334
|
-
};
|
|
335
|
-
const sameOrigin = (href) => {
|
|
336
|
-
try {
|
|
337
|
-
const url = new URL(href, location.href);
|
|
338
|
-
return url.origin === location.origin ? url : null;
|
|
339
|
-
} catch {
|
|
340
|
-
return null;
|
|
341
|
-
}
|
|
342
|
-
};
|
|
343
|
-
if (location.hash && location.hash.startsWith('#/')) {
|
|
344
|
-
import(${JSON.stringify(resumeEntryPath)}).catch(() => {});
|
|
345
|
-
}
|
|
346
|
-
r.addEventListener('click', async (event) => {
|
|
347
|
-
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) return;
|
|
348
|
-
const anchor = anchorFrom(event);
|
|
349
|
-
if (!anchor || !anchor.hasAttribute(linkAttr) || anchor.hasAttribute('download')) return;
|
|
350
|
-
const target = anchor.getAttribute('target');
|
|
351
|
-
if (target && target !== '_self') return;
|
|
352
|
-
if (anchor.relList && anchor.relList.contains('external')) return;
|
|
353
|
-
const url = sameOrigin(anchor.href);
|
|
354
|
-
if (!url) return;
|
|
355
|
-
event.preventDefault();
|
|
356
|
-
try {
|
|
357
|
-
const mod = await import(${JSON.stringify(resumeEntryPath)});
|
|
358
|
-
const navigate = mod.navigateMarklessRouterLink;
|
|
359
|
-
if (typeof navigate === 'function') {
|
|
360
|
-
await navigate({
|
|
361
|
-
href: url.href,
|
|
362
|
-
replace: anchor.hasAttribute(replaceAttr),
|
|
363
|
-
scroll: anchor.getAttribute(scrollAttr) === 'manual' ? 'manual' : undefined,
|
|
364
|
-
});
|
|
365
|
-
} else {
|
|
366
|
-
location.assign(url.href);
|
|
367
|
-
}
|
|
368
|
-
} catch (error) {
|
|
369
|
-
setTimeout(() => { throw error; });
|
|
370
|
-
location.assign(url.href);
|
|
371
|
-
}
|
|
372
|
-
}, true);${debugRegistration}
|
|
373
|
-
})();`)}<\/script>`;
|
|
374
|
-
}
|
|
375
|
-
function htmlAttributes(documentModule, pageProps) {
|
|
376
|
-
const attributes = documentModule?.__marklessRouterHtmlAttributes?.(pageProps) ?? { lang: "en" };
|
|
377
|
-
const normalized = {};
|
|
378
|
-
for (const [name, value] of Object.entries(attributes)) {
|
|
379
|
-
if (value === false || value === null || value === void 0) continue;
|
|
380
|
-
normalized[name] = String(value);
|
|
381
|
-
}
|
|
382
|
-
return normalized;
|
|
383
|
-
}
|
|
384
|
-
function renderAttributes(attributes) {
|
|
385
|
-
return Object.entries(attributes).map(([name, value]) => ` ${name}="${escapeHtml(value)}"`).join("");
|
|
386
|
-
}
|
|
387
|
-
function escapeHtml(value) {
|
|
388
|
-
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """);
|
|
389
|
-
}
|
|
390
|
-
function escapeScriptJson(value) {
|
|
391
|
-
return JSON.stringify(value).replaceAll("<", "\\u003c");
|
|
392
|
-
}
|
|
393
|
-
function escapeInlineScript(value) {
|
|
394
|
-
return value.replace(/<\/script/gi, "<\\/script");
|
|
395
|
-
}
|
|
396
|
-
function isNitroApiPathname(pathname) {
|
|
397
|
-
return pathname === "/api" || pathname.startsWith("/api/");
|
|
398
|
-
}
|
|
399
|
-
//#endregion
|
|
400
|
-
export { createServerEntry };
|
|
1
|
+
import { n as routerLinkBridgeModules, t as createServerEntry } from "../../create-server-entry-DaYssO9x.js";
|
|
2
|
+
export { createServerEntry, routerLinkBridgeModules };
|