mnfst-render 0.3.0 → 0.3.2
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 +66 -7
- package/package.json +1 -1
package/manifest.render.mjs
CHANGED
|
@@ -895,25 +895,37 @@ function stripPrerenderedXDataDirectives(html) {
|
|
|
895
895
|
|
|
896
896
|
// --- Don't bake Alpine-only state into the snapshot; only $x-driven content should be prerendered.
|
|
897
897
|
// For any :attr or x-bind:attr whose expression does NOT contain $x, remove the literal attr from the tag
|
|
898
|
-
// so Alpine re-evaluates on load. Bindings that use $x are left as-is (content stays for SEO)
|
|
898
|
+
// so Alpine re-evaluates on load. Bindings that use $x are left as-is (content stays for SEO), except
|
|
899
|
+
// :style / x-bind:style with $x: those must be removed when a baked inline style exists, or Alpine will
|
|
900
|
+
// overwrite prerendered values (e.g. mask-image) on hydrate when $x is briefly empty in production.
|
|
899
901
|
// Use (?<!:) so we only strip literal attr=, not :attr= (e.g. class= not :class=).
|
|
900
902
|
// Never touch <script> tags (loader + injected plugins must be preserved; static HTML still runs them).
|
|
901
903
|
function stripPrerenderDynamicBindings(html) {
|
|
902
904
|
return html.replace(/<(\w+)([^>]*)>/g, (match, tagName, attrsStr) => {
|
|
903
905
|
if (tagName.toLowerCase() === 'script') return match;
|
|
904
906
|
const isAnchor = tagName.toLowerCase() === 'a';
|
|
907
|
+
let workAttrs = attrsStr;
|
|
908
|
+
workAttrs = workAttrs.replace(/\s+:style=(?:"([^"]*)"|'([^']*)')/gi, (sub, d, s) => {
|
|
909
|
+
const val = (d !== undefined ? d : s) || '';
|
|
910
|
+
return val.indexOf('$x') !== -1 ? '' : sub;
|
|
911
|
+
});
|
|
912
|
+
workAttrs = workAttrs.replace(/\s+x-bind:style=(?:"([^"]*)"|'([^']*)')/gi, (sub, d, s) => {
|
|
913
|
+
const val = (d !== undefined ? d : s) || '';
|
|
914
|
+
return val.indexOf('$x') !== -1 ? '' : sub;
|
|
915
|
+
});
|
|
916
|
+
|
|
905
917
|
const toStrip = new Set();
|
|
906
918
|
const bindingRegex = /(?:^|\s)(?::|x-bind:)(\w+)=(?:"([^"]*)"|'([^']*)')/g;
|
|
907
919
|
let m;
|
|
908
|
-
while ((m = bindingRegex.exec(
|
|
920
|
+
while ((m = bindingRegex.exec(workAttrs)) !== null) {
|
|
909
921
|
const attrName = (m[1] || '').toLowerCase();
|
|
910
922
|
// Keep href on anchors so prerendered static navigation stays valid.
|
|
911
923
|
if (attrName === 'class' || attrName === 'style' || (isAnchor && attrName === 'href')) continue;
|
|
912
924
|
const val = (m[2] !== undefined ? m[2] : m[3]) || '';
|
|
913
925
|
if (val.indexOf('$x') === -1) toStrip.add(attrName);
|
|
914
926
|
}
|
|
915
|
-
if (toStrip.size === 0) return match;
|
|
916
|
-
let newAttrs =
|
|
927
|
+
if (toStrip.size === 0 && workAttrs === attrsStr) return match;
|
|
928
|
+
let newAttrs = workAttrs;
|
|
917
929
|
for (const attr of toStrip) {
|
|
918
930
|
const esc = attr.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
919
931
|
newAttrs = newAttrs.replace(new RegExp(`\\s*(?<!:)${esc}="[^"]*"`, 'gi'), '');
|
|
@@ -1664,6 +1676,8 @@ async function runPrerender(config) {
|
|
|
1664
1676
|
return {
|
|
1665
1677
|
templateCount: templates.length,
|
|
1666
1678
|
nonCollapsedTemplateCount: templates.filter((t) => t.getAttribute('data-prerender-collapsed') !== '1').length,
|
|
1679
|
+
hint:
|
|
1680
|
+
'entries.staticGenerated is read before the x-for mark pass and is always false; use stage post-xfor-mark for data-prerender-static-generated.',
|
|
1667
1681
|
entries,
|
|
1668
1682
|
listDiagnostics,
|
|
1669
1683
|
};
|
|
@@ -1758,6 +1772,26 @@ async function runPrerender(config) {
|
|
|
1758
1772
|
});
|
|
1759
1773
|
});
|
|
1760
1774
|
|
|
1775
|
+
if (config.debugPrerender) {
|
|
1776
|
+
const afterMark = await page.evaluate(() => {
|
|
1777
|
+
const rows = [];
|
|
1778
|
+
for (const tpl of document.querySelectorAll('template[x-for]')) {
|
|
1779
|
+
rows.push({
|
|
1780
|
+
xFor: (tpl.getAttribute('x-for') || '').slice(0, 140),
|
|
1781
|
+
collapsed: tpl.getAttribute('data-prerender-collapsed') === '1',
|
|
1782
|
+
staticGenerated: tpl.getAttribute('data-prerender-static-generated') === '1',
|
|
1783
|
+
});
|
|
1784
|
+
}
|
|
1785
|
+
return {
|
|
1786
|
+
templateCount: rows.length,
|
|
1787
|
+
staticMarkedCount: rows.filter((r) => r.staticGenerated).length,
|
|
1788
|
+
collapsedCount: rows.filter((r) => r.collapsed).length,
|
|
1789
|
+
entries: rows.slice(0, 60),
|
|
1790
|
+
};
|
|
1791
|
+
}).catch(() => null);
|
|
1792
|
+
pushDebug({ path: displayPath, stage: 'post-xfor-mark', metrics: afterMark });
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1761
1795
|
// Strip loop-scope bindings from x-for clones while <template> nodes still exist.
|
|
1762
1796
|
// (If we remove static templates first, querySelectorAll('template[x-for]') misses them and clones
|
|
1763
1797
|
// keep x-text/x-bind referencing card/item — Alpine then mutates or errors on the static HTML.)
|
|
@@ -1821,10 +1855,35 @@ async function runPrerender(config) {
|
|
|
1821
1855
|
});
|
|
1822
1856
|
|
|
1823
1857
|
// Remove static x-for templates once static clones are generated.
|
|
1824
|
-
//
|
|
1858
|
+
// Alpine registers a cleanup on <template x-for> that removes every node in _x_lookup when the
|
|
1859
|
+
// template is detached — so tpl.remove() alone deletes all sibling clones (empty grids in output).
|
|
1860
|
+
// Replace each clone with a deep cloneNode first so teardown targets detached nodes; copies stay in DOM.
|
|
1825
1861
|
await page.evaluate(() => {
|
|
1826
|
-
|
|
1827
|
-
|
|
1862
|
+
const A = window.Alpine;
|
|
1863
|
+
const runBatch = typeof A?.mutateDom === 'function' ? (fn) => A.mutateDom(fn) : (fn) => fn();
|
|
1864
|
+
runBatch(() => {
|
|
1865
|
+
document.querySelectorAll('template[x-for][data-prerender-static-generated="1"]').forEach((tpl) => {
|
|
1866
|
+
const parent = tpl.parentNode;
|
|
1867
|
+
if (!parent) {
|
|
1868
|
+
tpl.remove();
|
|
1869
|
+
return;
|
|
1870
|
+
}
|
|
1871
|
+
const first = tpl.content?.firstElementChild;
|
|
1872
|
+
if (!first) {
|
|
1873
|
+
tpl.remove();
|
|
1874
|
+
return;
|
|
1875
|
+
}
|
|
1876
|
+
const tag = first.tagName;
|
|
1877
|
+
const cls = first.getAttribute('class') || '';
|
|
1878
|
+
let n = tpl.nextElementSibling;
|
|
1879
|
+
while (n && n.tagName === tag) {
|
|
1880
|
+
if ((n.getAttribute('class') || '') !== cls) break;
|
|
1881
|
+
const next = n.nextElementSibling;
|
|
1882
|
+
n.replaceWith(n.cloneNode(true));
|
|
1883
|
+
n = next;
|
|
1884
|
+
}
|
|
1885
|
+
tpl.remove();
|
|
1886
|
+
});
|
|
1828
1887
|
});
|
|
1829
1888
|
});
|
|
1830
1889
|
|