mnfst-render 0.5.12 → 0.5.15
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 -3
- package/package.json +1 -1
package/manifest.render.mjs
CHANGED
|
@@ -619,14 +619,20 @@ function stripDevOnlyContent(html) {
|
|
|
619
619
|
// leaving it in place would cause Alpine to execute synchronously during HTML
|
|
620
620
|
// parse, before plugins have a chance to register their directives.
|
|
621
621
|
function stripInjectedPluginScripts(html) {
|
|
622
|
+
// Safety-net regex strip for any Manifest plugin scripts that survived the
|
|
623
|
+
// DOM-level removal in the Puppeteer evaluate phase. Uses a broad pattern
|
|
624
|
+
// that matches ANY `manifest.*.js` or `manifest.*.min.js` script — no need
|
|
625
|
+
// to enumerate individual plugin names. New plugins are covered automatically.
|
|
622
626
|
const pluginPattern =
|
|
623
|
-
/<script[^>]*\ssrc=["'][^"']*manifest\.
|
|
627
|
+
/<script[^>]*\ssrc=["'][^"']*manifest\.[a-z][\w.-]*\.(?:min\.)?js["'][^>]*>\s*<\/script>/gi;
|
|
624
628
|
let out = html.replace(pluginPattern, '');
|
|
629
|
+
// Alpine (re-injected by the loader at runtime after plugin registration)
|
|
625
630
|
const alpinePattern =
|
|
626
631
|
/<script[^>]*\ssrc=["'][^"']*\/alpinejs@[^"']*["'][^>]*>\s*<\/script>/gi;
|
|
627
632
|
out = out.replace(alpinePattern, '');
|
|
633
|
+
// Runtime library scripts loaded by plugins (yaml parser, markdown, etc.)
|
|
628
634
|
const runtimePattern =
|
|
629
|
-
/<script[^>]*\ssrc=["'][^"']*(?:papaparse
|
|
635
|
+
/<script[^>]*\ssrc=["'][^"']*(?:papaparse[^"']*\.min\.js|marked[^"']*\.min\.js|highlight[^"']*\.min\.js|js-yaml[^"']*\.min\.js)[^"']*["'][^>]*>\s*<\/script>/gi;
|
|
630
636
|
out = out.replace(runtimePattern, '');
|
|
631
637
|
return out;
|
|
632
638
|
}
|
|
@@ -1993,6 +1999,22 @@ async function runPrerender(config) {
|
|
|
1993
1999
|
// (`hydratePrerenderedPage` in manifest.js) reads the contract and
|
|
1994
2000
|
// restores source attributes before Alpine starts.
|
|
1995
2001
|
await page.evaluateOnNewDocument(() => {
|
|
2002
|
+
// Snapshot the script srcs that exist in the author's original HTML
|
|
2003
|
+
// BEFORE any loader/plugin injects additional scripts. Used later to
|
|
2004
|
+
// strip runtime-injected scripts from the serialized output while
|
|
2005
|
+
// keeping author-intentional ones (analytics, third-party widgets, etc.).
|
|
2006
|
+
window.__manifestOriginalScriptSrcs = new Set();
|
|
2007
|
+
const _snapScripts = () => {
|
|
2008
|
+
document.querySelectorAll('script[src]').forEach(s => {
|
|
2009
|
+
window.__manifestOriginalScriptSrcs.add(s.getAttribute('src'));
|
|
2010
|
+
});
|
|
2011
|
+
};
|
|
2012
|
+
if (document.readyState === 'loading') {
|
|
2013
|
+
document.addEventListener('DOMContentLoaded', _snapScripts, { once: true });
|
|
2014
|
+
}
|
|
2015
|
+
// Also snap immediately for any scripts already parsed
|
|
2016
|
+
_snapScripts();
|
|
2017
|
+
|
|
1996
2018
|
// element -> { attrName: originalValue (null if attribute was absent) }
|
|
1997
2019
|
// Keyed by reference so detached elements drop out naturally.
|
|
1998
2020
|
const sourceAttrs = new Map();
|
|
@@ -2461,7 +2483,7 @@ async function runPrerender(config) {
|
|
|
2461
2483
|
// The baked value IS the correct initial render.
|
|
2462
2484
|
if (src === null) {
|
|
2463
2485
|
const hasBinding =
|
|
2464
|
-
(name === 'style' && (':style' in source || 'x-bind:style' in source)) ||
|
|
2486
|
+
(name === 'style' && (':style' in source || 'x-bind:style' in source || 'x-show' in source)) ||
|
|
2465
2487
|
(name === 'class' && (':class' in source || 'x-bind:class' in source));
|
|
2466
2488
|
if (hasBinding) continue;
|
|
2467
2489
|
}
|
|
@@ -2722,6 +2744,28 @@ async function runPrerender(config) {
|
|
|
2722
2744
|
runBatch(() => {
|
|
2723
2745
|
document.querySelectorAll('template[x-for][data-prerender-static-generated="1"]').forEach((tpl) => {
|
|
2724
2746
|
if (tpl.hasAttribute('data-hydrate') || tpl.closest('[data-hydrate]')) return;
|
|
2747
|
+
// $x-driven x-for: keep the template so Alpine can re-render the
|
|
2748
|
+
// list at runtime (locale switching, filtering, etc.), but remove
|
|
2749
|
+
// the static clones — Alpine creates fresh clones on init and does
|
|
2750
|
+
// NOT adopt existing DOM nodes, so leaving them produces duplicates.
|
|
2751
|
+
// Individual article/pricing pages still have full baked content
|
|
2752
|
+
// (via x-text/x-html); the x-for list is only the index/grid view.
|
|
2753
|
+
const xFor = (tpl.getAttribute('x-for') || '');
|
|
2754
|
+
if (xFor.includes('$x')) {
|
|
2755
|
+
const first = tpl.content?.firstElementChild;
|
|
2756
|
+
if (first) {
|
|
2757
|
+
const tag = first.tagName;
|
|
2758
|
+
const cls = first.getAttribute('class') || '';
|
|
2759
|
+
let n = tpl.nextElementSibling;
|
|
2760
|
+
while (n && n.tagName === tag && (n.getAttribute('class') || '') === cls) {
|
|
2761
|
+
const next = n.nextElementSibling;
|
|
2762
|
+
n.remove();
|
|
2763
|
+
n = next;
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
tpl.removeAttribute('data-prerender-static-generated');
|
|
2767
|
+
return;
|
|
2768
|
+
}
|
|
2725
2769
|
const parent = tpl.parentNode;
|
|
2726
2770
|
if (!parent) {
|
|
2727
2771
|
tpl.remove();
|
|
@@ -2813,6 +2857,20 @@ async function runPrerender(config) {
|
|
|
2813
2857
|
toRemove.forEach((el) => { if (document.contains(el)) el.remove(); });
|
|
2814
2858
|
});
|
|
2815
2859
|
|
|
2860
|
+
// Remove scripts that were injected at runtime (by the loader, plugins,
|
|
2861
|
+
// or third-party libraries) but were NOT in the author's original HTML.
|
|
2862
|
+
// This is done in the browser context (before serialization) so the
|
|
2863
|
+
// removal is permanent in the captured outerHTML. The approach is
|
|
2864
|
+
// future-proof: new plugins and arbitrary third-party scripts are handled
|
|
2865
|
+
// automatically without updating a hardcoded allowlist.
|
|
2866
|
+
await page.evaluate(() => {
|
|
2867
|
+
const originals = window.__manifestOriginalScriptSrcs || new Set();
|
|
2868
|
+
document.querySelectorAll('script[src]').forEach(s => {
|
|
2869
|
+
const src = s.getAttribute('src');
|
|
2870
|
+
if (src && !originals.has(src)) s.remove();
|
|
2871
|
+
});
|
|
2872
|
+
});
|
|
2873
|
+
|
|
2816
2874
|
let html = await page.evaluate(() => document.documentElement.outerHTML);
|
|
2817
2875
|
// Inject the hydration contract blob into the raw HTML *before* caching
|
|
2818
2876
|
// it for locale variant generation, so every locale variant inherits the
|