@qwik.dev/router 2.0.0-beta.30 → 2.0.0-beta.32

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,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';
@@ -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;
@@ -1608,10 +1608,10 @@ function isBundlePartOfRoute(bundle, routeAndLayoutPaths) {
1608
1608
  if (!bundle.origins) {
1609
1609
  return false;
1610
1610
  }
1611
- for (const bundleOrigin of bundle.origins) {
1611
+ return bundle.origins.some((bundleOrigin) => {
1612
1612
  const originPath = removeExtension(bundleOrigin);
1613
1613
  return routeAndLayoutPaths.some((path) => path.endsWith(originPath));
1614
- }
1614
+ });
1615
1615
  }
1616
1616
  function getRouteImports(routes, manifest) {
1617
1617
  const result = {};
@@ -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,139 @@ 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),
1921
1997
  map: null
1922
1998
  };
1923
1999
  }
2000
+ if (supportedExtensions.includes(imageId.extension)) {
2001
+ return {
2002
+ code: createImageJsxModule(imageId.pathId, imageId.params),
2003
+ map: null
2004
+ };
2005
+ }
2006
+ return null;
1924
2007
  }
1925
- return null;
1926
2008
  }
1927
2009
  }
1928
2010
  ];
1929
2011
  }
1930
2012
 
2013
+ async function collectServerFnModuleIds(ctx, resolvedVirtualId, loadModule) {
2014
+ const serverFnModules = /* @__PURE__ */ new Set();
2015
+ const queuedModuleIds = /* @__PURE__ */ new Set();
2016
+ const seenModuleIds = /* @__PURE__ */ new Set();
2017
+ const routes = ctx.routes;
2018
+ const layouts = ctx.layouts;
2019
+ const serverPlugins = ctx.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 moduleInfo = await loadModule(id);
2042
+ if (moduleInfo.code?.includes("serverQrl(")) {
2043
+ serverFnModules.add(moduleInfo.id);
2044
+ }
2045
+ const resolvedImports = moduleInfo.importedIdResolutions.concat(moduleInfo.dynamicallyImportedIdResolutions);
2046
+ for (let i = 0; i < resolvedImports.length; i++) {
2047
+ const resolvedImport = resolvedImports[i];
2048
+ if (!resolvedImport.external && !seenModuleIds.has(resolvedImport.id)) {
2049
+ queuedModuleIds.add(resolvedImport.id);
2050
+ }
2051
+ }
2052
+ }
2053
+ return [
2054
+ ...serverFnModules
2055
+ ];
2056
+ }
2057
+
1931
2058
  async function validatePlugin(opts) {
1932
2059
  if (typeof opts.routesDir !== "string") {
1933
2060
  throw new Error(`qwikRouter plugin "routesDir" option missing`);
@@ -2187,16 +2314,14 @@ const makeRouterDevMiddleware = (server, ctx) => async (req, res, next) => {
2187
2314
  const render = async (opts) => {
2188
2315
  return await renderer(opts);
2189
2316
  };
2190
- const { router, staticFile, notFound } = createQwikRouter({
2317
+ const { router, staticFile } = createQwikRouter({
2191
2318
  render,
2192
2319
  // inject the platform from dev middleware options
2193
2320
  platform: ctx.opts.platform
2194
2321
  });
2195
2322
  const wrappedRes = wrapResponseForHtmlTransform(req, res, server);
2196
2323
  staticFile(req, wrappedRes, () => {
2197
- router(req, wrappedRes, () => {
2198
- notFound(req, wrappedRes, next);
2199
- });
2324
+ router(req, wrappedRes, next);
2200
2325
  });
2201
2326
  } catch (e) {
2202
2327
  if (e instanceof Error) {
@@ -2288,7 +2413,7 @@ async function generateServerPackageJson(outDir) {
2288
2413
  const serverPackageJsonCode = JSON.stringify(packageJson, null, 2);
2289
2414
  await fs.promises.writeFile(serverPackageJsonPath, serverPackageJsonCode);
2290
2415
  }
2291
- function qwikRouterPlugin(userOpts) {
2416
+ function qwikRouterPlugin(userOpts, buildContextRef) {
2292
2417
  let ctx = null;
2293
2418
  let mdxTransform = null;
2294
2419
  let rootDir = null;
@@ -2411,6 +2536,7 @@ function qwikRouterPlugin(userOpts) {
2411
2536
  rootDir = resolve(config.root);
2412
2537
  const target = config.build?.ssr || config.mode === "ssr" ? "ssr" : "client";
2413
2538
  ctx = createBuildContext(rootDir, config.base, userOpts, target, !userOpts?.staticImportRoutes);
2539
+ buildContextRef.current = ctx;
2414
2540
  await validatePlugin(ctx.opts);
2415
2541
  mdxTransform = await createMdxTransformer(ctx);
2416
2542
  qwikPlugin = config.plugins.find((p) => p.name === "vite-plugin-qwik");
@@ -2578,20 +2704,34 @@ function qwikRouterPlugin(userOpts) {
2578
2704
  };
2579
2705
  return plugin;
2580
2706
  }
2581
- function serverFnsPlugin() {
2707
+ function serverFnsPlugin(buildContextRef) {
2582
2708
  const RESOLVED_ID = "\0" + VIRTUAL_SERVER_FNS;
2583
2709
  const serverFnModules = /* @__PURE__ */ new Set();
2584
- let pendingModules = 0;
2585
- let resolveServerFns = null;
2586
- let serverFnsReady;
2710
+ let serverFnsReady = null;
2587
2711
  function reset() {
2588
2712
  serverFnModules.clear();
2589
- pendingModules = 0;
2590
- serverFnsReady = new Promise((r) => {
2591
- resolveServerFns = r;
2592
- });
2713
+ serverFnsReady = null;
2593
2714
  }
2594
2715
  reset();
2716
+ async function collectServerFnModules() {
2717
+ if (serverFnsReady) {
2718
+ await serverFnsReady;
2719
+ return;
2720
+ }
2721
+ serverFnsReady = (async () => {
2722
+ const ctx = buildContextRef.current;
2723
+ if (!ctx) {
2724
+ return;
2725
+ }
2726
+ const moduleIds = await collectServerFnModuleIds(ctx, RESOLVED_ID, (id) => this.load({
2727
+ id
2728
+ }));
2729
+ for (let i = 0; i < moduleIds.length; i++) {
2730
+ serverFnModules.add(moduleIds[i]);
2731
+ }
2732
+ })();
2733
+ await serverFnsReady;
2734
+ }
2595
2735
  return {
2596
2736
  name: "vite-plugin-qwik-router-server-fns",
2597
2737
  buildStart() {
@@ -2611,7 +2751,7 @@ function serverFnsPlugin() {
2611
2751
  const isServerBuild = this.environment.config.consumer === "server" && this.environment.mode === "build";
2612
2752
  if (id === RESOLVED_ID) {
2613
2753
  if (isServerBuild) {
2614
- await serverFnsReady;
2754
+ await collectServerFnModules.call(this);
2615
2755
  }
2616
2756
  if (!isServerBuild || serverFnModules.size === 0) {
2617
2757
  return "// No server$ functions";
@@ -2620,35 +2760,18 @@ function serverFnsPlugin() {
2620
2760
  ...serverFnModules
2621
2761
  ].map((mod) => `import ${JSON.stringify(mod)};`).join("\n");
2622
2762
  }
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
2763
  return null;
2644
2764
  }
2645
2765
  }
2646
2766
  };
2647
2767
  }
2648
2768
  function qwikRouter(userOpts) {
2769
+ const buildContextRef = {
2770
+ current: null
2771
+ };
2649
2772
  return [
2650
- qwikRouterPlugin(userOpts),
2651
- serverFnsPlugin(),
2773
+ qwikRouterPlugin(userOpts, buildContextRef),
2774
+ serverFnsPlugin(buildContextRef),
2652
2775
  ...imagePlugin(userOpts)
2653
2776
  ];
2654
2777
  }
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.30",
4
+ "version": "2.0.0-beta.32",
5
5
  "bugs": "https://github.com/QwikDev/qwik/issues",
6
6
  "dependencies": {
7
7
  "@azure/functions": "3.5.1",
@@ -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.30"
43
+ "@qwik.dev/core": "2.0.0-beta.32"
44
44
  },
45
45
  "engines": {
46
46
  "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
@@ -169,7 +169,7 @@
169
169
  "main": "./lib/index.qwik.mjs",
170
170
  "peerDependencies": {
171
171
  "vite": ">=6 <9",
172
- "@qwik.dev/core": "^2.0.0-beta.30"
172
+ "@qwik.dev/core": "^2.0.0-beta.32"
173
173
  },
174
174
  "publishConfig": {
175
175
  "access": "public"