@markless/router 0.3.3 → 0.5.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/debug-channel-CewnC9fH.js +2 -0
- package/dist/{debug-channel-CSzo-Zj8.js → debug-channel-Di6SppZv.js} +36 -17
- package/dist/fragment-navigation-CIiNnFFc.js +323 -0
- package/dist/index.d.ts +54 -33
- package/dist/index.js +50 -31
- package/dist/link-attributes-nY1Qkmx2.js +7 -0
- package/dist/protocol-constants-B4WxrZAS.js +26 -0
- package/dist/{request-files-C-0LLSsx.js → request-files-Ca8E0FOo.js} +3 -2
- package/dist/request-locals-BFPF6Vdm.js +28 -0
- package/dist/route-href-CGooO_g6.js +18 -0
- package/dist/{route-manifest-DPgGIyrS.js → route-manifest-Bw_nFohL.js} +9 -6
- package/dist/typescript-plugin.cjs +8 -5
- package/dist/typescript-plugin.d.ts +3 -11194
- package/dist/typescript-plugin.js +8 -5
- 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 +11 -5
- package/dist/vite/runtime/create-server-entry.js +354 -96
- package/dist/vite/runtime/mdx-route.d.ts +1097 -17
- package/dist/vite/runtime/mdx-route.js +1094 -31
- package/dist/vite.d.ts +10 -6
- package/dist/vite.js +782 -188
- package/package.json +7 -6
- package/src/vite/entries/client-entry.ts +10 -1
- package/src/vite/entries/fragment-entry.ts +543 -0
- package/src/vite/entries/resume-entry.ts +41 -7
package/dist/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { i as
|
|
2
|
-
import {
|
|
1
|
+
import { i as SCROLL_ATTRIBUTE, n as PREFETCH_ATTRIBUTE, r as REPLACE_ATTRIBUTE, t as LINK_ATTRIBUTE } from "./link-attributes-nY1Qkmx2.js";
|
|
2
|
+
import { a as routeMountTarget, r as HTML_ATTRIBUTES_SLOT, t as rememberRequestLocals } from "./request-locals-BFPF6Vdm.js";
|
|
3
|
+
import { a as normalizeRouteFileId, i as normalizeRequestPathname, r as matchRouteManifest, t as buildRouteManifestFromFileIds } from "./route-manifest-Bw_nFohL.js";
|
|
4
|
+
import { n as parseRequestFile, r as transformRequestFileSource, t as normalizeRequestFileId } from "./request-files-Ca8E0FOo.js";
|
|
3
5
|
import { createExternalDelegateStructureTokens, createExternalDelegateView } from "@markless/web/fns/external-delegate";
|
|
4
6
|
import { render } from "@markless/web/render";
|
|
5
|
-
import { MARKLESS_PENDING_MIN_VISIBLE_MS as MARKLESS_NAV_PENDING_MIN_MS, settleOrPendingDeadline } from "@markless/web";
|
|
7
|
+
import { MARKLESS_PENDING_MIN_VISIBLE_MS as MARKLESS_NAV_PENDING_MIN_MS, settleOrPendingDeadline } from "@markless/web/fns/pending-timing";
|
|
6
8
|
//#region src/navigation-hold.ts
|
|
7
9
|
async function holdNavigationSwapUntilSettled(input) {
|
|
8
10
|
const settled = input.runtime.whenAsyncBoundariesSettled?.();
|
|
@@ -14,6 +16,7 @@ async function holdNavigationSwapUntilSettled(input) {
|
|
|
14
16
|
//#endregion
|
|
15
17
|
//#region src/route-state.ts
|
|
16
18
|
const MARKLESS_ROUTER_ROUTE_EVENT = "marklessrouternavigate";
|
|
19
|
+
const MARKLESS_ROUTER_RENDERED_EVENT = "marklessrouterrendered";
|
|
17
20
|
const MARKLESS_ROUTER_RENDERER_STARTED = "__marklessRouterRouteRendererStarted";
|
|
18
21
|
function routePageProps(route) {
|
|
19
22
|
const url = new URL(route.url);
|
|
@@ -48,22 +51,26 @@ async function renderRouteUpdate(document, update) {
|
|
|
48
51
|
if (!artifact.renderData) throw new Error(`MARKLESS_ROUTER_RENDER_DATA_MISSING: Navigated route ${JSON.stringify(update.route.file)} has no linked render-data module.`);
|
|
49
52
|
const props = routePageProps(update.route);
|
|
50
53
|
const state = document;
|
|
54
|
+
const outgoing = state[CURRENT_ROUTE_CONTAINER];
|
|
51
55
|
const container = await render({
|
|
52
56
|
renderData: artifact.renderData,
|
|
53
57
|
loadSymbol: artifact.loadSymbol,
|
|
54
58
|
props
|
|
55
59
|
}, {
|
|
56
|
-
target: document
|
|
60
|
+
target: routeMountTarget(document, outgoing?.root),
|
|
57
61
|
beforeMount: async (incoming) => {
|
|
58
62
|
if (await holdNavigationSwapUntilSettled({
|
|
59
63
|
runtime: incoming.runtime,
|
|
60
64
|
signal: update.signal
|
|
61
65
|
}) === false) return false;
|
|
62
|
-
|
|
66
|
+
outgoing?.runtime?.dispose?.();
|
|
63
67
|
return true;
|
|
64
68
|
}
|
|
65
69
|
});
|
|
66
|
-
if (!update.signal?.aborted)
|
|
70
|
+
if (!update.signal?.aborted) {
|
|
71
|
+
state[CURRENT_ROUTE_CONTAINER] = container;
|
|
72
|
+
document.dispatchEvent(new CustomEvent(MARKLESS_ROUTER_RENDERED_EVENT));
|
|
73
|
+
}
|
|
67
74
|
} finally {
|
|
68
75
|
update.onRendered?.();
|
|
69
76
|
}
|
|
@@ -71,9 +78,6 @@ async function renderRouteUpdate(document, update) {
|
|
|
71
78
|
//#endregion
|
|
72
79
|
//#region src/spa-navigation.ts
|
|
73
80
|
const STARTED = "__marklessRouterSpaNavigationStarted";
|
|
74
|
-
const LINK_ATTRIBUTE = "data-markless-router-link";
|
|
75
|
-
const REPLACE_ATTRIBUTE = "data-markless-router-replace";
|
|
76
|
-
const SCROLL_ATTRIBUTE = "data-markless-router-scroll";
|
|
77
81
|
const LINK_INFO = "__marklessRouterLink";
|
|
78
82
|
function hashRoutePath(url) {
|
|
79
83
|
return url.hash.startsWith("#/") ? url.hash.slice(1) : void 0;
|
|
@@ -88,6 +92,7 @@ async function __marklessRouterStartSpaNavigation(options) {
|
|
|
88
92
|
state[STARTED] = true;
|
|
89
93
|
const context = {
|
|
90
94
|
documentModuleLoader: options.documentModuleLoader,
|
|
95
|
+
documentNavigation: options.documentNavigation,
|
|
91
96
|
manifest: buildRouteManifestFromFileIds(options.routeFileIds),
|
|
92
97
|
pageModuleLoaders: options.pageModuleLoaders,
|
|
93
98
|
preloadRouteModule: options.preloadRouteModule,
|
|
@@ -98,23 +103,30 @@ async function __marklessRouterStartSpaNavigation(options) {
|
|
|
98
103
|
navigation.addEventListener("navigate", (event) => {
|
|
99
104
|
handleNavigateEvent(event, context);
|
|
100
105
|
});
|
|
101
|
-
if (typeof __MARKLESS_DEBUG_ENABLED__ !== "undefined" && __MARKLESS_DEBUG_ENABLED__) await import("./debug-channel-
|
|
102
|
-
if (typeof __MARKLESS_DEBUG_ENABLED__ !== "undefined" && __MARKLESS_DEBUG_ENABLED__) await import("./debug-channel-
|
|
106
|
+
if (typeof __MARKLESS_DEBUG_ENABLED__ !== "undefined" && __MARKLESS_DEBUG_ENABLED__) await import("./debug-channel-CewnC9fH.js").then((debug) => debug.__marklessDebugRegisterRouter(void 0, "spa-click-listener")).catch(() => {});
|
|
107
|
+
if (typeof __MARKLESS_DEBUG_ENABLED__ !== "undefined" && __MARKLESS_DEBUG_ENABLED__) await import("./debug-channel-CewnC9fH.js").then((debug) => debug.__marklessDebugRegisterRouter(void 0, "navigation-event")).catch(() => {});
|
|
103
108
|
{
|
|
104
109
|
const bootUrl = parseSameOriginUrl(runtimeWindow.location.href, runtimeWindow.location.href);
|
|
105
110
|
const bootHashPath = bootUrl ? hashRoutePath(bootUrl) : void 0;
|
|
106
111
|
if (bootUrl && bootHashPath && matchRouteManifest(bootHashPath, context.manifest)) renderRoute(bootUrl, context);
|
|
107
112
|
}
|
|
108
113
|
}
|
|
114
|
+
const pendingPolyfills = /* @__PURE__ */ new WeakMap();
|
|
109
115
|
async function ensureNavigationRuntime(runtimeWindow = browserWindow(), loadPolyfill) {
|
|
110
|
-
if (
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
+
if (runtimeWindow.navigation) return runtimeWindow.navigation;
|
|
117
|
+
let pending = pendingPolyfills.get(runtimeWindow);
|
|
118
|
+
if (!pending) {
|
|
119
|
+
pending = (async () => {
|
|
120
|
+
const { applyPolyfill } = await (loadPolyfill?.() ?? import("@virtualstate/navigation"));
|
|
121
|
+
return runtimeWindow.navigation ??= applyPolyfill({
|
|
122
|
+
interceptEvents: false,
|
|
123
|
+
window: runtimeWindow
|
|
124
|
+
});
|
|
125
|
+
})();
|
|
126
|
+
pendingPolyfills.set(runtimeWindow, pending);
|
|
127
|
+
pending.catch(() => pendingPolyfills.delete(runtimeWindow));
|
|
116
128
|
}
|
|
117
|
-
return
|
|
129
|
+
return pending;
|
|
118
130
|
}
|
|
119
131
|
function handleNavigateEvent(event, context) {
|
|
120
132
|
const url = routeUrl(event, context);
|
|
@@ -136,7 +148,10 @@ async function renderRoute(url, context, signal) {
|
|
|
136
148
|
return;
|
|
137
149
|
}
|
|
138
150
|
preloadRouteModule(context, match.route.file);
|
|
139
|
-
const [page, document] = await Promise.all([loadPageModule(), context.documentModuleLoader?.()])
|
|
151
|
+
const [page, document] = await Promise.all([loadPageModule(), context.documentModuleLoader?.()]).catch((error) => {
|
|
152
|
+
context.window.location.assign(url.href);
|
|
153
|
+
throw error;
|
|
154
|
+
});
|
|
140
155
|
if (signal?.aborted) return;
|
|
141
156
|
const update = {
|
|
142
157
|
document,
|
|
@@ -167,17 +182,17 @@ function warnUnknownHashRoute(url) {
|
|
|
167
182
|
function handleLinkClick(event, context, navigation) {
|
|
168
183
|
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) return;
|
|
169
184
|
const anchor = sourceAnchor(event);
|
|
170
|
-
if (!anchor || !anchor.hasAttribute(
|
|
185
|
+
if (!anchor || !anchor.hasAttribute("data-markless-router-link") || !isEligibleLink(anchor)) return;
|
|
171
186
|
const url = parseSameOriginUrl(anchor.href, context.window.location.href);
|
|
172
187
|
const match = url && matchRouteManifest(routePathname(url, context), context.manifest);
|
|
173
|
-
if (!match) return;
|
|
188
|
+
if (!url || !match || context.documentNavigation === "document" && !hashRoutePath(url)) return;
|
|
174
189
|
preloadRouteModule(context, match.route.file);
|
|
175
190
|
event.preventDefault();
|
|
176
191
|
navigation.navigate(url.href, {
|
|
177
|
-
history: anchor.hasAttribute(
|
|
192
|
+
history: anchor.hasAttribute("data-markless-router-replace") ? "replace" : "push",
|
|
178
193
|
info: {
|
|
179
194
|
[LINK_INFO]: true,
|
|
180
|
-
scroll: anchor.getAttribute(
|
|
195
|
+
scroll: anchor.getAttribute("data-markless-router-scroll") === "manual" ? "manual" : void 0
|
|
181
196
|
}
|
|
182
197
|
});
|
|
183
198
|
}
|
|
@@ -189,7 +204,7 @@ function preloadRouteModule(context, file) {
|
|
|
189
204
|
function routeUrl(event, context) {
|
|
190
205
|
const destination = parseSameOriginUrl(event.destination.url, context.window.location.href);
|
|
191
206
|
const hashRouteNavigation = event.hashChange === true && !!destination && hashRoutePath(destination) !== void 0;
|
|
192
|
-
if (!isMarklessRouterNavigation(event) && !hashRouteNavigation || event.canIntercept === false || event.navigationType === "reload" || event.hashChange && !hashRouteNavigation || event.downloadRequest != null || event.formData != null) return;
|
|
207
|
+
if (!isMarklessRouterNavigation(event) && !hashRouteNavigation || context.documentNavigation === "document" && !hashRouteNavigation || event.canIntercept === false || event.navigationType === "reload" || event.hashChange && !hashRouteNavigation || event.downloadRequest != null || event.formData != null) return;
|
|
193
208
|
const matched = destination && matchRouteManifest(routePathname(destination), context.manifest) ? destination : void 0;
|
|
194
209
|
if (!matched && hashRouteNavigation && destination) warnUnknownHashRoute(destination);
|
|
195
210
|
return matched;
|
|
@@ -226,6 +241,7 @@ function browserWindow() {
|
|
|
226
241
|
function __marklessCreateHttpContext(event) {
|
|
227
242
|
const context = event.context ?? {};
|
|
228
243
|
const url = event.url ?? new URL("http://localhost/");
|
|
244
|
+
if (event.req) rememberRequestLocals(event.req, context);
|
|
229
245
|
return {
|
|
230
246
|
locals: context,
|
|
231
247
|
params: context.params ?? {},
|
|
@@ -236,9 +252,11 @@ function __marklessCreateHttpContext(event) {
|
|
|
236
252
|
}
|
|
237
253
|
const Html = Object.assign(function Html(props) {
|
|
238
254
|
return props.children;
|
|
239
|
-
}, { renderSsr(props = {}) {
|
|
255
|
+
}, { renderSsr(props = {}, renderContext) {
|
|
256
|
+
const { children, ...attributes } = props;
|
|
257
|
+
if (renderContext?.["__marklessRouterHtmlAttributes"]) Object.assign(renderContext[HTML_ATTRIBUTES_SLOT], attributes);
|
|
240
258
|
return {
|
|
241
|
-
html:
|
|
259
|
+
html: children == null ? "" : String(children),
|
|
242
260
|
elementCount: 0
|
|
243
261
|
};
|
|
244
262
|
} });
|
|
@@ -252,7 +270,7 @@ const Link = Object.assign(function Link(props = {}) {
|
|
|
252
270
|
return {
|
|
253
271
|
root: anchor,
|
|
254
272
|
view: linkExternalDelegateView(),
|
|
255
|
-
liveHostNodes: new Map([["router:link", anchor]])
|
|
273
|
+
liveHostNodes: /* @__PURE__ */ new Map([["router:link", anchor]])
|
|
256
274
|
};
|
|
257
275
|
},
|
|
258
276
|
renderSsr(props = {}, renderContext) {
|
|
@@ -285,9 +303,10 @@ function linkAnchorAttributes(props) {
|
|
|
285
303
|
if (attributeValue !== void 0) attributes.set(name, attributeValue);
|
|
286
304
|
}
|
|
287
305
|
attributes.set("href", typeof props.href === "string" ? props.href : "#");
|
|
288
|
-
attributes.set(
|
|
289
|
-
if (props.replace) attributes.set(
|
|
290
|
-
if (props.
|
|
306
|
+
attributes.set(LINK_ATTRIBUTE, "");
|
|
307
|
+
if (props.replace) attributes.set(REPLACE_ATTRIBUTE, "");
|
|
308
|
+
if (props.prefetch === false) attributes.set(PREFETCH_ATTRIBUTE, "none");
|
|
309
|
+
if (props.scroll === false) attributes.set(SCROLL_ATTRIBUTE, "manual");
|
|
291
310
|
return [...attributes];
|
|
292
311
|
}
|
|
293
312
|
function isLinkInternalProp(name) {
|
|
@@ -306,4 +325,4 @@ function escapeHtml(value) {
|
|
|
306
325
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """);
|
|
307
326
|
}
|
|
308
327
|
//#endregion
|
|
309
|
-
export { Html, Link, MARKLESS_ROUTER_ROUTE_EVENT, __marklessCreateHttpContext, __marklessRouterStartSpaNavigation, buildRouteManifestFromFileIds, dispatchRouteUpdate, ensureNavigationRuntime, matchRouteManifest, normalizeRequestFileId, normalizeRequestPathname, normalizeRouteFileId, parseRequestFile, routePageProps, startRouteUpdateRenderer, transformRequestFileSource };
|
|
328
|
+
export { Html, Link, MARKLESS_ROUTER_RENDERED_EVENT, MARKLESS_ROUTER_ROUTE_EVENT, __marklessCreateHttpContext, __marklessRouterStartSpaNavigation, buildRouteManifestFromFileIds, dispatchRouteUpdate, ensureNavigationRuntime, matchRouteManifest, normalizeRequestFileId, normalizeRequestPathname, normalizeRouteFileId, parseRequestFile, routePageProps, startRouteUpdateRenderer, transformRequestFileSource };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
//#region src/link-attributes.ts
|
|
2
|
+
const LINK_ATTRIBUTE = "data-markless-router-link";
|
|
3
|
+
const REPLACE_ATTRIBUTE = "data-markless-router-replace";
|
|
4
|
+
const SCROLL_ATTRIBUTE = "data-markless-router-scroll";
|
|
5
|
+
const PREFETCH_ATTRIBUTE = "data-markless-router-prefetch";
|
|
6
|
+
//#endregion
|
|
7
|
+
export { SCROLL_ATTRIBUTE as i, PREFETCH_ATTRIBUTE as n, REPLACE_ATTRIBUTE as r, LINK_ATTRIBUTE as t };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
function protocolStateVersion(storage) {
|
|
2
|
+
return (storage?.length ?? 0) > 0 ? 2 : 1;
|
|
3
|
+
}
|
|
4
|
+
const PROTOCOL_INSTANCE_PATH = /^(?:[cpm]\d+:|r:[^:]*:)+/;
|
|
5
|
+
/** One island's identity segment, outermost of any path it prefixes. */
|
|
6
|
+
function protocolIslandSegment(islandIndex) {
|
|
7
|
+
return `m${islandIndex}:`;
|
|
8
|
+
}
|
|
9
|
+
/** The leading instance path of a composed id, or '' when the id is page-local. */
|
|
10
|
+
function protocolInstancePath(id) {
|
|
11
|
+
return PROTOCOL_INSTANCE_PATH.exec(id)?.[0] ?? "";
|
|
12
|
+
}
|
|
13
|
+
const PROTOCOL_INSTANCE_QUALIFIABLE = [
|
|
14
|
+
"prop:",
|
|
15
|
+
"state:",
|
|
16
|
+
"computed:",
|
|
17
|
+
"element:"
|
|
18
|
+
];
|
|
19
|
+
const PROTOCOL_PAGE_SPACE_ID_PREFIXES = ["shared:", "storage:"];
|
|
20
|
+
function protocolInstanceQualifies(graphNodeId) {
|
|
21
|
+
const local = graphNodeId.slice(protocolInstancePath(graphNodeId).length);
|
|
22
|
+
if (PROTOCOL_PAGE_SPACE_ID_PREFIXES.some((prefix) => local.startsWith(prefix))) return false;
|
|
23
|
+
return PROTOCOL_INSTANCE_QUALIFIABLE.some((prefix) => local.startsWith(prefix)) ? true : void 0;
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { protocolStateVersion as i, protocolInstanceQualifies as n, protocolIslandSegment as r, protocolInstancePath as t };
|
|
@@ -4,7 +4,7 @@ import { joinURL, withLeadingSlash, withoutLeadingSlash } from "ufo";
|
|
|
4
4
|
const API_DIR = "api";
|
|
5
5
|
const MIDDLEWARE_DIR = "middleware";
|
|
6
6
|
const REQUEST_FILE_EXTENSION = ".ts";
|
|
7
|
-
const HTTP_METHODS = new Set([
|
|
7
|
+
const HTTP_METHODS = /* @__PURE__ */ new Set([
|
|
8
8
|
"connect",
|
|
9
9
|
"delete",
|
|
10
10
|
"get",
|
|
@@ -68,7 +68,8 @@ function parseMiddlewareRequestFile(file, sourceText) {
|
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
70
|
function apiRouteFromFile(apiFile) {
|
|
71
|
-
const
|
|
71
|
+
const withoutExtension = apiFile.slice(0, -3);
|
|
72
|
+
const rawSegments = normalize(join(withoutExtension)).split("/");
|
|
72
73
|
const finalSegment = rawSegments.at(-1);
|
|
73
74
|
const method = methodSuffix(finalSegment);
|
|
74
75
|
if (method !== "all") rawSegments[rawSegments.length - 1] = finalSegment.slice(0, -(method.length + 1));
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region src/route-dom.ts
|
|
2
|
+
const ROUTE_SCRIPT_TYPE = "@markless/core/route";
|
|
3
|
+
const HTML_ATTRIBUTES_SLOT = "__marklessRouterHtmlAttributes";
|
|
4
|
+
function routeMountTarget(document, currentRoot) {
|
|
5
|
+
const root = currentRoot ?? document.querySelector(`script[type="@markless/core/route"]`)?.parentElement;
|
|
6
|
+
if (!root) return document.body;
|
|
7
|
+
return {
|
|
8
|
+
ownerDocument: document,
|
|
9
|
+
replaceChildren(...children) {
|
|
10
|
+
root.replaceWith(...children);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/request-locals.ts
|
|
16
|
+
const REQUEST_LOCALS = Symbol.for("@markless/router/request-locals");
|
|
17
|
+
function table() {
|
|
18
|
+
const scope = globalThis;
|
|
19
|
+
return scope[REQUEST_LOCALS] ??= /* @__PURE__ */ new WeakMap();
|
|
20
|
+
}
|
|
21
|
+
function rememberRequestLocals(request, locals) {
|
|
22
|
+
table().set(request, locals);
|
|
23
|
+
}
|
|
24
|
+
function requestLocals(request) {
|
|
25
|
+
return table().get(request);
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { routeMountTarget as a, ROUTE_SCRIPT_TYPE as i, requestLocals as n, HTML_ATTRIBUTES_SLOT as r, rememberRequestLocals as t };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region src/vite/entries/route-href.ts
|
|
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
|
|
18
|
+
export { __marklessRouteHref as t };
|
|
@@ -2,9 +2,9 @@ import { extname, join, normalize, relative } from "pathe";
|
|
|
2
2
|
import { joinURL, withLeadingSlash, withoutLeadingSlash, withoutTrailingSlash } from "ufo";
|
|
3
3
|
//#region src/route-manifest.ts
|
|
4
4
|
const PAGES_DIR = "pages";
|
|
5
|
-
const PAGE_EXTENSIONS = new Set([".tsrx", ".mdx"]);
|
|
5
|
+
const PAGE_EXTENSIONS = /* @__PURE__ */ new Set([".tsrx", ".mdx"]);
|
|
6
6
|
function buildRouteManifestFromFileIds(fileIds) {
|
|
7
|
-
const pages = unique(fileIds.map(normalizeRouteFileId)).filter(isPageModuleFile).toSorted(
|
|
7
|
+
const pages = unique(fileIds.map(normalizeRouteFileId)).filter(isPageModuleFile).toSorted(compareCodeUnits).map(normalizePage);
|
|
8
8
|
const routes = pages.flatMap((page) => page.kind === "route" ? [page.route] : []);
|
|
9
9
|
assertNoRouteConflicts(routes);
|
|
10
10
|
const statusPages = {};
|
|
@@ -114,7 +114,7 @@ function assertNoRouteConflicts(routes) {
|
|
|
114
114
|
}
|
|
115
115
|
for (const [identity, conflictingRoutes] of routesByIdentity) {
|
|
116
116
|
if (conflictingRoutes.length < 2) continue;
|
|
117
|
-
const files = conflictingRoutes.map((route) => route.relativeFile).toSorted(
|
|
117
|
+
const files = conflictingRoutes.map((route) => route.relativeFile).toSorted(compareCodeUnits);
|
|
118
118
|
throw new Error([`Route conflict: ${identity} is defined by both:`, ...files.map((file) => `- ${file}`)].join("\n"));
|
|
119
119
|
}
|
|
120
120
|
}
|
|
@@ -137,9 +137,9 @@ function compareRoutes(left, right) {
|
|
|
137
137
|
if (rightSegment === void 0) return 1;
|
|
138
138
|
const rankDifference = segmentRank(leftSegment) - segmentRank(rightSegment);
|
|
139
139
|
if (rankDifference !== 0) return rankDifference;
|
|
140
|
-
if (segmentRank(leftSegment) === 0 && leftSegment !== rightSegment) return leftSegment
|
|
140
|
+
if (segmentRank(leftSegment) === 0 && leftSegment !== rightSegment) return compareCodeUnits(leftSegment, rightSegment);
|
|
141
141
|
}
|
|
142
|
-
return left.pathname
|
|
142
|
+
return compareCodeUnits(left.pathname, right.pathname);
|
|
143
143
|
}
|
|
144
144
|
function splitRoutePathname(pathname) {
|
|
145
145
|
const route = withoutLeadingSlash(pathname);
|
|
@@ -174,6 +174,9 @@ function segmentRank(segment) {
|
|
|
174
174
|
if (segment.startsWith(":")) return 1;
|
|
175
175
|
return 0;
|
|
176
176
|
}
|
|
177
|
+
function compareCodeUnits(left, right) {
|
|
178
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
179
|
+
}
|
|
177
180
|
function unique(values) {
|
|
178
181
|
return [...new Set(values)];
|
|
179
182
|
}
|
|
@@ -183,4 +186,4 @@ function routePathname(segments) {
|
|
|
183
186
|
return withLeadingSlash(joinURL(base, ...rest));
|
|
184
187
|
}
|
|
185
188
|
//#endregion
|
|
186
|
-
export { normalizeRouteFileId as i,
|
|
189
|
+
export { normalizeRouteFileId as a, normalizeRequestPathname as i, isPageModuleFile as n, matchRouteManifest as r, buildRouteManifestFromFileIds as t };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
let pathe = require("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 = (0, pathe.extname)(fileId);
|
|
167
|
-
const
|
|
168
|
+
const withoutExtension = fileId.slice(0, -extension.length);
|
|
169
|
+
const routeFile = (0, pathe.normalize)(withoutExtension);
|
|
168
170
|
const segments = routeFile === "." ? [] : routeFile.split("/").filter(Boolean);
|
|
169
171
|
return segments[0] === "api" || segments.length === 1 && (segments[0] === "404" || segments[0] === "500");
|
|
170
172
|
}
|
|
171
173
|
function routeFromFileId(fileId) {
|
|
172
174
|
const extension = (0, pathe.extname)(fileId);
|
|
173
|
-
const
|
|
175
|
+
const withoutExtension = fileId.slice(0, -extension.length);
|
|
176
|
+
const routeFile = (0, pathe.normalize)(withoutExtension);
|
|
174
177
|
const segments = routeFile === "." ? [] : routeFile.split("/").filter(Boolean);
|
|
175
178
|
if (segments.at(-1) === "index") segments.pop();
|
|
176
179
|
const params = [];
|