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

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.
Files changed (38) hide show
  1. package/lib/adapters/azure-swa/vite/index.mjs +2 -1
  2. package/lib/adapters/bun-server/vite/index.mjs +2 -1
  3. package/lib/adapters/cloud-run/vite/index.mjs +2 -1
  4. package/lib/adapters/cloudflare-pages/vite/index.mjs +2 -1
  5. package/lib/adapters/deno-server/vite/index.mjs +2 -1
  6. package/lib/adapters/netlify-edge/vite/index.mjs +2 -1
  7. package/lib/adapters/node-server/vite/index.mjs +2 -1
  8. package/lib/adapters/shared/vite/index.mjs +35 -31
  9. package/lib/adapters/ssg/vite/index.mjs +4 -2
  10. package/lib/adapters/vercel-edge/vite/index.mjs +18 -15
  11. package/lib/chunks/fs.mjs +26 -23
  12. package/lib/chunks/head.qwik.mjs +611 -445
  13. package/lib/chunks/http-error.qwik.mjs +8 -7
  14. package/lib/chunks/pathname.mjs +9 -8
  15. package/lib/chunks/request-path.mjs +8 -4
  16. package/lib/chunks/routes.mjs +14 -14
  17. package/lib/chunks/server-error.mjs +11 -5
  18. package/lib/chunks/{url.mjs → static-file.mjs} +12 -2
  19. package/lib/chunks/system.mjs +18 -17
  20. package/lib/chunks/user-response.mjs +419 -323
  21. package/lib/chunks/worker-thread.mjs +87 -86
  22. package/lib/index.d.ts +47 -59
  23. package/lib/index.qwik.mjs +550 -496
  24. package/lib/middleware/aws-lambda/index.mjs +6 -3
  25. package/lib/middleware/azure-swa/index.mjs +4 -2
  26. package/lib/middleware/bun/index.mjs +12 -12
  27. package/lib/middleware/cloudflare-pages/index.mjs +19 -4
  28. package/lib/middleware/deno/index.mjs +12 -12
  29. package/lib/middleware/firebase/index.mjs +4 -2
  30. package/lib/middleware/netlify-edge/index.mjs +4 -2
  31. package/lib/middleware/node/index.mjs +16 -15
  32. package/lib/middleware/request-handler/index.mjs +35 -24
  33. package/lib/middleware/vercel-edge/index.mjs +4 -2
  34. package/lib/service-worker/index.mjs +2 -1
  35. package/lib/ssg/index.mjs +20 -16
  36. package/lib/vite/index.d.ts +1 -1
  37. package/lib/vite/index.mjs +596 -586
  38. package/package.json +5 -5
@@ -21,7 +21,8 @@ import { toString } from "hast-util-to-string";
21
21
  import { refractor } from "refractor";
22
22
  import tsxLang from "refractor/lang/tsx.js";
23
23
  import { optimize } from "svgo";
24
- /** @public */ function extendConfig(baseConfigExport, serverConfigExport) {
24
+ /** @public */
25
+ function extendConfig(baseConfigExport, serverConfigExport) {
25
26
  return async (env) => {
26
27
  let resolvedBase = await baseConfigExport;
27
28
  if (typeof resolvedBase === "function") resolvedBase = await resolvedBase(env);
@@ -84,6 +85,11 @@ function createMenu(opts, filePath) {
84
85
  function resolveMenu(opts, menuSourceFile) {
85
86
  return createMenu(opts, menuSourceFile.filePath);
86
87
  }
88
+ async function transformMenu(opts, filePath, content) {
89
+ const parsedMenu = parseMenu(opts, filePath, content);
90
+ const id = createFileId(opts.routesDir, filePath);
91
+ return `${`const ${id} = ${JSON.stringify(parsedMenu, null, 2)};`} export default ${id}`;
92
+ }
87
93
  function parseMenu(opts, filePath, content, checkFileExists = true) {
88
94
  const tokens = marked.lexer(content, {});
89
95
  let currentDepth = 0;
@@ -127,18 +133,12 @@ function parseMenu(opts, filePath, content, checkFileExists = true) {
127
133
  if (stack.length === 0) throw new Error(`Menu must start with an h1 in the index: ${filePath}`);
128
134
  return stack[0];
129
135
  }
130
- async function transformMenu(opts, filePath, content) {
131
- const parsedMenu = parseMenu(opts, filePath, content);
132
- const id = createFileId(opts.routesDir, filePath);
133
- return `${`const ${id} = ${JSON.stringify(parsedMenu, null, 2)};`} export default ${id}`;
134
- }
135
- var PARAM_PATTERN = /^(\.\.\.)?(\w+)?$/;
136
- var DYNAMIC_SEGMENT = /\[(.+?)\]/;
137
136
  /**
138
137
  * Adopted from SvelteKit
139
138
  *
140
139
  * https://github.com/sveltejs/kit/blob/master/LICENSE
141
- */ function parseRoutePathname(basePathname, pathname) {
140
+ */
141
+ function parseRoutePathname(basePathname, pathname) {
142
142
  if (pathname === basePathname) return {
143
143
  pattern: new RegExp("^" + pathname.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "$"),
144
144
  routeName: pathname,
@@ -190,8 +190,8 @@ var DYNAMIC_SEGMENT = /\[(.+?)\]/;
190
190
  })
191
191
  };
192
192
  }
193
- var LAYOUT_NAMED_PREFIX = "layout-";
194
- var LAYOUT_TOP_SUFFIX = "!";
193
+ var PARAM_PATTERN = /^(\.\.\.)?(\w+)?$/;
194
+ var DYNAMIC_SEGMENT = /\[(.+?)\]/;
195
195
  function resolveLayout(opts, layoutSourceFile) {
196
196
  let extlessName = layoutSourceFile.extlessName;
197
197
  const filePath = layoutSourceFile.filePath;
@@ -202,7 +202,7 @@ function resolveLayout(opts, layoutSourceFile) {
202
202
  layoutType = "top";
203
203
  extlessName = extlessName.slice(0, extlessName.length - 1);
204
204
  } else layoutType = "nested";
205
- if (extlessName.startsWith(LAYOUT_NAMED_PREFIX)) layoutName = extlessName.slice(7);
205
+ if (extlessName.startsWith(LAYOUT_NAMED_PREFIX)) layoutName = extlessName.slice(LAYOUT_NAMED_PREFIX.length);
206
206
  else layoutName = "";
207
207
  return {
208
208
  id: createFileId(opts.routesDir, filePath),
@@ -212,6 +212,8 @@ function resolveLayout(opts, layoutSourceFile) {
212
212
  layoutName
213
213
  };
214
214
  }
215
+ var LAYOUT_NAMED_PREFIX = "layout-";
216
+ var LAYOUT_TOP_SUFFIX = "!";
215
217
  function resolveRoute(opts, appLayouts, sourceFile) {
216
218
  const filePath = sourceFile.filePath;
217
219
  const layouts = [];
@@ -247,7 +249,8 @@ function resolveRoute(opts, appLayouts, sourceFile) {
247
249
  ...parseRoutePathname(opts.basePathname, pathname)
248
250
  };
249
251
  }
250
- /** Sort routes by pathname, then by extension. Longer routes are sorted first. */ function routeSortCompare(a, b) {
252
+ /** Sort routes by pathname, then by extension. Longer routes are sorted first. */
253
+ function routeSortCompare(a, b) {
251
254
  const maxSegments = Math.max(a.segments.length, b.segments.length);
252
255
  for (let i = 0; i < maxSegments; i += 1) {
253
256
  const sa = a.segments[i];
@@ -277,7 +280,8 @@ function resolveRoute(opts, appLayouts, sourceFile) {
277
280
  * - `[...rest]` → `{ key: '_A', paramName: 'rest' }`
278
281
  * - `pre[slug]post` → `{ key: '_W', paramName: 'slug', prefix: 'pre', suffix: 'post' }`
279
282
  * - `blog` → `{ key: 'blog' }` (lowercased)
280
- */ function parseDirName(name) {
283
+ */
284
+ function parseDirName(name) {
281
285
  if (name.startsWith("(") && name.endsWith(")")) return { key: null };
282
286
  const restMatch = /^\[\.\.\.(\w+)\]$/.exec(name);
283
287
  if (restMatch) return {
@@ -293,49 +297,8 @@ function resolveRoute(opts, appLayouts, sourceFile) {
293
297
  };
294
298
  return { key: name.toLowerCase() };
295
299
  }
296
- async function walkRouteDir(node, dirPath) {
297
- const dirItemNames = await fs.promises.readdir(dirPath);
298
- await Promise.all(dirItemNames.map(async (itemName) => {
299
- const itemPath = normalizePath$1(join(dirPath, itemName));
300
- if ((await fs.promises.stat(itemPath)).isDirectory()) {
301
- const parsed = parseDirName(itemName);
302
- if (parsed.key === null) {
303
- let child = node.children.get(itemName);
304
- if (!child) {
305
- child = {
306
- _files: [],
307
- children: /* @__PURE__ */ new Map()
308
- };
309
- node.children.set(itemName, child);
310
- }
311
- await walkRouteDir(child, itemPath);
312
- } else {
313
- let child = node.children.get(parsed.key);
314
- if (!child) {
315
- child = {
316
- _files: [],
317
- children: /* @__PURE__ */ new Map()
318
- };
319
- if (parsed.paramName) child._P = parsed.paramName;
320
- if (parsed.prefix) child._0 = parsed.prefix;
321
- if (parsed.suffix) child._9 = parsed.suffix;
322
- node.children.set(parsed.key, child);
323
- }
324
- await walkRouteDir(child, itemPath);
325
- }
326
- } else {
327
- const sourceFileName = getSourceFile(itemName);
328
- if (sourceFileName !== null) node._files.push({
329
- ...sourceFileName,
330
- fileName: itemName,
331
- filePath: itemPath,
332
- dirName: basename(dirPath),
333
- dirPath: normalizePath$1(dirPath)
334
- });
335
- }
336
- }));
337
- }
338
- /** Walk the routes directory and return a BuildTrieNode with trie keys. */ async function walkRoutes(routesDir) {
300
+ /** Walk the routes directory and return a BuildTrieNode with trie keys. */
301
+ async function walkRoutes(routesDir) {
339
302
  const dirPath = normalizePath$1(routesDir);
340
303
  const root = {
341
304
  _files: [],
@@ -344,6 +307,48 @@ async function walkRouteDir(node, dirPath) {
344
307
  await walkRouteDir(root, dirPath);
345
308
  return root;
346
309
  }
310
+ async function walkRouteDir(node, dirPath) {
311
+ const dirItemNames = (await fs.promises.readdir(dirPath)).sort();
312
+ const dirItems = await Promise.all(dirItemNames.map(async (itemName) => {
313
+ const itemPath = normalizePath$1(join(dirPath, itemName));
314
+ return {
315
+ itemName,
316
+ itemPath,
317
+ isDirectory: (await fs.promises.stat(itemPath)).isDirectory()
318
+ };
319
+ }));
320
+ const childWalks = [];
321
+ for (const { itemName, itemPath, isDirectory } of dirItems) {
322
+ if (isDirectory) {
323
+ childWalks.push(walkRouteDir(getOrCreateChildNode(node, itemName), itemPath));
324
+ continue;
325
+ }
326
+ const sourceFileName = getSourceFile(itemName);
327
+ if (sourceFileName !== null) node._files.push({
328
+ ...sourceFileName,
329
+ fileName: itemName,
330
+ filePath: itemPath,
331
+ dirName: basename(dirPath),
332
+ dirPath: normalizePath$1(dirPath)
333
+ });
334
+ }
335
+ await Promise.all(childWalks);
336
+ }
337
+ function getOrCreateChildNode(node, dirName) {
338
+ const parsed = parseDirName(dirName);
339
+ const key = parsed.key ?? dirName;
340
+ const existing = node.children.get(key);
341
+ if (existing) return existing;
342
+ const child = {
343
+ _files: [],
344
+ children: /* @__PURE__ */ new Map()
345
+ };
346
+ if (parsed.paramName) child._P = parsed.paramName;
347
+ if (parsed.prefix) child._0 = parsed.prefix;
348
+ if (parsed.suffix) child._9 = parsed.suffix;
349
+ node.children.set(key, child);
350
+ return child;
351
+ }
347
352
  async function walkServerPlugins(opts) {
348
353
  const dirPath = opts.serverPluginsDir;
349
354
  const dirItemNames = await fs.promises.readdir(dirPath);
@@ -360,30 +365,40 @@ async function walkServerPlugins(opts) {
360
365
  }));
361
366
  return sourceFiles.sort((a, b) => a.filePath < b.filePath ? -1 : 1);
362
367
  }
363
- function resolveEntry(opts, sourceFile) {
364
- const pathname = getPathnameFromDirPath(opts, sourceFile.dirPath);
365
- const chunkFileName = pathname.slice(opts.basePathname.length);
366
- return {
367
- id: createFileId(opts.routesDir, sourceFile.filePath, "Route"),
368
- filePath: sourceFile.filePath,
369
- chunkFileName,
370
- ...parseRoutePathname(opts.basePathname, pathname)
371
- };
368
+ async function parseRoutesDir(ctx) {
369
+ try {
370
+ await updateRoutingContext(ctx);
371
+ validateBuild(ctx);
372
+ } catch (e) {
373
+ addError(ctx, e);
374
+ }
375
+ for (const d of ctx.diagnostics) if (d.type === "error") throw new Error(d.message);
376
+ else console.warn(d.message);
372
377
  }
373
- function resolveServiceWorkerEntry(opts, sourceFile) {
374
- const pathname = getPathnameFromDirPath(opts, sourceFile.dirPath) + sourceFile.extlessName + ".js";
375
- const chunkFileName = pathname.slice(opts.basePathname.length);
376
- return {
377
- id: createFileId(opts.routesDir, sourceFile.filePath, "ServiceWorker"),
378
- filePath: sourceFile.filePath,
379
- chunkFileName,
380
- ...parseRoutePathname(opts.basePathname, pathname)
381
- };
378
+ function updateRoutingContext(ctx) {
379
+ ctx.activeBuild ||= _updateRoutingContext(ctx).finally(() => {
380
+ ctx.activeBuild = null;
381
+ });
382
+ return ctx.activeBuild;
383
+ }
384
+ async function _updateRoutingContext(ctx) {
385
+ const serverPlugins = await walkServerPlugins(ctx.opts);
386
+ const routeTrie = await walkRoutes(ctx.opts.routesDir);
387
+ if (ctx.opts.rewriteRoutes) applyRewriteRoutes(routeTrie, ctx.opts.rewriteRoutes);
388
+ ctx.routeTrie = routeTrie;
389
+ ctx.serverPlugins = serverPlugins;
390
+ const derived = deriveFromTrie(ctx.opts, routeTrie);
391
+ ctx.layouts = derived.layouts;
392
+ ctx.routes = rewriteRoutes(ctx, derived.routes);
393
+ ctx.entries = derived.entries;
394
+ ctx.serviceWorkers = derived.serviceWorkers;
395
+ ctx.menus = derived.menus;
382
396
  }
383
397
  /**
384
398
  * Walk the trie and extract flat arrays for layouts, routes, entries, menus, service workers. These
385
399
  * are needed by generate-entries, generate-menus, generate-service-worker, vite plugin, etc.
386
- */ function deriveFromTrie(opts, root) {
400
+ */
401
+ function deriveFromTrie(opts, root) {
387
402
  const layouts = [];
388
403
  const routes = [];
389
404
  const entries = [];
@@ -434,6 +449,47 @@ function resolveServiceWorkerEntry(opts, sourceFile) {
434
449
  menus
435
450
  };
436
451
  }
452
+ function resolveEntry(opts, sourceFile) {
453
+ const pathname = getPathnameFromDirPath(opts, sourceFile.dirPath);
454
+ const chunkFileName = pathname.slice(opts.basePathname.length);
455
+ return {
456
+ id: createFileId(opts.routesDir, sourceFile.filePath, "Route"),
457
+ filePath: sourceFile.filePath,
458
+ chunkFileName,
459
+ ...parseRoutePathname(opts.basePathname, pathname)
460
+ };
461
+ }
462
+ function resolveServiceWorkerEntry(opts, sourceFile) {
463
+ const pathname = getPathnameFromDirPath(opts, sourceFile.dirPath) + sourceFile.extlessName + ".js";
464
+ const chunkFileName = pathname.slice(opts.basePathname.length);
465
+ return {
466
+ id: createFileId(opts.routesDir, sourceFile.filePath, "ServiceWorker"),
467
+ filePath: sourceFile.filePath,
468
+ chunkFileName,
469
+ ...parseRoutePathname(opts.basePathname, pathname)
470
+ };
471
+ }
472
+ /**
473
+ * Create rewritten route copies for each rewrite config. For backward compat: produces BuiltRoute
474
+ * entries with translated pathnames. At runtime, codegen emits /G nodes in the trie instead.
475
+ */
476
+ function rewriteRoutes(ctx, routes) {
477
+ if (!ctx.opts.rewriteRoutes) return routes;
478
+ const translatedRoutes = [];
479
+ let segmentsToTranslate = ctx.opts.rewriteRoutes.flatMap((rewriteConfig) => {
480
+ return Object.keys(rewriteConfig.paths || {});
481
+ });
482
+ segmentsToTranslate = Array.from(new Set(segmentsToTranslate));
483
+ routes.forEach((route) => {
484
+ translatedRoutes.push(route);
485
+ if (route.pathname.split("/").some((segment) => segmentsToTranslate.includes(segment)) || route.pathname === "/") ctx.opts.rewriteRoutes.forEach((config, configIndex) => {
486
+ if (route.pathname === "/" && !config.prefix) return;
487
+ const routeToPush = translateRoute(route, config, configIndex);
488
+ if (!translatedRoutes.some((item) => item.pathname === routeToPush.pathname && item.routeName === routeToPush.routeName)) translatedRoutes.push(routeToPush);
489
+ });
490
+ });
491
+ return translatedRoutes.sort(routeSortCompare);
492
+ }
437
493
  function translateRoute(route, config, configIndex) {
438
494
  const replacePath = (part) => (config.paths || {})[part] ?? part;
439
495
  const pathnamePrefix = config.prefix ? "/" + config.prefix : "";
@@ -470,26 +526,6 @@ function translateRoute(route, config, configIndex) {
470
526
  };
471
527
  }
472
528
  /**
473
- * Create rewritten route copies for each rewrite config. For backward compat: produces BuiltRoute
474
- * entries with translated pathnames. At runtime, codegen emits /G nodes in the trie instead.
475
- */ function rewriteRoutes(ctx, routes) {
476
- if (!ctx.opts.rewriteRoutes) return routes;
477
- const translatedRoutes = [];
478
- let segmentsToTranslate = ctx.opts.rewriteRoutes.flatMap((rewriteConfig) => {
479
- return Object.keys(rewriteConfig.paths || {});
480
- });
481
- segmentsToTranslate = Array.from(new Set(segmentsToTranslate));
482
- routes.forEach((route) => {
483
- translatedRoutes.push(route);
484
- if (route.pathname.split("/").some((segment) => segmentsToTranslate.includes(segment)) || route.pathname === "/") ctx.opts.rewriteRoutes.forEach((config, configIndex) => {
485
- if (route.pathname === "/" && !config.prefix) return;
486
- const routeToPush = translateRoute(route, config, configIndex);
487
- if (!translatedRoutes.some((item) => item.pathname === routeToPush.pathname && item.routeName === routeToPush.routeName)) translatedRoutes.push(routeToPush);
488
- });
489
- });
490
- return translatedRoutes.sort(routeSortCompare);
491
- }
492
- /**
493
529
  * Walk the trie and create _G (rewrite) nodes for each rewriteRoutes config.
494
530
  *
495
531
  * For each route in the trie, if any of its path segments are translatable (listed in a rewrite
@@ -498,7 +534,8 @@ function translateRoute(route, config, configIndex) {
498
534
  *
499
535
  * The `_G` value is a `/`-separated string of trie keys (skipping group nodes), which the runtime
500
536
  * resolver uses to walk the trie and find the target route's loaders and layouts.
501
- */ function applyRewriteRoutes(root, rewriteConfigs) {
537
+ */
538
+ function applyRewriteRoutes(root, rewriteConfigs) {
502
539
  const routables = [];
503
540
  function walk(node, steps) {
504
541
  if (node._files.some((f) => f.type === "route" && !isErrorName(f.extlessName))) routables.push({
@@ -552,25 +589,6 @@ function translateRoute(route, config, configIndex) {
552
589
  }
553
590
  }
554
591
  }
555
- async function _updateRoutingContext(ctx) {
556
- const serverPlugins = await walkServerPlugins(ctx.opts);
557
- const routeTrie = await walkRoutes(ctx.opts.routesDir);
558
- if (ctx.opts.rewriteRoutes) applyRewriteRoutes(routeTrie, ctx.opts.rewriteRoutes);
559
- ctx.routeTrie = routeTrie;
560
- ctx.serverPlugins = serverPlugins;
561
- const derived = deriveFromTrie(ctx.opts, routeTrie);
562
- ctx.layouts = derived.layouts;
563
- ctx.routes = rewriteRoutes(ctx, derived.routes);
564
- ctx.entries = derived.entries;
565
- ctx.serviceWorkers = derived.serviceWorkers;
566
- ctx.menus = derived.menus;
567
- }
568
- function updateRoutingContext(ctx) {
569
- ctx.activeBuild ||= _updateRoutingContext(ctx).finally(() => {
570
- ctx.activeBuild = null;
571
- });
572
- return ctx.activeBuild;
573
- }
574
592
  function validateBuild(ctx) {
575
593
  const pathnames = Array.from(new Set(ctx.routes.map((r) => r.pathname))).sort();
576
594
  for (const pathname of pathnames) {
@@ -581,15 +599,27 @@ function validateBuild(ctx) {
581
599
  addWarning(ctx, `The "top" layout feature, which is used by "${l.filePath}" has been deprecated and will be removed from future versions. In most cases the "group" layout feature can be used in its place: https://qwik.dev/docs/advanced/routing/`);
582
600
  });
583
601
  }
584
- async function parseRoutesDir(ctx) {
585
- try {
586
- await updateRoutingContext(ctx);
587
- validateBuild(ctx);
588
- } catch (e) {
589
- addError(ctx, e);
590
- }
591
- for (const d of ctx.diagnostics) if (d.type === "error") throw new Error(d.message);
592
- else console.warn(d.message);
602
+ function createBuildContext(rootDir, viteBasePath, userOpts, target, dynamicImports) {
603
+ return {
604
+ rootDir: normalizePath$1(rootDir),
605
+ opts: normalizeOptions(rootDir, viteBasePath, userOpts),
606
+ routeTrie: {
607
+ _files: [],
608
+ children: /* @__PURE__ */ new Map()
609
+ },
610
+ routes: [],
611
+ serverPlugins: [],
612
+ layouts: [],
613
+ entries: [],
614
+ serviceWorkers: [],
615
+ menus: [],
616
+ diagnostics: [],
617
+ frontmatter: /* @__PURE__ */ new Map(),
618
+ target: target || "ssr",
619
+ dynamicImports: target === "client" || !!dynamicImports,
620
+ isDirty: true,
621
+ activeBuild: null
622
+ };
593
623
  }
594
624
  function resetBuildContext(ctx) {
595
625
  if (ctx) {
@@ -602,6 +632,15 @@ function resetBuildContext(ctx) {
602
632
  ctx.isDirty = true;
603
633
  }
604
634
  }
635
+ /**
636
+ * Resolve the app's base pathname from plugin options and vite's `base`. Shared by option
637
+ * normalization and the `config()`-hook `define` (which runs before the build context exists).
638
+ */
639
+ function resolveBasePathname(userOpts, viteBasePath) {
640
+ let basePathname = typeof userOpts?.baseUrl === "string" ? userOpts.baseUrl : typeof userOpts?.basePathname === "string" ? userOpts.basePathname : viteBasePath || "/";
641
+ if (!basePathname.endsWith("/")) basePathname = ensureSlash(basePathname);
642
+ return new URL(basePathname, "https://qwik.dev/").pathname;
643
+ }
605
644
  function normalizeOptions(rootDir, viteBasePath, userOpts) {
606
645
  if (!(viteBasePath.startsWith("/") && viteBasePath.endsWith("/"))) {
607
646
  console.error(`warning: vite's config.base must begin and end with /. This will be an error in v2. If you have a valid use case, please open an issue.`);
@@ -614,47 +653,13 @@ function normalizeOptions(rootDir, viteBasePath, userOpts) {
614
653
  if (typeof opts.serverPluginsDir !== "string") opts.serverPluginsDir = opts.routesDir;
615
654
  else if (!isAbsolute(opts.serverPluginsDir)) opts.serverPluginsDir = resolve(rootDir, opts.serverPluginsDir);
616
655
  opts.serverPluginsDir = normalizePath$1(opts.serverPluginsDir);
617
- if (typeof opts.baseUrl === "string") opts.basePathname = opts.baseUrl;
618
- if (typeof opts.basePathname !== "string") opts.basePathname = viteBasePath;
619
- if (!opts.basePathname.endsWith("/")) {
620
- console.error(`Warning: qwik-router plugin basePathname must end with /. This will be an error in v2`);
621
- opts.basePathname = ensureSlash(opts.basePathname);
622
- }
623
- opts.basePathname = new URL(opts.basePathname, "https://qwik.dev/").pathname;
656
+ if (typeof opts.baseUrl !== "string" && typeof opts.basePathname === "string" && !opts.basePathname.endsWith("/")) console.error(`Warning: qwik-router plugin basePathname must end with /. This will be an error in v2`);
657
+ opts.basePathname = resolveBasePathname(userOpts, viteBasePath);
624
658
  opts.mdx = opts.mdx || {};
625
659
  opts.platform = opts.platform || {};
626
660
  if (opts.strictLoaders === void 0) opts.strictLoaders = true;
627
661
  return opts;
628
662
  }
629
- function createBuildContext(rootDir, viteBasePath, userOpts, target, dynamicImports) {
630
- return {
631
- rootDir: normalizePath$1(rootDir),
632
- opts: normalizeOptions(rootDir, viteBasePath, userOpts),
633
- routeTrie: {
634
- _files: [],
635
- children: /* @__PURE__ */ new Map()
636
- },
637
- routes: [],
638
- serverPlugins: [],
639
- layouts: [],
640
- entries: [],
641
- serviceWorkers: [],
642
- menus: [],
643
- diagnostics: [],
644
- frontmatter: /* @__PURE__ */ new Map(),
645
- target: target || "ssr",
646
- dynamicImports: target === "client" || !!dynamicImports,
647
- isDirty: true,
648
- activeBuild: null
649
- };
650
- }
651
- function parseFrontmatterAttrs(yaml) {
652
- if (typeof yaml === "string") {
653
- yaml = yaml.trim();
654
- if (yaml !== "") return parse(yaml);
655
- }
656
- return null;
657
- }
658
663
  function parseFrontmatter(ctx) {
659
664
  return (mdast, vfile) => {
660
665
  const attrs = {};
@@ -665,6 +670,13 @@ function parseFrontmatter(ctx) {
665
670
  if (Object.keys(attrs).length > 0) ctx.frontmatter.set(normalizePath$1(vfile.path), attrs);
666
671
  };
667
672
  }
673
+ function parseFrontmatterAttrs(yaml) {
674
+ if (typeof yaml === "string") {
675
+ yaml = yaml.trim();
676
+ if (yaml !== "") return parse(yaml);
677
+ }
678
+ return null;
679
+ }
668
680
  var metaNames = {
669
681
  author: true,
670
682
  creator: true,
@@ -720,17 +732,39 @@ function frontmatterAttrsToDocumentHead(attrs) {
720
732
  }
721
733
  return null;
722
734
  }
723
- function renameClassname() {
735
+ function rehypeSlug() {
724
736
  return (ast) => {
725
- visit(ast, "element", (node) => {
726
- if (node.properties) {
727
- if (node.properties.className) {
728
- node.properties.class = node.properties.className;
729
- node.properties.className = void 0;
730
- }
731
- }
732
- });
733
- };
737
+ const mdast = ast;
738
+ const slugs = new Slugger();
739
+ visit(mdast, "element", (node) => {
740
+ if (headingRank(node) && node.properties) {
741
+ const text = toString(node);
742
+ if (!hasProperty(node, "id")) node.properties.id = slugs.slug(text);
743
+ }
744
+ });
745
+ };
746
+ }
747
+ function rehypePage(ctx) {
748
+ return (ast, vfile) => {
749
+ const mdast = ast;
750
+ const sourcePath = normalizePath$1(vfile.path);
751
+ updateContentLinks(mdast, ctx.opts, sourcePath);
752
+ exportFrontmatter(ctx, mdast, sourcePath);
753
+ exportContentHead(ctx, mdast, sourcePath);
754
+ exportContentHeadings(mdast);
755
+ };
756
+ }
757
+ function renameClassname() {
758
+ return (ast) => {
759
+ visit(ast, "element", (node) => {
760
+ if (node.properties) {
761
+ if (node.properties.className) {
762
+ node.properties.class = node.properties.className;
763
+ node.properties.className = void 0;
764
+ }
765
+ }
766
+ });
767
+ };
734
768
  }
735
769
  function wrapTableWithDiv() {
736
770
  return (ast) => {
@@ -756,6 +790,30 @@ function updateContentLinks(mdast, opts, sourcePath) {
756
790
  }
757
791
  });
758
792
  }
793
+ function exportFrontmatter(ctx, mdast, sourcePath) {
794
+ createExport(mdast, "frontmatter", ctx.frontmatter.get(sourcePath));
795
+ }
796
+ function exportContentHead(ctx, mdast, sourcePath) {
797
+ const head = frontmatterAttrsToDocumentHead(ctx.frontmatter.get(sourcePath));
798
+ if (head) createExport(mdast, "head", head);
799
+ }
800
+ function exportContentHeadings(mdast) {
801
+ const headings = [];
802
+ visit(mdast, "element", (node) => {
803
+ const level = headingRank(node);
804
+ if (level && node.properties) {
805
+ if (hasProperty(node, "id")) {
806
+ const text = toString(node);
807
+ headings.push({
808
+ text,
809
+ id: node.properties.id,
810
+ level
811
+ });
812
+ }
813
+ }
814
+ });
815
+ if (headings.length > 0) createExport(mdast, "headings", headings);
816
+ }
759
817
  function createExport(mdast, identifierName, val) {
760
818
  const mdxjsEsm = {
761
819
  type: "mdxjsEsm",
@@ -785,69 +843,11 @@ function createExport(mdast, identifierName, val) {
785
843
  };
786
844
  mdast.children.unshift(mdxjsEsm);
787
845
  }
788
- function exportFrontmatter(ctx, mdast, sourcePath) {
789
- createExport(mdast, "frontmatter", ctx.frontmatter.get(sourcePath));
790
- }
791
- function exportContentHead(ctx, mdast, sourcePath) {
792
- const head = frontmatterAttrsToDocumentHead(ctx.frontmatter.get(sourcePath));
793
- if (head) createExport(mdast, "head", head);
794
- }
795
846
  var own = {}.hasOwnProperty;
796
847
  function hasProperty(node, propName) {
797
848
  const value = propName && node && typeof node === "object" && node.type === "element" && node.properties && own.call(node.properties, propName) && node.properties[propName];
798
849
  return value != null && value !== false;
799
850
  }
800
- function rehypeSlug() {
801
- return (ast) => {
802
- const mdast = ast;
803
- const slugs = new Slugger();
804
- visit(mdast, "element", (node) => {
805
- if (headingRank(node) && node.properties) {
806
- const text = toString(node);
807
- if (!hasProperty(node, "id")) node.properties.id = slugs.slug(text);
808
- }
809
- });
810
- };
811
- }
812
- function exportContentHeadings(mdast) {
813
- const headings = [];
814
- visit(mdast, "element", (node) => {
815
- const level = headingRank(node);
816
- if (level && node.properties) {
817
- if (hasProperty(node, "id")) {
818
- const text = toString(node);
819
- headings.push({
820
- text,
821
- id: node.properties.id,
822
- level
823
- });
824
- }
825
- }
826
- });
827
- if (headings.length > 0) createExport(mdast, "headings", headings);
828
- }
829
- function rehypePage(ctx) {
830
- return (ast, vfile) => {
831
- const mdast = ast;
832
- const sourcePath = normalizePath$1(vfile.path);
833
- updateContentLinks(mdast, ctx.opts, sourcePath);
834
- exportFrontmatter(ctx, mdast, sourcePath);
835
- exportContentHead(ctx, mdast, sourcePath);
836
- exportContentHeadings(mdast);
837
- };
838
- }
839
- function syntaxHighlight(node, lang) {
840
- const code = toString(node);
841
- const result = refractor.highlight(code, lang);
842
- if (result && Array.isArray(node.children)) node.children = result.children;
843
- }
844
- function getLanguage(className) {
845
- if (typeof className === "string") {
846
- className = className.toLowerCase();
847
- if (className.startsWith("language-")) return className.slice(9);
848
- }
849
- return null;
850
- }
851
851
  function rehypeSyntaxHighlight() {
852
852
  refractor.register(tsxLang);
853
853
  return async (ast) => {
@@ -865,6 +865,18 @@ function rehypeSyntaxHighlight() {
865
865
  });
866
866
  };
867
867
  }
868
+ function syntaxHighlight(node, lang) {
869
+ const code = toString(node);
870
+ const result = refractor.highlight(code, lang);
871
+ if (result && Array.isArray(node.children)) node.children = result.children;
872
+ }
873
+ function getLanguage(className) {
874
+ if (typeof className === "string") {
875
+ className = className.toLowerCase();
876
+ if (className.startsWith("language-")) return className.slice(9);
877
+ }
878
+ return null;
879
+ }
868
880
  async function createMdxTransformer(ctx) {
869
881
  const { compile } = await import("@mdx-js/mdx");
870
882
  const { default: remarkFrontmatter } = await import("remark-frontmatter");
@@ -962,118 +974,130 @@ function getImportPath(importPath) {
962
974
  if (lowerCasePath.endsWith(".ts")) return importPath.slice(0, importPath.length - 3);
963
975
  return importPath;
964
976
  }
965
- /** Check if a build trie key is a group directory name like `(common)` */ function isGroupKey(key) {
977
+ /** Check if a build trie key is a group directory name like `(common)` */
978
+ function isGroupKey(key) {
966
979
  return key.charCodeAt(0) === 40;
967
980
  }
968
- function collectFiles(node, cb) {
969
- for (const file of node._files) cb(file, node);
970
- for (const child of node.children.values()) collectFiles(child, cb);
971
- }
972
981
  /**
973
- * For a named layout `index@name.tsx`, walk up ancestors to find the named layout and collect
974
- * default layouts above it.
982
+ * Serialize the build trie into JS code.
975
983
  *
976
- * Returns the layout chain in order: [outermost default..., named layout]
977
- */ function resolveNamedLayoutChain(ancestorLayouts, nodeLayouts, targetName) {
978
- const allLayouts = [...ancestorLayouts, ...nodeLayouts];
979
- const result = [];
980
- let foundNamed = false;
981
- for (let i = allLayouts.length - 1; i >= 0; i--) {
982
- const layout = allLayouts[i];
983
- if (!foundNamed) {
984
- if (layout.layoutName === targetName) {
985
- result.unshift(layout);
986
- foundNamed = true;
984
+ * Emits:
985
+ *
986
+ * - Layout imports and lazy loaders
987
+ * - Route/page imports and lazy loaders
988
+ * - Menu imports and lazy loaders
989
+ * - The route trie object literal with _L, _I, _G, _B, _4, _E, _N, _P, _0, _9, _M
990
+ */
991
+ function createRoutes(ctx, qwikPlugin, c, esmImports, isSSR, loadersByFile, serverExcludePaths, routeLoaderSourceFiles) {
992
+ const includeEndpoints = isSSR;
993
+ const dynamicImports = ctx.dynamicImports;
994
+ const layoutIdMap = /* @__PURE__ */ new Map();
995
+ const routeIdMap = /* @__PURE__ */ new Map();
996
+ const menuIdMap = /* @__PURE__ */ new Map();
997
+ let layoutCount = 0;
998
+ let routeCount = 0;
999
+ let menuCount = 0;
1000
+ collectFiles(ctx.routeTrie, (file, _node) => {
1001
+ if (file.type === "layout") {
1002
+ const id = ctx.layouts.find((l) => l.filePath === file.filePath)?.id;
1003
+ if (id) {
1004
+ layoutIdMap.set(file.filePath, id);
1005
+ layoutCount++;
1006
+ }
1007
+ } else if (file.type === "route") {
1008
+ if (ctx.routes.find((r) => r.filePath === file.filePath)) routeCount++;
1009
+ } else if (file.type === "menu") {
1010
+ const id = createFileId(ctx.opts.routesDir, file.filePath);
1011
+ menuIdMap.set(file.filePath, id);
1012
+ menuCount++;
1013
+ }
1014
+ });
1015
+ if (layoutCount > 0) {
1016
+ c.push(`\n/** Qwik Router Layouts (${layoutCount}) */`);
1017
+ for (const [filePath, id] of layoutIdMap) {
1018
+ const importPath = JSON.stringify(getImportPath(filePath));
1019
+ if (dynamicImports) c.push(`const ${id} = ()=>import(${importPath});`);
1020
+ else {
1021
+ esmImports.push(`import * as ${id}_ from ${importPath};`);
1022
+ c.push(`const ${id} = ()=>${id}_;`);
987
1023
  }
988
- } else if (layout.layoutName === "") {
989
- result.unshift(layout);
990
- if (layout.layoutType === "top") break;
991
1024
  }
992
1025
  }
993
- return result;
994
- }
995
- /**
996
- * The `_I`/`_E`/`_4` loader expression: a bare loader (runtime prepends gathered layouts), or an
997
- * override chain for `!` (layout stop) / `@name` (named layout).
998
- */ function buildLoaderChainExpr(extlessName, loaderExpr, ancestorLayouts, nodeLayouts) {
999
- const { layoutName, layoutStop } = parseRouteIndexName(extlessName);
1000
- if (layoutStop) return `[ ${loaderExpr} ]`;
1001
- if (layoutName) return `[ ${[...resolveNamedLayoutChain(ancestorLayouts, nodeLayouts, layoutName).map((l) => l.id), loaderExpr].join(", ")} ]`;
1002
- return loaderExpr;
1003
- }
1004
- function getClientRouteBundleNames(qwikPlugin, r) {
1005
- const bundlesNames = [];
1006
- const manifest = qwikPlugin.api.getManifest();
1007
- if (manifest) {
1008
- const manifestBundleNames = Object.keys(manifest.bundles);
1009
- const addRouteFile = (filePath) => {
1010
- filePath = removeExtension(filePath);
1011
- for (const bundleName of manifestBundleNames) {
1012
- const bundle = manifest.bundles[bundleName];
1013
- if (bundle.origins) for (const bundleOrigin of bundle.origins) {
1014
- const originPath = removeExtension(bundleOrigin);
1015
- if (filePath.endsWith(originPath)) {
1016
- if (!bundlesNames.includes(bundleName)) bundlesNames.push(bundleName);
1017
- }
1018
- }
1026
+ if (menuCount > 0) {
1027
+ c.push(`\n/** Qwik Router Menus (${menuCount}) */`);
1028
+ for (const [filePath, id] of menuIdMap) {
1029
+ const importPath = JSON.stringify(getImportPath(filePath));
1030
+ if (dynamicImports) c.push(`const ${id} = ()=>import(${importPath});`);
1031
+ else {
1032
+ esmImports.push(`import * as ${id}_ from ${importPath};`);
1033
+ c.push(`const ${id} = ()=>${id}_;`);
1019
1034
  }
1020
- };
1021
- for (const layout of r.layouts) addRouteFile(layout.filePath);
1022
- addRouteFile(r.filePath);
1035
+ }
1023
1036
  }
1024
- return bundlesNames;
1037
+ c.push(`\n/** Qwik Router Routes (${routeCount}) */`);
1038
+ for (const route of ctx.routes) if (isPageExt(route.ext)) {
1039
+ if (serverExcludePaths?.has(route.filePath)) continue;
1040
+ const importPath = getImportPath(route.filePath);
1041
+ let loaderExpr;
1042
+ if (dynamicImports) loaderExpr = `()=>import(${JSON.stringify(importPath)})`;
1043
+ else {
1044
+ esmImports.push(`import * as ${route.id} from ${JSON.stringify(importPath)};`);
1045
+ loaderExpr = `()=>${route.id}`;
1046
+ }
1047
+ routeIdMap.set(route.filePath, loaderExpr);
1048
+ } else if (includeEndpoints && isModuleExt(route.ext)) {
1049
+ const importPath = getImportPath(route.filePath);
1050
+ esmImports.push(`import * as ${route.id} from ${JSON.stringify(importPath)};`);
1051
+ routeIdMap.set(route.filePath, `()=>${route.id}`);
1052
+ }
1053
+ const trieStr = serializeBuildTrie(ctx, qwikPlugin, ctx.routeTrie, layoutIdMap, routeIdMap, menuIdMap, isSSR, "", loadersByFile, routeLoaderSourceFiles);
1054
+ const baseSegments = ctx.opts.basePathname.split("/").filter((s) => s.length > 0);
1055
+ let routesExpr = trieStr;
1056
+ for (let j = baseSegments.length - 1; j >= 0; j--) routesExpr = `{ ${JSON.stringify(baseSegments[j])}: ${routesExpr} }`;
1057
+ c.push(`export const routes = ${routesExpr};`);
1025
1058
  }
1026
- /**
1027
- * Serialize a BuildTrieNode to a JS object literal string.
1028
- *
1029
- * The ancestor stack tracks layouts from parent nodes for layout chain resolution (needed for
1030
- * layout stops and named layouts).
1031
- */ function serializeBuildTrie(ctx, qwikPlugin, node, layoutIdMap, routeIdMap, menuIdMap, ancestorLayouts, isSSR, indent, loadersByFile, routeLoaderSourceFiles) {
1059
+ function collectFiles(node, cb) {
1060
+ for (const file of node._files) cb(file, node);
1061
+ for (const child of node.children.values()) collectFiles(child, cb);
1062
+ }
1063
+ /** Serialize a BuildTrieNode using the resolved route layout chains. */
1064
+ function serializeBuildTrie(ctx, qwikPlugin, node, layoutIdMap, routeIdMap, menuIdMap, isSSR, indent, loadersByFile, routeLoaderSourceFiles) {
1032
1065
  const lines = [];
1033
1066
  const nextIndent = indent + " ";
1034
1067
  if (node._P) lines.push(`${nextIndent}_P: ${JSON.stringify(node._P)},`);
1035
1068
  if (node._0) lines.push(`${nextIndent}_0: ${JSON.stringify(node._0)},`);
1036
1069
  if (node._9) lines.push(`${nextIndent}_9: ${JSON.stringify(node._9)},`);
1037
- if (node._G != null) lines.push(`${nextIndent}_G: ${JSON.stringify(node._G)},`);
1070
+ if (node._G != null) lines.push(`${nextIndent}_G: ${JSON.stringify(ctx.opts.basePathname + node._G)},`);
1038
1071
  let layoutExpr;
1039
1072
  let indexExpr;
1040
1073
  let errorExpr;
1041
1074
  let notFoundExpr;
1042
1075
  let menuExpr;
1043
1076
  let bundleRoute;
1044
- const nodeLayouts = [];
1077
+ let pageLayouts;
1045
1078
  for (const file of node._files) if (file.type === "menu") {
1046
1079
  const menuId = menuIdMap.get(file.filePath);
1047
1080
  if (menuId) menuExpr = menuId;
1048
1081
  } else if (file.type === "layout") {
1049
1082
  const layoutId = layoutIdMap.get(file.filePath);
1050
1083
  if (layoutId) {
1051
- let extlessName = file.extlessName;
1052
- let layoutType = "nested";
1053
- if (extlessName.endsWith("!")) {
1054
- layoutType = "top";
1055
- extlessName = extlessName.slice(0, -1);
1056
- }
1057
- const layoutName = extlessName.startsWith("layout-") ? extlessName.slice(7) : "";
1058
- nodeLayouts.push({
1059
- id: layoutId,
1060
- layoutName,
1061
- layoutType
1062
- });
1063
- if (layoutName === "") layoutExpr = layoutId;
1084
+ if (file.extlessName === "layout" || file.extlessName === "layout!") layoutExpr = layoutId;
1064
1085
  }
1065
1086
  }
1066
1087
  for (const file of node._files) {
1067
1088
  if (file.type !== "route") continue;
1068
1089
  const loaderExpr = routeIdMap.get(file.filePath);
1069
1090
  if (!loaderExpr) continue;
1070
- const expr = buildLoaderChainExpr(file.extlessName, loaderExpr, ancestorLayouts, nodeLayouts);
1091
+ const route = ctx.routes.find((route) => route.filePath === file.filePath);
1092
+ const expr = buildLoaderChainExpr(file.extlessName, loaderExpr, route?.layouts ?? []);
1071
1093
  const boundary = errorBoundaryName(file.extlessName);
1072
1094
  if (boundary === "404") notFoundExpr = expr;
1073
1095
  else if (boundary === "error") errorExpr = expr;
1074
1096
  else {
1075
1097
  indexExpr = expr;
1076
- bundleRoute = ctx.routes.find((r) => r.filePath === file.filePath);
1098
+ const { layoutName, layoutStop } = parseRouteIndexName(file.extlessName);
1099
+ pageLayouts = layoutStop || layoutName ? route?.layouts ?? [] : void 0;
1100
+ bundleRoute = route;
1077
1101
  }
1078
1102
  }
1079
1103
  if (layoutExpr) lines.push(`${nextIndent}_L: ${layoutExpr},`);
@@ -1084,29 +1108,25 @@ function getClientRouteBundleNames(qwikPlugin, r) {
1084
1108
  if (bundleNames.length > 0) lines.push(`${nextIndent}_B: ${JSON.stringify(bundleNames)},`);
1085
1109
  }
1086
1110
  }
1087
- {
1088
- const routeFiles = [];
1089
- for (const file of node._files.filter((f) => f.type === "layout" || f.type === "route" && routeIdMap.has(f.filePath))) routeFiles.push(file.filePath, ...routeLoaderSourceFiles?.get(file.filePath) ?? []);
1090
- if (node === ctx.routeTrie) for (const plugin of ctx.serverPlugins) routeFiles.push(plugin.filePath, ...routeLoaderSourceFiles?.get(plugin.filePath) ?? []);
1091
- const routeLoaderFiles = [...new Set(routeFiles)];
1092
- if (routeLoaderFiles.length > 0) {
1093
- if (loadersByFile) {
1094
- const nodeLoaderHashes = [];
1095
- for (const filePath of routeLoaderFiles) {
1096
- const hashes = loadersByFile.get(normalizePathKey(filePath));
1097
- if (hashes) nodeLoaderHashes.push(...hashes);
1098
- }
1099
- if (nodeLoaderHashes.length > 0) lines.push(`${nextIndent}_R: ${JSON.stringify(nodeLoaderHashes)},`);
1100
- } else {
1101
- const placeholder = `__LOADERS:${routeLoaderFiles.join("|")}__`;
1102
- lines.push(`${nextIndent}_R: ${JSON.stringify(placeholder)},`);
1103
- }
1111
+ const pluginFiles = ctx.serverPlugins.map((plugin) => plugin.filePath);
1112
+ const layoutFiles = node._files.filter((file) => file.type === "layout" && !file.extlessName.startsWith("layout-")).map((file) => file.filePath);
1113
+ if (node === ctx.routeTrie) layoutFiles.push(...pluginFiles);
1114
+ const pageFiles = bundleRoute ? [bundleRoute.filePath] : [];
1115
+ if (pageLayouts) pageFiles.push(...pageLayouts.map((layout) => layout.filePath), ...pluginFiles);
1116
+ for (const [field, routeFiles] of [["_R", layoutFiles], ["_D", pageFiles]]) {
1117
+ const routeLoaderFiles = [...new Set(routeFiles.flatMap((path) => [path, ...routeLoaderSourceFiles?.get(path) ?? []]))];
1118
+ if (routeLoaderFiles.length === 0) continue;
1119
+ if (loadersByFile) {
1120
+ const hashes = routeLoaderFiles.flatMap((filePath) => loadersByFile.get(normalizePathKey(filePath)) ?? []);
1121
+ if (hashes.length > 0) lines.push(`${nextIndent}${field}: ${JSON.stringify(hashes)},`);
1122
+ } else {
1123
+ const placeholder = `__LOADERS:${routeLoaderFiles.join("|")}__`;
1124
+ lines.push(`${nextIndent}${field}: ${JSON.stringify(placeholder)},`);
1104
1125
  }
1105
1126
  }
1106
1127
  if (errorExpr) lines.push(`${nextIndent}_E: ${errorExpr},`);
1107
1128
  if (notFoundExpr) lines.push(`${nextIndent}_4: ${notFoundExpr},`);
1108
1129
  if (menuExpr) lines.push(`${nextIndent}_N: ${menuExpr},`);
1109
- const childAncestors = [...ancestorLayouts, ...nodeLayouts];
1110
1130
  const groupChildren = [];
1111
1131
  const regularChildren = [];
1112
1132
  for (const [key, child] of node.children) if (isGroupKey(key)) groupChildren.push([key, child]);
@@ -1115,13 +1135,13 @@ function getClientRouteBundleNames(qwikPlugin, r) {
1115
1135
  groupChildren.sort((a, b) => a[0].localeCompare(b[0]));
1116
1136
  const groupStrs = [];
1117
1137
  for (const [_key, child] of groupChildren) {
1118
- const childStr = serializeBuildTrie(ctx, qwikPlugin, child, layoutIdMap, routeIdMap, menuIdMap, childAncestors, isSSR, nextIndent, loadersByFile, routeLoaderSourceFiles);
1138
+ const childStr = serializeBuildTrie(ctx, qwikPlugin, child, layoutIdMap, routeIdMap, menuIdMap, isSSR, nextIndent, loadersByFile, routeLoaderSourceFiles);
1119
1139
  if (childStr !== "{}") groupStrs.push(childStr);
1120
1140
  }
1121
1141
  if (groupStrs.length > 0) lines.push(`${nextIndent}_M: [${groupStrs.join(", ")}],`);
1122
1142
  }
1123
1143
  for (const [key, child] of regularChildren) {
1124
- const childStr = serializeBuildTrie(ctx, qwikPlugin, child, layoutIdMap, routeIdMap, menuIdMap, childAncestors, isSSR, nextIndent, loadersByFile, routeLoaderSourceFiles);
1144
+ const childStr = serializeBuildTrie(ctx, qwikPlugin, child, layoutIdMap, routeIdMap, menuIdMap, isSSR, nextIndent, loadersByFile, routeLoaderSourceFiles);
1125
1145
  if (childStr !== "{}") {
1126
1146
  const keyStr = JSON.stringify(key);
1127
1147
  lines.push(`${nextIndent}${keyStr}: ${childStr},`);
@@ -1131,81 +1151,35 @@ function getClientRouteBundleNames(qwikPlugin, r) {
1131
1151
  return `{\n${lines.join("\n")}\n${indent}}`;
1132
1152
  }
1133
1153
  /**
1134
- * Serialize the build trie into JS code.
1135
- *
1136
- * Emits:
1137
- *
1138
- * - Layout imports and lazy loaders
1139
- * - Route/page imports and lazy loaders
1140
- * - Menu imports and lazy loaders
1141
- * - The route trie object literal with _L, _I, _G, _B, _4, _E, _N, _P, _0, _9, _M
1142
- */ function createRoutes(ctx, qwikPlugin, c, esmImports, isSSR, loadersByFile, serverExcludePaths, routeLoaderSourceFiles) {
1143
- const includeEndpoints = isSSR;
1144
- const dynamicImports = ctx.dynamicImports;
1145
- const layoutIdMap = /* @__PURE__ */ new Map();
1146
- const routeIdMap = /* @__PURE__ */ new Map();
1147
- const menuIdMap = /* @__PURE__ */ new Map();
1148
- let layoutCount = 0;
1149
- let routeCount = 0;
1150
- let menuCount = 0;
1151
- collectFiles(ctx.routeTrie, (file, _node) => {
1152
- if (file.type === "layout") {
1153
- const id = ctx.layouts.find((l) => l.filePath === file.filePath)?.id;
1154
- if (id) {
1155
- layoutIdMap.set(file.filePath, id);
1156
- layoutCount++;
1157
- }
1158
- } else if (file.type === "route") {
1159
- if (ctx.routes.find((r) => r.filePath === file.filePath)) routeCount++;
1160
- } else if (file.type === "menu") {
1161
- const id = createFileId(ctx.opts.routesDir, file.filePath);
1162
- menuIdMap.set(file.filePath, id);
1163
- menuCount++;
1164
- }
1165
- });
1166
- if (layoutCount > 0) {
1167
- c.push(`\n/** Qwik Router Layouts (${layoutCount}) */`);
1168
- for (const [filePath, id] of layoutIdMap) {
1169
- const importPath = JSON.stringify(getImportPath(filePath));
1170
- if (dynamicImports) c.push(`const ${id} = ()=>import(${importPath});`);
1171
- else {
1172
- esmImports.push(`import * as ${id}_ from ${importPath};`);
1173
- c.push(`const ${id} = ()=>${id}_;`);
1174
- }
1175
- }
1176
- }
1177
- if (menuCount > 0) {
1178
- c.push(`\n/** Qwik Router Menus (${menuCount}) */`);
1179
- for (const [filePath, id] of menuIdMap) {
1180
- const importPath = JSON.stringify(getImportPath(filePath));
1181
- if (dynamicImports) c.push(`const ${id} = ()=>import(${importPath});`);
1182
- else {
1183
- esmImports.push(`import * as ${id}_ from ${importPath};`);
1184
- c.push(`const ${id} = ()=>${id}_;`);
1154
+ * The `_I`/`_E`/`_4` loader expression: a bare loader (runtime prepends gathered layouts), or an
1155
+ * override chain for `!` (layout stop) / `@name` (named layout).
1156
+ */
1157
+ function buildLoaderChainExpr(extlessName, loaderExpr, layouts) {
1158
+ const { layoutName, layoutStop } = parseRouteIndexName(extlessName);
1159
+ if (layoutStop || layoutName) return `[ ${[...layouts.map((layout) => layout.id), loaderExpr].join(", ")} ]`;
1160
+ return loaderExpr;
1161
+ }
1162
+ function getClientRouteBundleNames(qwikPlugin, r) {
1163
+ const bundlesNames = [];
1164
+ const manifest = qwikPlugin.api.getManifest();
1165
+ if (manifest) {
1166
+ const manifestBundleNames = Object.keys(manifest.bundles);
1167
+ const addRouteFile = (filePath) => {
1168
+ filePath = removeExtension(filePath);
1169
+ for (const bundleName of manifestBundleNames) {
1170
+ const bundle = manifest.bundles[bundleName];
1171
+ if (bundle.origins) for (const bundleOrigin of bundle.origins) {
1172
+ const originPath = removeExtension(bundleOrigin);
1173
+ if (filePath.endsWith(originPath)) {
1174
+ if (!bundlesNames.includes(bundleName)) bundlesNames.push(bundleName);
1175
+ }
1176
+ }
1185
1177
  }
1186
- }
1187
- }
1188
- c.push(`\n/** Qwik Router Routes (${routeCount}) */`);
1189
- for (const route of ctx.routes) if (isPageExt(route.ext)) {
1190
- if (serverExcludePaths?.has(route.filePath)) continue;
1191
- const importPath = getImportPath(route.filePath);
1192
- let loaderExpr;
1193
- if (dynamicImports) loaderExpr = `()=>import(${JSON.stringify(importPath)})`;
1194
- else {
1195
- esmImports.push(`import * as ${route.id} from ${JSON.stringify(importPath)};`);
1196
- loaderExpr = `()=>${route.id}`;
1197
- }
1198
- routeIdMap.set(route.filePath, loaderExpr);
1199
- } else if (includeEndpoints && isModuleExt(route.ext)) {
1200
- const importPath = getImportPath(route.filePath);
1201
- esmImports.push(`import * as ${route.id} from ${JSON.stringify(importPath)};`);
1202
- routeIdMap.set(route.filePath, `()=>${route.id}`);
1178
+ };
1179
+ for (const layout of r.layouts) addRouteFile(layout.filePath);
1180
+ addRouteFile(r.filePath);
1203
1181
  }
1204
- const trieStr = serializeBuildTrie(ctx, qwikPlugin, ctx.routeTrie, layoutIdMap, routeIdMap, menuIdMap, [], isSSR, "", loadersByFile, routeLoaderSourceFiles);
1205
- const baseSegments = ctx.opts.basePathname.split("/").filter((s) => s.length > 0);
1206
- let routesExpr = trieStr;
1207
- for (let j = baseSegments.length - 1; j >= 0; j--) routesExpr = `{ ${JSON.stringify(baseSegments[j])}: ${routesExpr} }`;
1208
- c.push(`export const routes = ${routesExpr};`);
1182
+ return bundlesNames;
1209
1183
  }
1210
1184
  function createServerPlugins(ctx, _qwikPlugin, c, esmImports, isSSR) {
1211
1185
  c.push(`\n/** Qwik Router ServerPlugins (${ctx.serverPlugins.length}) */`);
@@ -1219,24 +1193,23 @@ function createServerPlugins(ctx, _qwikPlugin, c, esmImports, isSSR) {
1219
1193
  }
1220
1194
  c.push(`];`);
1221
1195
  }
1222
- /** Generates the Qwik Router Config runtime code */ function generateQwikRouterConfig(ctx, qwikPlugin, isSSR, loadersByFile, serverExcludePaths, routeLoaderSourceFiles) {
1196
+ /** Generates the Qwik Router Config runtime code */
1197
+ function generateQwikRouterConfig(ctx, qwikPlugin, isSSR, loadersByFile, serverExcludePaths, routeLoaderSourceFiles) {
1223
1198
  const esmImports = [];
1224
1199
  const c = [];
1225
1200
  c.push(`\n/** Qwik Router Config */`);
1226
1201
  c.push(`\nimport { isDev } from '@qwik.dev/core/build';`);
1227
- if (isSSR) esmImports.push(`import 'virtual:qwik-router-server-fns';`);
1202
+ if (isSSR) esmImports.push(`import { importEagerModules } from 'virtual:qwik-router-server-fns';`, `export { importEagerModules } from 'virtual:qwik-router-server-fns';`);
1228
1203
  createServerPlugins(ctx, qwikPlugin, c, esmImports, isSSR);
1229
1204
  createRoutes(ctx, qwikPlugin, c, esmImports, isSSR, loadersByFile, serverExcludePaths, routeLoaderSourceFiles);
1230
1205
  createEntries(ctx, c);
1231
1206
  c.push(`export const trailingSlash = ${JSON.stringify(!globalThis.__NO_TRAILING_SLASH__)};`);
1232
1207
  c.push(`export const basePathname = ${JSON.stringify(ctx.opts.basePathname)};`);
1233
1208
  c.push(`export const cacheModules = !isDev;`);
1234
- c.push(`export default { routes, serverPlugins, trailingSlash, basePathname, cacheModules };`);
1209
+ if (isSSR) c.push(`export default { routes, serverPlugins, trailingSlash, basePathname, cacheModules, importEagerModules };`);
1210
+ else c.push(`export default { routes, serverPlugins, trailingSlash, basePathname, cacheModules };`);
1235
1211
  return esmImports.join("\n") + c.join("\n");
1236
1212
  }
1237
- var SW_UNREGISTER = `
1238
- "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)
1239
- `;
1240
1213
  function generateServiceWorkerRegister(ctx, swRegister) {
1241
1214
  let swReg;
1242
1215
  let swUrl = "/service-worker.js";
@@ -1249,10 +1222,14 @@ function generateServiceWorkerRegister(ctx, swRegister) {
1249
1222
  swReg = swReg.replace("__url", swUrl);
1250
1223
  return `export default ${JSON.stringify(swReg)};`;
1251
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
+ `;
1252
1228
  /**
1253
1229
  * Non-GET RouteModule handlers keep a route server-side (onGet is served statically). Typed against
1254
1230
  * keyof RouteModule so a renamed/added handler fails to compile until classified.
1255
- */ var NON_GET_HANDLERS = /* @__PURE__ */ new Set([
1231
+ */
1232
+ var NON_GET_HANDLERS = /* @__PURE__ */ new Set([
1256
1233
  "onRequest",
1257
1234
  "onPost",
1258
1235
  "onPut",
@@ -1266,11 +1243,13 @@ var ROUTER_MODULE = "@qwik.dev/router";
1266
1243
  * Imports tying a route to the server — loaders, actions, and server$ (which POSTs to the route's
1267
1244
  * own URL). Matched on the import statement (the optimizer's ctxName isn't available
1268
1245
  * pre-transform); fail-safe, so a stray match only keeps a route.
1269
- */ var SERVER_QRL = /\b(?:routeLoader|routeAction|globalAction|server)(?:\$|Qrl)/;
1246
+ */
1247
+ var SERVER_QRL = /\b(?:routeLoader|routeAction|globalAction|server)(?:\$|Qrl)/;
1270
1248
  /**
1271
1249
  * True when the module exports no non-GET handler and imports no loader/action/server$ from the
1272
1250
  * router. Pure (no I/O) for direct unit testing.
1273
- */ function isServerFreeSource(code) {
1251
+ */
1252
+ function isServerFreeSource(code) {
1274
1253
  for (const e of findExports(code)) {
1275
1254
  if (e.specifier) return false;
1276
1255
  if ([...e.name ? [e.name] : [], ...e.names ?? []].some((n) => NON_GET_HANDLERS.has(n))) return false;
@@ -1279,28 +1258,13 @@ var ROUTER_MODULE = "@qwik.dev/router";
1279
1258
  return true;
1280
1259
  }
1281
1260
  /**
1282
- * Read + lex a module once, memoized by path. Returns the cached in-flight promise (not async) so
1283
- * concurrent callers share one read; read/parse failure keeps the route.
1284
- */ function isFileServerFree(filePath, cache) {
1285
- let pending = cache.get(filePath);
1286
- if (!pending) {
1287
- pending = readFile(filePath, "utf-8").then(isServerFreeSource).catch(() => false);
1288
- cache.set(filePath, pending);
1289
- }
1290
- return pending;
1291
- }
1292
- /** The route's own module and every layout wrapping it must be free of server code. */ async function isRouteServerFree(route, cache) {
1293
- if (!isMarkdownExt(route.ext) && !await isFileServerFree(route.filePath, cache)) return false;
1294
- for (const layout of route.layouts) if (!await isFileServerFree(layout.filePath, cache)) return false;
1295
- return true;
1296
- }
1297
- /**
1298
1261
  * Route file paths to drop from the production server route plan — prerendered, static, server-free
1299
1262
  * pages (own module and every layout) — so they tree-shake out of size-capped edge bundles.
1300
1263
  * Candidates come from the SSG include/exclude; server surfaces are detected in route/layout source
1301
1264
  * only. Fail-safe: dynamic routes, re-exports, and read errors keep the route. A loader/action
1302
1265
  * route stays whole — stripping just its dead component needs optimizer segment data.
1303
- */ async function getServerExcludedRoutes(ctx, ssg) {
1266
+ */
1267
+ async function getServerExcludedRoutes(ctx, ssg) {
1304
1268
  const excluded = /* @__PURE__ */ new Set();
1305
1269
  if (!ssg?.include?.length) return excluded;
1306
1270
  const isPrerendered = createRouteTester(ctx.opts.basePathname, ssg.include, ssg.exclude);
@@ -1311,12 +1275,23 @@ var ROUTER_MODULE = "@qwik.dev/router";
1311
1275
  }));
1312
1276
  return excluded;
1313
1277
  }
1314
- function isBundlePartOfRoute(bundle, routeAndLayoutPaths) {
1315
- if (!bundle.origins) return false;
1316
- return bundle.origins.some((bundleOrigin) => {
1317
- const originPath = removeExtension(bundleOrigin);
1318
- return routeAndLayoutPaths.some((path) => path.endsWith(originPath));
1319
- });
1278
+ /** The route's own module and every layout wrapping it must be free of server code. */
1279
+ async function isRouteServerFree(route, cache) {
1280
+ if (!isMarkdownExt(route.ext) && !await isFileServerFree(route.filePath, cache)) return false;
1281
+ for (const layout of route.layouts) if (!await isFileServerFree(layout.filePath, cache)) return false;
1282
+ return true;
1283
+ }
1284
+ /**
1285
+ * Read + lex a module once, memoized by path. Returns the cached in-flight promise (not async) so
1286
+ * concurrent callers share one read; read/parse failure keeps the route.
1287
+ */
1288
+ function isFileServerFree(filePath, cache) {
1289
+ let pending = cache.get(filePath);
1290
+ if (!pending) {
1291
+ pending = readFile(filePath, "utf-8").then(isServerFreeSource).catch(() => false);
1292
+ cache.set(filePath, pending);
1293
+ }
1294
+ return pending;
1320
1295
  }
1321
1296
  function getRouteImports(routes, manifest) {
1322
1297
  const result = {};
@@ -1338,13 +1313,13 @@ function getRouteImports(routes, manifest) {
1338
1313
  }
1339
1314
  return result;
1340
1315
  }
1341
- var safeParseInt = (nu) => {
1342
- try {
1343
- return parseInt(nu, 10);
1344
- } catch {
1345
- return;
1346
- }
1347
- };
1316
+ function isBundlePartOfRoute(bundle, routeAndLayoutPaths) {
1317
+ if (!bundle.origins) return false;
1318
+ return bundle.origins.some((bundleOrigin) => {
1319
+ const originPath = removeExtension(bundleOrigin);
1320
+ return routeAndLayoutPaths.some((path) => path.endsWith(originPath));
1321
+ });
1322
+ }
1348
1323
  var findLocation = (e) => {
1349
1324
  const stack = e.stack;
1350
1325
  if (typeof stack === "string") {
@@ -1382,6 +1357,13 @@ var findLocation = (e) => {
1382
1357
  }
1383
1358
  }
1384
1359
  };
1360
+ var safeParseInt = (nu) => {
1361
+ try {
1362
+ return parseInt(nu, 10);
1363
+ } catch {
1364
+ return;
1365
+ }
1366
+ };
1385
1367
  var splitRE = /\r?\n/;
1386
1368
  var range = 2;
1387
1369
  function posToNumber(source, pos) {
@@ -1508,60 +1490,9 @@ function createImageJsxModule(pathId, params, userOpts) {
1508
1490
  function createSvgJsxModule(attrs) {
1509
1491
  return `export default p => <svg {...p} {...${JSON.stringify(attrs)}} />`;
1510
1492
  }
1511
- function optimizeSvg({ code, path }, userOpts) {
1512
- const svgAttributes = {};
1513
- const prefixIdsConfiguration = userOpts?.imageOptimization?.svgo?.prefixIds;
1514
- const maybePrefixIdsPlugin = prefixIdsConfiguration !== false ? [{
1515
- name: "prefixIds",
1516
- params: prefixIdsConfiguration
1517
- }] : [];
1518
- const userPlugins = userOpts?.imageOptimization?.svgo?.plugins?.filter((plugin) => {
1519
- if (plugin === "preset-default" || typeof plugin === "object" && plugin.name === "preset-default") {
1520
- console.warn(`You are trying to use the preset-default SVGO plugin. This plugin is already included by default, you can customize it through the defaultPresetOverrides option.`);
1521
- return false;
1522
- }
1523
- if (plugin === "prefixIds" || typeof plugin === "object" && plugin.name === "prefixIds") {
1524
- console.warn(`You are trying to use the prefixIds SVGO plugin. This plugin is already included by default, you can customize it through the prefixIds option.`);
1525
- return false;
1526
- }
1527
- return true;
1528
- }) || [];
1529
- const data = optimize(code, {
1530
- floatPrecision: userOpts?.imageOptimization?.svgo?.floatPrecision,
1531
- multipass: userOpts?.imageOptimization?.svgo?.multipass,
1532
- path,
1533
- plugins: [
1534
- {
1535
- name: "preset-default",
1536
- params: { overrides: {
1537
- removeViewBox: false,
1538
- ...userOpts?.imageOptimization?.svgo?.defaultPresetOverrides
1539
- } }
1540
- },
1541
- {
1542
- name: "customPluginName",
1543
- fn: () => {
1544
- return { element: { exit: (node) => {
1545
- if (node.name === "svg") {
1546
- node.name = "g";
1547
- Object.assign(svgAttributes, node.attributes);
1548
- node.attributes = {};
1549
- }
1550
- } } };
1551
- }
1552
- },
1553
- ...maybePrefixIdsPlugin,
1554
- ...userPlugins
1555
- ]
1556
- }).data;
1557
- svgAttributes.dangerouslySetInnerHTML = data.slice(3, -4);
1558
- return {
1559
- data,
1560
- svgAttributes
1561
- };
1562
- }
1563
- /** @public */ function imagePlugin(userOpts) {
1564
- return [import("vite-imagetools").then((_rawProps) => _rawProps.imagetools({
1493
+ /** @public */
1494
+ function imagePlugin(userOpts) {
1495
+ return [import("vite-imagetools").then(({ imagetools }) => imagetools({
1565
1496
  exclude: [],
1566
1497
  extendOutputFormats(builtins) {
1567
1498
  const jsx = () => (metadatas) => {
@@ -1654,6 +1585,58 @@ function optimizeSvg({ code, path }, userOpts) {
1654
1585
  }
1655
1586
  }];
1656
1587
  }
1588
+ function optimizeSvg({ code, path }, userOpts) {
1589
+ const svgAttributes = {};
1590
+ const prefixIdsConfiguration = userOpts?.imageOptimization?.svgo?.prefixIds;
1591
+ const maybePrefixIdsPlugin = prefixIdsConfiguration !== false ? [{
1592
+ name: "prefixIds",
1593
+ params: prefixIdsConfiguration
1594
+ }] : [];
1595
+ const userPlugins = userOpts?.imageOptimization?.svgo?.plugins?.filter((plugin) => {
1596
+ if (plugin === "preset-default" || typeof plugin === "object" && plugin.name === "preset-default") {
1597
+ console.warn(`You are trying to use the preset-default SVGO plugin. This plugin is already included by default, you can customize it through the defaultPresetOverrides option.`);
1598
+ return false;
1599
+ }
1600
+ if (plugin === "prefixIds" || typeof plugin === "object" && plugin.name === "prefixIds") {
1601
+ console.warn(`You are trying to use the prefixIds SVGO plugin. This plugin is already included by default, you can customize it through the prefixIds option.`);
1602
+ return false;
1603
+ }
1604
+ return true;
1605
+ }) || [];
1606
+ const data = optimize(code, {
1607
+ floatPrecision: userOpts?.imageOptimization?.svgo?.floatPrecision,
1608
+ multipass: userOpts?.imageOptimization?.svgo?.multipass,
1609
+ path,
1610
+ plugins: [
1611
+ {
1612
+ name: "preset-default",
1613
+ params: { overrides: {
1614
+ removeViewBox: false,
1615
+ ...userOpts?.imageOptimization?.svgo?.defaultPresetOverrides
1616
+ } }
1617
+ },
1618
+ {
1619
+ name: "customPluginName",
1620
+ fn: () => {
1621
+ return { element: { exit: (node) => {
1622
+ if (node.name === "svg") {
1623
+ node.name = "g";
1624
+ Object.assign(svgAttributes, node.attributes);
1625
+ node.attributes = {};
1626
+ }
1627
+ } } };
1628
+ }
1629
+ },
1630
+ ...maybePrefixIdsPlugin,
1631
+ ...userPlugins
1632
+ ]
1633
+ }).data;
1634
+ svgAttributes.dangerouslySetInnerHTML = data.slice(3, -4);
1635
+ return {
1636
+ data,
1637
+ svgAttributes
1638
+ };
1639
+ }
1657
1640
  async function collectServerFnModuleIds(routingContext, resolvedVirtualId, pluginContext) {
1658
1641
  const serverFnModules = /* @__PURE__ */ new Set();
1659
1642
  const queuedModuleIds = /* @__PURE__ */ new Set();
@@ -1725,11 +1708,23 @@ function formatError(e) {
1725
1708
  }
1726
1709
  return e;
1727
1710
  }
1711
+ var State = /* @__PURE__ */ function(State) {
1712
+ /** Collecting content until <body> is found */
1713
+ State[State["BUFFERING"] = 0] = "BUFFERING";
1714
+ /** Transforming the head portion */
1715
+ State[State["PROCESSING_HEAD"] = 1] = "PROCESSING_HEAD";
1716
+ /** Streaming transformed content until </body> */
1717
+ State[State["STREAMING_BODY"] = 2] = "STREAMING_BODY";
1718
+ /** Passing through content unchanged */
1719
+ State[State["PASSTHROUGH"] = 3] = "PASSTHROUGH";
1720
+ return State;
1721
+ }(State || {});
1728
1722
  /**
1729
1723
  * Patches a response object to intercept HTML streams and transform them using Vite's
1730
1724
  * transformIndexHtml.
1731
- */ var HtmlTransformPatcher = class {
1732
- state = 0;
1725
+ */
1726
+ var HtmlTransformPatcher = class {
1727
+ state = State.BUFFERING;
1733
1728
  buffer = "";
1734
1729
  headInnerIndex = -1;
1735
1730
  bodyInnerIndex = -1;
@@ -1782,7 +1777,7 @@ function formatError(e) {
1782
1777
  };
1783
1778
  }
1784
1779
  handleWrite(chunk, encoding, callback) {
1785
- if (!this.isHtmlResponse || this.state === 3) return this.origWrite(chunk, encoding, callback);
1780
+ if (!this.isHtmlResponse || this.state === State.PASSTHROUGH) return this.origWrite(chunk, encoding, callback);
1786
1781
  if (typeof encoding === "function") {
1787
1782
  callback = encoding;
1788
1783
  encoding = void 0;
@@ -1794,7 +1789,7 @@ function formatError(e) {
1794
1789
  else data = chunk?.toString() || "";
1795
1790
  this.buffer += data;
1796
1791
  switch (this.state) {
1797
- case 0:
1792
+ case State.BUFFERING:
1798
1793
  if (this.headInnerIndex === -1) {
1799
1794
  const headMatch = this.buffer.match(/<head[^>]*>/i);
1800
1795
  if (headMatch) {
@@ -1805,15 +1800,15 @@ function formatError(e) {
1805
1800
  if (this.headInnerIndex !== -1) {
1806
1801
  const bodyMatch = this.buffer.slice(this.headInnerIndex).match(/<body[^>]*>/i);
1807
1802
  if (bodyMatch) {
1808
- this.state = 1;
1803
+ this.state = State.PROCESSING_HEAD;
1809
1804
  const bodyOuterIndex = this.buffer.indexOf(bodyMatch[0]);
1810
1805
  this.bodyInnerIndex = bodyOuterIndex + bodyMatch[0].length;
1811
1806
  this.processingPromise = this.processHead();
1812
1807
  }
1813
1808
  }
1814
1809
  break;
1815
- case 1: break;
1816
- case 2:
1810
+ case State.PROCESSING_HEAD: break;
1811
+ case State.STREAMING_BODY:
1817
1812
  this.handleStreamingBodyState();
1818
1813
  break;
1819
1814
  default: throw new Error(`Invalid state: ${this.state}`);
@@ -1840,7 +1835,7 @@ function formatError(e) {
1840
1835
  if (headCloseIndex === -1) throw new Error("Buffered HTML does not contain </head>");
1841
1836
  this.buffer = this.buffer.slice(0, this.headInnerIndex) + headPreContent + this.buffer.slice(this.headInnerIndex, headCloseIndex) + headPostContent + this.buffer.slice(headCloseIndex, this.bodyInnerIndex) + bodyPreContent + this.buffer.slice(this.bodyInnerIndex);
1842
1837
  if (this.bodyPostContent.length > 0) {
1843
- this.state = 2;
1838
+ this.state = State.STREAMING_BODY;
1844
1839
  this.handleStreamingBodyState();
1845
1840
  return;
1846
1841
  }
@@ -1863,7 +1858,7 @@ function formatError(e) {
1863
1858
  this.flushBuffer(6);
1864
1859
  }
1865
1860
  transitionToPassthrough() {
1866
- this.state = 3;
1861
+ this.state = State.PASSTHROUGH;
1867
1862
  this.flushBuffer();
1868
1863
  }
1869
1864
  flushBuffer(keep = 0) {
@@ -1892,7 +1887,8 @@ function formatError(e) {
1892
1887
  this.origEnd(callback);
1893
1888
  }
1894
1889
  };
1895
- /** Patches a response to enable HTML transformation using Vite's transformIndexHtml */ function wrapResponseForHtmlTransform(request, response, server) {
1890
+ /** Patches a response to enable HTML transformation using Vite's transformIndexHtml */
1891
+ function wrapResponseForHtmlTransform(request, response, server) {
1896
1892
  new HtmlTransformPatcher(request, response, server);
1897
1893
  return response;
1898
1894
  }
@@ -1901,43 +1897,6 @@ var ROUTE_PATH_HEADER = "X-Qwik-route-path";
1901
1897
  function getDevMiddlewareRequestPath(req) {
1902
1898
  return req.originalUrl || req.url;
1903
1899
  }
1904
- async function loadSsrModule(server, filePath) {
1905
- return server.ssrLoadModule(getImportPath(normalizePath$1(filePath)));
1906
- }
1907
- function matchesLoaderId(loader, loaderId) {
1908
- return loader.__id === loaderId || loader.__qrl.getHash() === loaderId;
1909
- }
1910
- function isLoaderInternal(value) {
1911
- return typeof value === "function" && value.__brand === "server_loader";
1912
- }
1913
- function findMatchingLoader(mod, loaderId) {
1914
- for (const value of Object.values(mod)) if (isLoaderInternal(value) && matchesLoaderId(value, loaderId)) return value;
1915
- }
1916
- async function preloadQLoaderRouteLoader(server, ctx, req) {
1917
- const requestPath = getDevMiddlewareRequestPath(req);
1918
- if (!requestPath) return;
1919
- let requestUrl;
1920
- try {
1921
- requestUrl = new URL(requestPath, "http://qwik.dev");
1922
- } catch {
1923
- return;
1924
- }
1925
- const recognized = recognizeRequest(requestUrl.pathname);
1926
- if (recognized?.type !== "@isQLoader" || !recognized.data?.loaderId) return;
1927
- const loaderId = recognized.data.loaderId;
1928
- const headerValue = req.headers[FULLPATH_HEADER.toLowerCase()] ?? req.headers[ROUTE_PATH_HEADER.toLowerCase()];
1929
- const fullPathHeader = Array.isArray(headerValue) ? headerValue[0] : headerValue ?? null;
1930
- const loaderPathname = trimRecognizedInternalPathname(requestUrl.pathname, recognized);
1931
- const pathname = resolveValidInternalFullPathname(loaderPathname, fullPathHeader) ?? loaderPathname;
1932
- const route = ctx.routes.filter((route) => isPageExt(route.ext)).slice().sort(routeSortCompare).find((route) => route.pattern.test(pathname));
1933
- if (!route) return;
1934
- const routeRootFiles = [...route.layouts.map((layout) => layout.filePath), route.filePath];
1935
- const modules = await Promise.all([...ctx.serverPlugins.map((plugin) => loadSsrModule(server, plugin.filePath)), ...routeRootFiles.map((filePath) => loadSsrModule(server, filePath))]);
1936
- for (const mod of modules) {
1937
- const loader = findMatchingLoader(mod, loaderId);
1938
- if (loader) return loader;
1939
- }
1940
- }
1941
1900
  var makeRouterDevMiddleware = (server, ctx) => async (req, res, next) => {
1942
1901
  let mod;
1943
1902
  try {
@@ -1977,9 +1936,9 @@ var makeRouterDevMiddleware = (server, ctx) => async (req, res, next) => {
1977
1936
  globalThis.__qwik = void 0;
1978
1937
  const { createQwikRouter } = await server.ssrLoadModule("@qwik.dev/router/middleware/node");
1979
1938
  try {
1980
- const render = async (opts) => {
1939
+ const render = (async (opts) => {
1981
1940
  return await renderer(opts);
1982
- };
1941
+ });
1983
1942
  const { router, staticFile } = createQwikRouter({
1984
1943
  render,
1985
1944
  platform: ctx.opts.platform
@@ -1997,6 +1956,43 @@ var makeRouterDevMiddleware = (server, ctx) => async (req, res, next) => {
1997
1956
  return;
1998
1957
  }
1999
1958
  };
1959
+ async function preloadQLoaderRouteLoader(server, ctx, req) {
1960
+ const requestPath = getDevMiddlewareRequestPath(req);
1961
+ if (!requestPath) return;
1962
+ let requestUrl;
1963
+ try {
1964
+ requestUrl = new URL(requestPath, "http://qwik.dev");
1965
+ } catch {
1966
+ return;
1967
+ }
1968
+ const recognized = recognizeRequest(requestUrl.pathname);
1969
+ if (recognized?.type !== "@isQLoader" || !recognized.data?.loaderId) return;
1970
+ const loaderId = recognized.data.loaderId;
1971
+ const headerValue = req.headers[FULLPATH_HEADER.toLowerCase()] ?? req.headers[ROUTE_PATH_HEADER.toLowerCase()];
1972
+ const fullPathHeader = Array.isArray(headerValue) ? headerValue[0] : headerValue ?? null;
1973
+ const loaderPathname = trimRecognizedInternalPathname(requestUrl.pathname, recognized);
1974
+ const pathname = resolveValidInternalFullPathname(loaderPathname, fullPathHeader) ?? loaderPathname;
1975
+ const route = ctx.routes.filter((route) => isPageExt(route.ext)).slice().sort(routeSortCompare).find((route) => route.pattern.test(pathname));
1976
+ if (!route) return;
1977
+ const routeRootFiles = [...route.layouts.map((layout) => layout.filePath), route.filePath];
1978
+ const modules = await Promise.all([...ctx.serverPlugins.map((plugin) => loadSsrModule(server, plugin.filePath)), ...routeRootFiles.map((filePath) => loadSsrModule(server, filePath))]);
1979
+ for (const mod of modules) {
1980
+ const loader = findMatchingLoader(mod, loaderId);
1981
+ if (loader) return loader;
1982
+ }
1983
+ }
1984
+ async function loadSsrModule(server, filePath) {
1985
+ return server.ssrLoadModule(getImportPath(normalizePath$1(filePath)));
1986
+ }
1987
+ function findMatchingLoader(mod, loaderId) {
1988
+ for (const value of Object.values(mod)) if (isLoaderInternal(value) && matchesLoaderId(value, loaderId)) return value;
1989
+ }
1990
+ function matchesLoaderId(loader, loaderId) {
1991
+ return loader.__id === loaderId || loader.__qrl.getHash() === loaderId;
1992
+ }
1993
+ function isLoaderInternal(value) {
1994
+ return typeof value === "function" && value.__brand === "server_loader";
1995
+ }
2000
1996
  var CSS_EXTENSIONS = [
2001
1997
  ".css",
2002
1998
  ".scss",
@@ -2014,7 +2010,8 @@ var isCssPath = (url) => CSS_EXTENSIONS.some((ext) => url.endsWith(ext));
2014
2010
  * We check both the client and SSR module graphs because on the first page request only the SSR
2015
2011
  * graph has been populated (via ssrLoadModule). The client graph is empty until the browser
2016
2012
  * actually fetches modules through Vite's dev server.
2017
- */ var getCssUrls = (server) => {
2013
+ */
2014
+ var getCssUrls = (server) => {
2018
2015
  const clientGraph = server.environments.client.moduleGraph;
2019
2016
  const ssrGraph = server.environments.ssr.moduleGraph;
2020
2017
  const cssModules = /* @__PURE__ */ new Set();
@@ -2053,7 +2050,8 @@ var getRouterIndexTags = (server) => {
2053
2050
  /**
2054
2051
  * Live-reload route CSS that Qwik injects as `<link>` tags; it only lives in the SSR graph, so Vite
2055
2052
  * skips HMR. Emit a `css-update` to swap the `<link>` in place. Returns `true` when handled.
2056
- */ var sendRouterCssHotUpdate = (server, file, timestamp) => {
2053
+ */
2054
+ var sendRouterCssHotUpdate = (server, file, timestamp) => {
2057
2055
  const { client, ssr } = server.environments;
2058
2056
  if (!isCssPath(file) || client.moduleGraph.getModulesByFile(file)?.size) return false;
2059
2057
  const paths = /* @__PURE__ */ new Set();
@@ -2077,19 +2075,37 @@ var QWIK_ROUTER_CONFIG_ID = "@qwik-router-config";
2077
2075
  /**
2078
2076
  * This virtual module is used to generate dynamic entries for user route files, which are added as
2079
2077
  * dynamic imports to the qwik-router-config as a way to create new entry points for the build.
2080
- */ var QWIK_ROUTER_ENTRIES_ID = "@qwik-router-entries";
2078
+ */
2079
+ var QWIK_ROUTER_ENTRIES_ID = "@qwik-router-entries";
2081
2080
  var QWIK_ROUTER = "@qwik.dev/router";
2082
2081
  var QWIK_ROUTER_SW_REGISTER = "@qwik-router-sw-register";
2083
2082
  var VIRTUAL_SERVER_FNS = "virtual:qwik-router-server-fns";
2084
- /** Replace or strip `_R: "__LOADERS:path1|path2__"` placeholders in a bundle chunk. */ function replaceLoaderPlaceholders(code, loadersByFile) {
2085
- return code.replace(/_R\s*:\s*(["'`])__LOADERS:([^"'`]+)__\1\s*,?/g, (_match, _q, paths) => {
2083
+ /**
2084
+ * @deprecated Use `qwikRouter` instead. Will be removed in V3
2085
+ * @public
2086
+ */
2087
+ function qwikCity(userOpts) {
2088
+ return qwikRouter(userOpts);
2089
+ }
2090
+ /** @public */
2091
+ function qwikRouter(userOpts) {
2092
+ const buildContextRef = { current: null };
2093
+ return [
2094
+ qwikRouterPlugin(userOpts, buildContextRef),
2095
+ serverFnsPlugin(buildContextRef),
2096
+ ...imagePlugin(userOpts)
2097
+ ];
2098
+ }
2099
+ /** Replace or strip loader placeholders in a bundle chunk. */
2100
+ function replaceLoaderPlaceholders(code, loadersByFile) {
2101
+ return code.replace(/(_R|_D)\s*:\s*(["'`])__LOADERS:([^"'`]+)__\2\s*,?/g, (_match, field, _q, paths) => {
2086
2102
  const filePaths = JSON.parse(`"${paths}"`).split("|");
2087
2103
  const hashes = [];
2088
2104
  for (const filePath of filePaths) {
2089
2105
  const fileHashes = loadersByFile.get(normalizePathKey(filePath));
2090
2106
  if (fileHashes) hashes.push(...fileHashes);
2091
2107
  }
2092
- if (hashes.length > 0) return `_R: ${JSON.stringify(hashes)},`;
2108
+ if (hashes.length > 0) return `${field}: ${JSON.stringify(hashes)},`;
2093
2109
  return "";
2094
2110
  });
2095
2111
  }
@@ -2127,6 +2143,14 @@ function isDiscoveredRouteLoaderSource(filePath, sourceFiles) {
2127
2143
  for (const files of sourceFiles.values()) if (files.includes(normalizedPath)) return true;
2128
2144
  return false;
2129
2145
  }
2146
+ async function findRouteLoaderSourceFiles(ctx, resolveSource) {
2147
+ const sources = /* @__PURE__ */ new Map();
2148
+ const owners = /* @__PURE__ */ new Set();
2149
+ collectRouteLoaderOwners(ctx.routeTrie, owners);
2150
+ for (const plugin of ctx.serverPlugins) owners.add(plugin.filePath);
2151
+ await Promise.all([...owners].map((owner) => collectRouteLoaderSources(owner, owner, resolveSource, sources)));
2152
+ return sources;
2153
+ }
2130
2154
  function collectRouteLoaderOwners(node, owners) {
2131
2155
  for (const file of node._files) if (file.type === "layout" || file.type === "route") owners.add(file.filePath);
2132
2156
  for (const child of node.children.values()) collectRouteLoaderOwners(child, owners);
@@ -2166,14 +2190,6 @@ async function collectRouteLoaderSources(owner, filePath, resolveSource, sources
2166
2190
  }
2167
2191
  }
2168
2192
  }
2169
- async function findRouteLoaderSourceFiles(ctx, resolveSource) {
2170
- const sources = /* @__PURE__ */ new Map();
2171
- const owners = /* @__PURE__ */ new Set();
2172
- collectRouteLoaderOwners(ctx.routeTrie, owners);
2173
- for (const plugin of ctx.serverPlugins) owners.add(plugin.filePath);
2174
- await Promise.all([...owners].map((owner) => collectRouteLoaderSources(owner, owner, resolveSource, sources)));
2175
- return sources;
2176
- }
2177
2193
  function invalidateRouterConfigModules(server) {
2178
2194
  const modules = [];
2179
2195
  const moduleGraphs = /* @__PURE__ */ new Set();
@@ -2192,21 +2208,6 @@ function invalidateRouterConfigModules(server) {
2192
2208
  }
2193
2209
  return modules;
2194
2210
  }
2195
- async function generateServerPackageJson(outDir) {
2196
- await fs.promises.mkdir(outDir, { recursive: true });
2197
- const serverPackageJsonPath = join(outDir, "package.json");
2198
- let packageJson = {};
2199
- if (fs.existsSync(serverPackageJsonPath)) {
2200
- const content = await fs.promises.readFile(serverPackageJsonPath, "utf-8");
2201
- packageJson = { ...JSON.parse(content) };
2202
- }
2203
- packageJson = {
2204
- ...packageJson,
2205
- type: "module"
2206
- };
2207
- const serverPackageJsonCode = JSON.stringify(packageJson, null, 2);
2208
- await fs.promises.writeFile(serverPackageJsonPath, serverPackageJsonCode);
2209
- }
2210
2211
  function qwikRouterPlugin(userOpts, buildContextRef) {
2211
2212
  let ctx = null;
2212
2213
  let mdxTransform = null;
@@ -2218,9 +2219,11 @@ function qwikRouterPlugin(userOpts, buildContextRef) {
2218
2219
  let devSsrServer = userOpts?.devSsrServer;
2219
2220
  const routesDir = userOpts?.routesDir ?? "src/routes";
2220
2221
  const serverPluginsDir = userOpts?.serverPluginsDir ?? routesDir;
2221
- /** Map from source file path to array of routeLoader$ hashes found in that file */ const loadersByFile = /* @__PURE__ */ new Map();
2222
+ /** Map from source file path to array of routeLoader$ hashes found in that file */
2223
+ const loadersByFile = /* @__PURE__ */ new Map();
2222
2224
  let reExportedRouteLoaderSources;
2223
- /** SSG include/exclude from the adapter (see `_setSsgRoutes`); drives the server-route prune. */ let ssgRoutePatterns;
2225
+ /** SSG include/exclude from the adapter (see `_setSsgRoutes`); drives the server-route prune. */
2226
+ let ssgRoutePatterns;
2224
2227
  return {
2225
2228
  name: "vite-plugin-qwik-router",
2226
2229
  enforce: "pre",
@@ -2245,6 +2248,7 @@ function qwikRouterPlugin(userOpts, buildContextRef) {
2245
2248
  define: {
2246
2249
  "globalThis.__DEFAULT_LOADERS_SERIALIZATION_STRATEGY__": JSON.stringify(userOpts?.defaultLoadersSerializationStrategy || "never"),
2247
2250
  "globalThis.__NO_TRAILING_SLASH__": JSON.stringify(userOpts?.trailingSlash === false),
2251
+ "globalThis.__QWIK_ROUTER_BASE_PATHNAME__": JSON.stringify(resolveBasePathname(userOpts, _viteConfig.base || "/")),
2248
2252
  "globalThis.__SSR_CACHE_SIZE__": JSON.stringify(viteEnv.command === "serve" ? 0 : userOpts?.ssrCacheSize ?? 50),
2249
2253
  "globalThis.__STRICT_LOADERS__": JSON.stringify(userOpts?.strictLoaders !== false)
2250
2254
  },
@@ -2372,6 +2376,10 @@ function qwikRouterPlugin(userOpts, buildContextRef) {
2372
2376
  },
2373
2377
  buildStart() {
2374
2378
  resetBuildContext(ctx);
2379
+ if (this.environment.mode === "build" && this.environment.config.consumer === "client") this.emitFile({
2380
+ type: "chunk",
2381
+ id: QWIK_ROUTER_CONFIG_ID
2382
+ });
2375
2383
  },
2376
2384
  resolveId(id) {
2377
2385
  if (id === "@qwik-router-config" || id === QWIK_ROUTER_ENTRIES_ID) return {
@@ -2472,11 +2480,27 @@ function qwikRouterPlugin(userOpts, buildContextRef) {
2472
2480
  }
2473
2481
  };
2474
2482
  }
2483
+ async function generateServerPackageJson(outDir) {
2484
+ await fs.promises.mkdir(outDir, { recursive: true });
2485
+ const serverPackageJsonPath = join(outDir, "package.json");
2486
+ let packageJson = {};
2487
+ if (fs.existsSync(serverPackageJsonPath)) {
2488
+ const content = await fs.promises.readFile(serverPackageJsonPath, "utf-8");
2489
+ packageJson = { ...JSON.parse(content) };
2490
+ }
2491
+ packageJson = {
2492
+ ...packageJson,
2493
+ type: "module"
2494
+ };
2495
+ const serverPackageJsonCode = JSON.stringify(packageJson, null, 2);
2496
+ await fs.promises.writeFile(serverPackageJsonPath, serverPackageJsonCode);
2497
+ }
2475
2498
  /**
2476
2499
  * Separate plugin that tracks modules containing server$ functions during SSR builds and generates
2477
2500
  * `virtual:qwik-router-server-fns` — a virtual module that statically imports all such modules so
2478
2501
  * their `_regSymbol` side effects run before any RPC request arrives.
2479
- */ function serverFnsPlugin(buildContextRef) {
2502
+ */
2503
+ function serverFnsPlugin(buildContextRef) {
2480
2504
  const RESOLVED_ID = "\0virtual:qwik-router-server-fns";
2481
2505
  const serverFnModules = /* @__PURE__ */ new Set();
2482
2506
  let serverFnsReady = null;
@@ -2515,26 +2539,12 @@ function qwikRouterPlugin(userOpts, buildContextRef) {
2515
2539
  const isServerBuild = this.environment.config.consumer === "server" && this.environment.mode === "build";
2516
2540
  if (id === RESOLVED_ID) {
2517
2541
  if (isServerBuild) await collectServerFnModules.call(this);
2518
- if (!isServerBuild || serverFnModules.size === 0) return "// No server$ functions";
2519
- return [...serverFnModules].map((mod, index) => `import * as serverFnModule${index} from ${JSON.stringify(mod)};\nObject.values(serverFnModule${index});`).join("\n");
2542
+ if (!isServerBuild || serverFnModules.size === 0) return "export const importEagerModules = () => Promise.resolve();\n";
2543
+ return "export const importEagerModules = () =>\n Promise.all([\n" + [...serverFnModules].map((mod) => ` import(${JSON.stringify(mod)}),`).join("\n") + "\n ]);\n";
2520
2544
  }
2521
2545
  return null;
2522
2546
  }
2523
2547
  }
2524
2548
  };
2525
2549
  }
2526
- /** @public */ function qwikRouter(userOpts) {
2527
- const buildContextRef = { current: null };
2528
- return [
2529
- qwikRouterPlugin(userOpts, buildContextRef),
2530
- serverFnsPlugin(buildContextRef),
2531
- ...imagePlugin(userOpts)
2532
- ];
2533
- }
2534
- /**
2535
- * @deprecated Use `qwikRouter` instead. Will be removed in V3
2536
- * @public
2537
- */ function qwikCity(userOpts) {
2538
- return qwikRouter(userOpts);
2539
- }
2540
2550
  export { extendConfig, qwikCity, qwikRouter };