@qwik.dev/router 2.0.0-beta.40 → 2.0.0-beta.41

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.
@@ -1,6 +1,6 @@
1
1
  import { t as ensureSlash } from "../../../chunks/pathname.mjs";
2
2
  import { viteAdapter } from "../../shared/vite/index.mjs";
3
- import { v as normalizePathSlash } from "../../../chunks/fs.mjs";
3
+ import { y as normalizePathSlash } from "../../../chunks/fs.mjs";
4
4
  import fs from "node:fs";
5
5
  import { join, relative } from "node:path";
6
6
  /** @public */ function cloudflarePagesAdapter(opts = {}) {
@@ -31,21 +31,27 @@ var injectStatics = async (staticPathsCode, outDir) => {
31
31
  await walk(outDir);
32
32
  await Promise.all(promises);
33
33
  };
34
- /** Cleans the client output SSG results if needed and injects the SSG metadata into the build output */ async function postBuild(clientOutDir, serverOutDir, pathName, userStaticPaths, cleanStatic) {
34
+ /** Cleans the client output SSG results if needed and injects the SSG metadata into the build output */ async function postBuild(clientOutDir, serverOutDir, pathName, userStaticPaths, cleanStatic, noTrailingSlash) {
35
35
  if (pathName && !pathName.endsWith("/")) pathName = ensureSlash(pathName);
36
36
  const pathNameBase = pathName || "/";
37
37
  const ignorePathnames = /* @__PURE__ */ new Set([pathNameBase + (globalThis.__QWIK_BUILD_DIR__ || "build") + "/", pathNameBase + (globalThis.__QWIK_ASSETS_DIR__ || "assets") + "/"]);
38
- const staticPaths = new Set(userStaticPaths.map(ensureSlash));
38
+ const getStaticPathname = (pathname) => {
39
+ pathname = ensureSlash(pathname);
40
+ if (noTrailingSlash && pathname !== pathNameBase) return pathname.slice(0, -1);
41
+ return pathname;
42
+ };
43
+ const staticPaths = new Set(userStaticPaths.map(getStaticPathname));
39
44
  const loadItem = async (fsDir, fsName, pathname) => {
40
45
  pathname = ensureSlash(pathname);
41
46
  if (ignorePathnames.has(pathname)) return;
42
47
  const fsPath = join(fsDir, fsName);
43
48
  if (fsName === "index.html") {
44
- if (!staticPaths.has(pathname) && cleanStatic) await fs.promises.unlink(fsPath);
49
+ if (!staticPaths.has(getStaticPathname(pathname)) && cleanStatic) await fs.promises.unlink(fsPath);
45
50
  return;
46
51
  }
47
52
  if (LOADER_REGEX.test("/" + fsName)) {
48
- if (staticPaths.has(pathname)) staticPaths.add(pathname + fsName);
53
+ const staticPathname = getStaticPathname(pathname);
54
+ if (staticPaths.has(staticPathname)) staticPaths.add(ensureSlash(staticPathname) + fsName);
49
55
  else if (cleanStatic) await fs.promises.unlink(fsPath);
50
56
  return;
51
57
  }
@@ -217,6 +223,7 @@ var QWIK_SSG_WORKER_ENTRY_RESOLVED = "\0@qwik-ssg-worker-entry";
217
223
  const basePathname = qwikRouterPlugin.api.getBasePathname();
218
224
  const clientOutDir = qwikVitePlugin.api.getClientOutDir();
219
225
  const clientPublicOutDir = qwikVitePlugin.api.getClientPublicOutDir();
226
+ const noTrailingSlash = builder.config.define?.["globalThis.__NO_TRAILING_SLASH__"] === "true";
220
227
  const ssgEnv = builder.environments.ssg;
221
228
  const ssgOutDir = ssgOutDirFor(builder.config.root);
222
229
  if (ssgEnv && clientOutDir && clientPublicOutDir) {
@@ -248,7 +255,7 @@ var QWIK_SSG_WORKER_ENTRY_RESOLVED = "\0@qwik-ssg-worker-entry";
248
255
  force: true
249
256
  });
250
257
  }
251
- await postBuild(clientPublicOutDir, serverOutDir, basePathname, staticPaths, !!opts.cleanStaticGenerated);
258
+ await postBuild(clientPublicOutDir, serverOutDir, basePathname, staticPaths, !!opts.cleanStaticGenerated, noTrailingSlash);
252
259
  if (typeof opts.generate === "function") await opts.generate({
253
260
  outputEntries,
254
261
  serverOutDir,
package/lib/chunks/fs.mjs CHANGED
@@ -51,6 +51,13 @@ function getMenuPathname(opts, filePath) {
51
51
  const result = normalizePathname(pathname, opts.basePathname);
52
52
  return ensureSlash(result);
53
53
  }
54
+ /** Normalize equivalent filesystem paths for string-key comparisons. */ function normalizePathKey(path) {
55
+ if (path.startsWith("\\\\?\\UNC\\")) path = `\\\\${path.slice(8)}`;
56
+ else if (path.startsWith("\\\\?\\")) path = path.slice(4);
57
+ else if (path.startsWith("//?/UNC/")) path = `//${path.slice(8)}`;
58
+ else if (path.startsWith("//?/")) path = path.slice(4);
59
+ return normalizePath(path).replace(/\\/g, "/");
60
+ }
54
61
  /**
55
62
  * Creates an id for the module, based on its path.
56
63
  *
@@ -130,4 +137,4 @@ function getPathnameFromDirPath(opts, dirPath) {
130
137
  if (pathname === "") return "/";
131
138
  return pathname;
132
139
  }
133
- export { normalizePath as _, getPathnameFromDirPath as a, removeExtension as b, isIndexModule as c, isMenuFileName as d, isModuleExt as f, isServiceWorkerName as g, isPluginModule as h, getMenuPathname as i, isLayoutModule as l, isPageModuleExt as m, errorBoundaryName as n, isEntryName as o, isPageExt as p, getExtension as r, isErrorName as s, createFileId as t, isMarkdownExt as u, normalizePathSlash as v, parseRouteIndexName as y };
140
+ export { normalizePath as _, getPathnameFromDirPath as a, parseRouteIndexName as b, isIndexModule as c, isMenuFileName as d, isModuleExt as f, isServiceWorkerName as g, isPluginModule as h, getMenuPathname as i, isLayoutModule as l, isPageModuleExt as m, errorBoundaryName as n, isEntryName as o, isPageExt as p, getExtension as r, isErrorName as s, createFileId as t, isMarkdownExt as u, normalizePathKey as v, removeExtension as x, normalizePathSlash as y };
@@ -208,24 +208,12 @@ function getRouteLoaderCtx(requestEv) {
208
208
  }
209
209
  return ctx;
210
210
  }
211
- /**
212
- * Update the loader paths store on client-side navigation.
213
- *
214
- * Only adds/updates entries for loaders present on the new route. Entries for loaders that are NOT
215
- * on the new route are left untouched — their ComputedSignals keep their prior values, and no
216
- * track() fires to invalidate them. If the user navigates back to a route where the loader IS
217
- * present, the path updates and the signal re-fetches. This is the "stale is fine by default"
218
- * contract: readers see old data until new data arrives.
219
- */ var updateRouteLoaderPaths = (ctx, loaderPaths, pageUrl) => {
211
+ /** Update route loader matching state on navigation. */ var updateRouteLoaderPaths = (ctx, loaderPaths, pageUrl) => {
220
212
  if (!isServer) {
221
213
  ctx.pagePathname = pageUrl.pathname;
222
214
  ctx.pageSearch = pageUrl.search;
223
- if (isDev) ctx.devRouteLoaderPaths = {};
224
- }
225
- if (loaderPaths) for (const key in loaderPaths) {
226
- ctx.loaderPaths[key] = loaderPaths[key];
227
- if (!isServer && isDev) ctx.devRouteLoaderPaths[key] = true;
228
- }
215
+ ctx.routeLoaderCandidates = loaderPaths;
216
+ } else if (loaderPaths) for (const key in loaderPaths) ctx.loaderPaths[key] = loaderPaths[key];
229
217
  };
230
218
  var getModuleRouteLoaders = (mods) => {
231
219
  const routeLoaders = [];
@@ -262,6 +250,11 @@ var isImmutableLoader = (loaderId) => immutableLoaderIds.has(loaderId);
262
250
  */ var invalidateNavRouteLoaders = (state) => {
263
251
  for (const id in state) if (!id.startsWith(ROUTE_LOADER_VALUE_PREFIX) && !immutableLoaderIds.has(id)) state[id].invalidate();
264
252
  };
253
+ var applyClientRouteLoaderPath = (loaderId, ctx) => {
254
+ const currentPath = ctx.routeLoaderCandidates?.[loaderId];
255
+ if (currentPath) ctx.loaderPaths[loaderId] = currentPath;
256
+ else if (isDev && ctx.pagePathname) ctx.loaderPaths[loaderId] = ctx.pagePathname;
257
+ };
265
258
  /**
266
259
  * Inject a pre-loaded value into a ComputedSignal while preserving subscriptions. Delegates to the
267
260
  * core helper which calls `invalidate({__v})` + `$computeIfNeeded$()` so the compute function runs
@@ -304,7 +297,7 @@ var resolveRouteLoaderByHash = (routeLoaders, loaderId) => {
304
297
  enumerable: true
305
298
  },
306
299
  params: {
307
- value: {},
300
+ value: rootRequestEv.params,
308
301
  enumerable: true
309
302
  },
310
303
  pathname: {
@@ -518,20 +511,14 @@ var createRouteLoaderSignal = (loader, routeLoaderCtx, state) => {
518
511
  return signal;
519
512
  };
520
513
  var ensureRouteLoaderSignal = (loader, state, routeLoaderCtx) => {
514
+ if (!isServer && routeLoaderCtx.pagePathname) applyClientRouteLoaderPath(loader.__id, routeLoaderCtx);
521
515
  if (loader.__cacheControl === "immutable") immutableLoaderIds.add(loader.__id);
522
516
  if (isServer && loader.__serializationStrategy === "never") state[getRouteLoaderValueStateKey(loader.__id)] = _UNINITIALIZED;
523
517
  return state[loader.__id] ||= createRouteLoaderSignal(loader, routeLoaderCtx, state);
524
518
  };
525
519
  var ensureRouteLoaderSignals = (mods, state, routeLoaderCtx) => {
526
520
  const loaders = getModuleRouteLoaders(mods);
527
- for (let i = 0; i < loaders.length; i++) {
528
- const loader = loaders[i];
529
- if (isDev && !isServer) {
530
- const devCtx = routeLoaderCtx;
531
- if (devCtx.pagePathname && !devCtx.devRouteLoaderPaths?.[loader.__id]) devCtx.loaderPaths[loader.__id] = devCtx.pagePathname;
532
- }
533
- ensureRouteLoaderSignal(loader, state, routeLoaderCtx);
534
- }
521
+ for (let i = 0; i < loaders.length; i++) ensureRouteLoaderSignal(loaders[i], state, routeLoaderCtx);
535
522
  return loaders;
536
523
  };
537
524
  var loadRouteLoader = (loader, requestEv) => {
@@ -554,9 +541,7 @@ var loadRouteLoader = (loader, requestEv) => {
554
541
  /** @internal */ var routeLoaderQrl = (loaderQrl, ...rest) => {
555
542
  const { id, validators, serializationStrategy, cacheControl, eTag, cacheKey, search, blockSSR } = getLoaderOptions(rest);
556
543
  function loader() {
557
- const state = _resolveContextWithoutSequentialScope(RouteStateContext);
558
- let signal = state[loader.__id];
559
- if (!signal) signal = ensureRouteLoaderSignal(loader, state, _resolveContextWithoutSequentialScope(RouteLoaderCtxContext));
544
+ const signal = ensureRouteLoaderSignal(loader, _resolveContextWithoutSequentialScope(RouteStateContext), _resolveContextWithoutSequentialScope(RouteLoaderCtxContext));
560
545
  signal.promise();
561
546
  return signal;
562
547
  }
@@ -851,6 +851,19 @@ async function runValidators(requestEv, validators, data, devMode) {
851
851
  requestEv.send(requestEv.status(), serialized);
852
852
  };
853
853
  }
854
+ var staticPaths = /* @__PURE__ */ new Set(["__QWIK_ROUTER_STATIC_PATHS_ARRAY__"]);
855
+ /**
856
+ * Decide whether a given request path is a static path, for optimizing file access.
857
+ *
858
+ * @internal
859
+ */ function isStaticPath(method, url) {
860
+ method = method.toUpperCase();
861
+ if (method !== "GET" && method !== "HEAD") return false;
862
+ const p = url.pathname;
863
+ if (p.startsWith("/" + (globalThis.__QWIK_BUILD_DIR__ || "build") + "/")) return true;
864
+ if (p.startsWith("/" + (globalThis.__QWIK_ASSETS_DIR__ || "assets") + "/")) return true;
865
+ return staticPaths.has(p);
866
+ }
854
867
  /**
855
868
  * Hypertext Transfer Protocol (HTTP) response status codes.
856
869
  *
@@ -1167,7 +1180,7 @@ function createResolveRequestHandlers() {
1167
1180
  if (isPageRoute) {
1168
1181
  requestHandlers.push(loaderHandler(routeLoaders, route.$loaderPaths$));
1169
1182
  requestHandlers.push(actionHandler(routeActions));
1170
- if (!route.$notFound$) requestHandlers.push(fixTrailingSlash);
1183
+ requestHandlers.push(route.$notFound$ ? fixStaticTrailingSlash : fixTrailingSlash);
1171
1184
  }
1172
1185
  if (isPageRoute) {
1173
1186
  requestHandlers.push((ev) => {
@@ -1449,6 +1462,15 @@ function createResolveRequestHandlers() {
1449
1462
  } else if (pathname.endsWith("/")) throw ev.redirect(HttpStatus.MovedPermanently, pathname.slice(0, pathname.length - 1) + search);
1450
1463
  }
1451
1464
  }
1465
+ /**
1466
+ * Prerendered pages are pruned from the production server route plan, so a request missing the
1467
+ * canonical trailing slash lands here as not found. Redirect it when the canonical path was
1468
+ * prerendered, and leave a genuine 404 alone.
1469
+ */ function fixStaticTrailingSlash(ev) {
1470
+ const canonicalUrl = new URL(ev.originalUrl);
1471
+ canonicalUrl.pathname = globalThis.__NO_TRAILING_SLASH__ ? canonicalUrl.pathname.replace(/\/$/, "") : ensureSlash(canonicalUrl.pathname);
1472
+ if (isStaticPath(ev.request.method, canonicalUrl)) fixTrailingSlash(ev);
1473
+ }
1452
1474
  function verifySerializable(data, qrl) {
1453
1475
  try {
1454
1476
  _verifySerializable(data, void 0);
@@ -1567,6 +1589,7 @@ The request origin "${inputOrigin}" does not match the server origin "${origin}"
1567
1589
  actionsMiddleware,
1568
1590
  checkBrand,
1569
1591
  checkCSRF,
1592
+ fixStaticTrailingSlash,
1570
1593
  fixTrailingSlash,
1571
1594
  getPathname,
1572
1595
  isLastModulePageRoute,
@@ -1579,7 +1602,7 @@ The request origin "${inputOrigin}" does not match the server origin "${origin}"
1579
1602
  verifySerializable
1580
1603
  };
1581
1604
  }
1582
- var { actionsMiddleware, checkBrand, checkCSRF, fixTrailingSlash, getPathname, isLastModulePageRoute, isQrl, loadersMiddleware, measure, renderQwikMiddleware, resolveRequestHandlers, streamServerFunctionResult, verifySerializable } = createResolveRequestHandlers();
1605
+ var { actionsMiddleware, checkBrand, checkCSRF, fixStaticTrailingSlash, fixTrailingSlash, getPathname, isLastModulePageRoute, isQrl, loadersMiddleware, measure, renderQwikMiddleware, resolveRequestHandlers, streamServerFunctionResult, verifySerializable } = createResolveRequestHandlers();
1583
1606
  /** @public */ var ESCAPE_HTML = /[&<>]/g;
1584
1607
  var escapeHtml = (s) => {
1585
1608
  return s.replace(ESCAPE_HTML, (c) => {
@@ -1704,4 +1727,4 @@ function runQwikRouter(serverRequestEv, loadedRoute, requestHandlers, rebuildRou
1704
1727
  };
1705
1728
  }
1706
1729
  }
1707
- export { clearLoaderCache as a, mergeHeadersCookies as c, resolveRequestHandlers as i, getErrorHtml as n, clearSsrCache as o, renderQwikMiddleware as r, RewriteMessage as s, runQwikRouter as t };
1730
+ export { isStaticPath as a, RewriteMessage as c, resolveRequestHandlers as i, mergeHeadersCookies as l, getErrorHtml as n, clearLoaderCache as o, renderQwikMiddleware as r, clearSsrCache as s, runQwikRouter as t };
package/lib/index.d.ts CHANGED
@@ -1062,6 +1062,7 @@ declare type RouteLoaderCtx = {
1062
1062
  goto?: NoSerialize<RouteNavigate>;
1063
1063
  /** Client manifest hash for q-loader fetch URLs. */
1064
1064
  manifestHash?: string;
1065
+ routeLoaderCandidates?: Record<string, string>;
1065
1066
  };
1066
1067
 
1067
1068
  /* Excluded from this release type: routeLoaderQrl */
@@ -1316,16 +1317,16 @@ export declare const zod$: ZodConstructor;
1316
1317
  export declare type ZodConstructor = {
1317
1318
  <T extends z_2.ZodRawShape>(schema: T): ZodDataValidator<z_2.ZodObject<T>>;
1318
1319
  <T extends z_2.ZodRawShape>(schema: (zod: typeof z_2.z, ev: RequestEvent) => T): ZodDataValidator<z_2.ZodObject<T>>;
1319
- <T extends z_2.Schema>(schema: T): ZodDataValidator<T>;
1320
- <T extends z_2.Schema>(schema: (zod: typeof z_2.z, ev: RequestEvent) => T): ZodDataValidator<T>;
1320
+ <T extends z_2.ZodType>(schema: T): ZodDataValidator<T>;
1321
+ <T extends z_2.ZodType>(schema: (zod: typeof z_2.z, ev: RequestEvent) => T): ZodDataValidator<T>;
1321
1322
  };
1322
1323
 
1323
1324
  /** @public */
1324
1325
  declare type ZodConstructorQRL = {
1325
1326
  <T extends z_2.ZodRawShape>(schema: QRL<T>): ZodDataValidator<z_2.ZodObject<T>>;
1326
1327
  <T extends z_2.ZodRawShape>(schema: QRL<(zod: typeof z_2.z, ev: RequestEvent) => T>): ZodDataValidator<z_2.ZodObject<T>>;
1327
- <T extends z_2.Schema>(schema: QRL<T>): ZodDataValidator<T>;
1328
- <T extends z_2.Schema>(schema: QRL<(zod: typeof z_2.z, ev: RequestEvent) => T>): ZodDataValidator<T>;
1328
+ <T extends z_2.ZodType>(schema: QRL<T>): ZodDataValidator<T>;
1329
+ <T extends z_2.ZodType>(schema: QRL<(zod: typeof z_2.z, ev: RequestEvent) => T>): ZodDataValidator<T>;
1329
1330
  };
1330
1331
 
1331
1332
  /** @public */
@@ -1,5 +1,5 @@
1
1
  import { t as ensureSlash } from "./chunks/pathname.mjs";
2
- import { A as routeLoader$, B as RouteLoaderCtxContext, E as isImmutableLoader, F as ContentContext, G as QACTION_KEY, H as RouteNavigateContext, I as ContentInternalContext, J as Q_ROUTE, K as QDATA_KEY, L as DocumentHeadContext, M as setLoaderSignalValue, P as updateRouteLoaderPaths, R as HttpStatusContext, T as invalidateNavRouteLoaders, U as RoutePreventNavigateContext, V as RouteLocationContext, W as RouteStateContext, _ as fetchRouteLoaderData, b as getRequestEvent, c as shouldPreload, d as loadRoute, g as ensureRouteLoaderSignals, i as getClientNavPath, j as routeLoaderQrl, l as toPath, m as clearNavFetchCache, n as resolveHead, o as isSameOrigin, q as QFN_KEY, s as isSamePath, t as createDocumentHead, u as toUrl, y as getModuleRouteLoaders, z as RouteActionContext } from "./chunks/head.qwik.mjs";
2
+ import { A as routeLoader$, B as RouteLoaderCtxContext, E as isImmutableLoader, F as ContentContext, G as QACTION_KEY, H as RouteNavigateContext, I as ContentInternalContext, J as Q_ROUTE, K as QDATA_KEY, L as DocumentHeadContext, M as setLoaderSignalValue, P as updateRouteLoaderPaths, R as HttpStatusContext, T as invalidateNavRouteLoaders, U as RoutePreventNavigateContext, V as RouteLocationContext, W as RouteStateContext, _ as fetchRouteLoaderData, a as isPromise, b as getRequestEvent, c as shouldPreload, d as loadRoute, g as ensureRouteLoaderSignals, i as getClientNavPath, j as routeLoaderQrl, l as toPath, m as clearNavFetchCache, n as resolveHead, o as isSameOrigin, q as QFN_KEY, s as isSamePath, t as createDocumentHead, u as toUrl, y as getModuleRouteLoaders, z as RouteActionContext } from "./chunks/head.qwik.mjs";
3
3
  import { SkipRender, Slot, _captures, _fnSignal, _getConstProps, _getVarProps, _jsxSorted, _jsxSplit, _qrlSync, _restProps, _wrapProp, componentQrl, createComputedQrl, createElement, eventQrl, getLocale, implicit$FirstArg, inlinedQrl, isBrowser, isDev, isServer, noSerialize, untrack, useContext, useContextProvider, useErrorBoundary, useOnWindow, useServerData, useSignal, useStore, useTaskQrl, useVisibleTaskQrl, withLocale } from "@qwik.dev/core";
4
4
  import { Fragment } from "@qwik.dev/core/jsx-runtime";
5
5
  import * as qwikRouterConfig from "@qwik-router-config";
@@ -1026,7 +1026,14 @@ function initializeSPA(goto, scroller) {
1026
1026
  const contentModules = track(contentInternal);
1027
1027
  if (!contentModules) return;
1028
1028
  const actionData = track(actionDataSignal);
1029
- const head = track(() => resolveHead(actionData, loaderState, routeLocation, contentModules, getLocale(""), serverHead));
1029
+ let head;
1030
+ try {
1031
+ head = track(() => resolveHead(actionData, loaderState, routeLocation, contentModules, getLocale(""), serverHead));
1032
+ } catch (error) {
1033
+ if (isServer || isPromise(error)) throw error;
1034
+ console.error(error);
1035
+ return;
1036
+ }
1030
1037
  documentHead.links = head.links;
1031
1038
  documentHead.meta = head.meta;
1032
1039
  documentHead.styles = head.styles;
@@ -1220,7 +1227,7 @@ var flattenZodIssues = (issues) => {
1220
1227
  async validate(ev, inputData) {
1221
1228
  const schema = await qrl.resolve().then((obj) => {
1222
1229
  if (typeof obj === "function") obj = obj(z$1, ev);
1223
- if (obj instanceof z$1.Schema) return obj;
1230
+ if (obj instanceof z$1.ZodType) return obj;
1224
1231
  else return z$1.object(obj);
1225
1232
  });
1226
1233
  const data = inputData ?? await ev.parseBody();
@@ -28,11 +28,16 @@ import { _TextEncoderStream_polyfill, getErrorHtml, isStaticPath, mergeHeadersCo
28
28
  return env[key];
29
29
  } },
30
30
  getWritableStream: (status, headers, cookies, resolve) => {
31
- const { readable, writable } = new TransformStream();
32
- resolve(new Response(readable, {
31
+ const responseInit = {
33
32
  status,
34
33
  headers: mergeHeadersCookies(headers, cookies)
35
- }));
34
+ };
35
+ if (status === 204 || status === 205 || status === 304) {
36
+ resolve(new Response(null, responseInit));
37
+ return new WritableStream();
38
+ }
39
+ const { readable, writable } = new TransformStream();
40
+ resolve(new Response(readable, responseInit));
36
41
  return writable;
37
42
  },
38
43
  getClientConn: () => {
@@ -2,7 +2,7 @@ import { Y as _asyncRequestStore, d as loadRoute } from "../../chunks/head.qwik.
2
2
  import { c as resolveValidInternalFullPathname, l as trimInternalPathname, s as recognizeRequest, u as trimRecognizedInternalPathname } from "../../chunks/request-path.mjs";
3
3
  import { i as RedirectMessage, r as AbortMessage, t as ServerError } from "../../chunks/server-error.mjs";
4
4
  import { t as devPreloadedRouteLoaders } from "../../chunks/dev-preloaded-route-loader.mjs";
5
- import { a as clearLoaderCache, c as mergeHeadersCookies, i as resolveRequestHandlers, n as getErrorHtml, o as clearSsrCache, r as renderQwikMiddleware, s as RewriteMessage, t as runQwikRouter } from "../../chunks/user-response.mjs";
5
+ import { a as isStaticPath, c as RewriteMessage, i as resolveRequestHandlers, l as mergeHeadersCookies, n as getErrorHtml, o as clearLoaderCache, r as renderQwikMiddleware, s as clearSsrCache, t as runQwikRouter } from "../../chunks/user-response.mjs";
6
6
  import { isDev } from "@qwik.dev/core";
7
7
  var qwikRouterConfig;
8
8
  async function getConfig() {
@@ -46,18 +46,6 @@ async function loadRequestHandlers(qwikRouterConfig, pathname, method, checkOrig
46
46
  };
47
47
  return runQwikRouter(serverRequestEv, loadedRoute, requestHandlers, rebuildRouteInfo, config.basePathname);
48
48
  }
49
- var staticPaths = /* @__PURE__ */ new Set(["__QWIK_ROUTER_STATIC_PATHS_ARRAY__"]);
50
- /**
51
- * Decide whether a given request path is a static path, for optimizing file access.
52
- *
53
- * @internal
54
- */ function isStaticPath(method, url) {
55
- if (method.toUpperCase() !== "GET") return false;
56
- const p = url.pathname;
57
- if (p.startsWith("/" + (globalThis.__QWIK_BUILD_DIR__ || "build") + "/")) return true;
58
- if (p.startsWith("/" + (globalThis.__QWIK_ASSETS_DIR__ || "assets") + "/")) return true;
59
- return staticPaths.has(p);
60
- }
61
49
  /**
62
50
  * TextEncoderStream polyfill based on Node.js' implementation
63
51
  * https://github.com/nodejs/node/blob/3f3226c8e363a5f06c1e6a37abd59b6b8c1923f1/lib/internal/webstreams/encoding.js#L38-L119
@@ -1,14 +1,14 @@
1
1
  import { r as isSameOriginUrl, t as ensureSlash } from "../chunks/pathname.mjs";
2
2
  import { c as resolveValidInternalFullPathname, s as recognizeRequest, u as trimRecognizedInternalPathname } from "../chunks/request-path.mjs";
3
3
  import { n as addWarning, t as addError } from "../chunks/format.mjs";
4
- import { _ as normalizePath$1, a as getPathnameFromDirPath, b as removeExtension, c as isIndexModule, d as isMenuFileName, f as isModuleExt, g as isServiceWorkerName, h as isPluginModule, i as getMenuPathname, l as isLayoutModule, m as isPageModuleExt, n as errorBoundaryName, o as isEntryName, p as isPageExt, r as getExtension, s as isErrorName, t as createFileId, u as isMarkdownExt, y as parseRouteIndexName } from "../chunks/fs.mjs";
4
+ import { _ as normalizePath$1, a as getPathnameFromDirPath, b as parseRouteIndexName, c as isIndexModule, d as isMenuFileName, f as isModuleExt, g as isServiceWorkerName, h as isPluginModule, i as getMenuPathname, l as isLayoutModule, m as isPageModuleExt, n as errorBoundaryName, o as isEntryName, p as isPageExt, r as getExtension, s as isErrorName, t as createFileId, u as isMarkdownExt, v as normalizePathKey, x as removeExtension } from "../chunks/fs.mjs";
5
5
  import { t as devPreloadedRouteLoaders } from "../chunks/dev-preloaded-route-loader.mjs";
6
6
  import { t as createRouteTester } from "../chunks/routes.mjs";
7
7
  import fs, { existsSync } from "node:fs";
8
8
  import path, { basename, dirname, extname, isAbsolute, join, resolve } from "node:path";
9
9
  import { readFile } from "node:fs/promises";
10
10
  import { loadEnv, mergeConfig, normalizePath } from "vite";
11
- import { findExports, findStaticImports } from "mlly";
11
+ import { findExports, findStaticImports, parseStaticImport } from "mlly";
12
12
  import { marked } from "marked";
13
13
  import { createHash } from "node:crypto";
14
14
  import { SourceMapGenerator } from "source-map";
@@ -1093,7 +1093,7 @@ function getClientRouteBundleNames(qwikPlugin, r) {
1093
1093
  if (loadersByFile) {
1094
1094
  const nodeLoaderHashes = [];
1095
1095
  for (const filePath of routeLoaderFiles) {
1096
- const hashes = loadersByFile.get(filePath);
1096
+ const hashes = loadersByFile.get(normalizePathKey(filePath));
1097
1097
  if (hashes) nodeLoaderHashes.push(...hashes);
1098
1098
  }
1099
1099
  if (nodeLoaderHashes.length > 0) lines.push(`${nextIndent}_R: ${JSON.stringify(nodeLoaderHashes)},`);
@@ -2083,10 +2083,10 @@ var QWIK_ROUTER_SW_REGISTER = "@qwik-router-sw-register";
2083
2083
  var VIRTUAL_SERVER_FNS = "virtual:qwik-router-server-fns";
2084
2084
  /** Replace or strip `_R: "__LOADERS:path1|path2__"` placeholders in a bundle chunk. */ function replaceLoaderPlaceholders(code, loadersByFile) {
2085
2085
  return code.replace(/_R\s*:\s*(["'`])__LOADERS:([^"'`]+)__\1\s*,?/g, (_match, _q, paths) => {
2086
- const filePaths = paths.split("|");
2086
+ const filePaths = JSON.parse(`"${paths}"`).split("|");
2087
2087
  const hashes = [];
2088
2088
  for (const filePath of filePaths) {
2089
- const fileHashes = loadersByFile.get(filePath);
2089
+ const fileHashes = loadersByFile.get(normalizePathKey(filePath));
2090
2090
  if (fileHashes) hashes.push(...fileHashes);
2091
2091
  }
2092
2092
  if (hashes.length > 0) return `_R: ${JSON.stringify(hashes)},`;
@@ -2094,7 +2094,7 @@ var VIRTUAL_SERVER_FNS = "virtual:qwik-router-server-fns";
2094
2094
  });
2095
2095
  }
2096
2096
  function addRouteLoaderHash(loadersByFile, filePath, hash) {
2097
- const normalizedPath = normalizePath$1(filePath);
2097
+ const normalizedPath = normalizePathKey(filePath).split(/[?#]/, 1)[0];
2098
2098
  const existing = loadersByFile.get(normalizedPath);
2099
2099
  if (!existing) {
2100
2100
  loadersByFile.set(normalizedPath, [hash]);
@@ -2107,7 +2107,7 @@ function addRouteLoaderHash(loadersByFile, filePath, hash) {
2107
2107
  return false;
2108
2108
  }
2109
2109
  function clearRouteLoaderHashes(loadersByFile, filePath) {
2110
- return loadersByFile.delete(normalizePath$1(filePath));
2110
+ return loadersByFile.delete(normalizePathKey(filePath).split(/[?#]/, 1)[0]);
2111
2111
  }
2112
2112
  function isRouterSourceFilePath(filePath) {
2113
2113
  const fileName = basename(filePath);
@@ -2131,7 +2131,7 @@ function collectRouteLoaderOwners(node, owners) {
2131
2131
  for (const file of node._files) if (file.type === "layout" || file.type === "route") owners.add(file.filePath);
2132
2132
  for (const child of node.children.values()) collectRouteLoaderOwners(child, owners);
2133
2133
  }
2134
- async function collectReExportSources(owner, filePath, resolveSource, sources, seen = /* @__PURE__ */ new Set([owner])) {
2134
+ async function collectRouteLoaderSources(owner, filePath, resolveSource, sources, seen = /* @__PURE__ */ new Set([owner])) {
2135
2135
  let code, exportEntries;
2136
2136
  try {
2137
2137
  code = await readFile(filePath, "utf-8");
@@ -2140,26 +2140,38 @@ async function collectReExportSources(owner, filePath, resolveSource, sources, s
2140
2140
  } catch {
2141
2141
  return;
2142
2142
  }
2143
- await Promise.all(exportEntries.map(async (exp) => {
2144
- if (!exp.specifier) return;
2145
- const resolved = await resolveSource(exp.specifier, filePath);
2146
- if (!resolved || resolved.charCodeAt(0) === 0) return;
2143
+ const importedFactories = /* @__PURE__ */ new Map();
2144
+ for (const entry of findStaticImports(code)) {
2145
+ const parsed = parseStaticImport(entry);
2146
+ if (parsed.defaultImport) importedFactories.set(parsed.defaultImport, entry.specifier);
2147
+ if (parsed.namedImports) for (const localName of Object.values(parsed.namedImports)) importedFactories.set(localName, entry.specifier);
2148
+ }
2149
+ const specifiers = /* @__PURE__ */ new Set();
2150
+ for (const exp of exportEntries) if (exp.specifier) specifiers.add(exp.specifier);
2151
+ else if (/^export\s+(?:const|let|var)\s*{/.test(exp.code)) {
2152
+ const factoryName = code.slice(exp.end).match(/^\s*([\w$]+)(?:\s*<[^;()]*>)?\s*\(/)?.[1];
2153
+ const specifier = factoryName && importedFactories.get(factoryName);
2154
+ if (specifier) specifiers.add(specifier);
2155
+ }
2156
+ for (const specifier of specifiers) {
2157
+ const resolved = await resolveSource(specifier, filePath);
2158
+ if (!resolved || resolved.charCodeAt(0) === 0) continue;
2147
2159
  const resolvedPath = normalizePath$1(resolved.split(/[?#]/, 1)[0]);
2148
2160
  let ownerSources = sources.get(owner);
2149
2161
  if (!ownerSources) sources.set(owner, ownerSources = []);
2150
2162
  if (!ownerSources.includes(resolvedPath)) ownerSources.push(resolvedPath);
2151
2163
  if (!seen.has(resolvedPath)) {
2152
2164
  seen.add(resolvedPath);
2153
- await collectReExportSources(owner, resolvedPath, resolveSource, sources, seen);
2165
+ await collectRouteLoaderSources(owner, resolvedPath, resolveSource, sources, seen);
2154
2166
  }
2155
- }));
2167
+ }
2156
2168
  }
2157
2169
  async function findRouteLoaderSourceFiles(ctx, resolveSource) {
2158
2170
  const sources = /* @__PURE__ */ new Map();
2159
2171
  const owners = /* @__PURE__ */ new Set();
2160
2172
  collectRouteLoaderOwners(ctx.routeTrie, owners);
2161
2173
  for (const plugin of ctx.serverPlugins) owners.add(plugin.filePath);
2162
- await Promise.all([...owners].map((owner) => collectReExportSources(owner, owner, resolveSource, sources)));
2174
+ await Promise.all([...owners].map((owner) => collectRouteLoaderSources(owner, owner, resolveSource, sources)));
2163
2175
  return sources;
2164
2176
  }
2165
2177
  function invalidateRouterConfigModules(server) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@qwik.dev/router",
3
3
  "description": "The router for Qwik.",
4
- "version": "2.0.0-beta.40",
4
+ "version": "2.0.0-beta.41",
5
5
  "bugs": "https://github.com/QwikDev/qwik/issues",
6
6
  "dependencies": {
7
7
  "@azure/functions": "3.5.1",
@@ -41,7 +41,7 @@
41
41
  "typescript": "5.9.3",
42
42
  "uvu": "0.5.6",
43
43
  "vite": "8.2.1",
44
- "@qwik.dev/core": "2.0.0-beta.40"
44
+ "@qwik.dev/core": "2.0.0-beta.41"
45
45
  },
46
46
  "engines": {
47
47
  "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
@@ -147,10 +147,6 @@
147
147
  "types": "./lib/vite/index.d.ts",
148
148
  "import": "./lib/vite/index.mjs"
149
149
  },
150
- "./service-worker": {
151
- "types": "./service-worker.d.ts",
152
- "import": "./lib/service-worker.mjs"
153
- },
154
150
  "./package.json": "./package.json"
155
151
  },
156
152
  "files": [
@@ -170,7 +166,7 @@
170
166
  "main": "./lib/index.qwik.mjs",
171
167
  "peerDependencies": {
172
168
  "vite": ">=8 <9",
173
- "@qwik.dev/core": "^2.0.0-beta.40"
169
+ "@qwik.dev/core": "2.0.0-beta.41"
174
170
  },
175
171
  "publishConfig": {
176
172
  "access": "public"