mnfst-render 0.5.5 → 0.5.7
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/manifest.render.mjs +61 -27
- package/package.json +1 -1
package/manifest.render.mjs
CHANGED
|
@@ -150,11 +150,11 @@ async function waitForManifestRenderReady(page, { allLocales, currentLocale, tim
|
|
|
150
150
|
)
|
|
151
151
|
.catch((e) => ({ ok: false, reason: 'evaluate', message: String(e) }));
|
|
152
152
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
153
|
+
// Note: render-ready wait timeouts are silently tolerated. Earlier versions
|
|
154
|
+
// logged a warning per path, but it fires on essentially every route in
|
|
155
|
+
// projects whose data plugins don't dispatch `manifest:render-ready` (i.e.
|
|
156
|
+
// most of them), drowning the terminal in noise. The fallback timeout is
|
|
157
|
+
// intentional and benign — the DOM is still captured.
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
// --- Config ------------------------------------------------------------------
|
|
@@ -246,7 +246,11 @@ function resolveConfig() {
|
|
|
246
246
|
: [],
|
|
247
247
|
dryRun: !!cli.dryRun,
|
|
248
248
|
debugPrerender: !!cli.debugPrerender,
|
|
249
|
-
|
|
249
|
+
// Cap on the manifest:render-ready wait. When the data plugin dispatches
|
|
250
|
+
// the event, we resolve immediately; when it doesn't (most projects), we
|
|
251
|
+
// fall back to the timeout. Lowered from 25000 — the earlier settle
|
|
252
|
+
// waits already give plugins enough time to finish.
|
|
253
|
+
pipelineTimeout: 6000,
|
|
250
254
|
};
|
|
251
255
|
}
|
|
252
256
|
|
|
@@ -1356,7 +1360,7 @@ function generateLocaleVariantHtml({
|
|
|
1356
1360
|
html = stripEmptyInlineMaskStyles(html);
|
|
1357
1361
|
html = stripResolvedXIconDirectives(html);
|
|
1358
1362
|
// markPrerenderedManifestComponents must run BEFORE stripPrerenderHydrateMarkers so it can
|
|
1359
|
-
// detect data-
|
|
1363
|
+
// detect data-hydrate markers and skip components inside hydrate islands.
|
|
1360
1364
|
html = markPrerenderedManifestComponents(html);
|
|
1361
1365
|
|
|
1362
1366
|
const fileSegments = pathToFileSegments(pathSeg ? '/' + pathSeg : '/');
|
|
@@ -2024,16 +2028,21 @@ async function runPrerender(config) {
|
|
|
2024
2028
|
timeout: Math.min(timeout, 30000),
|
|
2025
2029
|
});
|
|
2026
2030
|
|
|
2031
|
+
// Belt-and-suspenders settle waits. Each one is bounded short because
|
|
2032
|
+
// the authoritative `manifest:render-ready` wait below is what we really
|
|
2033
|
+
// rely on. These earlier waits exist as fallbacks for projects whose
|
|
2034
|
+
// data plugins don't dispatch the modern signal — but they shouldn't
|
|
2035
|
+
// dominate per-path runtime when they DO time out.
|
|
2027
2036
|
await Promise.race([
|
|
2028
2037
|
page.evaluate(() => {
|
|
2029
2038
|
return new Promise((resolve) => {
|
|
2030
2039
|
const done = () => resolve();
|
|
2031
|
-
const t = setTimeout(done,
|
|
2040
|
+
const t = setTimeout(done, 1500); // was 6000
|
|
2032
2041
|
window.addEventListener(
|
|
2033
2042
|
'manifest:routing-ready',
|
|
2034
2043
|
() => {
|
|
2035
2044
|
clearTimeout(t);
|
|
2036
|
-
setTimeout(done,
|
|
2045
|
+
setTimeout(done, 500); // was 2000
|
|
2037
2046
|
},
|
|
2038
2047
|
{ once: true }
|
|
2039
2048
|
);
|
|
@@ -2042,13 +2051,13 @@ async function runPrerender(config) {
|
|
|
2042
2051
|
new Promise((_, rej) => setTimeout(() => rej(new Error('ready timeout')), timeout)),
|
|
2043
2052
|
]).catch(() => { });
|
|
2044
2053
|
|
|
2045
|
-
// Ensure
|
|
2046
|
-
//
|
|
2054
|
+
// Ensure the dynamic loader has injected at least one plugin script.
|
|
2055
|
+
// In practice this happens within ~100ms; the 1500ms cap is generous.
|
|
2047
2056
|
await page.evaluate(() => {
|
|
2048
2057
|
return new Promise((resolve) => {
|
|
2049
2058
|
const check = () => document.querySelectorAll('script[src*="manifest"]').length >= 2;
|
|
2050
2059
|
if (check()) return resolve();
|
|
2051
|
-
const deadline = Date.now() + 5000
|
|
2060
|
+
const deadline = Date.now() + 1500; // was 5000
|
|
2052
2061
|
const t = setInterval(() => {
|
|
2053
2062
|
if (check() || Date.now() >= deadline) {
|
|
2054
2063
|
clearInterval(t);
|
|
@@ -2058,19 +2067,21 @@ async function runPrerender(config) {
|
|
|
2058
2067
|
});
|
|
2059
2068
|
}).catch(() => { });
|
|
2060
2069
|
|
|
2061
|
-
|
|
2070
|
+
// Network idle: shorter idle window + shorter cap. Most plugin scripts
|
|
2071
|
+
// and data fetches complete in under 2s on a healthy local server.
|
|
2072
|
+
await page.waitForNetworkIdle({ idleTime: 800, timeout: 4000 }).catch(() => { });
|
|
2062
2073
|
|
|
2074
|
+
// DOM stability: 300ms quiet window is plenty after networkIdle has
|
|
2075
|
+
// already drained pending fetches.
|
|
2063
2076
|
await page.evaluate(() => {
|
|
2064
2077
|
return new Promise((resolve) => {
|
|
2065
2078
|
const observer = new MutationObserver(() => {
|
|
2066
2079
|
clearTimeout(stable);
|
|
2067
|
-
stable = setTimeout(
|
|
2080
|
+
stable = setTimeout(finish, 300); // was 800
|
|
2068
2081
|
});
|
|
2069
2082
|
observer.observe(document.documentElement, { childList: true, subtree: true, characterData: true });
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
resolve();
|
|
2073
|
-
}, 800);
|
|
2083
|
+
const finish = () => { observer.disconnect(); resolve(); };
|
|
2084
|
+
let stable = setTimeout(finish, 300); // was 800
|
|
2074
2085
|
});
|
|
2075
2086
|
}).catch(() => { });
|
|
2076
2087
|
|
|
@@ -2239,13 +2250,36 @@ async function runPrerender(config) {
|
|
|
2239
2250
|
});
|
|
2240
2251
|
|
|
2241
2252
|
// Strip x-markdown from elements that already have baked content.
|
|
2242
|
-
// The markdown plugin hides elements with opacity:0 on init, then re-fetches
|
|
2243
|
-
// For prerendered pages the content is already baked —
|
|
2244
|
-
// runtime plugin from re-processing (and
|
|
2253
|
+
// The markdown plugin hides elements with opacity:0 on init, then re-fetches
|
|
2254
|
+
// and re-renders. For prerendered pages the content is already baked —
|
|
2255
|
+
// removing x-markdown prevents the runtime plugin from re-processing (and
|
|
2256
|
+
// temporarily hiding) the static content.
|
|
2257
|
+
//
|
|
2258
|
+
// We ALSO clear any leftover `opacity: 0` inline style the plugin set
|
|
2259
|
+
// before/while rendering. On dynamic expressions that initially evaluate
|
|
2260
|
+
// empty (e.g. article content keyed off `$route` before `$x.articles` has
|
|
2261
|
+
// loaded), the plugin sets opacity to 0 and may never restore it to 1 if
|
|
2262
|
+
// the effect re-fires with an empty value. The end state in the
|
|
2263
|
+
// serialized HTML has rendered content but opacity:0 — invisible in
|
|
2264
|
+
// production. Since we're also removing x-markdown (so the runtime
|
|
2265
|
+
// plugin doesn't re-hide the element), leaving the inline style would
|
|
2266
|
+
// permanently hide authored content.
|
|
2245
2267
|
await page.evaluate(() => {
|
|
2246
2268
|
document.querySelectorAll('[x-markdown]').forEach((el) => {
|
|
2247
2269
|
if (!el.textContent.trim() && !el.innerHTML.trim()) return;
|
|
2248
2270
|
el.removeAttribute('x-markdown');
|
|
2271
|
+
// Clean up opacity-0 + transition inline styles the plugin left behind.
|
|
2272
|
+
const style = el.getAttribute('style') || '';
|
|
2273
|
+
if (style) {
|
|
2274
|
+
const cleaned = style
|
|
2275
|
+
.replace(/\bopacity\s*:\s*0(?:\.\d+)?\s*;?/gi, '')
|
|
2276
|
+
.replace(/\btransition\s*:\s*opacity[^;]*;?/gi, '')
|
|
2277
|
+
.replace(/;\s*;/g, ';')
|
|
2278
|
+
.replace(/^\s*;\s*|\s*;\s*$/g, '')
|
|
2279
|
+
.trim();
|
|
2280
|
+
if (cleaned) el.setAttribute('style', cleaned);
|
|
2281
|
+
else el.removeAttribute('style');
|
|
2282
|
+
}
|
|
2249
2283
|
});
|
|
2250
2284
|
});
|
|
2251
2285
|
|
|
@@ -2404,7 +2438,7 @@ async function runPrerender(config) {
|
|
|
2404
2438
|
// $url, $auth, or iterates over getter names (filtered*, results, searchResults). See docs prerender + local.data.
|
|
2405
2439
|
await page.evaluate(() => {
|
|
2406
2440
|
document.querySelectorAll('template[x-for]').forEach((tpl) => {
|
|
2407
|
-
if (tpl.hasAttribute('data-
|
|
2441
|
+
if (tpl.hasAttribute('data-hydrate') || tpl.closest('[data-hydrate]')) {
|
|
2408
2442
|
tpl.removeAttribute('data-prerender-collapsed');
|
|
2409
2443
|
tpl.removeAttribute('data-prerender-static-generated');
|
|
2410
2444
|
return;
|
|
@@ -2485,7 +2519,7 @@ async function runPrerender(config) {
|
|
|
2485
2519
|
await page.evaluate(() => {
|
|
2486
2520
|
const loopVarRx = /^\s*(?:\(\s*([A-Za-z_$][\w$]*)(?:\s*,\s*([A-Za-z_$][\w$]*))?\s*\)|([A-Za-z_$][\w$]*))\s+in\s+/;
|
|
2487
2521
|
document.querySelectorAll('template[x-for][data-prerender-static-generated="1"]').forEach((tpl) => {
|
|
2488
|
-
if (tpl.hasAttribute('data-
|
|
2522
|
+
if (tpl.hasAttribute('data-hydrate') || tpl.closest('[data-hydrate]')) return;
|
|
2489
2523
|
const xFor = (tpl.getAttribute('x-for') || '').trim();
|
|
2490
2524
|
const m = xFor.match(loopVarRx);
|
|
2491
2525
|
const itemVar = m ? (m[1] || m[3] || '') : '';
|
|
@@ -2499,7 +2533,7 @@ async function runPrerender(config) {
|
|
|
2499
2533
|
// Only process clones that contain data-hydrate descendants
|
|
2500
2534
|
if (
|
|
2501
2535
|
!n.hasAttribute('x-data') &&
|
|
2502
|
-
(n.hasAttribute('data-
|
|
2536
|
+
(n.hasAttribute('data-hydrate') || n.querySelector('[data-hydrate]'))
|
|
2503
2537
|
) {
|
|
2504
2538
|
try {
|
|
2505
2539
|
const A = window.Alpine;
|
|
@@ -2541,7 +2575,7 @@ async function runPrerender(config) {
|
|
|
2541
2575
|
const nodes = [el, ...Array.from(el.querySelectorAll('*'))];
|
|
2542
2576
|
for (const node of nodes) {
|
|
2543
2577
|
// Skip elements inside data-hydrate islands — their bindings must remain live
|
|
2544
|
-
if (node.hasAttribute('data-
|
|
2578
|
+
if (node.hasAttribute('data-hydrate') || node.closest('[data-hydrate]')) continue;
|
|
2545
2579
|
const attrs = node.attributes ? Array.from(node.attributes) : [];
|
|
2546
2580
|
for (const attr of attrs) {
|
|
2547
2581
|
if (!bindingAttrRegex.test(attr.name)) continue;
|
|
@@ -2579,7 +2613,7 @@ async function runPrerender(config) {
|
|
|
2579
2613
|
};
|
|
2580
2614
|
|
|
2581
2615
|
document.querySelectorAll('template[x-for]').forEach((tpl) => {
|
|
2582
|
-
if (tpl.hasAttribute('data-
|
|
2616
|
+
if (tpl.hasAttribute('data-hydrate') || tpl.closest('[data-hydrate]')) return;
|
|
2583
2617
|
const xFor = (tpl.getAttribute('x-for') || '').trim();
|
|
2584
2618
|
const m = xFor.match(loopVarRegex);
|
|
2585
2619
|
const itemVar = m ? (m[1] || m[3] || '') : '';
|
|
@@ -2608,7 +2642,7 @@ async function runPrerender(config) {
|
|
|
2608
2642
|
const runBatch = typeof A?.mutateDom === 'function' ? (fn) => A.mutateDom(fn) : (fn) => fn();
|
|
2609
2643
|
runBatch(() => {
|
|
2610
2644
|
document.querySelectorAll('template[x-for][data-prerender-static-generated="1"]').forEach((tpl) => {
|
|
2611
|
-
if (tpl.hasAttribute('data-
|
|
2645
|
+
if (tpl.hasAttribute('data-hydrate') || tpl.closest('[data-hydrate]')) return;
|
|
2612
2646
|
const parent = tpl.parentNode;
|
|
2613
2647
|
if (!parent) {
|
|
2614
2648
|
tpl.remove();
|