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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import { setServerPlatform } from '@qwik.dev/core/server';
2
- import { isStaticPath, getNotFound, mergeHeadersCookies, requestHandler } from '@qwik.dev/router/middleware/request-handler';
2
+ import { isStaticPath, mergeHeadersCookies, requestHandler } from '@qwik.dev/router/middleware/request-handler';
3
3
  import { M as MIME_TYPES, n as normalizeRequestUrl } from '../../chunks/url.mjs';
4
4
  import { join, fromFileUrl, extname } from 'https://deno.land/std/path/mod.ts';
5
5
  import { isDev } from '@qwik.dev/core/build';
@@ -69,27 +69,8 @@ function createQwikRouter(opts) {
69
69
  });
70
70
  }
71
71
  }
72
- const notFound = async (request) => {
73
- try {
74
- const url = getRequestUrl(request, opts);
75
- const notFoundHtml = !request.headers.get("accept")?.includes("text/html") || isStaticPath(request.method || "GET", url) ? "Not Found" : getNotFound(url.pathname);
76
- return new Response(notFoundHtml, {
77
- status: 404,
78
- headers: {
79
- "Content-Type": "text/html; charset=utf-8",
80
- "X-Not-Found": url.pathname
81
- }
82
- });
83
- } catch (e) {
84
- console.error(e);
85
- return new Response(isDev ? String(e || "Error") : "Internal Server Error", {
86
- status: 500,
87
- headers: {
88
- "Content-Type": "text/plain; charset=utf-8",
89
- "X-Error": "deno-server"
90
- }
91
- });
92
- }
72
+ const notFound = async (_request) => {
73
+ return null;
93
74
  };
94
75
  const openStaticFile = async (url) => {
95
76
  const pathname = url.pathname;
@@ -1,7 +1,7 @@
1
1
  import { createQwikRouter as createQwikRouter$1 } from '@qwik.dev/router/middleware/node';
2
2
 
3
3
  function createQwikRouter(opts) {
4
- const { staticFile, notFound, router } = createQwikRouter$1({
4
+ const { staticFile, router } = createQwikRouter$1({
5
5
  render: opts.render,
6
6
  manifest: opts.manifest,
7
7
  static: {
@@ -16,8 +16,8 @@ function createQwikRouter(opts) {
16
16
  });
17
17
  const qwikApp = (req, res) => {
18
18
  return staticFile(req, res, () => {
19
- router(req, res, () => notFound(req, res, () => {
20
- }));
19
+ router(req, res, () => {
20
+ });
21
21
  });
22
22
  };
23
23
  return qwikApp;
@@ -11,11 +11,7 @@ import type { ServerResponse } from 'node:http';
11
11
  export declare const createQwikCity: typeof createQwikRouter;
12
12
 
13
13
  /** @public */
14
- export declare function createQwikRouter(opts: QwikRouterNodeRequestOptions | QwikCityNodeRequestOptions): {
15
- router: (req: IncomingMessage | Http2ServerRequest, res: ServerResponse, next: NodeRequestNextFunction) => Promise<void>;
16
- notFound: (req: IncomingMessage | Http2ServerRequest, res: ServerResponse, next: (e: any) => void) => Promise<void>;
17
- staticFile: (req: IncomingMessage | Http2ServerRequest, res: ServerResponse, next: (e?: any) => void) => Promise<void>;
18
- };
14
+ export declare function createQwikRouter(opts: QwikRouterNodeRequestOptions | QwikCityNodeRequestOptions): QwikRouterNodeMiddleware;
19
15
 
20
16
  /** @public */
21
17
  export declare interface NodeRequestNextFunction {
@@ -35,6 +31,14 @@ export declare interface PlatformNode {
35
31
  */
36
32
  export declare type QwikCityNodeRequestOptions = QwikRouterNodeRequestOptions;
37
33
 
34
+ /** @public */
35
+ export declare interface QwikRouterNodeMiddleware {
36
+ router: (req: IncomingMessage | Http2ServerRequest, res: ServerResponse, next: NodeRequestNextFunction) => Promise<void>;
37
+ /** @deprecated `router` handles 404 responses. Will be removed in V3. */
38
+ notFound: (req: IncomingMessage | Http2ServerRequest, res: ServerResponse, next: (e: any) => void) => Promise<void>;
39
+ staticFile: (req: IncomingMessage | Http2ServerRequest, res: ServerResponse, next: (e?: any) => void) => Promise<void>;
40
+ }
41
+
38
42
  /** @public */
39
43
  export declare interface QwikRouterNodeRequestOptions extends ServerRenderOptions {
40
44
  /** Options for serving static files */
@@ -1,6 +1,6 @@
1
1
  import { isDev } from '@qwik.dev/core';
2
2
  import { setServerPlatform } from '@qwik.dev/core/server';
3
- import { isStaticPath, getNotFound, requestHandler } from '@qwik.dev/router/middleware/request-handler';
3
+ import { isStaticPath, requestHandler } from '@qwik.dev/router/middleware/request-handler';
4
4
  import { createReadStream } from 'node:fs';
5
5
  import { join, basename, extname } from 'node:path';
6
6
  import { fileURLToPath } from 'node:url';
@@ -32,6 +32,71 @@ function normalizeUrl(url, base) {
32
32
  function getUrl(req, origin) {
33
33
  return normalizeUrl(req.originalUrl || req.url || "/", origin);
34
34
  }
35
+ function createNodeResponseSink(res) {
36
+ let closed = res.closed || res.destroyed;
37
+ const closedPromise = closed ? Promise.resolve() : new Promise((resolve) => {
38
+ res.once("close", () => {
39
+ closed = true;
40
+ resolve();
41
+ });
42
+ });
43
+ const write = (chunk) => {
44
+ if (closed || res.closed || res.destroyed) {
45
+ return;
46
+ }
47
+ return new Promise((resolve, reject) => {
48
+ let settled = false;
49
+ const finish = () => {
50
+ if (settled) {
51
+ return;
52
+ }
53
+ settled = true;
54
+ setImmediate(resolve);
55
+ };
56
+ const fail = (error) => {
57
+ if (settled) {
58
+ return;
59
+ }
60
+ settled = true;
61
+ reject(error);
62
+ };
63
+ closedPromise.then(finish);
64
+ try {
65
+ res.write(chunk, (error) => {
66
+ if (error) {
67
+ if (isIgnoredError(error.message)) {
68
+ finish();
69
+ return;
70
+ }
71
+ fail(error);
72
+ return;
73
+ }
74
+ finish();
75
+ });
76
+ } catch (error) {
77
+ if (error instanceof Error && isIgnoredError(error.message)) {
78
+ finish();
79
+ return;
80
+ }
81
+ fail(error);
82
+ }
83
+ });
84
+ };
85
+ const close = () => {
86
+ if (closed || res.closed || res.destroyed) {
87
+ return;
88
+ }
89
+ return new Promise((resolve) => {
90
+ res.end(() => {
91
+ resolve();
92
+ });
93
+ });
94
+ };
95
+ return {
96
+ write,
97
+ close
98
+ };
99
+ }
35
100
  async function fromNodeHttp(url, req, res, mode, getClientConn) {
36
101
  const requestHeaders = new Headers();
37
102
  const nodeRequestHeaders = req.headers;
@@ -79,6 +144,7 @@ async function fromNodeHttp(url, req, res, mode, getClientConn) {
79
144
  },
80
145
  getWritableStream: (status, headers, cookies) => {
81
146
  res.statusCode = status;
147
+ const sink = createNodeResponseSink(res);
82
148
  try {
83
149
  for (const [key, value] of headers) {
84
150
  if (invalidHeadersPattern.test(key)) {
@@ -95,17 +161,10 @@ async function fromNodeHttp(url, req, res, mode, getClientConn) {
95
161
  }
96
162
  return new WritableStream({
97
163
  write(chunk) {
98
- if (res.closed || res.destroyed) {
99
- return;
100
- }
101
- res.write(chunk, (error) => {
102
- if (error && !isIgnoredError(error.message)) {
103
- console.error(error);
104
- }
105
- });
164
+ return sink.write(chunk);
106
165
  },
107
166
  close() {
108
- res.end();
167
+ return sink.close();
109
168
  }
110
169
  });
111
170
  },
@@ -139,12 +198,12 @@ function createQwikRouter(opts) {
139
198
  const handled = await requestHandler(serverRequestEv, opts);
140
199
  if (handled) {
141
200
  const err = await handled.completion;
142
- if (err) {
143
- throw err;
144
- }
145
201
  if (handled.requestEv.headersSent) {
146
202
  return;
147
203
  }
204
+ if (err) {
205
+ throw err;
206
+ }
148
207
  }
149
208
  next();
150
209
  } catch (e) {
@@ -152,23 +211,7 @@ function createQwikRouter(opts) {
152
211
  next(e);
153
212
  }
154
213
  };
155
- const notFound = async (req, res, next) => {
156
- try {
157
- if (!res.headersSent) {
158
- const origin = computeOrigin(req, opts);
159
- const url = getUrl(req, origin);
160
- const notFoundHtml = !req.headers.accept?.includes("text/html") || isStaticPath(req.method || "GET", url) ? "Not Found" : getNotFound(url.pathname);
161
- res.writeHead(404, {
162
- "Content-Type": "text/html; charset=utf-8",
163
- "X-Not-Found": url.pathname
164
- });
165
- res.end(notFoundHtml);
166
- }
167
- } catch (e) {
168
- console.error(e);
169
- next(e);
170
- }
171
- };
214
+ const notFound = async (_req, _res, next) => next();
172
215
  const staticFile = async (req, res, next) => {
173
216
  try {
174
217
  const origin = computeOrigin(req, opts);
@@ -1,4 +1,4 @@
1
- import { mergeConfig, loadEnv } from 'vite';
1
+ import { mergeConfig, normalizePath as normalizePath$1, loadEnv } from 'vite';
2
2
  import fs, { existsSync } from 'node:fs';
3
3
  import path, { join, dirname, basename, resolve, isAbsolute, extname } from 'node:path';
4
4
  import { g as getExtension, r as removeExtension, i as isIndexModule, a as isErrorName, b as isLayoutModule, c as isEntryName, d as isMenuFileName, e as isServiceWorkerName, f as isPageModuleExt, h as isModuleExt, j as isMarkdownExt, k as normalizePath, l as getPathnameFromDirPath, m as getMenuPathname, o as createFileId, p as parseRouteIndexName, q as isPluginModule, s as isPageExt } from '../chunks/fs.mjs';
@@ -479,7 +479,7 @@ async function walkServerPlugins(opts) {
479
479
  });
480
480
  }
481
481
  }));
482
- return sourceFiles;
482
+ return sourceFiles.sort((a, b) => a.filePath < b.filePath ? -1 : 1);
483
483
  }
484
484
 
485
485
  function resolveEntry(opts, sourceFile) {
@@ -1326,7 +1326,7 @@ function serializeBuildTrie(ctx, qwikPlugin, node, layoutIdMap, routeIdMap, menu
1326
1326
  if (node._9) {
1327
1327
  lines.push(`${nextIndent}_9: ${JSON.stringify(node._9)},`);
1328
1328
  }
1329
- if (node._G) {
1329
+ if (node._G != null) {
1330
1330
  lines.push(`${nextIndent}_G: ${JSON.stringify(node._G)},`);
1331
1331
  }
1332
1332
  let layoutExpr;
@@ -1756,6 +1756,67 @@ function parseId(originalId) {
1756
1756
  };
1757
1757
  }
1758
1758
 
1759
+ const supportedExtensions = [
1760
+ ".jpg",
1761
+ ".jpeg",
1762
+ ".png",
1763
+ ".webp",
1764
+ ".gif",
1765
+ ".avif",
1766
+ ".tiff"
1767
+ ];
1768
+ const JSX_QUERY_PARAM = "jsx";
1769
+ const INTERNAL_IMAGE_JSX_QUERY_PARAM = "qwik-asset-jsx";
1770
+ const VIRTUAL_IMAGE_JSX_PREFIX = "virtual:";
1771
+ const VIRTUAL_IMAGE_JSX_SUFFIX = ".qwik.jsx";
1772
+ const TO_IMG_ID = "@to-img.qwik.jsx";
1773
+ const VIRTUAL_TO_IMG_ID = "virtual:to-img.qwik.jsx";
1774
+ function createVirtualImageJsxId(pathId, params) {
1775
+ const query = new URLSearchParams(params);
1776
+ query.delete(INTERNAL_IMAGE_JSX_QUERY_PARAM);
1777
+ const queryString = query.toString();
1778
+ return `${VIRTUAL_IMAGE_JSX_PREFIX}${normalizePath$1(pathId)}${VIRTUAL_IMAGE_JSX_SUFFIX}${queryString ? `?${queryString}` : ""}`;
1779
+ }
1780
+ function createImageJsxImportId(pathId, params) {
1781
+ const query = new URLSearchParams(params);
1782
+ query.set(INTERNAL_IMAGE_JSX_QUERY_PARAM, "");
1783
+ return `${normalizePath$1(pathId)}?${query.toString()}`;
1784
+ }
1785
+ function parseVirtualImageJsxId(id) {
1786
+ const parsed = parseId(id);
1787
+ if (!parsed.pathId.startsWith(VIRTUAL_IMAGE_JSX_PREFIX) || !parsed.pathId.endsWith(VIRTUAL_IMAGE_JSX_SUFFIX)) {
1788
+ return null;
1789
+ }
1790
+ const pathId = parsed.pathId.slice(VIRTUAL_IMAGE_JSX_PREFIX.length, -VIRTUAL_IMAGE_JSX_SUFFIX.length);
1791
+ return {
1792
+ ...parsed,
1793
+ extension: path.extname(pathId).toLowerCase(),
1794
+ pathId
1795
+ };
1796
+ }
1797
+ function getToImg() {
1798
+ return `
1799
+ import { _jsxSplit, _getVarProps as v, _getConstProps as c} from '@qwik.dev/core';
1800
+ const decoding = "async";
1801
+ const loading = "lazy";
1802
+ export default (s, w, h) =>
1803
+ // Try to preserve const attrs by not spreading when props are not provided
1804
+ p => p
1805
+ ? _jsxSplit("img", {decoding, loading, ...v(p)}, {...c(p), height: h, srcSet: s, width: w })
1806
+ : _jsxSplit("img", null, {decoding, height: h, loading, srcSet: s, width: w});
1807
+ `;
1808
+ }
1809
+ function createImageJsxModule(pathId, params) {
1810
+ return `
1811
+ import { srcSet, width, height } from ${JSON.stringify(createImageJsxImportId(pathId, params))};
1812
+ import toImg from ${JSON.stringify(TO_IMG_ID)};
1813
+
1814
+ export default toImg(srcSet, width, height);
1815
+ `;
1816
+ }
1817
+ function createSvgJsxModule(attrs) {
1818
+ return `export default p => <svg {...p} {...${JSON.stringify(attrs)}} />`;
1819
+ }
1759
1820
  function optimizeSvg({ code, path: path2 }, userOpts) {
1760
1821
  const svgAttributes = {};
1761
1822
  const prefixIdsConfiguration = userOpts?.imageOptimization?.svgo?.prefixIds;
@@ -1771,7 +1832,7 @@ function optimizeSvg({ code, path: path2 }, userOpts) {
1771
1832
  return false;
1772
1833
  }
1773
1834
  if (plugin === "prefixIds" || typeof plugin === "object" && plugin.name === "prefixIds") {
1774
- 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 prefixIds option.`);
1835
+ 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.`);
1775
1836
  return false;
1776
1837
  }
1777
1838
  return true;
@@ -1817,15 +1878,6 @@ function optimizeSvg({ code, path: path2 }, userOpts) {
1817
1878
  };
1818
1879
  }
1819
1880
  function imagePlugin(userOpts) {
1820
- const supportedExtensions = [
1821
- ".jpg",
1822
- ".jpeg",
1823
- ".png",
1824
- ".webp",
1825
- ".gif",
1826
- ".avif",
1827
- ".tiff"
1828
- ];
1829
1881
  return [
1830
1882
  import('vite-imagetools').then((_rawProps) => _rawProps.imagetools({
1831
1883
  exclude: [],
@@ -1853,8 +1905,10 @@ function imagePlugin(userOpts) {
1853
1905
  };
1854
1906
  },
1855
1907
  defaultDirectives: (url) => {
1856
- if (url.searchParams.has("jsx")) {
1857
- const { jsx: _, ...params } = Object.fromEntries(url.searchParams.entries());
1908
+ if (url.searchParams.has(JSX_QUERY_PARAM)) {
1909
+ const params = Object.fromEntries(url.searchParams.entries());
1910
+ delete params[JSX_QUERY_PARAM];
1911
+ delete params[INTERNAL_IMAGE_JSX_QUERY_PARAM];
1858
1912
  return new URLSearchParams({
1859
1913
  format: "webp",
1860
1914
  quality: "75",
@@ -1868,66 +1922,150 @@ function imagePlugin(userOpts) {
1868
1922
  return new URLSearchParams();
1869
1923
  }
1870
1924
  })).catch((err) => {
1871
- console.error("Error loading vite-imagetools, image imports are not available", err);
1925
+ console.error('Error loading vite-imagetools, image imports ("foo.png?jsx") are not available', err);
1872
1926
  return null;
1873
1927
  }),
1874
1928
  {
1875
1929
  name: "qwik-router-image-jsx",
1930
+ resolveId: {
1931
+ order: "pre",
1932
+ async handler(id, importer, options) {
1933
+ if (parseVirtualImageJsxId(id)) {
1934
+ return null;
1935
+ }
1936
+ if (id.endsWith(TO_IMG_ID)) {
1937
+ return VIRTUAL_TO_IMG_ID;
1938
+ }
1939
+ const { pathId, params } = parseId(id);
1940
+ if (!params.has(JSX_QUERY_PARAM) || params.has(INTERNAL_IMAGE_JSX_QUERY_PARAM)) {
1941
+ return null;
1942
+ }
1943
+ const resolved = await this.resolve(pathId, importer, {
1944
+ ...options,
1945
+ skipSelf: true
1946
+ });
1947
+ const resolvedId = parseId((resolved ?? {
1948
+ id: pathId
1949
+ }).id).pathId;
1950
+ const extension = path.extname(resolvedId).toLowerCase();
1951
+ if (extension !== ".svg" && !supportedExtensions.includes(extension)) {
1952
+ return null;
1953
+ }
1954
+ return {
1955
+ id: createVirtualImageJsxId(resolvedId, params),
1956
+ moduleSideEffects: false
1957
+ };
1958
+ }
1959
+ },
1876
1960
  load: {
1877
1961
  order: "pre",
1878
1962
  handler: async (id) => {
1879
- const { params, pathId } = parseId(id);
1880
- const extension = path.extname(pathId).toLowerCase();
1881
- if (extension === ".svg" && params.has("jsx")) {
1882
- const code = await fs.promises.readFile(pathId, "utf-8");
1963
+ if (id === VIRTUAL_TO_IMG_ID) {
1964
+ return getToImg();
1965
+ }
1966
+ const imageId = parseVirtualImageJsxId(id);
1967
+ if (!imageId) {
1968
+ return null;
1969
+ }
1970
+ if (imageId.extension === ".svg") {
1971
+ const code = await fs.promises.readFile(imageId.pathId, "utf-8");
1883
1972
  return {
1884
1973
  code,
1885
1974
  moduleSideEffects: false
1886
1975
  };
1887
1976
  }
1977
+ return {
1978
+ code: "export default undefined;",
1979
+ moduleSideEffects: false
1980
+ };
1888
1981
  }
1889
1982
  },
1890
- transform(code, id) {
1891
- id = id.toLowerCase();
1892
- const { params, pathId } = parseId(id);
1893
- if (params.has("jsx")) {
1894
- const extension = path.extname(pathId).toLowerCase();
1895
- if (supportedExtensions.includes(extension)) {
1896
- if (!code.includes("srcSet")) {
1897
- this.error(`Image '${id}' could not be optimized to JSX`);
1898
- }
1899
- const index = code.indexOf("export default");
1900
- return {
1901
- code: code.slice(0, index) + `
1902
- import { _jsxSorted } from '@qwik.dev/core';
1903
- const PROPS = {srcSet, width, height};
1904
- export default function (props, key, _, dev) {
1905
- return _jsxSorted('img', {...{decoding: 'async', loading: 'lazy'}, ...props}, PROPS, undefined, 3, key, dev);
1906
- }`,
1907
- map: null
1908
- };
1909
- } else if (extension === ".svg") {
1983
+ transform: {
1984
+ order: "pre",
1985
+ handler(code, id) {
1986
+ const imageId = parseVirtualImageJsxId(id);
1987
+ if (!imageId) {
1988
+ return null;
1989
+ }
1990
+ if (imageId.extension === ".svg") {
1910
1991
  const { svgAttributes } = optimizeSvg({
1911
1992
  code,
1912
- path: pathId
1993
+ path: imageId.pathId
1913
1994
  }, userOpts);
1914
1995
  return {
1915
- code: `
1916
- import { _jsxSorted } from '@qwik.dev/core';
1917
- const PROPS = ${JSON.stringify(svgAttributes)};
1918
- export default function (props, key, _, dev) {
1919
- return _jsxSorted('svg', props, PROPS, undefined, 3, key, dev);
1920
- }`,
1996
+ code: createSvgJsxModule(svgAttributes),
1997
+ map: null
1998
+ };
1999
+ }
2000
+ if (supportedExtensions.includes(imageId.extension)) {
2001
+ return {
2002
+ code: createImageJsxModule(imageId.pathId, imageId.params),
1921
2003
  map: null
1922
2004
  };
1923
2005
  }
2006
+ return null;
1924
2007
  }
1925
- return null;
1926
2008
  }
1927
2009
  }
1928
2010
  ];
1929
2011
  }
1930
2012
 
2013
+ async function collectServerFnModuleIds(routingContext, resolvedVirtualId, pluginContext) {
2014
+ const serverFnModules = /* @__PURE__ */ new Set();
2015
+ const queuedModuleIds = /* @__PURE__ */ new Set();
2016
+ const seenModuleIds = /* @__PURE__ */ new Set();
2017
+ const routes = routingContext.routes;
2018
+ const layouts = routingContext.layouts;
2019
+ const serverPlugins = routingContext.serverPlugins;
2020
+ for (let i = 0; i < routes.length; i++) {
2021
+ const route = routes[i];
2022
+ queuedModuleIds.add(route.filePath);
2023
+ for (let j = 0; j < route.layouts.length; j++) {
2024
+ const layout = route.layouts[j];
2025
+ queuedModuleIds.add(layout.filePath);
2026
+ }
2027
+ }
2028
+ for (let i = 0; i < layouts.length; i++) {
2029
+ queuedModuleIds.add(layouts[i].filePath);
2030
+ }
2031
+ for (let i = 0; i < serverPlugins.length; i++) {
2032
+ queuedModuleIds.add(serverPlugins[i].filePath);
2033
+ }
2034
+ while (queuedModuleIds.size > 0) {
2035
+ const [id] = queuedModuleIds;
2036
+ queuedModuleIds.delete(id);
2037
+ if (seenModuleIds.has(id) || id === resolvedVirtualId) {
2038
+ continue;
2039
+ }
2040
+ seenModuleIds.add(id);
2041
+ const resolved = await pluginContext.resolve(id, void 0, {
2042
+ skipSelf: true
2043
+ });
2044
+ if (!resolved || resolved.external) {
2045
+ continue;
2046
+ }
2047
+ const moduleInfo = await pluginContext.load({
2048
+ id: resolved.id
2049
+ });
2050
+ if (moduleInfo.code == null) {
2051
+ continue;
2052
+ }
2053
+ if (moduleInfo.code.includes("serverQrl(")) {
2054
+ serverFnModules.add(moduleInfo.id);
2055
+ }
2056
+ const resolvedImports = moduleInfo.importedIds.concat(moduleInfo.dynamicallyImportedIds);
2057
+ for (let i = 0; i < resolvedImports.length; i++) {
2058
+ const resolvedImport = resolvedImports[i];
2059
+ if (resolvedImport && !seenModuleIds.has(resolvedImport)) {
2060
+ queuedModuleIds.add(resolvedImport);
2061
+ }
2062
+ }
2063
+ }
2064
+ return [
2065
+ ...serverFnModules
2066
+ ];
2067
+ }
2068
+
1931
2069
  async function validatePlugin(opts) {
1932
2070
  if (typeof opts.routesDir !== "string") {
1933
2071
  throw new Error(`qwikRouter plugin "routesDir" option missing`);
@@ -2187,16 +2325,14 @@ const makeRouterDevMiddleware = (server, ctx) => async (req, res, next) => {
2187
2325
  const render = async (opts) => {
2188
2326
  return await renderer(opts);
2189
2327
  };
2190
- const { router, staticFile, notFound } = createQwikRouter({
2328
+ const { router, staticFile } = createQwikRouter({
2191
2329
  render,
2192
2330
  // inject the platform from dev middleware options
2193
2331
  platform: ctx.opts.platform
2194
2332
  });
2195
2333
  const wrappedRes = wrapResponseForHtmlTransform(req, res, server);
2196
2334
  staticFile(req, wrappedRes, () => {
2197
- router(req, wrappedRes, () => {
2198
- notFound(req, wrappedRes, next);
2199
- });
2335
+ router(req, wrappedRes, next);
2200
2336
  });
2201
2337
  } catch (e) {
2202
2338
  if (e instanceof Error) {
@@ -2288,7 +2424,7 @@ async function generateServerPackageJson(outDir) {
2288
2424
  const serverPackageJsonCode = JSON.stringify(packageJson, null, 2);
2289
2425
  await fs.promises.writeFile(serverPackageJsonPath, serverPackageJsonCode);
2290
2426
  }
2291
- function qwikRouterPlugin(userOpts) {
2427
+ function qwikRouterPlugin(userOpts, buildContextRef) {
2292
2428
  let ctx = null;
2293
2429
  let mdxTransform = null;
2294
2430
  let rootDir = null;
@@ -2411,6 +2547,7 @@ function qwikRouterPlugin(userOpts) {
2411
2547
  rootDir = resolve(config.root);
2412
2548
  const target = config.build?.ssr || config.mode === "ssr" ? "ssr" : "client";
2413
2549
  ctx = createBuildContext(rootDir, config.base, userOpts, target, !userOpts?.staticImportRoutes);
2550
+ buildContextRef.current = ctx;
2414
2551
  await validatePlugin(ctx.opts);
2415
2552
  mdxTransform = await createMdxTransformer(ctx);
2416
2553
  qwikPlugin = config.plugins.find((p) => p.name === "vite-plugin-qwik");
@@ -2578,20 +2715,32 @@ function qwikRouterPlugin(userOpts) {
2578
2715
  };
2579
2716
  return plugin;
2580
2717
  }
2581
- function serverFnsPlugin() {
2718
+ function serverFnsPlugin(buildContextRef) {
2582
2719
  const RESOLVED_ID = "\0" + VIRTUAL_SERVER_FNS;
2583
2720
  const serverFnModules = /* @__PURE__ */ new Set();
2584
- let pendingModules = 0;
2585
- let resolveServerFns = null;
2586
- let serverFnsReady;
2721
+ let serverFnsReady = null;
2587
2722
  function reset() {
2588
2723
  serverFnModules.clear();
2589
- pendingModules = 0;
2590
- serverFnsReady = new Promise((r) => {
2591
- resolveServerFns = r;
2592
- });
2724
+ serverFnsReady = null;
2593
2725
  }
2594
2726
  reset();
2727
+ async function collectServerFnModules() {
2728
+ if (serverFnsReady) {
2729
+ await serverFnsReady;
2730
+ return;
2731
+ }
2732
+ serverFnsReady = (async () => {
2733
+ const ctx = buildContextRef.current;
2734
+ if (!ctx) {
2735
+ return;
2736
+ }
2737
+ const moduleIds = await collectServerFnModuleIds(ctx, RESOLVED_ID, this);
2738
+ for (let i = 0; i < moduleIds.length; i++) {
2739
+ serverFnModules.add(moduleIds[i]);
2740
+ }
2741
+ })();
2742
+ await serverFnsReady;
2743
+ }
2595
2744
  return {
2596
2745
  name: "vite-plugin-qwik-router-server-fns",
2597
2746
  buildStart() {
@@ -2611,7 +2760,7 @@ function serverFnsPlugin() {
2611
2760
  const isServerBuild = this.environment.config.consumer === "server" && this.environment.mode === "build";
2612
2761
  if (id === RESOLVED_ID) {
2613
2762
  if (isServerBuild) {
2614
- await serverFnsReady;
2763
+ await collectServerFnModules.call(this);
2615
2764
  }
2616
2765
  if (!isServerBuild || serverFnModules.size === 0) {
2617
2766
  return "// No server$ functions";
@@ -2620,35 +2769,18 @@ function serverFnsPlugin() {
2620
2769
  ...serverFnModules
2621
2770
  ].map((mod) => `import ${JSON.stringify(mod)};`).join("\n");
2622
2771
  }
2623
- if (isServerBuild && id !== RESOLVED_ID) {
2624
- pendingModules++;
2625
- this.load({
2626
- id
2627
- }).then((result) => {
2628
- if (typeof result.code === "string" && result.code.includes("serverQrl(")) {
2629
- serverFnModules.add(id);
2630
- }
2631
- }).finally(() => {
2632
- pendingModules--;
2633
- if (pendingModules <= 0 && resolveServerFns) {
2634
- setTimeout(() => {
2635
- if (pendingModules <= 0 && resolveServerFns) {
2636
- resolveServerFns();
2637
- resolveServerFns = null;
2638
- }
2639
- }, 50);
2640
- }
2641
- });
2642
- }
2643
2772
  return null;
2644
2773
  }
2645
2774
  }
2646
2775
  };
2647
2776
  }
2648
2777
  function qwikRouter(userOpts) {
2778
+ const buildContextRef = {
2779
+ current: null
2780
+ };
2649
2781
  return [
2650
- qwikRouterPlugin(userOpts),
2651
- serverFnsPlugin(),
2782
+ qwikRouterPlugin(userOpts, buildContextRef),
2783
+ serverFnsPlugin(buildContextRef),
2652
2784
  ...imagePlugin(userOpts)
2653
2785
  ];
2654
2786
  }