@markless/router 0.4.0 → 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/index.js CHANGED
@@ -1,8 +1,10 @@
1
- import { i as normalizeRouteFileId, n as matchRouteManifest, r as normalizeRequestPathname, t as buildRouteManifestFromFileIds } from "./route-manifest-DPgGIyrS.js";
2
- import { n as parseRequestFile, r as transformRequestFileSource, t as normalizeRequestFileId } from "./request-files-C-0LLSsx.js";
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.body,
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
- state[CURRENT_ROUTE_CONTAINER]?.runtime?.dispose?.();
66
+ outgoing?.runtime?.dispose?.();
63
67
  return true;
64
68
  }
65
69
  });
66
- if (!update.signal?.aborted) state[CURRENT_ROUTE_CONTAINER] = container;
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-CSzo-Zj8.js").then((n) => n.n).then((debug) => debug.__marklessDebugRegisterRouter(void 0, "spa-click-listener")).catch(() => {});
102
- if (typeof __MARKLESS_DEBUG_ENABLED__ !== "undefined" && __MARKLESS_DEBUG_ENABLED__) await import("./debug-channel-CSzo-Zj8.js").then((n) => n.n).then((debug) => debug.__marklessDebugRegisterRouter(void 0, "navigation-event")).catch(() => {});
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 (!runtimeWindow.navigation) {
111
- const { applyPolyfill } = await (loadPolyfill?.() ?? import("@virtualstate/navigation"));
112
- runtimeWindow.navigation = applyPolyfill({
113
- interceptEvents: false,
114
- window: runtimeWindow
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 runtimeWindow.navigation;
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(LINK_ATTRIBUTE) || !isEligibleLink(anchor)) return;
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(REPLACE_ATTRIBUTE) ? "replace" : "push",
192
+ history: anchor.hasAttribute("data-markless-router-replace") ? "replace" : "push",
178
193
  info: {
179
194
  [LINK_INFO]: true,
180
- scroll: anchor.getAttribute(SCROLL_ATTRIBUTE) === "manual" ? "manual" : void 0
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: props.children == null ? "" : String(props.children),
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("data-markless-router-link", "");
289
- if (props.replace) attributes.set("data-markless-router-replace", "");
290
- if (props.scroll === false) attributes.set("data-markless-router-scroll", "manual");
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("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;");
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 rawSegments = normalize(join(apiFile.slice(0, -3))).split("/");
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((left, right) => left.localeCompare(right)).map(normalizePage);
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((left, right) => left.localeCompare(right));
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.localeCompare(rightSegment);
140
+ if (segmentRank(leftSegment) === 0 && leftSegment !== rightSegment) return compareCodeUnits(leftSegment, rightSegment);
141
141
  }
142
- return left.pathname.localeCompare(right.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, matchRouteManifest as n, normalizeRequestPathname as r, buildRouteManifestFromFileIds as t };
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
- return withRouteHrefCompletions(typeScript, info, pagesDir, fileName, position, languageService.getCompletionsAtPosition(fileName, position, options, formattingSettings));
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 routeFile = (0, pathe.normalize)(fileId.slice(0, -extension.length));
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 routeFile = (0, pathe.normalize)(fileId.slice(0, -extension.length));
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 = [];