mnfst-render 0.2.4 → 0.2.6

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.
@@ -1385,6 +1385,25 @@ async function runPrerender(config) {
1385
1385
  const forceCollapse = explicit || inferred;
1386
1386
  if (!forceCollapse) {
1387
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
+ const cls = first.getAttribute('class') || '';
1394
+ let next = tpl.nextElementSibling;
1395
+ let generatedCount = 0;
1396
+ while (next) {
1397
+ if (next.tagName !== tag) break;
1398
+ const sameClass = (next.getAttribute('class') || '') === cls;
1399
+ if (!sameClass) break;
1400
+ generatedCount++;
1401
+ next = next.nextElementSibling;
1402
+ }
1403
+ if (generatedCount > 0) {
1404
+ tpl.setAttribute('data-prerender-static-generated', '1');
1405
+ }
1406
+ }
1388
1407
  return; // keep prerendered list for SEO
1389
1408
  }
1390
1409
  tpl.setAttribute('data-prerender-collapsed', '1');
@@ -1405,6 +1424,14 @@ async function runPrerender(config) {
1405
1424
  });
1406
1425
  });
1407
1426
 
1427
+ // Remove static x-for templates once static clones are generated.
1428
+ // This prevents Alpine from rendering duplicate lists at runtime.
1429
+ await page.evaluate(() => {
1430
+ document.querySelectorAll('template[x-for][data-prerender-static-generated="1"]').forEach((tpl) => {
1431
+ tpl.remove();
1432
+ });
1433
+ });
1434
+
1408
1435
  // Remove orphan x-for clones that still reference loop-scope vars (e.g. image/index)
1409
1436
  // outside their template scope. These throw Alpine errors in live static hosting.
1410
1437
  await page.evaluate(() => {
@@ -1467,7 +1494,35 @@ async function runPrerender(config) {
1467
1494
  if (!bindingAttrRegex.test(attr.name)) continue;
1468
1495
  const expr = attr.value || '';
1469
1496
  if (hasVar(expr, itemVar) || hasVar(expr, indexVar)) {
1470
- node.removeAttribute(attr.name);
1497
+ const name = attr.name;
1498
+ // Remove text/html bindings only when static content already exists.
1499
+ if (name === 'x-text' || name === 'x-html') {
1500
+ if ((node.textContent || '').trim() || (node.innerHTML || '').trim()) {
1501
+ node.removeAttribute(name);
1502
+ }
1503
+ continue;
1504
+ }
1505
+
1506
+ // Remove x-show/x-if if they reference loop vars; cloned node is now static.
1507
+ if (name === 'x-show' || name === 'x-if') {
1508
+ node.removeAttribute(name);
1509
+ continue;
1510
+ }
1511
+
1512
+ // For :attr / x-bind:attr, only remove binding if a concrete attr is present.
1513
+ let boundAttr = '';
1514
+ if (name.startsWith(':')) boundAttr = name.slice(1);
1515
+ else if (name.startsWith('x-bind:')) boundAttr = name.slice('x-bind:'.length);
1516
+ if (boundAttr) {
1517
+ const concrete = node.getAttribute(boundAttr);
1518
+ if (concrete != null && String(concrete).trim() !== '') {
1519
+ node.removeAttribute(name);
1520
+ }
1521
+ continue;
1522
+ }
1523
+
1524
+ // Event/other loop-scoped bindings are unsafe on static clones.
1525
+ node.removeAttribute(name);
1471
1526
  }
1472
1527
  }
1473
1528
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mnfst-render",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "Render Manifest sites to static HTML for SEO",
5
5
  "type": "module",
6
6
  "bin": {