@qwik.dev/router 2.0.0-beta.3 → 2.0.0-beta.31

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 (84) hide show
  1. package/adapters/static/vite.d.ts +1 -1
  2. package/lib/adapters/azure-swa/vite/index.d.ts +2 -2
  3. package/lib/adapters/azure-swa/vite/index.mjs +39 -44
  4. package/lib/adapters/bun-server/vite/index.d.ts +2 -2
  5. package/lib/adapters/bun-server/vite/index.mjs +6 -7
  6. package/lib/adapters/cloud-run/vite/index.d.ts +2 -2
  7. package/lib/adapters/cloud-run/vite/index.mjs +6 -7
  8. package/lib/adapters/cloudflare-pages/vite/index.d.ts +2 -2
  9. package/lib/adapters/cloudflare-pages/vite/index.mjs +23 -32
  10. package/lib/adapters/deno-server/vite/index.d.ts +2 -2
  11. package/lib/adapters/deno-server/vite/index.mjs +13 -9
  12. package/lib/adapters/netlify-edge/vite/index.d.ts +2 -2
  13. package/lib/adapters/netlify-edge/vite/index.mjs +22 -36
  14. package/lib/adapters/node-server/vite/index.d.ts +2 -2
  15. package/lib/adapters/node-server/vite/index.mjs +6 -7
  16. package/lib/adapters/shared/vite/index.d.ts +7 -19
  17. package/lib/adapters/shared/vite/index.mjs +244 -233
  18. package/lib/adapters/ssg/vite/index.d.ts +13 -0
  19. package/lib/adapters/ssg/vite/index.mjs +17 -0
  20. package/lib/adapters/vercel-edge/vite/index.d.ts +3 -3
  21. package/lib/adapters/vercel-edge/vite/index.mjs +33 -19
  22. package/lib/chunks/deepFreeze.qwik.mjs +18 -0
  23. package/lib/chunks/error-handler.mjs +57 -0
  24. package/lib/chunks/fs.mjs +144 -0
  25. package/lib/chunks/http-error.qwik.mjs +35 -0
  26. package/lib/chunks/not-found-wrapper.qwik.mjs +25 -0
  27. package/lib/chunks/pathname.mjs +105 -0
  28. package/lib/chunks/redirect-handler.mjs +6 -0
  29. package/lib/chunks/routing.qwik.mjs +821 -0
  30. package/lib/chunks/system.mjs +333 -0
  31. package/lib/chunks/url.mjs +61 -0
  32. package/lib/chunks/use-functions.qwik.mjs +35 -0
  33. package/lib/chunks/worker-thread.qwik.mjs +2573 -0
  34. package/lib/index.d.ts +362 -142
  35. package/lib/index.qwik.mjs +949 -1244
  36. package/lib/middleware/aws-lambda/index.d.ts +0 -5
  37. package/lib/middleware/aws-lambda/index.mjs +14 -17
  38. package/lib/middleware/azure-swa/index.mjs +16 -221
  39. package/lib/middleware/bun/index.d.ts +11 -0
  40. package/lib/middleware/bun/index.mjs +50 -97
  41. package/lib/middleware/cloudflare-pages/index.mjs +22 -31
  42. package/lib/middleware/deno/index.d.ts +11 -0
  43. package/lib/middleware/deno/index.mjs +49 -97
  44. package/lib/middleware/firebase/index.mjs +6 -15
  45. package/lib/middleware/netlify-edge/index.mjs +22 -32
  46. package/lib/middleware/node/index.mjs +31 -105
  47. package/lib/middleware/request-handler/index.d.ts +163 -88
  48. package/lib/middleware/request-handler/index.mjs +1458 -1260
  49. package/lib/middleware/vercel-edge/index.mjs +27 -36
  50. package/lib/modules.d.ts +11 -16
  51. package/lib/service-worker/index.mjs +4 -0
  52. package/lib/{static → ssg}/index.d.ts +45 -13
  53. package/lib/ssg/index.mjs +336 -0
  54. package/lib/vite/index.d.ts +38 -10
  55. package/lib/vite/index.mjs +2067 -26841
  56. package/modules.d.ts +11 -16
  57. package/package.json +62 -67
  58. package/ssg.d.ts +2 -0
  59. package/static.d.ts +1 -1
  60. package/lib/adapters/azure-swa/vite/index.cjs +0 -96
  61. package/lib/adapters/bun-server/vite/index.cjs +0 -50
  62. package/lib/adapters/cloud-run/vite/index.cjs +0 -47
  63. package/lib/adapters/cloudflare-pages/vite/index.cjs +0 -115
  64. package/lib/adapters/deno-server/vite/index.cjs +0 -62
  65. package/lib/adapters/netlify-edge/vite/index.cjs +0 -129
  66. package/lib/adapters/node-server/vite/index.cjs +0 -50
  67. package/lib/adapters/shared/vite/index.cjs +0 -378
  68. package/lib/adapters/static/vite/index.cjs +0 -368
  69. package/lib/adapters/static/vite/index.d.ts +0 -10
  70. package/lib/adapters/static/vite/index.mjs +0 -331
  71. package/lib/adapters/vercel-edge/vite/index.cjs +0 -118
  72. package/lib/index.qwik.cjs +0 -1947
  73. package/lib/middleware/node/index.cjs +0 -314
  74. package/lib/middleware/request-handler/index.cjs +0 -1614
  75. package/lib/service-worker.cjs +0 -17
  76. package/lib/service-worker.mjs +0 -15
  77. package/lib/static/deno.mjs +0 -8
  78. package/lib/static/index.cjs +0 -67
  79. package/lib/static/index.mjs +0 -48
  80. package/lib/static/node.cjs +0 -1124
  81. package/lib/static/node.mjs +0 -1086
  82. package/lib/vite/index.cjs +0 -27445
  83. package/middleware/request-handler/generated/not-found-paths.ts +0 -7
  84. package/middleware/request-handler/generated/static-paths.ts +0 -35
@@ -1,27 +1,14 @@
1
- // packages/qwik-router/src/middleware/vercel-edge/index.ts
2
- import { getNotFound } from "@qwik-router-not-found-paths";
3
- import { isStaticPath } from "@qwik-router-static-paths";
4
- import { _deserialize, _serialize, _verifySerializable } from "@qwik.dev/core/internal";
5
- import { setServerPlatform } from "@qwik.dev/core/server";
6
- import { mergeHeadersCookies, requestHandler } from "../request-handler/index.mjs";
7
- var COUNTRY_HEADER_NAME = "x-vercel-ip-country";
8
- var IP_HEADER_NAME = "x-real-ip";
9
- var VERCEL_COOKIE = "__vdpl";
10
- var VERCEL_SKEW_PROTECTION_ENABLED = "VERCEL_SKEW_PROTECTION_ENABLED";
11
- var VERCEL_DEPLOYMENT_ID = "VERCEL_DEPLOYMENT_ID";
12
- var BASE_URL = "BASE_URL";
1
+ import { setServerPlatform } from '@qwik.dev/core/server';
2
+ import { isStaticPath, mergeHeadersCookies, requestHandler, getNotFound } from '@qwik.dev/router/middleware/request-handler';
3
+ import { isDev } from '@qwik.dev/core/build';
4
+
5
+ const COUNTRY_HEADER_NAME = "x-vercel-ip-country";
6
+ const IP_HEADER_NAME = "x-real-ip";
7
+ const VERCEL_COOKIE = "__vdpl";
8
+ const VERCEL_SKEW_PROTECTION_ENABLED = "VERCEL_SKEW_PROTECTION_ENABLED";
9
+ const VERCEL_DEPLOYMENT_ID = "VERCEL_DEPLOYMENT_ID";
10
+ const BASE_URL = "BASE_URL";
13
11
  function createQwikRouter(opts) {
14
- if (opts.qwikCityPlan && !opts.qwikRouterConfig) {
15
- console.warn("qwikCityPlan is deprecated. Use qwikRouterConfig instead.");
16
- opts.qwikRouterConfig = opts.qwikCityPlan;
17
- } else if (!opts.qwikRouterConfig) {
18
- throw new Error("qwikRouterConfig is required.");
19
- }
20
- const qwikSerializer = {
21
- _deserialize,
22
- _serialize,
23
- _verifySerializable
24
- };
25
12
  if (opts.manifest) {
26
13
  setServerPlatform(opts.manifest);
27
14
  }
@@ -75,11 +62,11 @@ function createQwikRouter(opts) {
75
62
  };
76
63
  }
77
64
  };
78
- const handledResponse = await requestHandler(serverRequestEv, opts, qwikSerializer);
65
+ const handledResponse = await requestHandler(serverRequestEv, opts);
79
66
  if (handledResponse) {
80
- handledResponse.completion.then((v) => {
81
- if (v) {
82
- console.error(v);
67
+ handledResponse.completion.then((completion) => {
68
+ if (completion) {
69
+ console.error(completion);
83
70
  }
84
71
  });
85
72
  const response = await handledResponse.response;
@@ -87,23 +74,27 @@ function createQwikRouter(opts) {
87
74
  return response;
88
75
  }
89
76
  }
90
- const notFoundHtml = isStaticPath(request.method || "GET", url) ? "Not Found" : getNotFound(url.pathname);
77
+ const notFoundHtml = !request.headers.get("accept")?.includes("text/html") || isStaticPath(request.method || "GET", url) ? "Not Found" : getNotFound(url.pathname);
91
78
  return new Response(notFoundHtml, {
92
79
  status: 404,
93
- headers: { "Content-Type": "text/html; charset=utf-8", "X-Not-Found": url.pathname }
80
+ headers: {
81
+ "Content-Type": "text/html; charset=utf-8",
82
+ "X-Not-Found": url.pathname
83
+ }
94
84
  });
95
85
  } catch (e) {
96
86
  console.error(e);
97
- return new Response(String(e || "Error"), {
87
+ return new Response(isDev ? String(e || "Error") : "Internal Server Error", {
98
88
  status: 500,
99
- headers: { "Content-Type": "text/plain; charset=utf-8", "X-Error": "vercel-edge" }
89
+ headers: {
90
+ "Content-Type": "text/plain; charset=utf-8",
91
+ "X-Error": "vercel-edge"
92
+ }
100
93
  });
101
94
  }
102
95
  }
103
96
  return onVercelEdgeRequest;
104
97
  }
105
- var createQwikCity = createQwikRouter;
106
- export {
107
- createQwikCity,
108
- createQwikRouter
109
- };
98
+ const createQwikCity = createQwikRouter;
99
+
100
+ export { createQwikCity, createQwikRouter };
package/lib/modules.d.ts CHANGED
@@ -1,15 +1,18 @@
1
1
  declare module '@qwik-router-config' {
2
- export const routes: any[];
3
- export const menus: any[];
2
+ import type { RouteData, RouteModule } from '@qwik.dev/router';
3
+ export const routes: RouteData;
4
+ export const serverPlugins: RouteModule[];
4
5
  export const trailingSlash: boolean;
5
6
  export const basePathname: string;
6
7
  export const cacheModules: boolean;
8
+ export const fallthrough: boolean;
7
9
  const defaultExport: {
8
- routes: any[];
9
- menus: any[];
10
+ routes: RouteData;
11
+ serverPlugins: RouteModule[];
10
12
  trailingSlash: boolean;
11
13
  basePathname: string;
12
14
  cacheModules: boolean;
15
+ fallthrough: boolean;
13
16
  };
14
17
  export default defaultExport;
15
18
  }
@@ -19,20 +22,12 @@ declare module '@qwik-city-plan' {
19
22
  export { default } from '@qwik-router-config';
20
23
  }
21
24
 
22
- declare module '@qwik-router-not-found-paths' {
23
- function getNotFound(_pathname: string): string;
24
- export { getNotFound };
25
- }
26
-
27
25
  declare module '@qwik-city-not-found-paths' {
28
- export * from '@qwik-router-not-found-paths';
29
- }
30
-
31
- declare module '@qwik-router-static-paths' {
32
- function isStaticPath(method: string, url: URL): boolean;
33
- export { isStaticPath };
26
+ /** @deprecated Use `getNotFound` from `@qwik.dev/router/middleware/request-handler` instead. */
27
+ export { getNotFound } from '@qwik.dev/router/middleware/request-handler';
34
28
  }
35
29
 
36
30
  declare module '@qwik-city-static-paths' {
37
- export * from '@qwik-router-static-paths';
31
+ /** @deprecated Use `isStaticPath` from `@qwik.dev/router/middleware/request-handler` instead. */
32
+ export { isStaticPath } from '@qwik.dev/router/middleware/request-handler';
38
33
  }
@@ -0,0 +1,4 @@
1
+ const setupServiceWorker = () => {
2
+ };
3
+
4
+ export { setupServiceWorker };
@@ -1,3 +1,5 @@
1
+ import type { QwikRouterConfig } from '@qwik.dev/router';
2
+ import type { Render } from '@qwik.dev/core/server';
1
3
  import type { RenderOptions } from '@qwik.dev/core/server';
2
4
 
3
5
  /**
@@ -6,26 +8,46 @@ import type { RenderOptions } from '@qwik.dev/core/server';
6
8
  *
7
9
  * @public
8
10
  */
9
- export declare function generate(opts: StaticGenerateOptions): Promise<StaticGenerateResult>;
11
+ export declare function generate(opts: SsgInternalOptions): Promise<StaticGenerateResult>;
10
12
 
11
- /** @public */
12
- export declare interface StaticGenerateOptions extends StaticGenerateRenderOptions {
13
+ /**
14
+ * Run SSG as a standalone process. Writes `_static-paths.json` to `outDir` and exits with code 0 on
15
+ * success, 1 on error.
16
+ *
17
+ * Intended to be called from a generated `run-ssg.js` wrapper that provides render/config.
18
+ *
19
+ * @public
20
+ */
21
+ export declare function runSsg(opts: SsgInternalOptions): Promise<never>;
22
+
23
+ /**
24
+ * Internal SSG options that include the render function and router config.
25
+ *
26
+ * @public
27
+ */
28
+ export declare interface SsgInternalOptions extends SsgOptions {
29
+ /** The SSR render function (default export from entry.ssr). */
30
+ render: Render;
31
+ /** The Qwik Router Config object (default export from `@qwik-router-config`). */
32
+ qwikRouterConfig: QwikRouterConfig;
13
33
  /**
14
- * Path to the SSR module exporting the default render function. In most cases it'll be
15
- * `./src/entry.ssr.tsx`.
34
+ * Path or URL to the worker entry file. Workers are spawned using this file. When run-ssg.js
35
+ * serves as both main and worker entry, this should be `import.meta.url` of that file.
16
36
  */
17
- renderModulePath: string;
18
- /** Path to the Qwik Router Config module exporting the default `@qwik-router-config`. */
19
- qwikRouterConfigModulePath: string;
20
- /** @deprecated Use `qwikRouterConfigModulePath` instead. Will be removed in V3 */
21
- qwikCityPlanModulePath?: string;
37
+ workerFilePath?: string | URL;
38
+ }
39
+
40
+ /** @public */
41
+ declare interface SsgOptions extends SsgRenderOptions {
22
42
  /** Defaults to `/` */
23
43
  basePathname?: string;
24
44
  rootDir?: string;
25
45
  }
46
+ export { SsgOptions }
47
+ export { SsgOptions as StaticGenerateOptions }
26
48
 
27
49
  /** @public */
28
- export declare interface StaticGenerateRenderOptions extends RenderOptions {
50
+ export declare interface SsgRenderOptions extends RenderOptions {
29
51
  /** File system directory where the static files should be written. */
30
52
  outDir: string;
31
53
  /**
@@ -53,8 +75,8 @@ export declare interface StaticGenerateRenderOptions extends RenderOptions {
53
75
  * root of the `outDir`. Setting to `null` will prevent the sitemap from being created.
54
76
  */
55
77
  sitemapOutFile?: string | null;
56
- /** Log level. */
57
- log?: 'debug';
78
+ /** Log level. `'quiet'` suppresses per-page output, `'debug'` enables verbose logging. */
79
+ log?: 'debug' | 'quiet';
58
80
  /**
59
81
  * Set to `false` if the generated static HTML files should not be written to disk. Setting to
60
82
  * `false` is useful if the SSG should only write the `q-data.json` files to disk. Defaults to
@@ -85,6 +107,16 @@ export declare interface StaticGenerateRenderOptions extends RenderOptions {
85
107
  exclude?: string[];
86
108
  }
87
109
 
110
+ /**
111
+ * Bootstrap a worker thread for SSG rendering. Called from the generated `run-ssg.js` when it
112
+ * detects it's running as a worker thread.
113
+ *
114
+ * @param opts - Must include `render` and `qwikRouterConfig` (imported by the worker entry). The
115
+ * remaining serializable options come from `workerData`.
116
+ * @public
117
+ */
118
+ export declare function startWorker(opts: Pick<SsgInternalOptions, 'render' | 'qwikRouterConfig'>): Promise<void>;
119
+
88
120
  /** @public */
89
121
  export declare interface StaticGenerateResult {
90
122
  duration: number;
@@ -0,0 +1,336 @@
1
+ import { bold, green, dim, red, magenta } from 'kleur/colors';
2
+ import { relative } from 'node:path';
3
+ import { m as msToString, g as getPathnameForDynamicRoute } from '../chunks/pathname.mjs';
4
+
5
+ function routeToRegExp(rule) {
6
+ let transformedRule;
7
+ if (rule === "/" || rule === "/*") {
8
+ transformedRule = rule;
9
+ } else if (rule.endsWith("/*")) {
10
+ transformedRule = `${rule.substring(0, rule.length - 2)}(/*)?`;
11
+ } else if (rule.endsWith("/")) {
12
+ transformedRule = `${rule.substring(0, rule.length - 1)}(/)?`;
13
+ } else if (rule.endsWith("*")) {
14
+ transformedRule = rule;
15
+ } else {
16
+ transformedRule = `${rule}(/)?`;
17
+ }
18
+ transformedRule = `^${transformedRule.replace(/\*/g, ".*")}$`;
19
+ return new RegExp(transformedRule);
20
+ }
21
+ function routesToRegExps(routes) {
22
+ if (!Array.isArray(routes)) {
23
+ return [];
24
+ }
25
+ return routes.filter((r) => typeof r === "string").map(routeToRegExp);
26
+ }
27
+ function createRouteTester(basePathname, includeRoutes, excludeRoutes) {
28
+ const includes = routesToRegExps(includeRoutes);
29
+ const excludes = routesToRegExps(excludeRoutes);
30
+ return (pathname) => {
31
+ if (pathname.endsWith("404.html")) {
32
+ return true;
33
+ }
34
+ if (basePathname !== "/") {
35
+ pathname = pathname.slice(basePathname.length - 1);
36
+ }
37
+ for (const exclude of excludes) {
38
+ if (exclude.test(pathname)) {
39
+ return false;
40
+ }
41
+ }
42
+ for (const include of includes) {
43
+ if (include.test(pathname)) {
44
+ return true;
45
+ }
46
+ }
47
+ return false;
48
+ };
49
+ }
50
+
51
+ function validateOptions(opts) {
52
+ if (!opts.render) {
53
+ throw new Error(`Missing "render" option`);
54
+ }
55
+ if (!opts.qwikRouterConfig) {
56
+ throw new Error(`Missing "qwikRouterConfig" option`);
57
+ }
58
+ let siteOrigin = opts.origin;
59
+ if (typeof siteOrigin !== "string" || siteOrigin.trim().length === 0) {
60
+ throw new Error(`Missing "origin" option`);
61
+ }
62
+ siteOrigin = siteOrigin.trim();
63
+ if (!/:\/\//.test(siteOrigin) || siteOrigin.startsWith("://")) {
64
+ throw new Error(`"origin" must start with a valid protocol, such as "https://" or "http://", received "${siteOrigin}"`);
65
+ }
66
+ try {
67
+ new URL(siteOrigin);
68
+ } catch (e) {
69
+ throw new Error(`Invalid "origin"`, {
70
+ cause: e
71
+ });
72
+ }
73
+ }
74
+ async function mainThread(sys) {
75
+ const opts = sys.getOptions();
76
+ validateOptions(opts);
77
+ const main = await sys.createMainProcess();
78
+ const log = await sys.createLogger();
79
+ log.info("\n" + bold(green("Starting Qwik Router SSG...")));
80
+ const qwikRouterConfig = opts.qwikRouterConfig;
81
+ const renderTimeout = 3e4;
82
+ const queue = [];
83
+ const active = /* @__PURE__ */ new Set();
84
+ const routes = qwikRouterConfig.routes;
85
+ const trailingSlash = !!qwikRouterConfig.trailingSlash;
86
+ const includeRoute = createRouteTester(opts.basePathname || "/", opts.include, opts.exclude);
87
+ return new Promise((resolve, reject) => {
88
+ try {
89
+ const timer = sys.createTimer();
90
+ const generatorResult = {
91
+ duration: 0,
92
+ rendered: 0,
93
+ errors: 0,
94
+ staticPaths: []
95
+ };
96
+ let isCompleted = false;
97
+ let isRoutesLoaded = false;
98
+ const completed = async () => {
99
+ const closePromise = main.close();
100
+ generatorResult.duration = timer();
101
+ if (generatorResult.errors === 0) {
102
+ log.info(`
103
+ ${green("SSG results")}`);
104
+ if (generatorResult.rendered > 0) {
105
+ log.info(`- Generated: ${dim(`${generatorResult.rendered} page${generatorResult.rendered === 1 ? "" : "s"}`)}`);
106
+ }
107
+ log.info(`- Duration: ${dim(msToString(generatorResult.duration))}`);
108
+ const total = generatorResult.rendered + generatorResult.errors;
109
+ if (total > 0) {
110
+ log.info(`- Average: ${dim(msToString(generatorResult.duration / total) + " per page")}`);
111
+ }
112
+ log.info(``);
113
+ }
114
+ closePromise.then(() => {
115
+ setTimeout(() => resolve(generatorResult));
116
+ }).catch(reject);
117
+ };
118
+ const next = () => {
119
+ while (!isCompleted && main.hasAvailableWorker() && queue.length > 0) {
120
+ const staticRoute = queue.shift();
121
+ if (staticRoute) {
122
+ render(staticRoute).catch((e) => {
123
+ console.error(`render failed for ${staticRoute.pathname}`, e);
124
+ });
125
+ }
126
+ }
127
+ if (!isCompleted && isRoutesLoaded && queue.length === 0 && active.size === 0) {
128
+ isCompleted = true;
129
+ completed().catch((e) => {
130
+ console.error("SSG completion failed", e);
131
+ });
132
+ }
133
+ };
134
+ let isPendingDrain = false;
135
+ const flushQueue = () => {
136
+ if (!isPendingDrain) {
137
+ isPendingDrain = true;
138
+ setTimeout(() => {
139
+ isPendingDrain = false;
140
+ next();
141
+ });
142
+ }
143
+ };
144
+ const render = async (staticRoute) => {
145
+ try {
146
+ active.add(staticRoute.pathname);
147
+ log.debug(`render start: ${staticRoute.pathname}`);
148
+ const result = await Promise.race([
149
+ main.render({
150
+ type: "render",
151
+ ...staticRoute
152
+ }),
153
+ new Promise((_, reject2) => setTimeout(() => reject2(new Error(`SSG render timed out after ${renderTimeout}ms`)), renderTimeout))
154
+ ]);
155
+ active.delete(staticRoute.pathname);
156
+ log.debug(`render done: ${staticRoute.pathname}`);
157
+ if (result.error) {
158
+ const err = new Error(result.error.message);
159
+ err.stack = result.error.stack;
160
+ log.error(`
161
+ ${bold(red(`!!! ${result.pathname}: Error during SSG`))}`);
162
+ log.error(red(err.message));
163
+ log.error(` Pathname: ${magenta(staticRoute.pathname)}`);
164
+ if (err.stack) {
165
+ log.error(dim(err.stack));
166
+ }
167
+ generatorResult.errors++;
168
+ }
169
+ if (result.filePath != null) {
170
+ generatorResult.rendered++;
171
+ generatorResult.staticPaths.push(result.pathname);
172
+ const base = opts.rootDir ?? opts.outDir;
173
+ const path = relative(base, result.filePath);
174
+ const lastSlash = path.lastIndexOf("/");
175
+ log.info(`${dim(path.slice(0, lastSlash + 1))}${path.slice(lastSlash + 1)}`);
176
+ }
177
+ flushQueue();
178
+ } catch (e) {
179
+ console.error(`render failed for ${staticRoute.pathname}`, e);
180
+ isCompleted = true;
181
+ reject(e);
182
+ }
183
+ };
184
+ const addToQueue = (pathname, params) => {
185
+ if (pathname) {
186
+ pathname = new URL(pathname, `https://qwik.dev`).pathname;
187
+ if (pathname !== opts.basePathname) {
188
+ if (trailingSlash) {
189
+ if (!pathname.endsWith("/")) {
190
+ const segments = pathname.split("/");
191
+ const lastSegment = segments[segments.length - 1];
192
+ if (!lastSegment.includes(".")) {
193
+ pathname += "/";
194
+ }
195
+ }
196
+ } else {
197
+ if (pathname.endsWith("/")) {
198
+ pathname = pathname.slice(0, pathname.length - 1);
199
+ }
200
+ }
201
+ }
202
+ if (includeRoute(pathname) && !queue.some((s) => s.pathname === pathname)) {
203
+ queue.push({
204
+ pathname,
205
+ params
206
+ });
207
+ flushQueue();
208
+ }
209
+ }
210
+ };
211
+ const traverseRouteTree = async (node, pathParts, basePathname, ancestorLoaders) => {
212
+ const currentLoaders = node._L ? [
213
+ ...ancestorLoaders,
214
+ node._L
215
+ ] : ancestorLoaders;
216
+ const index = node._I;
217
+ if (index) {
218
+ const pageLoaders = Array.isArray(index) ? index : [
219
+ ...currentLoaders,
220
+ index
221
+ ];
222
+ const joinedParts = pathParts.join("/");
223
+ const originalPathname = basePathname + (joinedParts ? joinedParts + "/" : "");
224
+ const paramNames = pathParts.filter((p) => p.startsWith("[") && p.endsWith("]")).map((p) => p.startsWith("[...") ? p.slice(4, -1) : p.slice(1, -1));
225
+ if (paramNames.length === 0) {
226
+ addToQueue(originalPathname, void 0);
227
+ } else {
228
+ const pageLoader = pageLoaders[pageLoaders.length - 1];
229
+ if (typeof pageLoader === "function") {
230
+ const pageModule = await pageLoader();
231
+ if (typeof pageModule.onStaticGenerate === "function") {
232
+ const staticGenerate = await pageModule.onStaticGenerate({
233
+ env: {
234
+ get(key) {
235
+ return sys.getEnv(key);
236
+ }
237
+ }
238
+ });
239
+ if (Array.isArray(staticGenerate.params)) {
240
+ for (const params of staticGenerate.params) {
241
+ const pathname = getPathnameForDynamicRoute(originalPathname, paramNames, params);
242
+ addToQueue(pathname, params);
243
+ }
244
+ }
245
+ }
246
+ }
247
+ }
248
+ }
249
+ if (node._M) {
250
+ for (const group of node._M) {
251
+ await traverseRouteTree(group, pathParts, basePathname, currentLoaders);
252
+ }
253
+ }
254
+ if (node._W && typeof node._W === "object") {
255
+ const childNode = node._W;
256
+ const paramName = childNode._P;
257
+ const pathPart = paramName ? `[${paramName}]` : "[param]";
258
+ await traverseRouteTree(childNode, [
259
+ ...pathParts,
260
+ pathPart
261
+ ], basePathname, currentLoaders);
262
+ }
263
+ if (node._A && typeof node._A === "object") {
264
+ const childNode = node._A;
265
+ const paramName = childNode._P;
266
+ const pathPart = paramName ? `[...${paramName}]` : "[...rest]";
267
+ await traverseRouteTree(childNode, [
268
+ ...pathParts,
269
+ pathPart
270
+ ], basePathname, currentLoaders);
271
+ }
272
+ for (const [key, child] of Object.entries(node)) {
273
+ if (key.charCodeAt(0) === 95) {
274
+ continue;
275
+ }
276
+ const childNode = child;
277
+ await traverseRouteTree(childNode, [
278
+ ...pathParts,
279
+ key
280
+ ], basePathname, currentLoaders);
281
+ }
282
+ };
283
+ const loadStaticRoutes = async () => {
284
+ log.debug("traversing route tree...");
285
+ await traverseRouteTree(routes, [], "/", []);
286
+ log.debug(`route tree traversed, ${queue.length} routes queued, ${active.size} active`);
287
+ isRoutesLoaded = true;
288
+ flushQueue();
289
+ };
290
+ loadStaticRoutes().catch((e) => {
291
+ console.error("SSG route loading failed", e);
292
+ reject(e);
293
+ });
294
+ } catch (e) {
295
+ console.error("SSG main thread failed", e);
296
+ reject(e);
297
+ }
298
+ });
299
+ }
300
+
301
+ async function generate(opts) {
302
+ const { createSystem } = await import('../chunks/system.mjs');
303
+ const sys = await createSystem(opts);
304
+ return mainThread(sys);
305
+ }
306
+ async function runSsg(opts) {
307
+ try {
308
+ const result = await generate(opts);
309
+ const fs = await import('node:fs');
310
+ const { join } = await import('node:path');
311
+ const staticPathsFile = join(opts.outDir, "_static-paths.json");
312
+ await fs.promises.writeFile(staticPathsFile, JSON.stringify(result.staticPaths));
313
+ if (result.errors > 0) {
314
+ console.error(`SSG completed with ${result.errors} error(s)`);
315
+ process.exit(1);
316
+ }
317
+ process.exit(0);
318
+ } catch (e) {
319
+ console.error("SSG failed:", e);
320
+ process.exit(1);
321
+ }
322
+ }
323
+ async function startWorker(opts) {
324
+ const { workerData } = await import('node:worker_threads');
325
+ const mergedOpts = {
326
+ ...workerData,
327
+ render: opts.render,
328
+ qwikRouterConfig: opts.qwikRouterConfig
329
+ };
330
+ const { createSystem } = await import('../chunks/system.mjs');
331
+ const { workerThread } = await import('../chunks/worker-thread.qwik.mjs');
332
+ const sys = await createSystem(mergedOpts, workerData?.threadId);
333
+ await workerThread(sys);
334
+ }
335
+
336
+ export { generate, runSsg, startWorker };
@@ -4,15 +4,16 @@ import type { Config } from 'svgo';
4
4
  import { ConfigEnv } from 'vite';
5
5
  import type { Plugin as Plugin_2 } from 'vite';
6
6
  import type { PluginOption } from 'vite';
7
+ import type { SerializationStrategy } from '@qwik.dev/core/internal';
7
8
  import { UserConfigExport } from 'vite';
8
9
 
9
- declare interface BuildEntry extends ParsedPathname {
10
+ declare interface BuiltEntry extends ParsedPathname {
10
11
  id: string;
11
12
  chunkFileName: string;
12
13
  filePath: string;
13
14
  }
14
15
 
15
- declare interface BuildLayout {
16
+ declare interface BuiltLayout {
16
17
  filePath: string;
17
18
  dirPath: string;
18
19
  id: string;
@@ -20,7 +21,7 @@ declare interface BuildLayout {
20
21
  layoutName: string;
21
22
  }
22
23
 
23
- declare interface BuildRoute extends ParsedPathname {
24
+ declare interface BuiltRoute extends ParsedPathname {
24
25
  /** Unique id built from its relative file system path */
25
26
  id: string;
26
27
  /** Local file system path */
@@ -28,7 +29,7 @@ declare interface BuildRoute extends ParsedPathname {
28
29
  ext: string;
29
30
  /** URL Pathname */
30
31
  pathname: string;
31
- layouts: BuildLayout[];
32
+ layouts: BuiltLayout[];
32
33
  }
33
34
 
34
35
  /** @public */
@@ -80,13 +81,13 @@ declare interface PathnameSegmentPart {
80
81
 
81
82
  /** @public */
82
83
  declare interface PluginOptions {
83
- /** Directory of the `routes`. Defaults to `src/routes`. */
84
+ /** Directory of the `routes`. Defaults to `"src/routes"`. */
84
85
  routesDir?: string;
85
- /** Directory of the `server plugins`. Defaults to `src/server-plugins`. */
86
+ /** Directory of the `server plugins`. Defaults to `routesDir`. */
86
87
  serverPluginsDir?: string;
87
88
  /**
88
89
  * The base pathname is used to create absolute URL paths up to the `hostname`, and must always
89
- * start and end with a `/`. Defaults to `/`.
90
+ * start and end with a `/`. Defaults to `"/""`.
90
91
  */
91
92
  basePathname?: string;
92
93
  /**
@@ -98,10 +99,18 @@ declare interface PluginOptions {
98
99
  mdxPlugins?: MdxPlugins;
99
100
  /** MDX Options https://mdxjs.com/ */
100
101
  mdx?: any;
101
- /** The platform object which can be used to mock the Cloudflare bindings. */
102
+ /**
103
+ * Extend the `platform` object in RequestEvent, which can be used to e.g. mock Cloudflare
104
+ * bindings.
105
+ *
106
+ * This only works in **dev mode** and only when using the in-process Vite dev server middleware
107
+ * (the default).
108
+ */
102
109
  platform?: Record<string, unknown>;
103
110
  /** Configuration to rewrite url paths */
104
111
  rewriteRoutes?: RewriteRouteOption[];
112
+ /** The serialization strategy for route loaders. Defaults to `never`. */
113
+ defaultLoadersSerializationStrategy?: SerializationStrategy;
105
114
  }
106
115
 
107
116
  /**
@@ -133,8 +142,8 @@ export declare interface QwikRouterPlugin extends P<QwikRouterPluginApi> {
133
142
  /** @public */
134
143
  declare interface QwikRouterPluginApi {
135
144
  getBasePathname: () => string;
136
- getRoutes: () => BuildRoute[];
137
- getServiceWorkers: () => BuildEntry[];
145
+ getRoutes: () => BuiltRoute[];
146
+ getServiceWorkers: () => BuiltEntry[];
138
147
  }
139
148
 
140
149
  /** @public */
@@ -143,6 +152,25 @@ export declare interface QwikRouterVitePluginOptions extends Omit<PluginOptions,
143
152
  mdx?: MdxOptions;
144
153
  platform?: Record<string, unknown>;
145
154
  imageOptimization?: ImageOptimizationOptions;
155
+ /** Whether to use static imports for route modules (layout and index files). Defaults to `false`. */
156
+ staticImportRoutes?: boolean;
157
+ /**
158
+ * Qwik is an SSR first framework. This means that Qwik requires either SSR or SSG. In Vite dev
159
+ * mode the dev SSR server is responsible for rendering and pausing the application on the
160
+ * server.
161
+ *
162
+ * Under normal circumstances this should be on, unless you have your own dev SSR server setup and
163
+ * wish to disable this one.
164
+ *
165
+ * Default: true
166
+ */
167
+ devSsrServer?: boolean;
168
+ /**
169
+ * Maximum number of SSR-rendered pages to keep in the in-memory cache. Set to 0 to disable.
170
+ *
171
+ * Default: 50
172
+ */
173
+ ssrCacheSize?: number;
146
174
  }
147
175
 
148
176
  /** @public */