@qwik.dev/router 2.0.0-beta.45 → 2.0.0-beta.47

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,7 +1,8 @@
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
+ import { t as escapeStaticTrieKey } from "../chunks/route-trie-key.mjs";
3
4
  import { n as addWarning, t as addError } from "../chunks/format.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
+ 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 createFileId, o as isEntryName, p as isPageExt, r as getExtension, s as isErrorName, t as caughtErrorName, u as isMarkdownExt, v as normalizePathKey, x as removeExtension } from "../chunks/fs.mjs";
5
6
  import { t as devPreloadedRouteLoaders } from "../chunks/dev-preloaded-route-loader.mjs";
6
7
  import { t as createRouteTester } from "../chunks/routes.mjs";
7
8
  import fs, { existsSync } from "node:fs";
@@ -31,7 +32,6 @@ function extendConfig(baseConfigExport, serverConfigExport) {
31
32
  return mergeConfig(resolvedBase, resolvedServer);
32
33
  };
33
34
  }
34
- var sw_register_build_default = "export default\"\"";
35
35
  function getSourceFile(fileName) {
36
36
  const ext = getExtension(fileName);
37
37
  const extlessName = removeExtension(fileName);
@@ -220,7 +220,7 @@ function resolveRoute(opts, appLayouts, sourceFile) {
220
220
  const routesDir = opts.routesDir;
221
221
  const { layoutName, layoutStop } = parseRouteIndexName(sourceFile.extlessName);
222
222
  let pathname = getPathnameFromDirPath(opts, sourceFile.dirPath);
223
- const boundary = errorBoundaryName(sourceFile.extlessName);
223
+ const boundary = caughtErrorName(sourceFile.extlessName);
224
224
  if (boundary) pathname += boundary + ".html";
225
225
  if (!layoutStop) {
226
226
  let currentDir = normalizePath$1(dirname(filePath));
@@ -978,6 +978,14 @@ function getImportPath(importPath) {
978
978
  function isGroupKey(key) {
979
979
  return key.charCodeAt(0) === 40;
980
980
  }
981
+ /** The runtime trie key for a build trie key; `_W`/`_A` are the only non-static ones. */
982
+ function toRuntimeTrieKey(key) {
983
+ return key === "_W" || key === "_A" ? key : escapeStaticTrieKey(key);
984
+ }
985
+ /** A rewrite target or base path with each segment converted to its runtime trie key. */
986
+ function toRuntimeTriePath(path) {
987
+ return path.split("/").map(toRuntimeTrieKey).join("/");
988
+ }
981
989
  /**
982
990
  * Serialize the build trie into JS code.
983
991
  *
@@ -1053,7 +1061,7 @@ function createRoutes(ctx, qwikPlugin, c, esmImports, isSSR, loadersByFile, serv
1053
1061
  const trieStr = serializeBuildTrie(ctx, qwikPlugin, ctx.routeTrie, layoutIdMap, routeIdMap, menuIdMap, isSSR, "", loadersByFile, routeLoaderSourceFiles);
1054
1062
  const baseSegments = ctx.opts.basePathname.split("/").filter((s) => s.length > 0);
1055
1063
  let routesExpr = trieStr;
1056
- for (let j = baseSegments.length - 1; j >= 0; j--) routesExpr = `{ ${JSON.stringify(baseSegments[j])}: ${routesExpr} }`;
1064
+ for (let j = baseSegments.length - 1; j >= 0; j--) routesExpr = `{ ${JSON.stringify(escapeStaticTrieKey(baseSegments[j]))}: ${routesExpr} }`;
1057
1065
  c.push(`export const routes = ${routesExpr};`);
1058
1066
  }
1059
1067
  function collectFiles(node, cb) {
@@ -1067,7 +1075,10 @@ function serializeBuildTrie(ctx, qwikPlugin, node, layoutIdMap, routeIdMap, menu
1067
1075
  if (node._P) lines.push(`${nextIndent}_P: ${JSON.stringify(node._P)},`);
1068
1076
  if (node._0) lines.push(`${nextIndent}_0: ${JSON.stringify(node._0)},`);
1069
1077
  if (node._9) lines.push(`${nextIndent}_9: ${JSON.stringify(node._9)},`);
1070
- if (node._G != null) lines.push(`${nextIndent}_G: ${JSON.stringify(ctx.opts.basePathname + node._G)},`);
1078
+ if (node._G != null) {
1079
+ const target = toRuntimeTriePath(ctx.opts.basePathname + node._G);
1080
+ lines.push(`${nextIndent}_G: ${JSON.stringify(target)},`);
1081
+ }
1071
1082
  let layoutExpr;
1072
1083
  let indexExpr;
1073
1084
  let errorExpr;
@@ -1090,7 +1101,7 @@ function serializeBuildTrie(ctx, qwikPlugin, node, layoutIdMap, routeIdMap, menu
1090
1101
  if (!loaderExpr) continue;
1091
1102
  const route = ctx.routes.find((route) => route.filePath === file.filePath);
1092
1103
  const expr = buildLoaderChainExpr(file.extlessName, loaderExpr, route?.layouts ?? []);
1093
- const boundary = errorBoundaryName(file.extlessName);
1104
+ const boundary = caughtErrorName(file.extlessName);
1094
1105
  if (boundary === "404") notFoundExpr = expr;
1095
1106
  else if (boundary === "error") errorExpr = expr;
1096
1107
  else {
@@ -1143,7 +1154,7 @@ function serializeBuildTrie(ctx, qwikPlugin, node, layoutIdMap, routeIdMap, menu
1143
1154
  for (const [key, child] of regularChildren) {
1144
1155
  const childStr = serializeBuildTrie(ctx, qwikPlugin, child, layoutIdMap, routeIdMap, menuIdMap, isSSR, nextIndent, loadersByFile, routeLoaderSourceFiles);
1145
1156
  if (childStr !== "{}") {
1146
- const keyStr = JSON.stringify(key);
1157
+ const keyStr = JSON.stringify(toRuntimeTrieKey(key));
1147
1158
  lines.push(`${nextIndent}${keyStr}: ${childStr},`);
1148
1159
  }
1149
1160
  }
@@ -1193,38 +1204,44 @@ function createServerPlugins(ctx, _qwikPlugin, c, esmImports, isSSR) {
1193
1204
  }
1194
1205
  c.push(`];`);
1195
1206
  }
1207
+ /** The URL of the app's service worker entry, when the routes define one. */
1208
+ function resolveServiceWorkerUrl(ctx) {
1209
+ if (ctx.serviceWorkers.length === 0) return;
1210
+ const sw = ctx.serviceWorkers.sort((a, b) => a.chunkFileName.length < b.chunkFileName.length ? -1 : 1)[0];
1211
+ return ctx.opts.basePathname + sw.chunkFileName;
1212
+ }
1213
+ /** The `@qwik-router-sw-register` module, kept for apps that import it themselves. */
1214
+ function generateServiceWorkerRegister(ctx) {
1215
+ return `import { _getServiceWorkerScript } from '@qwik.dev/router';\nexport default _getServiceWorkerScript(${JSON.stringify(resolveServiceWorkerUrl(ctx))});\n`;
1216
+ }
1196
1217
  /** Generates the Qwik Router Config runtime code */
1197
1218
  function generateQwikRouterConfig(ctx, qwikPlugin, isSSR, loadersByFile, serverExcludePaths, routeLoaderSourceFiles) {
1198
1219
  const esmImports = [];
1199
1220
  const c = [];
1200
1221
  c.push(`\n/** Qwik Router Config */`);
1201
1222
  c.push(`\nimport { isDev } from '@qwik.dev/core/build';`);
1202
- if (isSSR) esmImports.push(`import { importEagerModules } from 'virtual:qwik-router-server-fns';`, `export { importEagerModules } from 'virtual:qwik-router-server-fns';`);
1223
+ if (isSSR) esmImports.push(`import { importEagerModules } from 'virtual:qwik-router-server-fns';`, `export { importEagerModules } from 'virtual:qwik-router-server-fns';`, `import { _setRouterConfig } from '@qwik.dev/router';`);
1203
1224
  createServerPlugins(ctx, qwikPlugin, c, esmImports, isSSR);
1204
1225
  createRoutes(ctx, qwikPlugin, c, esmImports, isSSR, loadersByFile, serverExcludePaths, routeLoaderSourceFiles);
1205
1226
  createEntries(ctx, c);
1206
- c.push(`export const trailingSlash = ${JSON.stringify(!globalThis.__NO_TRAILING_SLASH__)};`);
1227
+ c.push(`export const trailingSlash = !globalThis.__NO_TRAILING_SLASH__;`);
1207
1228
  c.push(`export const basePathname = ${JSON.stringify(ctx.opts.basePathname)};`);
1208
1229
  c.push(`export const cacheModules = !isDev;`);
1209
- if (isSSR) c.push(`export default { routes, serverPlugins, trailingSlash, basePathname, cacheModules, importEagerModules };`);
1210
- else c.push(`export default { routes, serverPlugins, trailingSlash, basePathname, cacheModules };`);
1230
+ c.push(`export const serviceWorkerUrl = ${JSON.stringify(resolveServiceWorkerUrl(ctx))};`);
1231
+ const fields = [
1232
+ "routes",
1233
+ "serverPlugins",
1234
+ "trailingSlash",
1235
+ "basePathname",
1236
+ "cacheModules",
1237
+ "serviceWorkerUrl"
1238
+ ];
1239
+ if (isSSR) fields.push("importEagerModules");
1240
+ c.push(`const config = { ${fields.join(", ")} };`);
1241
+ c.push(`export default config;`);
1242
+ if (isSSR) c.push(`_setRouterConfig(config);`);
1211
1243
  return esmImports.join("\n") + c.join("\n");
1212
1244
  }
1213
- function generateServiceWorkerRegister(ctx, swRegister) {
1214
- let swReg;
1215
- let swUrl = "/service-worker.js";
1216
- if (ctx.serviceWorkers.length === 0) swReg = SW_UNREGISTER;
1217
- else {
1218
- swReg = swRegister;
1219
- const sw = ctx.serviceWorkers.sort((a, b) => a.chunkFileName.length < b.chunkFileName.length ? -1 : 1)[0];
1220
- swUrl = ctx.opts.basePathname + sw.chunkFileName;
1221
- }
1222
- swReg = swReg.replace("__url", swUrl);
1223
- return `export default ${JSON.stringify(swReg)};`;
1224
- }
1225
- var SW_UNREGISTER = `
1226
- "serviceWorker"in navigator&&navigator.serviceWorker.getRegistrations().then(r=>{for(const e of r){const c='__url'.split("/").pop();e.active?.scriptURL.endsWith(c||"service-worker.js")&&e.unregister().catch(console.error)}}),"caches"in window&&caches.keys().then(r=>{const e=r.find(c=>c.startsWith("QwikBuild"));e&&caches.delete(e).catch(console.error)}).catch(console.error)
1227
- `;
1228
1245
  /**
1229
1246
  * Non-GET RouteModule handlers keep a route server-side (onGet is served statically). Typed against
1230
1247
  * keyof RouteModule so a renamed/added handler fails to compile until classified.
@@ -1933,7 +1950,6 @@ var makeRouterDevMiddleware = (server, ctx) => async (req, res, next) => {
1933
1950
  res.end("/* Qwik Router Dev Service Worker */self.addEventListener('install', () => self.skipWaiting());self.addEventListener('activate', (ev) => ev.waitUntil(self.clients.claim()));");
1934
1951
  return;
1935
1952
  }
1936
- globalThis.__qwik = void 0;
1937
1953
  const { createQwikRouter } = await server.ssrLoadModule("@qwik.dev/router/middleware/node");
1938
1954
  try {
1939
1955
  const render = (async (opts) => {
@@ -2004,6 +2020,14 @@ var CSS_EXTENSIONS = [
2004
2020
  var JS_EXTENSIONS = /\.[mc]?[tj]sx?$/;
2005
2021
  var isCssPath = (url) => CSS_EXTENSIONS.some((ext) => url.endsWith(ext));
2006
2022
  /**
2023
+ * The URL the Vite dev server serves a module under. For a file, the module graph URL is already
2024
+ * one (`/src/a.css`, `/@fs/...`). A virtual module from a plugin has its id as URL, and Vite serves
2025
+ * it under `/@id/` with the `\0` written as `__x00__`, the same as Vite's own `wrapId`.
2026
+ *
2027
+ * `\0virtual:theme.css` -> `/@id/__x00__virtual:theme.css`
2028
+ */
2029
+ var toDevServerUrl = (url) => url.startsWith("/") ? url : `/@id/${url.replace("\0", "__x00__")}`;
2030
+ /**
2007
2031
  * Qwik handles CSS imports itself, meaning vite doesn't get to see them, so we need to manually
2008
2032
  * inject the CSS URLs.
2009
2033
  *
@@ -2031,8 +2055,8 @@ var getCssUrls = (server) => {
2031
2055
  return importerPath && JS_EXTENSIONS.test(importerPath);
2032
2056
  });
2033
2057
  if ((isEntryCSS || hasJSImporter) && !hasCSSImporter && !cssImportedByCSS.has(mod.url)) {
2034
- cssModules.add(`${mod.url}${mod.lastHMRTimestamp ? `?t=${mod.lastHMRTimestamp}` : ""}`);
2035
- if (mod.file) server.watcher.add(mod.file);
2058
+ cssModules.add(`${toDevServerUrl(mod.url)}${mod.lastHMRTimestamp ? `?t=${mod.lastHMRTimestamp}` : ""}`);
2059
+ if (mod.file && mod.url.startsWith("/")) server.watcher.add(mod.file);
2036
2060
  }
2037
2061
  }
2038
2062
  }
@@ -2224,6 +2248,8 @@ function qwikRouterPlugin(userOpts, buildContextRef) {
2224
2248
  let reExportedRouteLoaderSources;
2225
2249
  /** SSG include/exclude from the adapter (see `_setSsgRoutes`); drives the server-route prune. */
2226
2250
  let ssgRoutePatterns;
2251
+ /** Extensionless ids of the server entries that must import the generated config. */
2252
+ let serverEntryIds = [];
2227
2253
  return {
2228
2254
  name: "vite-plugin-qwik-router",
2229
2255
  enforce: "pre",
@@ -2336,6 +2362,7 @@ function qwikRouterPlugin(userOpts, buildContextRef) {
2336
2362
  return getRouteImports(ctx.routes, manifest);
2337
2363
  });
2338
2364
  outDir = config.build?.outDir;
2365
+ serverEntryIds = getServerEntryIds(rootDir, qwikPlugin.api.getOptions().srcDir ?? void 0, config.build?.ssr, config.build?.rolldownOptions?.input);
2339
2366
  },
2340
2367
  async configureServer(server) {
2341
2368
  devServer = server;
@@ -2410,12 +2437,16 @@ function qwikRouterPlugin(userOpts, buildContextRef) {
2410
2437
  });
2411
2438
  return generateQwikRouterConfig(ctx, qwikPlugin, isServerConsumer, devServer ? loadersByFile : void 0, serverExcludePaths, reExportedRouteLoaderSources);
2412
2439
  }
2413
- if (isSwRegister) return generateServiceWorkerRegister(ctx, sw_register_build_default);
2440
+ if (isSwRegister) return generateServiceWorkerRegister(ctx);
2414
2441
  }
2415
2442
  }
2416
2443
  return null;
2417
2444
  },
2418
2445
  async transform(code, id) {
2446
+ if (this.environment.config.consumer === "server" && isServerEntryId(id, serverEntryIds)) return {
2447
+ code: `${code}\nimport '${QWIK_ROUTER_CONFIG_ID}';`,
2448
+ map: null
2449
+ };
2419
2450
  if (id.startsWith("\0")) return;
2420
2451
  const ext = extname(id).toLowerCase();
2421
2452
  if (ctx && (ext === ".md" || ext === ".mdx")) {
@@ -2547,4 +2578,28 @@ function serverFnsPlugin(buildContextRef) {
2547
2578
  }
2548
2579
  };
2549
2580
  }
2581
+ /**
2582
+ * The server entries: `src/entry.ssr` (what the dev server renders through) plus the configured SSR
2583
+ * build inputs, which may be virtual ids.
2584
+ */
2585
+ function getServerEntryIds(rootDir, srcDir, buildSsr, input) {
2586
+ const candidates = [];
2587
+ if (srcDir) candidates.push(resolve(srcDir, "entry.ssr"));
2588
+ if (typeof buildSsr === "string") candidates.push(buildSsr);
2589
+ if (typeof input === "string") candidates.push(input);
2590
+ else if (Array.isArray(input)) candidates.push(...input);
2591
+ else if (input) candidates.push(...Object.values(input));
2592
+ return candidates.map((candidate) => normalizeEntryId(candidate, rootDir));
2593
+ }
2594
+ function isServerEntryId(id, serverEntryIds) {
2595
+ const entryId = normalizeEntryId(id.split("?", 1)[0], "");
2596
+ return serverEntryIds.includes(entryId);
2597
+ }
2598
+ var isVirtualEntryId = (id) => id.startsWith("@") || id.startsWith("\0");
2599
+ /** Absolute path without its script extension, or the id itself for a virtual entry. */
2600
+ function normalizeEntryId(id, rootDir) {
2601
+ if (isVirtualEntryId(id)) return id;
2602
+ const absolute = rootDir ? resolve(rootDir, id) : id;
2603
+ return normalizePath$1(absolute).replace(/\.[cm]?[jt]sx?$/, "");
2604
+ }
2550
2605
  export { extendConfig, qwikCity, qwikRouter };
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.45",
4
+ "version": "2.0.0-beta.47",
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.45"
44
+ "@qwik.dev/core": "2.0.0-beta.47"
45
45
  },
46
46
  "engines": {
47
47
  "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
@@ -166,7 +166,7 @@
166
166
  "main": "./lib/index.qwik.mjs",
167
167
  "peerDependencies": {
168
168
  "vite": ">=8 <9",
169
- "@qwik.dev/core": "2.0.0-beta.45"
169
+ "@qwik.dev/core": "2.0.0-beta.47"
170
170
  },
171
171
  "publishConfig": {
172
172
  "access": "public"