@pyreon/zero 0.13.1 → 0.15.0

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 (80) hide show
  1. package/lib/api-routes-DANluJic.js +146 -0
  2. package/lib/client.js +3 -1
  3. package/lib/csp.js +19 -9
  4. package/lib/{fs-router-CQ7Zxeca.js → fs-router-ZebyutPa.js} +43 -6
  5. package/lib/image-plugin.js +4 -0
  6. package/lib/image.js +1 -50
  7. package/lib/index.js +1 -50
  8. package/lib/link.js +1 -49
  9. package/lib/script.js +1 -49
  10. package/lib/server.js +6 -688
  11. package/lib/theme.js +1 -50
  12. package/lib/types/i18n-routing.d.ts +4 -4
  13. package/lib/types/index.d.ts +23 -13
  14. package/lib/types/link.d.ts +3 -3
  15. package/lib/types/server.d.ts +28 -5
  16. package/lib/types/theme.d.ts +2 -2
  17. package/lib/vite-plugin-E4BHYvYW.js +855 -0
  18. package/package.json +15 -13
  19. package/src/app.ts +21 -1
  20. package/src/csp.ts +28 -12
  21. package/src/fs-router.ts +53 -3
  22. package/src/ssg-plugin.ts +366 -0
  23. package/src/types.ts +28 -9
  24. package/src/vite-plugin.ts +220 -40
  25. package/lib/actions.js.map +0 -1
  26. package/lib/ai.js.map +0 -1
  27. package/lib/api-routes.js.map +0 -1
  28. package/lib/cache.js.map +0 -1
  29. package/lib/client.js.map +0 -1
  30. package/lib/compression.js.map +0 -1
  31. package/lib/config.js.map +0 -1
  32. package/lib/cors.js.map +0 -1
  33. package/lib/csp.js.map +0 -1
  34. package/lib/env.js.map +0 -1
  35. package/lib/favicon.js.map +0 -1
  36. package/lib/font.js.map +0 -1
  37. package/lib/fs-router-3xzp-4Wj.js.map +0 -1
  38. package/lib/fs-router-CQ7Zxeca.js.map +0 -1
  39. package/lib/i18n-routing.js.map +0 -1
  40. package/lib/image-plugin.js.map +0 -1
  41. package/lib/image.js.map +0 -1
  42. package/lib/index.js.map +0 -1
  43. package/lib/link.js.map +0 -1
  44. package/lib/logger.js.map +0 -1
  45. package/lib/meta.js.map +0 -1
  46. package/lib/middleware.js.map +0 -1
  47. package/lib/og-image.js.map +0 -1
  48. package/lib/rate-limit.js.map +0 -1
  49. package/lib/script.js.map +0 -1
  50. package/lib/seo.js.map +0 -1
  51. package/lib/server.js.map +0 -1
  52. package/lib/testing.js.map +0 -1
  53. package/lib/theme.js.map +0 -1
  54. package/lib/types/actions.d.ts.map +0 -1
  55. package/lib/types/ai.d.ts.map +0 -1
  56. package/lib/types/api-routes.d.ts.map +0 -1
  57. package/lib/types/cache.d.ts.map +0 -1
  58. package/lib/types/client.d.ts.map +0 -1
  59. package/lib/types/compression.d.ts.map +0 -1
  60. package/lib/types/config.d.ts.map +0 -1
  61. package/lib/types/cors.d.ts.map +0 -1
  62. package/lib/types/csp.d.ts.map +0 -1
  63. package/lib/types/env.d.ts.map +0 -1
  64. package/lib/types/favicon.d.ts.map +0 -1
  65. package/lib/types/font.d.ts.map +0 -1
  66. package/lib/types/i18n-routing.d.ts.map +0 -1
  67. package/lib/types/image-plugin.d.ts.map +0 -1
  68. package/lib/types/image.d.ts.map +0 -1
  69. package/lib/types/index.d.ts.map +0 -1
  70. package/lib/types/link.d.ts.map +0 -1
  71. package/lib/types/logger.d.ts.map +0 -1
  72. package/lib/types/meta.d.ts.map +0 -1
  73. package/lib/types/middleware.d.ts.map +0 -1
  74. package/lib/types/og-image.d.ts.map +0 -1
  75. package/lib/types/rate-limit.d.ts.map +0 -1
  76. package/lib/types/script.d.ts.map +0 -1
  77. package/lib/types/seo.d.ts.map +0 -1
  78. package/lib/types/server.d.ts.map +0 -1
  79. package/lib/types/testing.d.ts.map +0 -1
  80. package/lib/types/theme.d.ts.map +0 -1
@@ -0,0 +1,146 @@
1
+ import { l as __exportAll } from "./fs-router-ZebyutPa.js";
2
+
3
+ //#region src/api-routes.ts
4
+ var api_routes_exports = /* @__PURE__ */ __exportAll({
5
+ apiFilePathToPattern: () => apiFilePathToPattern,
6
+ createApiMiddleware: () => createApiMiddleware,
7
+ generateApiRouteModule: () => generateApiRouteModule,
8
+ isApiRoute: () => isApiRoute,
9
+ matchApiRoute: () => matchApiRoute
10
+ });
11
+ /**
12
+ * Match a URL path against an API route pattern.
13
+ * Returns extracted params or null if no match.
14
+ */
15
+ function matchApiRoute(pattern, path) {
16
+ const patternParts = pattern.split("/").filter(Boolean);
17
+ const pathParts = path.split("/").filter(Boolean);
18
+ const params = {};
19
+ for (let i = 0; i < patternParts.length; i++) {
20
+ const pp = patternParts[i];
21
+ if (!pp) continue;
22
+ if (pp.endsWith("*")) {
23
+ const paramName = pp.slice(1, -1);
24
+ params[paramName] = pathParts.slice(i).join("/");
25
+ return params;
26
+ }
27
+ if (i >= pathParts.length) return null;
28
+ if (pp.startsWith(":")) {
29
+ params[pp.slice(1)] = pathParts[i];
30
+ continue;
31
+ }
32
+ if (pp !== pathParts[i]) return null;
33
+ }
34
+ return patternParts.length === pathParts.length ? params : null;
35
+ }
36
+ const HTTP_METHODS = [
37
+ "GET",
38
+ "POST",
39
+ "PUT",
40
+ "PATCH",
41
+ "DELETE",
42
+ "HEAD",
43
+ "OPTIONS"
44
+ ];
45
+ /**
46
+ * Create a middleware that dispatches API route requests.
47
+ * API routes are matched by URL pattern and HTTP method.
48
+ */
49
+ function createApiMiddleware(routes) {
50
+ return async (ctx) => {
51
+ for (const route of routes) {
52
+ const params = matchApiRoute(route.pattern, ctx.path);
53
+ if (!params) continue;
54
+ const method = ctx.req.method.toUpperCase();
55
+ const handler = route.module[method];
56
+ if (!handler) {
57
+ const allowed = HTTP_METHODS.filter((m) => route.module[m]).join(", ");
58
+ return new Response(null, {
59
+ status: 405,
60
+ headers: {
61
+ Allow: allowed,
62
+ "Content-Type": "application/json"
63
+ }
64
+ });
65
+ }
66
+ return handler({
67
+ request: ctx.req,
68
+ url: ctx.url,
69
+ path: ctx.path,
70
+ params,
71
+ headers: ctx.req.headers
72
+ });
73
+ }
74
+ };
75
+ }
76
+ /**
77
+ * Detect whether a route file is an API route.
78
+ * API routes are `.ts` or `.js` files inside an `api/` directory.
79
+ */
80
+ function isApiRoute(filePath) {
81
+ const normalized = filePath.replace(/\\/g, "/");
82
+ return normalized.startsWith("api/") && (normalized.endsWith(".ts") || normalized.endsWith(".js")) && !normalized.endsWith(".tsx") && !normalized.endsWith(".jsx");
83
+ }
84
+ /**
85
+ * Convert an API route file path to a URL pattern.
86
+ *
87
+ * Examples:
88
+ * "api/posts.ts" → "/api/posts"
89
+ * "api/posts/index.ts" → "/api/posts"
90
+ * "api/posts/[id].ts" → "/api/posts/:id"
91
+ * "api/[...path].ts" → "/api/:path*"
92
+ */
93
+ function apiFilePathToPattern(filePath) {
94
+ let route = filePath;
95
+ for (const ext of [".ts", ".js"]) if (route.endsWith(ext)) {
96
+ route = route.slice(0, -ext.length);
97
+ break;
98
+ }
99
+ const segments = route.split("/");
100
+ const urlSegments = [];
101
+ for (const seg of segments) {
102
+ if (seg === "index") continue;
103
+ const catchAll = seg.match(/^\[\.\.\.(\w+)\]$/);
104
+ if (catchAll) {
105
+ urlSegments.push(`:${catchAll[1]}*`);
106
+ continue;
107
+ }
108
+ const dynamic = seg.match(/^\[(\w+)\]$/);
109
+ if (dynamic) {
110
+ urlSegments.push(`:${dynamic[1]}`);
111
+ continue;
112
+ }
113
+ urlSegments.push(seg);
114
+ }
115
+ return `/${urlSegments.join("/")}`;
116
+ }
117
+ /**
118
+ * Generate a virtual module that exports API route entries.
119
+ * Each entry maps a URL pattern to a module with HTTP method handlers.
120
+ */
121
+ function generateApiRouteModule(files, routesDir) {
122
+ const apiFiles = files.filter(isApiRoute);
123
+ if (apiFiles.length === 0) return "export const apiRoutes = []\n";
124
+ const imports = [];
125
+ const entries = [];
126
+ for (let i = 0; i < apiFiles.length; i++) {
127
+ const name = `_api${i}`;
128
+ const file = apiFiles[i];
129
+ if (!file) continue;
130
+ const fullPath = `${routesDir}/${file}`;
131
+ const pattern = apiFilePathToPattern(file);
132
+ imports.push(`import * as ${name} from "${fullPath}"`);
133
+ entries.push(` { pattern: ${JSON.stringify(pattern)}, module: ${name} }`);
134
+ }
135
+ return [
136
+ ...imports,
137
+ "",
138
+ "export const apiRoutes = [",
139
+ entries.join(",\n"),
140
+ "]"
141
+ ].join("\n");
142
+ }
143
+
144
+ //#endregion
145
+ export { matchApiRoute as i, createApiMiddleware as n, generateApiRouteModule as r, api_routes_exports as t };
146
+ //# sourceMappingURL=api-routes-DANluJic.js.map
package/lib/client.js CHANGED
@@ -16,7 +16,9 @@ function createApp(options) {
16
16
  ...options.url ? { url: options.url } : {},
17
17
  scrollBehavior: "top"
18
18
  });
19
- const Layout = options.layout ?? DefaultLayout;
19
+ const hasLayoutInRoutes = options.layout !== void 0 && options.routes.some((r) => r.component === options.layout);
20
+ if (hasLayoutInRoutes && process.env.NODE_ENV !== "production") console.warn("[Pyreon] `createApp({ layout })` was passed a component that is ALSO a parent route in the matched chain (likely an fs-router `_layout.tsx`). The explicit `layout` option is being ignored to prevent double-mount. Remove the `layout` argument from `createApp`/`startClient` — the fs-router-emitted route handles it.");
21
+ const Layout = hasLayoutInRoutes ? DefaultLayout : options.layout ?? DefaultLayout;
20
22
  function App() {
21
23
  return h(HeadProvider, null, h(RouterProvider, { router }, h(Layout, null, h(RouterView, null))));
22
24
  }
package/lib/csp.js CHANGED
@@ -63,17 +63,27 @@ function buildCspHeader(directives, nonce) {
63
63
  return parts.join("; ");
64
64
  }
65
65
  /**
66
- * Generate a random nonce string (base64, 16 bytes).
66
+ * Generate a cryptographically-random nonce string (base64, 16 bytes).
67
+ *
68
+ * Throws when `crypto.getRandomValues` is unavailable. CSP nonces protect
69
+ * against XSS by gating inline script execution; a predictable nonce
70
+ * (`Math.random` ~31 bits of entropy) bypasses CSP entirely. Silent
71
+ * degradation here was a security anti-pattern — we surface the
72
+ * misconfiguration loudly instead.
73
+ *
74
+ * Realistic deployments always have `crypto.getRandomValues`: Node 18+,
75
+ * Bun, Deno, browsers, edge workers (Cloudflare/Vercel/Netlify), and
76
+ * vitest/happy-dom all expose it via `globalThis.crypto`. If you hit
77
+ * this throw, your environment is unusual — fix the env, don't downgrade
78
+ * the security primitive.
67
79
  */
68
80
  function generateNonce() {
69
- if (typeof crypto !== "undefined" && crypto.getRandomValues) {
70
- const bytes = new Uint8Array(16);
71
- crypto.getRandomValues(bytes);
72
- let binary = "";
73
- for (const byte of bytes) binary += String.fromCharCode(byte);
74
- return typeof btoa === "function" ? btoa(binary) : Buffer.from(bytes).toString("base64");
75
- }
76
- return Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2);
81
+ if (typeof crypto === "undefined" || !crypto.getRandomValues) throw new Error("[Pyreon] CSP nonce generation requires `crypto.getRandomValues` (Web Crypto API). No secure RNG is available in this environment. CSP nonces must be cryptographically random — falling back to `Math.random` would silently weaken XSS protection. Ensure Node 18+, Bun, Deno, an edge runtime, or a browser environment.");
82
+ const bytes = new Uint8Array(16);
83
+ crypto.getRandomValues(bytes);
84
+ let binary = "";
85
+ for (const byte of bytes) binary += String.fromCharCode(byte);
86
+ return typeof btoa === "function" ? btoa(binary) : Buffer.from(bytes).toString("base64");
77
87
  }
78
88
  /**
79
89
  * CSP middleware — sets Content-Security-Policy header.
@@ -16,6 +16,10 @@ var __exportAll = (all, no_symbols) => {
16
16
  }
17
17
  return target;
18
18
  };
19
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) {
20
+ if (typeof require !== "undefined") return require.apply(this, arguments);
21
+ throw Error("Calling `require` for \"" + x + "\" in an environment that doesn't expose the `require` function. See https://rolldown.rs/in-depth/bundling-cjs#require-external-modules for more details.");
22
+ });
19
23
 
20
24
  //#endregion
21
25
  //#region src/fs-router.ts
@@ -44,7 +48,9 @@ const ROUTE_EXPORT_NAMES = [
44
48
  "meta",
45
49
  "renderMode",
46
50
  "error",
47
- "middleware"
51
+ "middleware",
52
+ "loaderKey",
53
+ "gcTime"
48
54
  ];
49
55
  /**
50
56
  * Detect which optional metadata exports a route file source declares.
@@ -85,6 +91,8 @@ function detectRouteExports(source) {
85
91
  hasRenderMode: found.has("renderMode"),
86
92
  hasError: found.has("error"),
87
93
  hasMiddleware: found.has("middleware"),
94
+ hasLoaderKey: found.has("loaderKey"),
95
+ hasGcTime: found.has("gcTime"),
88
96
  ...metaLiteral !== void 0 ? { metaLiteral } : {},
89
97
  ...renderModeLiteral !== void 0 ? { renderModeLiteral } : {}
90
98
  };
@@ -568,7 +576,9 @@ const EMPTY_EXPORTS = {
568
576
  hasMeta: false,
569
577
  hasRenderMode: false,
570
578
  hasError: false,
571
- hasMiddleware: false
579
+ hasMiddleware: false,
580
+ hasLoaderKey: false,
581
+ hasGcTime: false
572
582
  };
573
583
  /**
574
584
  * True if a route file declares ANY metadata export.
@@ -576,7 +586,7 @@ const EMPTY_EXPORTS = {
576
586
  * `import * as mod` (for metadata access) instead of lazy().
577
587
  */
578
588
  function hasAnyMetaExport(exports) {
579
- return exports.hasLoader || exports.hasGuard || exports.hasMeta || exports.hasRenderMode || exports.hasError || exports.hasMiddleware;
589
+ return exports.hasLoader || exports.hasGuard || exports.hasMeta || exports.hasRenderMode || exports.hasError || exports.hasMiddleware || exports.hasLoaderKey || exports.hasGcTime;
580
590
  }
581
591
  /**
582
592
  * Parse a set of file paths (relative to routes dir) into FileRoute objects.
@@ -776,6 +786,8 @@ function generateRouteModuleFromRoutes(routes, routesDir, options) {
776
786
  props.push(`${indent} component: ${mod}.default`);
777
787
  if (exp.hasLoader) props.push(`${indent} loader: ${mod}.loader`);
778
788
  if (exp.hasGuard) props.push(`${indent} beforeEnter: ${mod}.guard`);
789
+ if (exp.hasLoaderKey) props.push(`${indent} loaderKey: ${mod}.loaderKey`);
790
+ if (exp.hasGcTime) props.push(`${indent} gcTime: ${mod}.gcTime`);
779
791
  if (exp.hasMeta || exp.hasRenderMode) {
780
792
  const metaParts = [];
781
793
  if (exp.hasMeta) metaParts.push(`...${mod}.meta`);
@@ -805,6 +817,14 @@ function generateRouteModuleFromRoutes(routes, routesDir, options) {
805
817
  props.push(`${indent} component: ${comp}`);
806
818
  if (exp.hasLoader) props.push(`${indent} loader: (ctx) => import("${fullPath}").then((m) => m.loader(ctx))`);
807
819
  if (exp.hasGuard) props.push(`${indent} beforeEnter: (to, from) => import("${fullPath}").then((m) => m.guard(to, from))`);
820
+ if (exp.hasLoaderKey) {
821
+ const mod = nextModuleImport(page.filePath);
822
+ props.push(`${indent} loaderKey: ${mod}.loaderKey`);
823
+ }
824
+ if (exp.hasGcTime) {
825
+ const mod = nextModuleImport(page.filePath);
826
+ props.push(`${indent} gcTime: ${mod}.gcTime`);
827
+ }
808
828
  emitInlineMeta(exp, props, indent);
809
829
  if (errorName) {
810
830
  const errorRef = exp.hasError ? `lazy(() => import("${fullPath}").then((m) => ({ default: m.error })))` : errorName;
@@ -816,6 +836,8 @@ function generateRouteModuleFromRoutes(routes, routesDir, options) {
816
836
  props.push(`${indent} component: ${mod}.default`);
817
837
  if (exp.hasLoader) props.push(`${indent} loader: ${mod}.loader`);
818
838
  if (exp.hasGuard) props.push(`${indent} beforeEnter: ${mod}.guard`);
839
+ if (exp.hasLoaderKey) props.push(`${indent} loaderKey: ${mod}.loaderKey`);
840
+ if (exp.hasGcTime) props.push(`${indent} gcTime: ${mod}.gcTime`);
819
841
  if (exp.hasMeta || exp.hasRenderMode) {
820
842
  const metaParts = [];
821
843
  if (exp.hasMeta) metaParts.push(`...${mod}.meta`);
@@ -849,6 +871,8 @@ function generateRouteModuleFromRoutes(routes, routesDir, options) {
849
871
  if (layoutMod !== void 0) {
850
872
  if (exp.hasLoader) props.push(`${indent}loader: ${layoutMod}.loader`);
851
873
  if (exp.hasGuard) props.push(`${indent}beforeEnter: ${layoutMod}.guard`);
874
+ if (exp.hasLoaderKey) props.push(`${indent}loaderKey: ${layoutMod}.loaderKey`);
875
+ if (exp.hasGcTime) props.push(`${indent}gcTime: ${layoutMod}.gcTime`);
852
876
  if (exp.hasMeta || exp.hasRenderMode) {
853
877
  const metaParts = [];
854
878
  if (exp.hasMeta) metaParts.push(`...${layoutMod}.meta`);
@@ -885,14 +909,26 @@ function generateRouteModuleFromRoutes(routes, routesDir, options) {
885
909
  /**
886
910
  * Generate a virtual module that maps URL patterns to their middleware exports.
887
911
  * Used by the server entry to dispatch per-route middleware.
912
+ *
913
+ * Detects whether each route file actually exports `middleware` (via
914
+ * `detectRouteExports` source scanning) and only emits an import for files
915
+ * that do. The `lazy()` import path tolerates missing exports, but the SSG
916
+ * static-import path fails Rolldown's missing-export check at build time —
917
+ * skipping no-middleware files keeps both paths working.
888
918
  */
889
919
  function generateMiddlewareModule(files, routesDir) {
920
+ const { readFileSync } = __require("node:fs");
890
921
  const routes = parseFileRoutes(files);
891
922
  const imports = [];
892
923
  const entries = [];
893
924
  let counter = 0;
894
925
  for (const route of routes) {
895
926
  if (route.isLayout || route.isError || route.isLoading || route.isNotFound) continue;
927
+ let hasMw = false;
928
+ try {
929
+ hasMw = detectRouteExports(readFileSync(`${routesDir}/${route.filePath}`, "utf-8")).hasMiddleware;
930
+ } catch {}
931
+ if (!hasMw) continue;
896
932
  const name = `_mw${counter++}`;
897
933
  const fullPath = `${routesDir}/${route.filePath}`;
898
934
  imports.push(`import { middleware as ${name} } from "${fullPath}"`);
@@ -937,7 +973,8 @@ async function scanRouteFiles(routesDir) {
937
973
  */
938
974
  async function scanRouteFilesWithExports(routesDir, defaultMode = "ssr") {
939
975
  const { readFile } = await import("node:fs/promises");
940
- const files = await scanRouteFiles(routesDir);
976
+ const { isApiRoute } = await import("./api-routes-DANluJic.js").then((n) => n.t);
977
+ const files = (await scanRouteFiles(routesDir)).filter((f) => !isApiRoute(f));
941
978
  const exportsMap = /* @__PURE__ */ new Map();
942
979
  await Promise.all(files.map(async (filePath) => {
943
980
  try {
@@ -951,5 +988,5 @@ async function scanRouteFilesWithExports(routesDir, defaultMode = "ssr") {
951
988
  }
952
989
 
953
990
  //#endregion
954
- export { generateRouteModuleFromRoutes as a, scanRouteFilesWithExports as c, generateRouteModule as i, fs_router_exports as n, parseFileRoutes as o, generateMiddlewareModule as r, scanRouteFiles as s, filePathToUrlPath as t };
955
- //# sourceMappingURL=fs-router-CQ7Zxeca.js.map
991
+ export { generateRouteModuleFromRoutes as a, scanRouteFilesWithExports as c, generateRouteModule as i, __exportAll as l, fs_router_exports as n, parseFileRoutes as o, generateMiddlewareModule as r, scanRouteFiles as s, filePathToUrlPath as t };
992
+ //# sourceMappingURL=fs-router-ZebyutPa.js.map
@@ -11,9 +11,13 @@ function warnSharpMissing() {
11
11
  }
12
12
  /** Built-in CDN providers. */
13
13
  const cdnProviders = {
14
+ /** Cloudinary: `https://res.cloudinary.com/{cloud}/image/upload/...` */
14
15
  cloudinary: (cloudName) => (src, { width, quality, format }) => `https://res.cloudinary.com/${cloudName}/image/upload/w_${width},q_${quality},f_${format}/${src}`,
16
+ /** Imgix: `https://{domain}.imgix.net/...?w=...&q=...&fm=...` */
15
17
  imgix: (domain) => (src, { width, quality, format }) => `https://${domain}.imgix.net/${src}?w=${width}&q=${quality}&fm=${format}&auto=format`,
18
+ /** Vercel Image Optimization: `/_next/image?url=...&w=...&q=...` */
16
19
  vercel: () => (src, { width, quality }) => `/_vercel/image?url=${encodeURIComponent(src)}&w=${width}&q=${quality}`,
20
+ /** Bunny CDN: `https://{pullZone}.b-cdn.net/...?width=...&quality=...` */
17
21
  bunny: (pullZone) => (src, { width, quality }) => `https://${pullZone}.b-cdn.net/${src}?width=${width}&quality=${quality}`
18
22
  };
19
23
  const IMAGE_EXT_RE = /\.(jpe?g|png|webp|avif)$/i;
package/lib/image.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createRef, onMount, onUnmount } from "@pyreon/core";
2
2
  import { signal } from "@pyreon/reactivity";
3
+ import { jsx, jsxs } from "@pyreon/core/jsx-runtime";
3
4
 
4
5
  //#region src/utils/use-intersection-observer.ts
5
6
  /**
@@ -25,56 +26,6 @@ function useIntersectionObserver(getElement, onIntersect, rootMargin = "200px")
25
26
  });
26
27
  }
27
28
 
28
- //#endregion
29
- //#region ../../core/core/lib/jsx-runtime.js
30
- /**
31
- * Hyperscript function — the compiled output of JSX.
32
- * `<div class="x">hello</div>` → `h("div", { class: "x" }, "hello")`
33
- *
34
- * Generic on P so TypeScript validates props match the component's signature
35
- * at the call site, then stores the result in the loosely-typed VNode.
36
- */
37
- /** Shared empty props sentinel — identity-checked in mountElement to skip applyProps. */
38
- const EMPTY_PROPS = {};
39
- function h(type, props, ...children) {
40
- return {
41
- type,
42
- props: props ?? EMPTY_PROPS,
43
- children: normalizeChildren(children),
44
- key: props?.key ?? null
45
- };
46
- }
47
- function normalizeChildren(children) {
48
- for (let i = 0; i < children.length; i++) if (Array.isArray(children[i])) return flattenChildren(children);
49
- return children;
50
- }
51
- function flattenChildren(children) {
52
- const result = [];
53
- for (const child of children) if (Array.isArray(child)) result.push(...flattenChildren(child));
54
- else result.push(child);
55
- return result;
56
- }
57
- /**
58
- * JSX automatic runtime.
59
- *
60
- * When tsconfig has `"jsxImportSource": "@pyreon/core"`, the TS/bundler compiler
61
- * rewrites JSX to imports from this file automatically:
62
- * <div class="x" /> → jsx("div", { class: "x" })
63
- */
64
- function jsx(type, props, key) {
65
- const { children, ...rest } = props;
66
- const propsWithKey = key != null ? {
67
- ...rest,
68
- key
69
- } : rest;
70
- if (typeof type === "function") return h(type, children !== void 0 ? {
71
- ...propsWithKey,
72
- children
73
- } : propsWithKey);
74
- return h(type, propsWithKey, ...children === void 0 ? [] : Array.isArray(children) ? children : [children]);
75
- }
76
- const jsxs = jsx;
77
-
78
29
  //#endregion
79
30
  //#region src/image.tsx
80
31
  /**
package/lib/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createContext, createRef, onMount, onUnmount } from "@pyreon/core";
2
2
  import { effect, signal } from "@pyreon/reactivity";
3
+ import { jsx, jsxs } from "@pyreon/core/jsx-runtime";
3
4
  import { useRouter } from "@pyreon/router";
4
5
  import { useHead } from "@pyreon/head";
5
6
 
@@ -27,56 +28,6 @@ function useIntersectionObserver(getElement, onIntersect, rootMargin = "200px")
27
28
  });
28
29
  }
29
30
 
30
- //#endregion
31
- //#region ../../core/core/lib/jsx-runtime.js
32
- /**
33
- * Hyperscript function — the compiled output of JSX.
34
- * `<div class="x">hello</div>` → `h("div", { class: "x" }, "hello")`
35
- *
36
- * Generic on P so TypeScript validates props match the component's signature
37
- * at the call site, then stores the result in the loosely-typed VNode.
38
- */
39
- /** Shared empty props sentinel — identity-checked in mountElement to skip applyProps. */
40
- const EMPTY_PROPS = {};
41
- function h(type, props, ...children) {
42
- return {
43
- type,
44
- props: props ?? EMPTY_PROPS,
45
- children: normalizeChildren(children),
46
- key: props?.key ?? null
47
- };
48
- }
49
- function normalizeChildren(children) {
50
- for (let i = 0; i < children.length; i++) if (Array.isArray(children[i])) return flattenChildren(children);
51
- return children;
52
- }
53
- function flattenChildren(children) {
54
- const result = [];
55
- for (const child of children) if (Array.isArray(child)) result.push(...flattenChildren(child));
56
- else result.push(child);
57
- return result;
58
- }
59
- /**
60
- * JSX automatic runtime.
61
- *
62
- * When tsconfig has `"jsxImportSource": "@pyreon/core"`, the TS/bundler compiler
63
- * rewrites JSX to imports from this file automatically:
64
- * <div class="x" /> → jsx("div", { class: "x" })
65
- */
66
- function jsx(type, props, key) {
67
- const { children, ...rest } = props;
68
- const propsWithKey = key != null ? {
69
- ...rest,
70
- key
71
- } : rest;
72
- if (typeof type === "function") return h(type, children !== void 0 ? {
73
- ...propsWithKey,
74
- children
75
- } : propsWithKey);
76
- return h(type, propsWithKey, ...children === void 0 ? [] : Array.isArray(children) ? children : [children]);
77
- }
78
- const jsxs = jsx;
79
-
80
31
  //#endregion
81
32
  //#region src/image.tsx
82
33
  /**
package/lib/link.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createRef, onMount, onUnmount } from "@pyreon/core";
2
2
  import { useRouter } from "@pyreon/router";
3
+ import { jsx } from "@pyreon/core/jsx-runtime";
3
4
 
4
5
  //#region src/utils/use-intersection-observer.ts
5
6
  /**
@@ -25,55 +26,6 @@ function useIntersectionObserver(getElement, onIntersect, rootMargin = "200px")
25
26
  });
26
27
  }
27
28
 
28
- //#endregion
29
- //#region ../../core/core/lib/jsx-runtime.js
30
- /**
31
- * Hyperscript function — the compiled output of JSX.
32
- * `<div class="x">hello</div>` → `h("div", { class: "x" }, "hello")`
33
- *
34
- * Generic on P so TypeScript validates props match the component's signature
35
- * at the call site, then stores the result in the loosely-typed VNode.
36
- */
37
- /** Shared empty props sentinel — identity-checked in mountElement to skip applyProps. */
38
- const EMPTY_PROPS = {};
39
- function h(type, props, ...children) {
40
- return {
41
- type,
42
- props: props ?? EMPTY_PROPS,
43
- children: normalizeChildren(children),
44
- key: props?.key ?? null
45
- };
46
- }
47
- function normalizeChildren(children) {
48
- for (let i = 0; i < children.length; i++) if (Array.isArray(children[i])) return flattenChildren(children);
49
- return children;
50
- }
51
- function flattenChildren(children) {
52
- const result = [];
53
- for (const child of children) if (Array.isArray(child)) result.push(...flattenChildren(child));
54
- else result.push(child);
55
- return result;
56
- }
57
- /**
58
- * JSX automatic runtime.
59
- *
60
- * When tsconfig has `"jsxImportSource": "@pyreon/core"`, the TS/bundler compiler
61
- * rewrites JSX to imports from this file automatically:
62
- * <div class="x" /> → jsx("div", { class: "x" })
63
- */
64
- function jsx(type, props, key) {
65
- const { children, ...rest } = props;
66
- const propsWithKey = key != null ? {
67
- ...rest,
68
- key
69
- } : rest;
70
- if (typeof type === "function") return h(type, children !== void 0 ? {
71
- ...propsWithKey,
72
- children
73
- } : propsWithKey);
74
- return h(type, propsWithKey, ...children === void 0 ? [] : Array.isArray(children) ? children : [children]);
75
- }
76
-
77
29
  //#endregion
78
30
  //#region src/link.tsx
79
31
  const MAX_PREFETCH_CACHE = 200;
package/lib/script.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { createRef, onMount, onUnmount } from "@pyreon/core";
2
+ import { jsx } from "@pyreon/core/jsx-runtime";
2
3
 
3
4
  //#region src/utils/use-intersection-observer.ts
4
5
  /**
@@ -24,55 +25,6 @@ function useIntersectionObserver(getElement, onIntersect, rootMargin = "200px")
24
25
  });
25
26
  }
26
27
 
27
- //#endregion
28
- //#region ../../core/core/lib/jsx-runtime.js
29
- /**
30
- * Hyperscript function — the compiled output of JSX.
31
- * `<div class="x">hello</div>` → `h("div", { class: "x" }, "hello")`
32
- *
33
- * Generic on P so TypeScript validates props match the component's signature
34
- * at the call site, then stores the result in the loosely-typed VNode.
35
- */
36
- /** Shared empty props sentinel — identity-checked in mountElement to skip applyProps. */
37
- const EMPTY_PROPS = {};
38
- function h(type, props, ...children) {
39
- return {
40
- type,
41
- props: props ?? EMPTY_PROPS,
42
- children: normalizeChildren(children),
43
- key: props?.key ?? null
44
- };
45
- }
46
- function normalizeChildren(children) {
47
- for (let i = 0; i < children.length; i++) if (Array.isArray(children[i])) return flattenChildren(children);
48
- return children;
49
- }
50
- function flattenChildren(children) {
51
- const result = [];
52
- for (const child of children) if (Array.isArray(child)) result.push(...flattenChildren(child));
53
- else result.push(child);
54
- return result;
55
- }
56
- /**
57
- * JSX automatic runtime.
58
- *
59
- * When tsconfig has `"jsxImportSource": "@pyreon/core"`, the TS/bundler compiler
60
- * rewrites JSX to imports from this file automatically:
61
- * <div class="x" /> → jsx("div", { class: "x" })
62
- */
63
- function jsx(type, props, key) {
64
- const { children, ...rest } = props;
65
- const propsWithKey = key != null ? {
66
- ...rest,
67
- key
68
- } : rest;
69
- if (typeof type === "function") return h(type, children !== void 0 ? {
70
- ...propsWithKey,
71
- children
72
- } : propsWithKey);
73
- return h(type, propsWithKey, ...children === void 0 ? [] : Array.isArray(children) ? children : [children]);
74
- }
75
-
76
28
  //#endregion
77
29
  //#region src/script.tsx
78
30
  /**