mnfst-render 0.2.3 → 0.2.5
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 +73 -6
- package/package.json +1 -1
package/manifest.render.mjs
CHANGED
|
@@ -482,11 +482,15 @@ function extractUtilityStyleBlocks(html) {
|
|
|
482
482
|
return { html: out, blocks };
|
|
483
483
|
}
|
|
484
484
|
|
|
485
|
-
function
|
|
485
|
+
function injectBeforeHeadClose(html, snippet) {
|
|
486
486
|
if (!snippet) return html;
|
|
487
487
|
const hrefMatch = snippet.match(/href=["']([^"']+)["']/);
|
|
488
|
-
|
|
489
|
-
|
|
488
|
+
const href = hrefMatch ? hrefMatch[1] : null;
|
|
489
|
+
let out = html;
|
|
490
|
+
if (href) {
|
|
491
|
+
out = out.replace(new RegExp(`\\s*<link[^>]*href=["']${href.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}["'][^>]*>\\s*`, 'gi'), '\n');
|
|
492
|
+
}
|
|
493
|
+
return out.replace(/<\/head>/i, `${snippet}\n</head>`);
|
|
490
494
|
}
|
|
491
495
|
|
|
492
496
|
function indexHtmlUsesTailwind(rootDir) {
|
|
@@ -663,11 +667,10 @@ function postProcessInjectStylesheetLink(outputDir, filename) {
|
|
|
663
667
|
const files = walkHtmlFiles(outputDir);
|
|
664
668
|
for (const file of files) {
|
|
665
669
|
let html = readFileSync(file, 'utf8');
|
|
666
|
-
if (html.includes(filename)) continue;
|
|
667
670
|
const depth = depthFromOutputRoot(outputDir, file);
|
|
668
671
|
const prefix = depth ? '../'.repeat(depth) : '';
|
|
669
672
|
const tag = `<link rel="stylesheet" href="${prefix}${filename}">`;
|
|
670
|
-
html =
|
|
673
|
+
html = injectBeforeHeadClose(html, tag);
|
|
671
674
|
writeFileSync(file, html, 'utf8');
|
|
672
675
|
}
|
|
673
676
|
}
|
|
@@ -1382,6 +1385,22 @@ async function runPrerender(config) {
|
|
|
1382
1385
|
const forceCollapse = explicit || inferred;
|
|
1383
1386
|
if (!forceCollapse) {
|
|
1384
1387
|
tpl.removeAttribute('data-prerender-collapsed');
|
|
1388
|
+
tpl.removeAttribute('data-prerender-static-generated');
|
|
1389
|
+
// Static mode: if prerender produced concrete siblings, mark template for removal later.
|
|
1390
|
+
const first = tpl.content?.firstElementChild;
|
|
1391
|
+
if (first) {
|
|
1392
|
+
const tag = first.tagName;
|
|
1393
|
+
let next = tpl.nextElementSibling;
|
|
1394
|
+
let generatedCount = 0;
|
|
1395
|
+
while (next) {
|
|
1396
|
+
if (next.tagName !== tag) break;
|
|
1397
|
+
generatedCount++;
|
|
1398
|
+
next = next.nextElementSibling;
|
|
1399
|
+
}
|
|
1400
|
+
if (generatedCount > 0) {
|
|
1401
|
+
tpl.setAttribute('data-prerender-static-generated', '1');
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1385
1404
|
return; // keep prerendered list for SEO
|
|
1386
1405
|
}
|
|
1387
1406
|
tpl.setAttribute('data-prerender-collapsed', '1');
|
|
@@ -1402,6 +1421,14 @@ async function runPrerender(config) {
|
|
|
1402
1421
|
});
|
|
1403
1422
|
});
|
|
1404
1423
|
|
|
1424
|
+
// Remove static x-for templates once static clones are generated.
|
|
1425
|
+
// This prevents Alpine from rendering duplicate lists at runtime.
|
|
1426
|
+
await page.evaluate(() => {
|
|
1427
|
+
document.querySelectorAll('template[x-for][data-prerender-static-generated="1"]').forEach((tpl) => {
|
|
1428
|
+
tpl.remove();
|
|
1429
|
+
});
|
|
1430
|
+
});
|
|
1431
|
+
|
|
1405
1432
|
// Remove orphan x-for clones that still reference loop-scope vars (e.g. image/index)
|
|
1406
1433
|
// outside their template scope. These throw Alpine errors in live static hosting.
|
|
1407
1434
|
await page.evaluate(() => {
|
|
@@ -1450,6 +1477,46 @@ async function runPrerender(config) {
|
|
|
1450
1477
|
});
|
|
1451
1478
|
});
|
|
1452
1479
|
|
|
1480
|
+
// For static clones kept from x-for templates, remove loop-scope bindings (card/title/etc)
|
|
1481
|
+
// so Alpine doesn't re-evaluate them outside template scope in production.
|
|
1482
|
+
await page.evaluate(() => {
|
|
1483
|
+
const loopVarRegex = /^\s*(?:\(\s*([A-Za-z_$][\w$]*)(?:\s*,\s*([A-Za-z_$][\w$]*))?\s*\)|([A-Za-z_$][\w$]*))\s+in\s+/;
|
|
1484
|
+
const bindingAttrRegex = /^(?:x-bind:|:|x-text|x-html|x-show|x-if|x-model|x-effect|x-on:|@)/;
|
|
1485
|
+
const hasVar = (expr, varName) => varName && new RegExp(`\\b${varName}\\b`).test(expr || '');
|
|
1486
|
+
const stripLoopBindings = (el, itemVar, indexVar) => {
|
|
1487
|
+
const nodes = [el, ...Array.from(el.querySelectorAll('*'))];
|
|
1488
|
+
for (const node of nodes) {
|
|
1489
|
+
const attrs = node.attributes ? Array.from(node.attributes) : [];
|
|
1490
|
+
for (const attr of attrs) {
|
|
1491
|
+
if (!bindingAttrRegex.test(attr.name)) continue;
|
|
1492
|
+
const expr = attr.value || '';
|
|
1493
|
+
if (hasVar(expr, itemVar) || hasVar(expr, indexVar)) {
|
|
1494
|
+
node.removeAttribute(attr.name);
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
};
|
|
1499
|
+
|
|
1500
|
+
document.querySelectorAll('template[x-for]').forEach((tpl) => {
|
|
1501
|
+
const xFor = (tpl.getAttribute('x-for') || '').trim();
|
|
1502
|
+
const m = xFor.match(loopVarRegex);
|
|
1503
|
+
const itemVar = m ? (m[1] || m[3] || '') : '';
|
|
1504
|
+
const indexVar = m ? (m[2] || '') : '';
|
|
1505
|
+
if (!itemVar && !indexVar) return;
|
|
1506
|
+
|
|
1507
|
+
const first = tpl.content?.firstElementChild;
|
|
1508
|
+
if (!first) return;
|
|
1509
|
+
const tag = first.tagName;
|
|
1510
|
+
|
|
1511
|
+
let next = tpl.nextElementSibling;
|
|
1512
|
+
while (next) {
|
|
1513
|
+
if (next.tagName !== tag) break;
|
|
1514
|
+
stripLoopBindings(next, itemVar, indexVar);
|
|
1515
|
+
next = next.nextElementSibling;
|
|
1516
|
+
}
|
|
1517
|
+
});
|
|
1518
|
+
});
|
|
1519
|
+
|
|
1453
1520
|
// Remove elements marked data-dynamic (so they are not in static HTML; client will render them).
|
|
1454
1521
|
// Skip <template> since we only collapse those above; other elements and their subtree are removed.
|
|
1455
1522
|
await page.evaluate(() => {
|
|
@@ -1481,7 +1548,7 @@ async function runPrerender(config) {
|
|
|
1481
1548
|
for (const b of extracted.blocks) utilityBlocks.push(b);
|
|
1482
1549
|
}
|
|
1483
1550
|
if (tailwindBuilt) {
|
|
1484
|
-
html =
|
|
1551
|
+
html = injectBeforeHeadClose(html, '<link rel="stylesheet" href="/prerender.tailwind.css">');
|
|
1485
1552
|
}
|
|
1486
1553
|
html = stripDuplicatedLoopDirectives(html);
|
|
1487
1554
|
html = stripPrerenderedXDataDirectives(html);
|