@pyreon/zero 0.14.0 → 0.16.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.
- package/lib/api-routes-Ci0kVmM4.js +146 -0
- package/lib/client.js +7 -2
- package/lib/csp.js +19 -9
- package/lib/env.js +6 -6
- package/lib/font.js +3 -3
- package/lib/{fs-router-CQ7Zxeca.js → fs-router-MewHc5SB.js} +56 -24
- package/lib/i18n-routing.js +112 -1
- package/lib/image-plugin.js +4 -0
- package/lib/image.js +141 -108
- package/lib/index.js +253 -132
- package/lib/link.js +1 -49
- package/lib/og-image.js +5 -5
- package/lib/rolldown-runtime-CjeV3_4I.js +18 -0
- package/lib/script.js +115 -74
- package/lib/seo.js +186 -15
- package/lib/server.js +275 -1247
- package/lib/theme.js +1 -50
- package/lib/types/config.d.ts +275 -3
- package/lib/types/env.d.ts +2 -2
- package/lib/types/i18n-routing.d.ts +197 -6
- package/lib/types/image.d.ts +105 -5
- package/lib/types/index.d.ts +640 -178
- package/lib/types/link.d.ts +3 -3
- package/lib/types/script.d.ts +78 -6
- package/lib/types/seo.d.ts +128 -4
- package/lib/types/server.d.ts +603 -77
- package/lib/types/theme.d.ts +2 -2
- package/lib/vite-plugin-xjWZwudX.js +2454 -0
- package/package.json +16 -13
- package/src/adapters/bun.ts +20 -1
- package/src/adapters/cloudflare.ts +78 -1
- package/src/adapters/index.ts +25 -3
- package/src/adapters/netlify.ts +63 -1
- package/src/adapters/node.ts +25 -1
- package/src/adapters/static.ts +26 -1
- package/src/adapters/validate.ts +8 -1
- package/src/adapters/vercel.ts +76 -1
- package/src/adapters/warn-missing-env.ts +49 -0
- package/src/app.ts +35 -1
- package/src/client.ts +18 -0
- package/src/csp.ts +28 -12
- package/src/entry-server.ts +55 -5
- package/src/env.ts +7 -7
- package/src/font.ts +3 -3
- package/src/fs-router.ts +123 -4
- package/src/i18n-routing.ts +246 -12
- package/src/image.tsx +242 -91
- package/src/index.ts +4 -4
- package/src/isr.ts +24 -6
- package/src/manifest.ts +675 -0
- package/src/og-image.ts +5 -5
- package/src/script.tsx +159 -36
- package/src/seo.ts +346 -15
- package/src/server.ts +10 -2
- package/src/ssg-plugin.ts +1523 -0
- package/src/types.ts +329 -19
- package/src/vercel-revalidate-handler.ts +204 -0
- package/src/vite-plugin.ts +326 -68
- package/lib/actions.js.map +0 -1
- package/lib/ai.js.map +0 -1
- package/lib/api-routes.js.map +0 -1
- package/lib/cache.js.map +0 -1
- package/lib/client.js.map +0 -1
- package/lib/compression.js.map +0 -1
- package/lib/config.js.map +0 -1
- package/lib/cors.js.map +0 -1
- package/lib/csp.js.map +0 -1
- package/lib/env.js.map +0 -1
- package/lib/favicon.js.map +0 -1
- package/lib/font.js.map +0 -1
- package/lib/fs-router-3xzp-4Wj.js.map +0 -1
- package/lib/fs-router-CQ7Zxeca.js.map +0 -1
- package/lib/i18n-routing.js.map +0 -1
- package/lib/image-plugin.js.map +0 -1
- package/lib/image.js.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/link.js.map +0 -1
- package/lib/logger.js.map +0 -1
- package/lib/meta.js.map +0 -1
- package/lib/middleware.js.map +0 -1
- package/lib/og-image.js.map +0 -1
- package/lib/rate-limit.js.map +0 -1
- package/lib/script.js.map +0 -1
- package/lib/seo.js.map +0 -1
- package/lib/server.js.map +0 -1
- package/lib/testing.js.map +0 -1
- package/lib/theme.js.map +0 -1
- package/lib/types/actions.d.ts.map +0 -1
- package/lib/types/ai.d.ts.map +0 -1
- package/lib/types/api-routes.d.ts.map +0 -1
- package/lib/types/cache.d.ts.map +0 -1
- package/lib/types/client.d.ts.map +0 -1
- package/lib/types/compression.d.ts.map +0 -1
- package/lib/types/config.d.ts.map +0 -1
- package/lib/types/cors.d.ts.map +0 -1
- package/lib/types/csp.d.ts.map +0 -1
- package/lib/types/env.d.ts.map +0 -1
- package/lib/types/favicon.d.ts.map +0 -1
- package/lib/types/font.d.ts.map +0 -1
- package/lib/types/i18n-routing.d.ts.map +0 -1
- package/lib/types/image-plugin.d.ts.map +0 -1
- package/lib/types/image.d.ts.map +0 -1
- package/lib/types/index.d.ts.map +0 -1
- package/lib/types/link.d.ts.map +0 -1
- package/lib/types/logger.d.ts.map +0 -1
- package/lib/types/meta.d.ts.map +0 -1
- package/lib/types/middleware.d.ts.map +0 -1
- package/lib/types/og-image.d.ts.map +0 -1
- package/lib/types/rate-limit.d.ts.map +0 -1
- package/lib/types/script.d.ts.map +0 -1
- package/lib/types/seo.d.ts.map +0 -1
- package/lib/types/server.d.ts.map +0 -1
- package/lib/types/testing.d.ts.map +0 -1
- package/lib/types/theme.d.ts.map +0 -1
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,83 +28,37 @@ 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
|
/**
|
|
83
|
-
*
|
|
84
|
-
*
|
|
34
|
+
* Composable that provides all image optimization behavior — lazy loading,
|
|
35
|
+
* srcset/sizes resolution, format selection, blur-placeholder state,
|
|
36
|
+
* load tracking.
|
|
85
37
|
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
* <Image {...hero} alt="Hero" priority />
|
|
38
|
+
* Use this for full control when `createImage` is too opinionated about
|
|
39
|
+
* the surrounding markup (e.g. custom container layouts, non-`<div>`
|
|
40
|
+
* wrappers, additional overlay elements).
|
|
90
41
|
*
|
|
91
42
|
* @example
|
|
92
|
-
*
|
|
93
|
-
*
|
|
43
|
+
* function MyImage(props: ImageProps) {
|
|
44
|
+
* const img = useImage(props)
|
|
45
|
+
* return (
|
|
46
|
+
* <figure ref={img.containerRef} style={img.containerStyle}>
|
|
47
|
+
* <img
|
|
48
|
+
* src={img.src}
|
|
49
|
+
* srcSet={img.srcSet}
|
|
50
|
+
* sizes={img.sizes}
|
|
51
|
+
* alt={props.alt}
|
|
52
|
+
* loading={img.loading}
|
|
53
|
+
* onLoad={img.handleLoad}
|
|
54
|
+
* style={img.imageStyle}
|
|
55
|
+
* />
|
|
56
|
+
* <figcaption>{props.alt}</figcaption>
|
|
57
|
+
* </figure>
|
|
58
|
+
* )
|
|
59
|
+
* }
|
|
94
60
|
*/
|
|
95
|
-
function
|
|
96
|
-
if (props.raw) return /* @__PURE__ */ jsx("img", {
|
|
97
|
-
src: props.src,
|
|
98
|
-
alt: props.alt,
|
|
99
|
-
width: props.width,
|
|
100
|
-
height: props.height,
|
|
101
|
-
class: props.class,
|
|
102
|
-
style: props.style,
|
|
103
|
-
decoding: props.decoding ?? "async",
|
|
104
|
-
loading: props.loading ?? "lazy",
|
|
105
|
-
fetchPriority: props.priority ? "high" : void 0
|
|
106
|
-
});
|
|
61
|
+
function useImage(props) {
|
|
107
62
|
const isEager = props.priority || props.loading === "eager";
|
|
108
63
|
const loaded = signal(isEager);
|
|
109
64
|
const inView = signal(isEager);
|
|
@@ -111,7 +66,7 @@ function Image(props) {
|
|
|
111
66
|
const resolvedSrcset = typeof props.srcset === "string" ? props.srcset : props.srcset?.map((s) => `${s.src} ${s.width}w`).join(", ");
|
|
112
67
|
const sizes = props.sizes ?? "100vw";
|
|
113
68
|
const fit = props.fit ?? "cover";
|
|
114
|
-
const hasFormats = props.formats && props.formats.length > 0;
|
|
69
|
+
const hasFormats = !!(props.formats && props.formats.length > 0);
|
|
115
70
|
const aspectRatio = `${props.width} / ${props.height}`;
|
|
116
71
|
if (!isEager) useIntersectionObserver(() => containerRef.current ?? void 0, () => inView.set(true));
|
|
117
72
|
const containerStyle = [
|
|
@@ -122,53 +77,131 @@ function Image(props) {
|
|
|
122
77
|
"width: 100%",
|
|
123
78
|
props.style
|
|
124
79
|
].filter(Boolean).join("; ");
|
|
125
|
-
const
|
|
80
|
+
const imageStyle = () => [
|
|
81
|
+
"display: block",
|
|
82
|
+
"width: 100%",
|
|
83
|
+
"height: 100%",
|
|
84
|
+
`object-fit: ${fit}`,
|
|
85
|
+
"transition: opacity 0.3s ease",
|
|
86
|
+
props.placeholder && !loaded() ? "opacity: 0" : "opacity: 1"
|
|
87
|
+
].join("; ");
|
|
88
|
+
const placeholderStyle = () => [
|
|
89
|
+
"position: absolute",
|
|
90
|
+
"inset: 0",
|
|
91
|
+
"width: 100%",
|
|
92
|
+
"height: 100%",
|
|
93
|
+
"object-fit: cover",
|
|
94
|
+
"filter: blur(20px)",
|
|
95
|
+
"transform: scale(1.1)",
|
|
96
|
+
"transition: opacity 0.4s ease",
|
|
97
|
+
loaded() ? "opacity: 0; pointer-events: none" : "opacity: 1"
|
|
98
|
+
].join("; ");
|
|
99
|
+
return {
|
|
100
|
+
containerRef,
|
|
101
|
+
inView,
|
|
102
|
+
loaded,
|
|
126
103
|
src: () => inView() ? props.src : "",
|
|
127
104
|
srcSet: () => !hasFormats && inView() && resolvedSrcset ? resolvedSrcset : "",
|
|
128
105
|
sizes: resolvedSrcset ? sizes : void 0,
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
106
|
+
aspectRatio,
|
|
107
|
+
containerStyle,
|
|
108
|
+
imageStyle,
|
|
109
|
+
placeholderStyle,
|
|
132
110
|
loading: isEager ? "eager" : "lazy",
|
|
133
|
-
decoding: props.decoding ?? "async",
|
|
134
111
|
fetchPriority: props.priority ? "high" : void 0,
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
112
|
+
handleLoad: () => loaded.set(true),
|
|
113
|
+
formats: props.formats,
|
|
114
|
+
hasFormats
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Higher-order component that wraps any component with image optimization.
|
|
119
|
+
*
|
|
120
|
+
* The wrapped component receives {@link ImageRenderProps} with the pre-rendered
|
|
121
|
+
* `image` JSX (bare `<img>` OR `<picture>` tree depending on formats), the
|
|
122
|
+
* pre-rendered `placeholder` JSX, and the container ref + styles. Consumers
|
|
123
|
+
* compose those pieces with whatever wrapper element / layout they want.
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* // Custom figure-based image with caption
|
|
127
|
+
* const FigureImage = createImage((props) => (
|
|
128
|
+
* <figure ref={props.containerRef} class={props.class} style={props.containerStyle}>
|
|
129
|
+
* {props.placeholder}
|
|
130
|
+
* {props.image}
|
|
131
|
+
* <figcaption>Caption goes here</figcaption>
|
|
132
|
+
* </figure>
|
|
133
|
+
* ))
|
|
134
|
+
*
|
|
135
|
+
* // Usage — identical to default <Image>
|
|
136
|
+
* <FigureImage src="/hero.jpg" alt="Hero" width={1200} height={630} />
|
|
137
|
+
*/
|
|
138
|
+
function createImage(Component) {
|
|
139
|
+
return function WrappedImage(props) {
|
|
140
|
+
if (props.raw) return /* @__PURE__ */ jsx("img", {
|
|
141
|
+
src: props.src,
|
|
142
|
+
alt: props.alt,
|
|
143
|
+
width: props.width,
|
|
144
|
+
height: props.height,
|
|
145
|
+
class: props.class,
|
|
146
|
+
style: props.style,
|
|
147
|
+
decoding: props.decoding ?? "async",
|
|
148
|
+
loading: props.loading ?? "lazy",
|
|
149
|
+
fetchPriority: props.priority ? "high" : void 0
|
|
150
|
+
});
|
|
151
|
+
const img = useImage(props);
|
|
152
|
+
const imgEl = /* @__PURE__ */ jsx("img", {
|
|
153
|
+
src: img.src,
|
|
154
|
+
srcSet: img.srcSet,
|
|
155
|
+
sizes: img.sizes,
|
|
156
|
+
alt: props.alt,
|
|
157
|
+
width: props.width,
|
|
158
|
+
height: props.height,
|
|
159
|
+
loading: img.loading,
|
|
160
|
+
decoding: props.decoding ?? "async",
|
|
161
|
+
fetchPriority: img.fetchPriority,
|
|
162
|
+
onLoad: img.handleLoad,
|
|
163
|
+
style: img.imageStyle
|
|
164
|
+
});
|
|
165
|
+
const placeholderEl = props.placeholder ? /* @__PURE__ */ jsx("img", {
|
|
150
166
|
src: props.placeholder,
|
|
151
167
|
alt: "",
|
|
152
168
|
"aria-hidden": "true",
|
|
153
169
|
loading: "eager",
|
|
154
|
-
style:
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
"width: 100%",
|
|
158
|
-
"height: 100%",
|
|
159
|
-
"object-fit: cover",
|
|
160
|
-
"filter: blur(20px)",
|
|
161
|
-
"transform: scale(1.1)",
|
|
162
|
-
"transition: opacity 0.4s ease",
|
|
163
|
-
loaded() ? "opacity: 0; pointer-events: none" : "opacity: 1"
|
|
164
|
-
].join("; ")
|
|
165
|
-
}), hasFormats ? /* @__PURE__ */ jsxs("picture", { children: [props.formats?.map((fmt) => /* @__PURE__ */ jsx("source", {
|
|
170
|
+
style: img.placeholderStyle
|
|
171
|
+
}) : null;
|
|
172
|
+
const imageEl = img.hasFormats ? /* @__PURE__ */ jsxs("picture", { children: [img.formats?.map((fmt) => /* @__PURE__ */ jsx("source", {
|
|
166
173
|
type: fmt.type,
|
|
167
|
-
srcSet: () => inView() ? fmt.srcset ?? "" : "",
|
|
168
|
-
sizes
|
|
169
|
-
})), imgEl] }) : imgEl
|
|
170
|
-
|
|
174
|
+
srcSet: () => img.inView() ? fmt.srcset ?? "" : "",
|
|
175
|
+
sizes: img.sizes
|
|
176
|
+
})), imgEl] }) : imgEl;
|
|
177
|
+
return /* @__PURE__ */ jsx(Component, {
|
|
178
|
+
containerRef: img.containerRef,
|
|
179
|
+
class: props.class,
|
|
180
|
+
containerStyle: img.containerStyle,
|
|
181
|
+
placeholder: placeholderEl,
|
|
182
|
+
image: imageEl
|
|
183
|
+
});
|
|
184
|
+
};
|
|
171
185
|
}
|
|
186
|
+
/**
|
|
187
|
+
* Default optimized image component with lazy loading, responsive srcset,
|
|
188
|
+
* `<picture>` multi-format support, and blur-up placeholders.
|
|
189
|
+
*
|
|
190
|
+
* @example
|
|
191
|
+
* // With imagePlugin — spread the import directly
|
|
192
|
+
* import hero from "./hero.jpg?optimize"
|
|
193
|
+
* <Image {...hero} alt="Hero" priority />
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* // Manual usage
|
|
197
|
+
* <Image src="/hero.jpg" alt="Hero" width={1200} height={630} />
|
|
198
|
+
*/
|
|
199
|
+
const Image = createImage((props) => /* @__PURE__ */ jsxs("div", {
|
|
200
|
+
ref: props.containerRef,
|
|
201
|
+
class: props.class,
|
|
202
|
+
style: props.containerStyle,
|
|
203
|
+
children: [props.placeholder, props.image]
|
|
204
|
+
}));
|
|
172
205
|
|
|
173
206
|
//#endregion
|
|
174
207
|
//#region src/link.tsx
|
|
@@ -355,36 +388,67 @@ const Link = createLink((props) => /* @__PURE__ */ jsx("a", {
|
|
|
355
388
|
//#endregion
|
|
356
389
|
//#region src/script.tsx
|
|
357
390
|
/**
|
|
358
|
-
*
|
|
359
|
-
*
|
|
360
|
-
*
|
|
361
|
-
* // Load analytics after page is interactive
|
|
362
|
-
* <Script src="https://analytics.example.com/script.js" strategy="onIdle" />
|
|
391
|
+
* Composable that provides all script loading behavior — strategy state
|
|
392
|
+
* machine (afterHydration / onIdle / onInteraction / onViewport),
|
|
393
|
+
* deduplication, load/error tracking.
|
|
363
394
|
*
|
|
364
|
-
*
|
|
365
|
-
*
|
|
395
|
+
* Returns reactive signals (`loaded`, `errored`, `pending`) so consumers
|
|
396
|
+
* can render loading indicators, retry buttons, or analytics-readiness
|
|
397
|
+
* gates without re-implementing the strategy machine.
|
|
366
398
|
*
|
|
367
|
-
*
|
|
368
|
-
*
|
|
369
|
-
*
|
|
370
|
-
*
|
|
399
|
+
* @example
|
|
400
|
+
* function MyScript(props: ScriptProps) {
|
|
401
|
+
* const s = useScript(props)
|
|
402
|
+
* return (
|
|
403
|
+
* <>
|
|
404
|
+
* {() => s.loaded() ? <Analytics /> : <Skeleton />}
|
|
405
|
+
* {() => s.needsSentinel && <div ref={s.sentinelRef} style="width:0;height:0" />}
|
|
406
|
+
* </>
|
|
407
|
+
* )
|
|
408
|
+
* }
|
|
371
409
|
*/
|
|
372
|
-
function
|
|
410
|
+
function useScript(props) {
|
|
411
|
+
const strategy = props.strategy ?? "afterHydration";
|
|
412
|
+
const loaded = signal(false);
|
|
413
|
+
const errored = signal(false);
|
|
414
|
+
const pending = signal(strategy !== "beforeHydration" && strategy !== "afterHydration");
|
|
415
|
+
const sentinelRef = strategy === "onViewport" ? createRef() : void 0;
|
|
373
416
|
function loadScript() {
|
|
374
417
|
if (typeof document === "undefined") return;
|
|
375
|
-
if (props.id && document.getElementById(props.id))
|
|
418
|
+
if (props.id && document.getElementById(props.id)) {
|
|
419
|
+
loaded.set(true);
|
|
420
|
+
pending.set(false);
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
376
423
|
const script = document.createElement("script");
|
|
377
424
|
if (props.src) script.src = props.src;
|
|
378
425
|
if (props.id) script.id = props.id;
|
|
379
426
|
script.async = props.async !== false;
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
427
|
+
script.onload = () => {
|
|
428
|
+
loaded.set(true);
|
|
429
|
+
pending.set(false);
|
|
430
|
+
props.onLoad?.();
|
|
431
|
+
};
|
|
432
|
+
script.onerror = () => {
|
|
433
|
+
errored.set(true);
|
|
434
|
+
pending.set(false);
|
|
435
|
+
props.onError?.(/* @__PURE__ */ new Error(`Failed to load: ${props.src}`));
|
|
436
|
+
};
|
|
437
|
+
if (props.children && !props.src) {
|
|
438
|
+
script.textContent = props.children;
|
|
439
|
+
setTimeout(() => {
|
|
440
|
+
loaded.set(true);
|
|
441
|
+
pending.set(false);
|
|
442
|
+
}, 0);
|
|
443
|
+
}
|
|
383
444
|
document.head.appendChild(script);
|
|
384
445
|
}
|
|
385
446
|
onMount(() => {
|
|
386
|
-
switch (
|
|
387
|
-
case "beforeHydration":
|
|
447
|
+
switch (strategy) {
|
|
448
|
+
case "beforeHydration":
|
|
449
|
+
loaded.set(true);
|
|
450
|
+
pending.set(false);
|
|
451
|
+
break;
|
|
388
452
|
case "afterHydration":
|
|
389
453
|
loadScript();
|
|
390
454
|
break;
|
|
@@ -415,15 +479,72 @@ function Script(props) {
|
|
|
415
479
|
case "onViewport": break;
|
|
416
480
|
}
|
|
417
481
|
});
|
|
418
|
-
const sentinelRef = createRef();
|
|
419
|
-
const strategy = props.strategy ?? "afterHydration";
|
|
420
482
|
if (strategy === "onViewport") useIntersectionObserver(() => sentinelRef.current ?? void 0, () => loadScript());
|
|
421
|
-
|
|
422
|
-
|
|
483
|
+
return {
|
|
484
|
+
sentinelRef,
|
|
485
|
+
loaded,
|
|
486
|
+
errored,
|
|
487
|
+
pending,
|
|
488
|
+
needsSentinel: strategy === "onViewport",
|
|
489
|
+
load: loadScript
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Higher-order component that wraps any component with script load behavior.
|
|
494
|
+
*
|
|
495
|
+
* The wrapped component receives {@link ScriptRenderProps} with the sentinel
|
|
496
|
+
* ref, load-state signals, and a `needsSentinel` flag. Use this when you want
|
|
497
|
+
* to render a loading indicator, retry button, or custom analytics-readiness
|
|
498
|
+
* gate around the script load.
|
|
499
|
+
*
|
|
500
|
+
* @example
|
|
501
|
+
* // Script with a loading indicator
|
|
502
|
+
* const TrackedScript = createScript((props) => (
|
|
503
|
+
* <>
|
|
504
|
+
* {() => props.pending() && <Spinner />}
|
|
505
|
+
* {() => props.errored() && <button onClick={() => location.reload()}>Retry</button>}
|
|
506
|
+
* {props.needsSentinel && <div ref={props.sentinelRef} style="width:0;height:0" />}
|
|
507
|
+
* </>
|
|
508
|
+
* ))
|
|
509
|
+
*
|
|
510
|
+
* <TrackedScript src="/analytics.js" strategy="onIdle" />
|
|
511
|
+
*/
|
|
512
|
+
function createScript(Component) {
|
|
513
|
+
return function WrappedScript(props) {
|
|
514
|
+
const s = useScript(props);
|
|
515
|
+
return /* @__PURE__ */ jsx(Component, {
|
|
516
|
+
sentinelRef: s.sentinelRef,
|
|
517
|
+
needsSentinel: s.needsSentinel,
|
|
518
|
+
loaded: s.loaded,
|
|
519
|
+
errored: s.errored,
|
|
520
|
+
pending: s.pending
|
|
521
|
+
});
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Default optimized script component. Renders a 0×0 sentinel `<div>` for the
|
|
526
|
+
* `onViewport` strategy (so IntersectionObserver has an element to observe),
|
|
527
|
+
* `null` for every other strategy.
|
|
528
|
+
*
|
|
529
|
+
* @example
|
|
530
|
+
* // Load analytics after page is interactive
|
|
531
|
+
* <Script src="https://analytics.example.com/script.js" strategy="onIdle" />
|
|
532
|
+
*
|
|
533
|
+
* // Load chat widget when user scrolls
|
|
534
|
+
* <Script src="/chat-widget.js" strategy="onViewport" />
|
|
535
|
+
*
|
|
536
|
+
* // Inline script with deferred execution
|
|
537
|
+
* <Script strategy="afterHydration">
|
|
538
|
+
* {`console.log("App hydrated!")`}
|
|
539
|
+
* <\/Script>
|
|
540
|
+
*/
|
|
541
|
+
const Script = createScript((props) => {
|
|
542
|
+
if (!props.needsSentinel) return null;
|
|
543
|
+
return /* @__PURE__ */ jsx("div", {
|
|
544
|
+
ref: props.sentinelRef,
|
|
423
545
|
style: "width:0;height:0;overflow:hidden"
|
|
424
546
|
});
|
|
425
|
-
|
|
426
|
-
}
|
|
547
|
+
});
|
|
427
548
|
|
|
428
549
|
//#endregion
|
|
429
550
|
//#region src/i18n-routing.ts
|
|
@@ -997,5 +1118,5 @@ function aiPlugin(..._) {
|
|
|
997
1118
|
}
|
|
998
1119
|
|
|
999
1120
|
//#endregion
|
|
1000
|
-
export { Image, Link, Meta, Script, ThemeToggle, aiPlugin, buildLocalePath, buildMetaTags, createLink, createServer, defineConfig, extractLocaleFromPath, faviconPlugin, initTheme, ogImagePlugin, prefetchRoute, resolvedTheme, seoPlugin, setLocale, setSSRThemeDefault, setTheme, theme, themeScript, toggleTheme, useLink, useLocale, validateEnv };
|
|
1121
|
+
export { Image, Link, Meta, Script, ThemeToggle, aiPlugin, buildLocalePath, buildMetaTags, createImage, createLink, createScript, createServer, defineConfig, extractLocaleFromPath, faviconPlugin, initTheme, ogImagePlugin, prefetchRoute, resolvedTheme, seoPlugin, setLocale, setSSRThemeDefault, setTheme, theme, themeScript, toggleTheme, useImage, useLink, useLocale, useScript, validateEnv };
|
|
1001
1122
|
//# sourceMappingURL=index.js.map
|
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/og-image.js
CHANGED
|
@@ -71,14 +71,14 @@ function buildTextOverlaySvg(layers, width, height, locale) {
|
|
|
71
71
|
const lines = [];
|
|
72
72
|
let currentLine = "";
|
|
73
73
|
const estimateWidth = (s) => {
|
|
74
|
-
let
|
|
74
|
+
let w = 0;
|
|
75
75
|
for (let i = 0; i < s.length; i++) {
|
|
76
76
|
const code = s.charCodeAt(i);
|
|
77
|
-
if (code >= 12288 && code <= 40959)
|
|
78
|
-
else if (code <= 126 && "iljft!|:;.,'".includes(s[i]))
|
|
79
|
-
else
|
|
77
|
+
if (code >= 12288 && code <= 40959) w += fontSize * 1;
|
|
78
|
+
else if (code <= 126 && "iljft!|:;.,'".includes(s[i])) w += fontSize * .35;
|
|
79
|
+
else w += fontSize * .55;
|
|
80
80
|
}
|
|
81
|
-
return
|
|
81
|
+
return w;
|
|
82
82
|
};
|
|
83
83
|
for (const word of words) {
|
|
84
84
|
const testLine = currentLine ? `${currentLine} ${word}` : word;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __exportAll = (all, no_symbols) => {
|
|
4
|
+
let target = {};
|
|
5
|
+
for (var name in all) {
|
|
6
|
+
__defProp(target, name, {
|
|
7
|
+
get: all[name],
|
|
8
|
+
enumerable: true
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
if (!no_symbols) {
|
|
12
|
+
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
13
|
+
}
|
|
14
|
+
return target;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
export { __exportAll as t };
|