@qwik.dev/router 2.0.0-beta.27 → 2.0.0-beta.28

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.
@@ -117,7 +117,6 @@ function viteAdapter(opts) {
117
117
  let serverOutDir = null;
118
118
  let renderModulePath = null;
119
119
  let qwikRouterConfigModulePath = null;
120
- let isSsrBuild = false;
121
120
  let viteCommand;
122
121
  const outputEntries = [];
123
122
  const plugin = {
@@ -135,36 +134,23 @@ function viteAdapter(opts) {
135
134
  return config;
136
135
  },
137
136
  configResolved(config) {
138
- isSsrBuild = !!config.build.ssr;
139
137
  viteCommand = config.command;
140
- if (isSsrBuild) {
141
- qwikRouterPlugin = config.plugins.find(
142
- (p) => p.name === "vite-plugin-qwik-router"
143
- );
144
- if (!qwikRouterPlugin) {
145
- throw new Error("Missing vite-plugin-qwik-router");
146
- }
147
- qwikVitePlugin = config.plugins.find(
148
- (p) => p.name === "vite-plugin-qwik"
149
- );
150
- if (!qwikVitePlugin) {
151
- throw new Error("Missing vite-plugin-qwik");
152
- }
153
- serverOutDir = config.build.outDir;
154
- if (config.build?.ssr !== true) {
155
- throw new Error(
156
- `"build.ssr" must be set to "true" in order to use the "${opts.name}" adapter.`
157
- );
158
- }
159
- if (!config.build?.rollupOptions?.input) {
160
- throw new Error(
161
- `"build.rollupOptions.input" must be set in order to use the "${opts.name}" adapter.`
162
- );
163
- }
138
+ qwikRouterPlugin = config.plugins.find(
139
+ (p) => p.name === "vite-plugin-qwik-router"
140
+ );
141
+ if (!qwikRouterPlugin) {
142
+ throw new Error("Missing vite-plugin-qwik-router");
143
+ }
144
+ qwikVitePlugin = config.plugins.find(
145
+ (p) => p.name === "vite-plugin-qwik"
146
+ );
147
+ if (!qwikVitePlugin) {
148
+ throw new Error("Missing vite-plugin-qwik");
164
149
  }
150
+ serverOutDir = config.build.outDir;
165
151
  },
166
152
  buildStart() {
167
- if (isSsrBuild && opts.ssg !== null) {
153
+ if (this.environment.config.consumer === "server" && opts.ssg !== null) {
168
154
  const { srcDir } = qwikVitePlugin.api.getOptions();
169
155
  if (viteCommand === "build" && serverOutDir && srcDir) {
170
156
  this.emitFile({
@@ -181,7 +167,7 @@ function viteAdapter(opts) {
181
167
  }
182
168
  },
183
169
  generateBundle(_, bundles) {
184
- if (isSsrBuild) {
170
+ if (this.environment.config.consumer === "server") {
185
171
  outputEntries.length = 0;
186
172
  for (const fileName in bundles) {
187
173
  const chunk = bundles[fileName];
@@ -199,7 +185,7 @@ function viteAdapter(opts) {
199
185
  closeBundle: {
200
186
  sequential: true,
201
187
  async handler() {
202
- if (isSsrBuild && serverOutDir && qwikRouterPlugin?.api && qwikVitePlugin?.api) {
188
+ if (this.environment.config.consumer === "server" && serverOutDir && qwikRouterPlugin?.api && qwikVitePlugin?.api) {
203
189
  const staticPaths = opts.staticPaths || [];
204
190
  const routes = qwikRouterPlugin.api.getRoutes();
205
191
  const basePathname = qwikRouterPlugin.api.getBasePathname();
@@ -15,7 +15,7 @@ import { WritableStream } from 'node:stream/web';
15
15
  async function createNodeMainProcess(sys, opts) {
16
16
  const ssgWorkers = [];
17
17
  const sitemapBuffer = [];
18
- let sitemapPromise = null;
18
+ let sitemapStream = null;
19
19
  opts = { ...opts };
20
20
  let outDir = opts.outDir;
21
21
  if (typeof outDir !== "string") {
@@ -137,25 +137,35 @@ async function createNodeMainProcess(sys, opts) {
137
137
  if (sitemapOutFile && result.ok && result.resourceType === "page") {
138
138
  sitemapBuffer.push(`<url><loc>${result.url}</loc></url>`);
139
139
  if (sitemapBuffer.length > 50) {
140
- if (sitemapPromise) {
141
- await sitemapPromise;
142
- }
143
140
  const siteMapUrls = sitemapBuffer.join("\n") + "\n";
144
141
  sitemapBuffer.length = 0;
145
- sitemapPromise = fs.promises.appendFile(sitemapOutFile, siteMapUrls);
142
+ if (sitemapStream) {
143
+ sitemapStream.write(siteMapUrls);
144
+ }
146
145
  }
147
146
  }
148
147
  return result;
149
148
  };
150
149
  const close = async () => {
151
150
  const promises = [];
152
- if (sitemapOutFile) {
153
- if (sitemapPromise) {
154
- await sitemapPromise;
155
- }
151
+ if (sitemapStream) {
156
152
  sitemapBuffer.push(`</urlset>`);
157
- promises.push(fs.promises.appendFile(sitemapOutFile, sitemapBuffer.join("\n")));
153
+ sitemapStream.write(sitemapBuffer.join("\n"));
158
154
  sitemapBuffer.length = 0;
155
+ await new Promise((resolve2, reject) => {
156
+ if (sitemapStream) {
157
+ sitemapStream.end((err) => {
158
+ if (err) {
159
+ reject(err);
160
+ } else {
161
+ resolve2();
162
+ }
163
+ });
164
+ } else {
165
+ resolve2();
166
+ }
167
+ });
168
+ sitemapStream = null;
159
169
  }
160
170
  for (const ssgWorker of ssgWorkers) {
161
171
  try {
@@ -172,8 +182,10 @@ async function createNodeMainProcess(sys, opts) {
172
182
  };
173
183
  if (sitemapOutFile) {
174
184
  await ensureDir(sitemapOutFile);
175
- await fs.promises.writeFile(
176
- sitemapOutFile,
185
+ sitemapStream = fs.createWriteStream(sitemapOutFile, {
186
+ flags: "w"
187
+ });
188
+ sitemapStream.write(
177
189
  `<?xml version="1.0" encoding="UTF-8"?>
178
190
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
179
191
  `
@@ -246,7 +258,7 @@ async function createSystem(opts, threadId) {
246
258
  const basePathname = opts.basePathname || "/";
247
259
  const basenameLen = basePathname.length;
248
260
  const getRouteFilePath = (pathname, isHtml) => {
249
- pathname = pathname.slice(basenameLen);
261
+ pathname = decodeURIComponent(pathname.slice(basenameLen));
250
262
  if (isHtml) {
251
263
  if (!pathname.endsWith(".html")) {
252
264
  if (pathname.endsWith("/")) {
@@ -263,7 +275,7 @@ async function createSystem(opts, threadId) {
263
275
  return join(outDir, pathname);
264
276
  };
265
277
  const getDataFilePath = (pathname) => {
266
- pathname = pathname.slice(basenameLen);
278
+ pathname = decodeURIComponent(pathname.slice(basenameLen));
267
279
  if (pathname.endsWith("/")) {
268
280
  pathname += "q-data.json";
269
281
  } else {
@@ -2,6 +2,7 @@ import { setServerPlatform } from '@qwik.dev/core/server';
2
2
  import { _TextEncoderStream_polyfill, isStaticPath, getNotFound, mergeHeadersCookies, requestHandler } from '@qwik.dev/router/middleware/request-handler';
3
3
  import { join, extname } from 'node:path';
4
4
  import { M as MIME_TYPES } from '../../chunks/mime-types.mjs';
5
+ import { isDev } from '@qwik.dev/core/build';
5
6
 
6
7
  function getRequestUrl(request, opts) {
7
8
  const url = new URL(request.url);
@@ -71,7 +72,7 @@ function createQwikRouter(opts) {
71
72
  return null;
72
73
  } catch (e) {
73
74
  console.error(e);
74
- return new Response(String(e || "Error"), {
75
+ return new Response(isDev ? String(e || "Error") : "Internal Server Error", {
75
76
  status: 500,
76
77
  headers: { "Content-Type": "text/plain; charset=utf-8", "X-Error": "bun-server" }
77
78
  });
@@ -87,7 +88,7 @@ function createQwikRouter(opts) {
87
88
  });
88
89
  } catch (e) {
89
90
  console.error(e);
90
- return new Response(String(e || "Error"), {
91
+ return new Response(isDev ? String(e || "Error") : "Internal Server Error", {
91
92
  status: 500,
92
93
  headers: { "Content-Type": "text/plain; charset=utf-8", "X-Error": "bun-server" }
93
94
  });
@@ -132,7 +133,7 @@ function createQwikRouter(opts) {
132
133
  return null;
133
134
  } catch (e) {
134
135
  console.error(e);
135
- return new Response(String(e || "Error"), {
136
+ return new Response(isDev ? String(e || "Error") : "Internal Server Error", {
136
137
  status: 500,
137
138
  headers: { "Content-Type": "text/plain; charset=utf-8", "X-Error": "bun-server" }
138
139
  });
@@ -1,5 +1,6 @@
1
1
  import { setServerPlatform } from '@qwik.dev/core/server';
2
2
  import { _TextEncoderStream_polyfill, isStaticPath, mergeHeadersCookies, requestHandler, getNotFound } from '@qwik.dev/router/middleware/request-handler';
3
+ import { isDev } from '@qwik.dev/core/build';
3
4
 
4
5
  function createQwikRouter(opts) {
5
6
  if (opts.qwikCityPlan && !opts.qwikRouterConfig) {
@@ -82,7 +83,7 @@ function createQwikRouter(opts) {
82
83
  });
83
84
  } catch (e) {
84
85
  console.error(e);
85
- return new Response(String(e || "Error"), {
86
+ return new Response(isDev ? String(e || "Error") : "Internal Server Error", {
86
87
  status: 500,
87
88
  headers: { "Content-Type": "text/plain; charset=utf-8", "X-Error": "cloudflare-pages" }
88
89
  });
@@ -2,6 +2,7 @@ import { setServerPlatform } from '@qwik.dev/core/server';
2
2
  import { isStaticPath, getNotFound, mergeHeadersCookies, requestHandler } from '@qwik.dev/router/middleware/request-handler';
3
3
  import { M as MIME_TYPES } from '../../chunks/mime-types.mjs';
4
4
  import { join, fromFileUrl, extname } from 'https://deno.land/std/path/mod.ts';
5
+ import { isDev } from '@qwik.dev/core/build';
5
6
 
6
7
  function getRequestUrl(request, opts, info) {
7
8
  const url = new URL(request.url);
@@ -63,7 +64,7 @@ function createQwikRouter(opts) {
63
64
  return null;
64
65
  } catch (e) {
65
66
  console.error(e);
66
- return new Response(String(e || "Error"), {
67
+ return new Response(isDev ? String(e || "Error") : "Internal Server Error", {
67
68
  status: 500,
68
69
  headers: { "Content-Type": "text/plain; charset=utf-8", "X-Error": "deno-server" }
69
70
  });
@@ -79,7 +80,7 @@ function createQwikRouter(opts) {
79
80
  });
80
81
  } catch (e) {
81
82
  console.error(e);
82
- return new Response(String(e || "Error"), {
83
+ return new Response(isDev ? String(e || "Error") : "Internal Server Error", {
83
84
  status: 500,
84
85
  headers: { "Content-Type": "text/plain; charset=utf-8", "X-Error": "deno-server" }
85
86
  });
@@ -119,7 +120,7 @@ function createQwikRouter(opts) {
119
120
  return null;
120
121
  } catch (e) {
121
122
  console.error(e);
122
- return new Response(String(e || "Error"), {
123
+ return new Response(isDev ? String(e || "Error") : "Internal Server Error", {
123
124
  status: 500,
124
125
  headers: { "Content-Type": "text/plain; charset=utf-8", "X-Error": "deno-server" }
125
126
  });
@@ -1,5 +1,6 @@
1
1
  import { setServerPlatform } from '@qwik.dev/core/server';
2
2
  import { isStaticPath, mergeHeadersCookies, requestHandler, getNotFound } from '@qwik.dev/router/middleware/request-handler';
3
+ import { isDev } from '@qwik.dev/core/build';
3
4
 
4
5
  function createQwikRouter(opts) {
5
6
  if (opts.qwikCityPlan && !opts.qwikRouterConfig) {
@@ -57,7 +58,7 @@ function createQwikRouter(opts) {
57
58
  });
58
59
  } catch (e) {
59
60
  console.error(e);
60
- return new Response(String(e || "Error"), {
61
+ return new Response(isDev ? String(e || "Error") : "Internal Server Error", {
61
62
  status: 500,
62
63
  headers: { "Content-Type": "text/plain; charset=utf-8", "X-Error": "netlify-edge" }
63
64
  });
@@ -1,5 +1,6 @@
1
1
  import { setServerPlatform } from '@qwik.dev/core/server';
2
2
  import { isStaticPath, mergeHeadersCookies, requestHandler, getNotFound } from '@qwik.dev/router/middleware/request-handler';
3
+ import { isDev } from '@qwik.dev/core/build';
3
4
 
4
5
  const COUNTRY_HEADER_NAME = "x-vercel-ip-country";
5
6
  const IP_HEADER_NAME = "x-real-ip";
@@ -84,7 +85,7 @@ function createQwikRouter(opts) {
84
85
  });
85
86
  } catch (e) {
86
87
  console.error(e);
87
- return new Response(String(e || "Error"), {
88
+ return new Response(isDev ? String(e || "Error") : "Internal Server Error", {
88
89
  status: 500,
89
90
  headers: { "Content-Type": "text/plain; charset=utf-8", "X-Error": "vercel-edge" }
90
91
  });
@@ -1545,21 +1545,19 @@ class HtmlTransformPatcher {
1545
1545
  }
1546
1546
  async processHead() {
1547
1547
  try {
1548
- const fakeHtml = "<html><head>[FAKE_HEAD]</head><body>[FAKE_BODY]</body></html>";
1548
+ const fakeHtml = `<html><head>[FAKE_HEAD]</head><body>[FAKE_BODY]</body></html>`;
1549
1549
  const transformedHtml = await this.server.transformIndexHtml(
1550
1550
  this.request.url || "/",
1551
1551
  fakeHtml
1552
1552
  );
1553
+ const hmrBridge = this.server.hot ? `<script type="module" src="/@id/@qwik-hmr-bridge"><\/script>` : "";
1553
1554
  const fakeHeadIndex = transformedHtml.indexOf("[FAKE_HEAD]");
1554
1555
  const fakeHeadCloseIndex = transformedHtml.indexOf("</head>", fakeHeadIndex);
1555
1556
  if (fakeHeadIndex === -1 || fakeHeadCloseIndex === -1) {
1556
1557
  throw new Error("Transformed HTML does not contain [FAKE_HEAD]...</head>");
1557
1558
  }
1558
1559
  const headPreContent = transformedHtml.slice("<html><head>".length, fakeHeadIndex).trim();
1559
- const headPostContent = transformedHtml.slice(
1560
- fakeHeadIndex + "[FAKE_HEAD]".length,
1561
- fakeHeadCloseIndex
1562
- );
1560
+ const headPostContent = transformedHtml.slice(fakeHeadIndex + "[FAKE_HEAD]".length, fakeHeadCloseIndex) + hmrBridge;
1563
1561
  const fakeBodyStartIndex = transformedHtml.indexOf("<body>", fakeHeadCloseIndex);
1564
1562
  const fakeBodyIndex = transformedHtml.indexOf("[FAKE_BODY]", fakeBodyStartIndex);
1565
1563
  const fakeBodyEndIndex = transformedHtml.indexOf("</body>", fakeBodyIndex);
@@ -1698,31 +1696,30 @@ const CSS_EXTENSIONS = [".css", ".scss", ".sass", ".less", ".styl", ".stylus"];
1698
1696
  const JS_EXTENSIONS = /\.[mc]?[tj]sx?$/;
1699
1697
  const isCssPath = (url) => CSS_EXTENSIONS.some((ext) => url.endsWith(ext));
1700
1698
  const getCssUrls = (server) => {
1699
+ const graph = server.environments.client.moduleGraph;
1701
1700
  const cssModules = /* @__PURE__ */ new Set();
1702
1701
  const cssImportedByCSS = /* @__PURE__ */ new Set();
1703
- Array.from(server.moduleGraph.fileToModulesMap.entries()).forEach(([_name, modules]) => {
1704
- modules.forEach((mod) => {
1705
- const [pathId, query] = mod.url.split("?");
1706
- if (!query && isCssPath(pathId)) {
1707
- const isEntryCSS = mod.importers.size === 0;
1708
- const hasCSSImporter = Array.from(mod.importers).some((importer) => {
1709
- const importerPath = importer.url || importer.file;
1710
- const isCSS = importerPath && isCssPath(importerPath);
1711
- if (isCSS && mod.url) {
1712
- cssImportedByCSS.add(mod.url);
1713
- }
1714
- return isCSS;
1715
- });
1716
- const hasJSImporter = Array.from(mod.importers).some((importer) => {
1717
- const importerPath = importer.url || importer.file;
1718
- return importerPath && JS_EXTENSIONS.test(importerPath);
1719
- });
1720
- if ((isEntryCSS || hasJSImporter) && !hasCSSImporter && !cssImportedByCSS.has(mod.url)) {
1721
- cssModules.add(mod);
1702
+ for (const mod of graph.idToModuleMap.values()) {
1703
+ const [pathId, query] = mod.url.split("?");
1704
+ if (!query && isCssPath(pathId)) {
1705
+ const isEntryCSS = mod.importers.size === 0;
1706
+ const hasCSSImporter = Array.from(mod.importers).some((importer) => {
1707
+ const importerPath = importer.url || importer.file;
1708
+ const isCSS = importerPath && isCssPath(importerPath);
1709
+ if (isCSS && mod.url) {
1710
+ cssImportedByCSS.add(mod.url);
1722
1711
  }
1712
+ return isCSS;
1713
+ });
1714
+ const hasJSImporter = Array.from(mod.importers).some((importer) => {
1715
+ const importerPath = importer.url || importer.file;
1716
+ return importerPath && JS_EXTENSIONS.test(importerPath);
1717
+ });
1718
+ if ((isEntryCSS || hasJSImporter) && !hasCSSImporter && !cssImportedByCSS.has(mod.url)) {
1719
+ cssModules.add(mod);
1723
1720
  }
1724
- });
1725
- });
1721
+ }
1722
+ }
1726
1723
  return [...cssModules].map(
1727
1724
  ({ url, lastHMRTimestamp }) => `${url}${lastHMRTimestamp ? `?t=${lastHMRTimestamp}` : ""}`
1728
1725
  );
@@ -1804,6 +1801,7 @@ function qwikRouterPlugin(userOpts) {
1804
1801
  QWIK_ROUTER_SW_REGISTER
1805
1802
  ]
1806
1803
  },
1804
+ // Duplicated from configEnvironment to support legacy vite build --ssr compatibility
1807
1805
  ssr: {
1808
1806
  external: ["node:async_hooks"],
1809
1807
  noExternal: [
@@ -1824,6 +1822,23 @@ function qwikRouterPlugin(userOpts) {
1824
1822
  };
1825
1823
  return updatedViteConfig;
1826
1824
  },
1825
+ configEnvironment(name, _config, _env) {
1826
+ if (name === "ssr") {
1827
+ return {
1828
+ resolve: {
1829
+ external: ["node:async_hooks"],
1830
+ noExternal: [
1831
+ QWIK_ROUTER,
1832
+ QWIK_ROUTER_CONFIG_ID,
1833
+ QWIK_ROUTER_ENTRIES_ID,
1834
+ QWIK_ROUTER_SW_REGISTER,
1835
+ "zod"
1836
+ ]
1837
+ }
1838
+ };
1839
+ }
1840
+ return {};
1841
+ },
1827
1842
  async configResolved(config) {
1828
1843
  Object.assign(process.env, loadEnv(config.mode, process.cwd(), ""));
1829
1844
  rootDir = resolve(config.root);
@@ -1918,7 +1933,11 @@ function qwikRouterPlugin(userOpts) {
1918
1933
  });
1919
1934
  }
1920
1935
  if (isRouterConfig) {
1921
- return generateQwikRouterConfig(ctx, qwikPlugin, opts?.ssr ?? false);
1936
+ return generateQwikRouterConfig(
1937
+ ctx,
1938
+ qwikPlugin,
1939
+ this.environment.config.consumer === "server"
1940
+ );
1922
1941
  }
1923
1942
  if (isSwRegister) {
1924
1943
  return generateServiceWorkerRegister(ctx, swRegister);
@@ -1969,7 +1988,7 @@ function qwikRouterPlugin(userOpts) {
1969
1988
  return null;
1970
1989
  },
1971
1990
  generateBundle(_, bundles) {
1972
- if (ctx?.target === "client") {
1991
+ if (this.environment.config.consumer === "client") {
1973
1992
  const entries = [...ctx.entries, ...ctx.serviceWorkers].map((entry) => {
1974
1993
  return {
1975
1994
  chunkFileName: entry.chunkFileName,
@@ -1993,7 +2012,7 @@ function qwikRouterPlugin(userOpts) {
1993
2012
  closeBundle: {
1994
2013
  sequential: true,
1995
2014
  async handler() {
1996
- if (ctx?.target === "ssr" && outDir) {
2015
+ if (this.environment.config.consumer === "server" && outDir) {
1997
2016
  await generateServerPackageJson(outDir);
1998
2017
  }
1999
2018
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@qwik.dev/router",
3
3
  "description": "The router for Qwik.",
4
- "version": "2.0.0-beta.27",
4
+ "version": "2.0.0-beta.28",
5
5
  "bugs": "https://github.com/QwikDev/qwik/issues",
6
6
  "dependencies": {
7
7
  "@azure/functions": "3.5.1",
@@ -21,18 +21,18 @@
21
21
  "remark-gfm": "^4.0.1",
22
22
  "set-cookie-parser": "^2.7.2",
23
23
  "source-map": "^0.7.6",
24
- "svgo": "^3.3.2",
24
+ "svgo": "^3.3.3",
25
25
  "unified": "^11.0.5",
26
26
  "unist-util-visit": "^5.0.0",
27
27
  "valibot": ">=0.36.0 <2",
28
28
  "vfile": "6.0.3",
29
- "vite": ">=5 <8",
30
- "vite-imagetools": "9.0.0",
29
+ "vite": ">=6 <9",
30
+ "vite-imagetools": "10.0.0",
31
31
  "yaml": "^2.8.2",
32
32
  "zod": "^3.25.48"
33
33
  },
34
34
  "devDependencies": {
35
- "@microsoft/api-extractor": "7.55.2",
35
+ "@microsoft/api-extractor": "7.57.6",
36
36
  "@types/mdast": "4.0.4",
37
37
  "@types/node": "24.10.0",
38
38
  "@types/refractor": "4.0.0",
@@ -40,7 +40,7 @@
40
40
  "tsm": "2.3.0",
41
41
  "typescript": "5.9.3",
42
42
  "uvu": "0.5.6",
43
- "@qwik.dev/core": "2.0.0-beta.27"
43
+ "@qwik.dev/core": "2.0.0-beta.28"
44
44
  },
45
45
  "engines": {
46
46
  "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
@@ -168,8 +168,8 @@
168
168
  "license": "MIT",
169
169
  "main": "./lib/index.qwik.mjs",
170
170
  "peerDependencies": {
171
- "vite": ">=5 <8",
172
- "@qwik.dev/core": "^2.0.0-beta.27"
171
+ "vite": ">=6 <9",
172
+ "@qwik.dev/core": "^2.0.0-beta.28"
173
173
  },
174
174
  "publishConfig": {
175
175
  "access": "public"