@uniweb/runtime 0.6.13 → 0.6.14
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/dist/ssr.js +318 -327
- package/dist/ssr.js.map +1 -1
- package/package.json +2 -2
- package/src/ssr-renderer.js +585 -0
- package/src/ssr.js +25 -28
package/dist/ssr.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import React
|
|
2
|
-
import {
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { renderToString } from "react-dom/server";
|
|
3
|
+
import { createUniweb } from "@uniweb/core";
|
|
4
|
+
import { buildSectionOverrides } from "@uniweb/theming";
|
|
3
5
|
function guaranteeItemStructure(item) {
|
|
4
6
|
return {
|
|
5
7
|
title: item.title || "",
|
|
@@ -124,85 +126,31 @@ function getComponentMeta(componentName) {
|
|
|
124
126
|
function getComponentDefaults(componentName) {
|
|
125
127
|
return globalThis.uniweb?.getComponentDefaults?.(componentName) || {};
|
|
126
128
|
}
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if (!url || !url.startsWith("/")) return url;
|
|
135
|
-
const basePath = globalThis.uniweb?.activeWebsite?.basePath || "";
|
|
136
|
-
if (!basePath) return url;
|
|
137
|
-
if (url.startsWith(basePath + "/") || url === basePath) return url;
|
|
138
|
-
return basePath + url;
|
|
139
|
-
}
|
|
140
|
-
function GradientOverlay({ gradient, opacity = 0.5 }) {
|
|
141
|
-
const {
|
|
142
|
-
start = "rgba(0,0,0,0.7)",
|
|
143
|
-
end = "rgba(0,0,0,0)",
|
|
144
|
-
angle = 180,
|
|
145
|
-
startPosition = 0,
|
|
146
|
-
endPosition = 100
|
|
147
|
-
} = gradient;
|
|
148
|
-
const style = {
|
|
149
|
-
position: "absolute",
|
|
150
|
-
inset: 0,
|
|
151
|
-
background: `linear-gradient(${angle}deg, ${start} ${startPosition}%, ${end} ${endPosition}%)`,
|
|
152
|
-
opacity,
|
|
153
|
-
pointerEvents: "none"
|
|
154
|
-
};
|
|
155
|
-
return /* @__PURE__ */ jsx("div", { className: "background-overlay background-overlay--gradient", style, "aria-hidden": "true" });
|
|
156
|
-
}
|
|
157
|
-
function SolidOverlay({ type = "dark", opacity = 0.5 }) {
|
|
158
|
-
const baseColor = type === "light" ? "255, 255, 255" : "0, 0, 0";
|
|
159
|
-
const style = {
|
|
160
|
-
position: "absolute",
|
|
161
|
-
inset: 0,
|
|
162
|
-
backgroundColor: `rgba(${baseColor}, ${opacity})`,
|
|
163
|
-
pointerEvents: "none"
|
|
164
|
-
};
|
|
165
|
-
return /* @__PURE__ */ jsx("div", { className: "background-overlay background-overlay--solid", style, "aria-hidden": "true" });
|
|
166
|
-
}
|
|
167
|
-
function Overlay({ overlay }) {
|
|
168
|
-
if (!overlay?.enabled) return null;
|
|
169
|
-
if (overlay.gradient) {
|
|
170
|
-
return /* @__PURE__ */ jsx(GradientOverlay, { gradient: overlay.gradient, opacity: overlay.opacity });
|
|
129
|
+
const VALID_CONTEXTS = ["light", "medium", "dark"];
|
|
130
|
+
function getWrapperProps(block) {
|
|
131
|
+
const theme = block.themeName;
|
|
132
|
+
const blockClassName = block.state?.className || "";
|
|
133
|
+
let contextClass = "";
|
|
134
|
+
if (theme && VALID_CONTEXTS.includes(theme)) {
|
|
135
|
+
contextClass = `context-${theme}`;
|
|
171
136
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
const {
|
|
190
|
-
start = "transparent",
|
|
191
|
-
end = "transparent",
|
|
192
|
-
angle = 0,
|
|
193
|
-
startPosition = 0,
|
|
194
|
-
endPosition = 100,
|
|
195
|
-
startOpacity = 1,
|
|
196
|
-
endOpacity = 1
|
|
197
|
-
} = gradient;
|
|
198
|
-
const startColor = startOpacity < 1 ? withOpacity(start, startOpacity) : start;
|
|
199
|
-
const endColor = endOpacity < 1 ? withOpacity(end, endOpacity) : end;
|
|
200
|
-
const style = {
|
|
201
|
-
position: "absolute",
|
|
202
|
-
inset: 0,
|
|
203
|
-
background: `linear-gradient(${angle}deg, ${startColor} ${startPosition}%, ${endColor} ${endPosition}%)`
|
|
204
|
-
};
|
|
205
|
-
return /* @__PURE__ */ jsx("div", { className: "background-gradient", style, "aria-hidden": "true" });
|
|
137
|
+
let className = contextClass;
|
|
138
|
+
if (blockClassName) {
|
|
139
|
+
className = className ? `${className} ${blockClassName}` : blockClassName;
|
|
140
|
+
}
|
|
141
|
+
const { background = {} } = block.standardOptions;
|
|
142
|
+
const style = {};
|
|
143
|
+
if (background.mode) {
|
|
144
|
+
style.position = "relative";
|
|
145
|
+
style.isolation = "isolate";
|
|
146
|
+
}
|
|
147
|
+
if (block.contextOverrides) {
|
|
148
|
+
for (const [key, value] of Object.entries(block.contextOverrides)) {
|
|
149
|
+
style[`--${key}`] = value;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const sectionId = block.stableId || block.id;
|
|
153
|
+
return { id: `section-${sectionId}`, style, className, background };
|
|
206
154
|
}
|
|
207
155
|
function withOpacity(color, opacity) {
|
|
208
156
|
if (color.startsWith("#")) {
|
|
@@ -219,192 +167,143 @@ function withOpacity(color, opacity) {
|
|
|
219
167
|
}
|
|
220
168
|
return color;
|
|
221
169
|
}
|
|
222
|
-
function
|
|
223
|
-
if (!
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
lazy = true
|
|
229
|
-
} = image;
|
|
230
|
-
const style = {
|
|
231
|
-
position: "absolute",
|
|
232
|
-
inset: 0,
|
|
233
|
-
backgroundImage: `url(${resolveUrl(src)})`,
|
|
234
|
-
backgroundPosition: position,
|
|
235
|
-
backgroundSize: size,
|
|
236
|
-
backgroundRepeat: "no-repeat"
|
|
237
|
-
};
|
|
238
|
-
return /* @__PURE__ */ jsx("div", { className: "background-image", style, "aria-hidden": "true" });
|
|
239
|
-
}
|
|
240
|
-
function prefersReducedMotion() {
|
|
241
|
-
if (typeof window === "undefined") return false;
|
|
242
|
-
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
243
|
-
}
|
|
244
|
-
function VideoBackground({ video }) {
|
|
245
|
-
if (!video?.src) return null;
|
|
246
|
-
const {
|
|
247
|
-
src,
|
|
248
|
-
sources,
|
|
249
|
-
// Array of { src, type } for multiple formats
|
|
250
|
-
poster,
|
|
251
|
-
loop = true,
|
|
252
|
-
muted = true
|
|
253
|
-
} = video;
|
|
254
|
-
if (prefersReducedMotion() && poster) {
|
|
255
|
-
return /* @__PURE__ */ jsx(ImageBackground, { image: { src: poster, size: "cover", position: "center" } });
|
|
256
|
-
}
|
|
257
|
-
const style = {
|
|
258
|
-
position: "absolute",
|
|
259
|
-
inset: 0,
|
|
260
|
-
width: "100%",
|
|
261
|
-
height: "100%",
|
|
262
|
-
objectFit: "cover"
|
|
263
|
-
};
|
|
264
|
-
const sourceList = (sources || inferSources(src)).map((s) => ({
|
|
265
|
-
...s,
|
|
266
|
-
src: resolveUrl(s.src)
|
|
267
|
-
}));
|
|
268
|
-
return /* @__PURE__ */ jsx(
|
|
269
|
-
"video",
|
|
270
|
-
{
|
|
271
|
-
className: "background-video",
|
|
272
|
-
style,
|
|
273
|
-
autoPlay: true,
|
|
274
|
-
loop,
|
|
275
|
-
muted,
|
|
276
|
-
playsInline: true,
|
|
277
|
-
poster: resolveUrl(poster),
|
|
278
|
-
"aria-hidden": "true",
|
|
279
|
-
children: sourceList.map(({ src: sourceSrc, type }, index) => /* @__PURE__ */ jsx("source", { src: sourceSrc, type }, index))
|
|
280
|
-
}
|
|
281
|
-
);
|
|
282
|
-
}
|
|
283
|
-
function inferSources(src) {
|
|
284
|
-
const sources = [];
|
|
285
|
-
const ext = src.split(".").pop()?.toLowerCase();
|
|
286
|
-
const basePath = src.slice(0, src.lastIndexOf("."));
|
|
287
|
-
if (ext === "mp4") {
|
|
288
|
-
sources.push({ src: `${basePath}.webm`, type: "video/webm" });
|
|
289
|
-
sources.push({ src, type: "video/mp4" });
|
|
290
|
-
} else if (ext === "webm") {
|
|
291
|
-
sources.push({ src, type: "video/webm" });
|
|
292
|
-
sources.push({ src: `${basePath}.mp4`, type: "video/mp4" });
|
|
293
|
-
} else {
|
|
294
|
-
sources.push({ src, type: getVideoMimeType(src) });
|
|
295
|
-
}
|
|
296
|
-
return sources;
|
|
297
|
-
}
|
|
298
|
-
function getVideoMimeType(src) {
|
|
299
|
-
if (src.endsWith(".webm")) return "video/webm";
|
|
300
|
-
if (src.endsWith(".ogg") || src.endsWith(".ogv")) return "video/ogg";
|
|
301
|
-
return "video/mp4";
|
|
170
|
+
function resolveUrl(url) {
|
|
171
|
+
if (!url || !url.startsWith("/")) return url;
|
|
172
|
+
const basePath = globalThis.uniweb?.activeWebsite?.basePath || "";
|
|
173
|
+
if (!basePath) return url;
|
|
174
|
+
if (url.startsWith(basePath + "/") || url === basePath) return url;
|
|
175
|
+
return basePath + url;
|
|
302
176
|
}
|
|
303
|
-
function
|
|
304
|
-
mode
|
|
305
|
-
color,
|
|
306
|
-
gradient,
|
|
307
|
-
image,
|
|
308
|
-
video,
|
|
309
|
-
overlay,
|
|
310
|
-
className = ""
|
|
311
|
-
}) {
|
|
312
|
-
if (!mode) return null;
|
|
177
|
+
function renderBackground(background) {
|
|
178
|
+
if (!background?.mode) return null;
|
|
313
179
|
const containerStyle = {
|
|
314
180
|
position: "absolute",
|
|
315
|
-
inset: 0,
|
|
181
|
+
inset: "0",
|
|
316
182
|
overflow: "hidden",
|
|
317
183
|
zIndex: 0
|
|
318
184
|
};
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
mode === MODES.VIDEO && /* @__PURE__ */ jsx(VideoBackground, { video }),
|
|
330
|
-
/* @__PURE__ */ jsx(Overlay, { overlay })
|
|
331
|
-
]
|
|
332
|
-
}
|
|
333
|
-
);
|
|
334
|
-
}
|
|
335
|
-
const VALID_CONTEXTS = ["light", "medium", "dark"];
|
|
336
|
-
const getWrapperProps = (block) => {
|
|
337
|
-
const theme = block.themeName;
|
|
338
|
-
const blockClassName = block.state?.className || "";
|
|
339
|
-
let contextClass = "";
|
|
340
|
-
if (theme && VALID_CONTEXTS.includes(theme)) {
|
|
341
|
-
contextClass = `context-${theme}`;
|
|
185
|
+
const children = [];
|
|
186
|
+
if (background.mode === "color" && background.color) {
|
|
187
|
+
children.push(
|
|
188
|
+
React.createElement("div", {
|
|
189
|
+
key: "bg-color",
|
|
190
|
+
className: "background-color",
|
|
191
|
+
style: { position: "absolute", inset: "0", backgroundColor: background.color },
|
|
192
|
+
"aria-hidden": "true"
|
|
193
|
+
})
|
|
194
|
+
);
|
|
342
195
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
196
|
+
if (background.mode === "gradient" && background.gradient) {
|
|
197
|
+
const g = background.gradient;
|
|
198
|
+
let bgValue;
|
|
199
|
+
if (typeof g === "string") {
|
|
200
|
+
bgValue = g;
|
|
201
|
+
} else {
|
|
202
|
+
const {
|
|
203
|
+
start = "transparent",
|
|
204
|
+
end = "transparent",
|
|
205
|
+
angle = 0,
|
|
206
|
+
startPosition = 0,
|
|
207
|
+
endPosition = 100,
|
|
208
|
+
startOpacity = 1,
|
|
209
|
+
endOpacity = 1
|
|
210
|
+
} = g;
|
|
211
|
+
const startColor = startOpacity < 1 ? withOpacity(start, startOpacity) : start;
|
|
212
|
+
const endColor = endOpacity < 1 ? withOpacity(end, endOpacity) : end;
|
|
213
|
+
bgValue = `linear-gradient(${angle}deg, ${startColor} ${startPosition}%, ${endColor} ${endPosition}%)`;
|
|
214
|
+
}
|
|
215
|
+
children.push(
|
|
216
|
+
React.createElement("div", {
|
|
217
|
+
key: "bg-gradient",
|
|
218
|
+
className: "background-gradient",
|
|
219
|
+
style: { position: "absolute", inset: "0", background: bgValue },
|
|
220
|
+
"aria-hidden": "true"
|
|
221
|
+
})
|
|
222
|
+
);
|
|
346
223
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
224
|
+
if (background.mode === "image" && background.image?.src) {
|
|
225
|
+
const img = background.image;
|
|
226
|
+
children.push(
|
|
227
|
+
React.createElement("div", {
|
|
228
|
+
key: "bg-image",
|
|
229
|
+
className: "background-image",
|
|
230
|
+
style: {
|
|
231
|
+
position: "absolute",
|
|
232
|
+
inset: "0",
|
|
233
|
+
backgroundImage: `url(${resolveUrl(img.src)})`,
|
|
234
|
+
backgroundPosition: img.position || "center",
|
|
235
|
+
backgroundSize: img.size || "cover",
|
|
236
|
+
backgroundRepeat: "no-repeat"
|
|
237
|
+
},
|
|
238
|
+
"aria-hidden": "true"
|
|
239
|
+
})
|
|
240
|
+
);
|
|
352
241
|
}
|
|
353
|
-
if (
|
|
354
|
-
|
|
355
|
-
|
|
242
|
+
if (background.overlay?.enabled) {
|
|
243
|
+
const ov = background.overlay;
|
|
244
|
+
let overlayStyle;
|
|
245
|
+
if (ov.gradient) {
|
|
246
|
+
const g = ov.gradient;
|
|
247
|
+
overlayStyle = {
|
|
248
|
+
position: "absolute",
|
|
249
|
+
inset: "0",
|
|
250
|
+
pointerEvents: "none",
|
|
251
|
+
background: `linear-gradient(${g.angle || 180}deg, ${g.start || "rgba(0,0,0,0.7)"} ${g.startPosition || 0}%, ${g.end || "rgba(0,0,0,0)"} ${g.endPosition || 100}%)`,
|
|
252
|
+
opacity: ov.opacity ?? 0.5
|
|
253
|
+
};
|
|
254
|
+
} else {
|
|
255
|
+
const baseColor = ov.type === "light" ? "255, 255, 255" : "0, 0, 0";
|
|
256
|
+
overlayStyle = {
|
|
257
|
+
position: "absolute",
|
|
258
|
+
inset: "0",
|
|
259
|
+
pointerEvents: "none",
|
|
260
|
+
backgroundColor: `rgba(${baseColor}, ${ov.opacity ?? 0.5})`
|
|
261
|
+
};
|
|
356
262
|
}
|
|
263
|
+
children.push(
|
|
264
|
+
React.createElement("div", {
|
|
265
|
+
key: "bg-overlay",
|
|
266
|
+
className: ov.gradient ? "background-overlay background-overlay--gradient" : "background-overlay background-overlay--solid",
|
|
267
|
+
style: overlayStyle,
|
|
268
|
+
"aria-hidden": "true"
|
|
269
|
+
})
|
|
270
|
+
);
|
|
357
271
|
}
|
|
358
|
-
|
|
359
|
-
return {
|
|
360
|
-
|
|
361
|
-
style,
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
}
|
|
366
|
-
function BlockRenderer({ block, pure = false, as = "section", extra = {} }) {
|
|
272
|
+
if (children.length === 0) return null;
|
|
273
|
+
return React.createElement("div", {
|
|
274
|
+
className: `background background--${background.mode}`,
|
|
275
|
+
style: containerStyle,
|
|
276
|
+
"aria-hidden": "true"
|
|
277
|
+
}, ...children);
|
|
278
|
+
}
|
|
279
|
+
function renderBlock(block, { pure = false } = {}) {
|
|
367
280
|
const Component = block.initComponent();
|
|
368
|
-
const entityStore = block.website.entityStore;
|
|
369
|
-
const meta = getComponentMeta(block.type);
|
|
370
|
-
const resolved = entityStore.resolve(block, meta);
|
|
371
|
-
const [asyncData, setAsyncData] = useState(null);
|
|
372
|
-
useEffect(() => {
|
|
373
|
-
setAsyncData(null);
|
|
374
|
-
}, [block]);
|
|
375
|
-
useEffect(() => {
|
|
376
|
-
if (resolved.status !== "pending") return;
|
|
377
|
-
let cancelled = false;
|
|
378
|
-
entityStore.fetch(block, meta).then((result) => {
|
|
379
|
-
if (!cancelled && result.data) setAsyncData(result.data);
|
|
380
|
-
});
|
|
381
|
-
return () => {
|
|
382
|
-
cancelled = true;
|
|
383
|
-
};
|
|
384
|
-
}, [block]);
|
|
385
|
-
const entityData = resolved.status === "ready" ? resolved.data : asyncData;
|
|
386
|
-
block.dataLoading = resolved.status === "pending" && !entityData;
|
|
387
281
|
if (!Component) {
|
|
388
|
-
return
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
282
|
+
return React.createElement("div", {
|
|
283
|
+
className: "block-error",
|
|
284
|
+
style: { padding: "1rem", background: "#fef2f2", color: "#dc2626" }
|
|
285
|
+
}, `Component not found: ${block.type}`);
|
|
392
286
|
}
|
|
287
|
+
const meta = getComponentMeta(block.type);
|
|
393
288
|
const prepared = prepareProps(block, meta);
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
289
|
+
const params = prepared.params;
|
|
290
|
+
const content = { ...prepared.content, ...block.properties };
|
|
291
|
+
const entityStore = block.website?.entityStore;
|
|
292
|
+
if (entityStore) {
|
|
293
|
+
const resolved = entityStore.resolve(block, meta);
|
|
294
|
+
if (resolved.status === "ready" && resolved.data) {
|
|
295
|
+
const merged = { ...content.data };
|
|
296
|
+
for (const key of Object.keys(resolved.data)) {
|
|
297
|
+
if (merged[key] === void 0) {
|
|
298
|
+
merged[key] = resolved.data[key];
|
|
299
|
+
}
|
|
405
300
|
}
|
|
301
|
+
content.data = merged;
|
|
406
302
|
}
|
|
407
|
-
|
|
303
|
+
}
|
|
304
|
+
const componentProps = { content, params, block };
|
|
305
|
+
if (pure) {
|
|
306
|
+
return React.createElement(Component, componentProps);
|
|
408
307
|
}
|
|
409
308
|
const { background, ...wrapperProps } = getWrapperProps(block);
|
|
410
309
|
const componentClassName = Component.className;
|
|
@@ -413,109 +312,201 @@ function BlockRenderer({ block, pure = false, as = "section", extra = {} }) {
|
|
|
413
312
|
}
|
|
414
313
|
const hasBackground = background?.mode && meta?.background !== "self";
|
|
415
314
|
block.hasBackground = hasBackground;
|
|
416
|
-
const
|
|
417
|
-
content,
|
|
418
|
-
params,
|
|
419
|
-
block
|
|
420
|
-
};
|
|
421
|
-
if (pure) {
|
|
422
|
-
return /* @__PURE__ */ jsx(Component, { ...componentProps, extra });
|
|
423
|
-
}
|
|
424
|
-
const componentAs = Component.as;
|
|
425
|
-
const Wrapper = as === false ? React.Fragment : as !== "section" ? as : componentAs || "section";
|
|
426
|
-
const wrapperElementProps = as === false ? {} : wrapperProps;
|
|
315
|
+
const wrapperTag = Component.as || "section";
|
|
427
316
|
if (hasBackground) {
|
|
428
|
-
return
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
}
|
|
439
|
-
),
|
|
440
|
-
/* @__PURE__ */ jsx("div", { style: { position: "relative", zIndex: 10 }, children: /* @__PURE__ */ jsx(Component, { ...componentProps }) })
|
|
441
|
-
] });
|
|
317
|
+
return React.createElement(
|
|
318
|
+
wrapperTag,
|
|
319
|
+
wrapperProps,
|
|
320
|
+
renderBackground(background),
|
|
321
|
+
React.createElement(
|
|
322
|
+
"div",
|
|
323
|
+
{ style: { position: "relative", zIndex: 10 } },
|
|
324
|
+
React.createElement(Component, componentProps)
|
|
325
|
+
)
|
|
326
|
+
);
|
|
442
327
|
}
|
|
443
|
-
return
|
|
328
|
+
return React.createElement(
|
|
329
|
+
wrapperTag,
|
|
330
|
+
wrapperProps,
|
|
331
|
+
React.createElement(Component, componentProps)
|
|
332
|
+
);
|
|
444
333
|
}
|
|
445
|
-
function
|
|
334
|
+
function renderBlocks(blocks) {
|
|
446
335
|
if (!blocks || blocks.length === 0) return null;
|
|
447
|
-
return blocks.map(
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
] });
|
|
455
|
-
}
|
|
456
|
-
function initializeAllBlocks(...blockGroups) {
|
|
457
|
-
for (const blocks of blockGroups) {
|
|
458
|
-
if (!blocks) continue;
|
|
459
|
-
for (const block of blocks) {
|
|
460
|
-
block.initComponent();
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
function mergeParams(pageParams = {}, defaults = {}) {
|
|
465
|
-
return { ...defaults, ...pageParams };
|
|
336
|
+
return blocks.map(
|
|
337
|
+
(block, index) => React.createElement(
|
|
338
|
+
React.Fragment,
|
|
339
|
+
{ key: block.id || index },
|
|
340
|
+
renderBlock(block)
|
|
341
|
+
)
|
|
342
|
+
);
|
|
466
343
|
}
|
|
467
|
-
function
|
|
344
|
+
function renderLayout(page, website) {
|
|
468
345
|
const layoutName = page.getLayoutName();
|
|
469
346
|
const RemoteLayout = website.getRemoteLayout(layoutName);
|
|
470
347
|
const layoutMeta = website.getLayoutMeta(layoutName);
|
|
471
348
|
const bodyBlocks = page.getBodyBlocks();
|
|
472
349
|
const areas = page.getLayoutAreas();
|
|
473
|
-
const
|
|
474
|
-
initializeAllBlocks(...allBlockGroups);
|
|
475
|
-
const bodyElement = bodyBlocks ? /* @__PURE__ */ jsx(Blocks, { blocks: bodyBlocks }) : null;
|
|
350
|
+
const bodyElement = bodyBlocks ? renderBlocks(bodyBlocks) : null;
|
|
476
351
|
const areaElements = {};
|
|
477
352
|
for (const [name, blocks] of Object.entries(areas)) {
|
|
478
|
-
areaElements[name] =
|
|
353
|
+
areaElements[name] = renderBlocks(blocks);
|
|
479
354
|
}
|
|
480
355
|
if (RemoteLayout) {
|
|
481
|
-
const params =
|
|
482
|
-
return
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
website,
|
|
487
|
-
params,
|
|
488
|
-
body: bodyElement,
|
|
489
|
-
...areaElements
|
|
490
|
-
},
|
|
491
|
-
layoutName
|
|
492
|
-
);
|
|
493
|
-
}
|
|
494
|
-
return /* @__PURE__ */ jsx(
|
|
495
|
-
DefaultLayout,
|
|
496
|
-
{
|
|
356
|
+
const params = { ...layoutMeta?.defaults || {}, ...page.getLayoutParams() || {} };
|
|
357
|
+
return React.createElement(RemoteLayout, {
|
|
358
|
+
page,
|
|
359
|
+
website,
|
|
360
|
+
params,
|
|
497
361
|
body: bodyElement,
|
|
498
362
|
...areaElements
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
}
|
|
502
|
-
function PageElement({ page, website }) {
|
|
363
|
+
});
|
|
364
|
+
}
|
|
503
365
|
return React.createElement(
|
|
504
|
-
|
|
366
|
+
React.Fragment,
|
|
505
367
|
null,
|
|
506
|
-
React.createElement(
|
|
368
|
+
areaElements.header && React.createElement("header", null, areaElements.header),
|
|
369
|
+
bodyElement && React.createElement("main", null, bodyElement),
|
|
370
|
+
areaElements.footer && React.createElement("footer", null, areaElements.footer)
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
function initPrerender(content, foundation, options = {}) {
|
|
374
|
+
const { onProgress = () => {
|
|
375
|
+
} } = options;
|
|
376
|
+
onProgress("Initializing runtime...");
|
|
377
|
+
const uniweb = createUniweb(content);
|
|
378
|
+
uniweb.setFoundation(foundation);
|
|
379
|
+
if (foundation.default?.capabilities) {
|
|
380
|
+
uniweb.setFoundationConfig(foundation.default.capabilities);
|
|
381
|
+
}
|
|
382
|
+
if (foundation.default?.layoutMeta && uniweb.foundationConfig) {
|
|
383
|
+
uniweb.foundationConfig.layoutMeta = foundation.default.layoutMeta;
|
|
384
|
+
}
|
|
385
|
+
if (content.config?.base && uniweb.activeWebsite?.setBasePath) {
|
|
386
|
+
uniweb.activeWebsite.setBasePath(content.config.base);
|
|
387
|
+
}
|
|
388
|
+
uniweb.childBlockRenderer = function InlineChildBlocks({ blocks, from, pure = false }) {
|
|
389
|
+
const blockList = blocks || from?.childBlocks || [];
|
|
390
|
+
return blockList.map(
|
|
391
|
+
(childBlock, index) => React.createElement(
|
|
392
|
+
React.Fragment,
|
|
393
|
+
{ key: childBlock.id || index },
|
|
394
|
+
renderBlock(childBlock, { pure })
|
|
395
|
+
)
|
|
396
|
+
);
|
|
397
|
+
};
|
|
398
|
+
return uniweb;
|
|
399
|
+
}
|
|
400
|
+
async function prefetchIcons(siteContent, uniweb, onProgress = () => {
|
|
401
|
+
}) {
|
|
402
|
+
const icons = siteContent.icons?.used || [];
|
|
403
|
+
if (icons.length === 0) return;
|
|
404
|
+
const cdnBase = siteContent.config?.icons?.cdnUrl || "https://uniweb.github.io/icons";
|
|
405
|
+
onProgress(`Fetching ${icons.length} icons for SSR...`);
|
|
406
|
+
const results = await Promise.allSettled(
|
|
407
|
+
icons.map(async (iconRef) => {
|
|
408
|
+
const [family, name] = iconRef.split(":");
|
|
409
|
+
const url = `${cdnBase}/${family}/${family}-${name}.svg`;
|
|
410
|
+
const response = await fetch(url);
|
|
411
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
412
|
+
const svg = await response.text();
|
|
413
|
+
uniweb.iconCache.set(`${family}:${name}`, svg);
|
|
414
|
+
})
|
|
415
|
+
);
|
|
416
|
+
const succeeded = results.filter((r) => r.status === "fulfilled").length;
|
|
417
|
+
const failed = results.filter((r) => r.status === "rejected").length;
|
|
418
|
+
if (failed > 0) {
|
|
419
|
+
const msg = `Fetched ${succeeded}/${icons.length} icons (${failed} failed)`;
|
|
420
|
+
console.warn(`[prerender] ${msg}`);
|
|
421
|
+
onProgress(` ${msg}`);
|
|
422
|
+
}
|
|
423
|
+
if (uniweb.iconCache.size > 0) {
|
|
424
|
+
siteContent._iconCache = Object.fromEntries(uniweb.iconCache);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
function classifyRenderError(err) {
|
|
428
|
+
const msg = err.message || "";
|
|
429
|
+
if (msg.includes("Invalid hook call") || msg.includes("useState") || msg.includes("useEffect")) {
|
|
430
|
+
return {
|
|
431
|
+
type: "hooks",
|
|
432
|
+
message: "contains components with React hooks (renders client-side)"
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
if (msg.includes("Element type is invalid") && msg.includes("null")) {
|
|
436
|
+
return {
|
|
437
|
+
type: "null-component",
|
|
438
|
+
message: "a component resolved to null (often hook-related, renders client-side)"
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
return {
|
|
442
|
+
type: "unknown",
|
|
443
|
+
message: msg
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
function renderPage(page, website) {
|
|
447
|
+
website.setActivePage(page.route);
|
|
448
|
+
const element = renderLayout(page, website);
|
|
449
|
+
let renderedContent;
|
|
450
|
+
try {
|
|
451
|
+
renderedContent = renderToString(element);
|
|
452
|
+
} catch (err) {
|
|
453
|
+
return { error: classifyRenderError(err) };
|
|
454
|
+
}
|
|
455
|
+
const appearance = website.themeData?.appearance;
|
|
456
|
+
const sectionOverrideCSS = buildSectionOverrides(page.getPageBlocks(), appearance);
|
|
457
|
+
return { renderedContent, sectionOverrideCSS };
|
|
458
|
+
}
|
|
459
|
+
function escapeHtml(str) {
|
|
460
|
+
if (!str) return "";
|
|
461
|
+
return String(str).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
462
|
+
}
|
|
463
|
+
function injectPageContent(html, renderedContent, page, options = {}) {
|
|
464
|
+
let result = html;
|
|
465
|
+
if (options.sectionOverrideCSS) {
|
|
466
|
+
const overrideStyle = `<style id="uniweb-page-overrides">
|
|
467
|
+
${options.sectionOverrideCSS}
|
|
468
|
+
</style>`;
|
|
469
|
+
result = result.replace("</head>", `${overrideStyle}
|
|
470
|
+
</head>`);
|
|
471
|
+
}
|
|
472
|
+
result = result.replace(
|
|
473
|
+
/<div id="root">[\s\S]*?<\/div>/,
|
|
474
|
+
`<div id="root">${renderedContent}</div>`
|
|
507
475
|
);
|
|
476
|
+
if (page.title) {
|
|
477
|
+
result = result.replace(
|
|
478
|
+
/<title>.*?<\/title>/,
|
|
479
|
+
`<title>${escapeHtml(page.title)}</title>`
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
if (page.description) {
|
|
483
|
+
const metaDesc = `<meta name="description" content="${escapeHtml(page.description)}">`;
|
|
484
|
+
if (result.includes('<meta name="description"')) {
|
|
485
|
+
result = result.replace(/<meta name="description"[^>]*>/, metaDesc);
|
|
486
|
+
} else {
|
|
487
|
+
result = result.replace("</head>", `${metaDesc}
|
|
488
|
+
</head>`);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
return result;
|
|
508
492
|
}
|
|
509
493
|
export {
|
|
510
|
-
BlockRenderer,
|
|
511
|
-
Blocks,
|
|
512
|
-
Layout,
|
|
513
|
-
PageElement,
|
|
514
494
|
applyDefaults,
|
|
515
495
|
applySchemas,
|
|
496
|
+
classifyRenderError,
|
|
497
|
+
escapeHtml,
|
|
516
498
|
getComponentDefaults,
|
|
517
499
|
getComponentMeta,
|
|
500
|
+
getWrapperProps,
|
|
518
501
|
guaranteeContentStructure,
|
|
519
|
-
|
|
502
|
+
initPrerender,
|
|
503
|
+
injectPageContent,
|
|
504
|
+
prefetchIcons,
|
|
505
|
+
prepareProps,
|
|
506
|
+
renderBackground,
|
|
507
|
+
renderBlock,
|
|
508
|
+
renderBlocks,
|
|
509
|
+
renderLayout,
|
|
510
|
+
renderPage
|
|
520
511
|
};
|
|
521
512
|
//# sourceMappingURL=ssr.js.map
|