mnfst-render 0.5.14 → 0.5.16
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 +48 -27
- package/package.json +1 -1
package/manifest.render.mjs
CHANGED
|
@@ -609,26 +609,46 @@ function stripDevOnlyContent(html) {
|
|
|
609
609
|
return out;
|
|
610
610
|
}
|
|
611
611
|
|
|
612
|
-
// --- Strip
|
|
613
|
-
//
|
|
614
|
-
//
|
|
615
|
-
//
|
|
616
|
-
//
|
|
617
|
-
//
|
|
618
|
-
//
|
|
619
|
-
//
|
|
620
|
-
//
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
612
|
+
// --- Strip scripts injected at runtime during prerender ---
|
|
613
|
+
// The Manifest loader, Alpine, plugins, and third-party libraries inject
|
|
614
|
+
// <script> tags into the DOM during the Puppeteer render. These must be
|
|
615
|
+
// removed from the serialized HTML so the loader can re-inject them fresh
|
|
616
|
+
// at runtime (otherwise the addScript function finds an existing tag, waits
|
|
617
|
+
// for a load event that already fired, and hangs forever).
|
|
618
|
+
//
|
|
619
|
+
// Approach: diff the prerendered HTML against the ORIGINAL index.html from
|
|
620
|
+
// disk. Any <script src="..."> whose src does NOT appear in the original
|
|
621
|
+
// file was injected at runtime and must be stripped. Inline scripts without
|
|
622
|
+
// src are left alone (author-written analytics snippets, etc.).
|
|
623
|
+
//
|
|
624
|
+
// This is future-proof — new framework plugins, Alpine version bumps, and
|
|
625
|
+
// arbitrary third-party scripts (webchat, analytics) are all handled
|
|
626
|
+
// automatically without maintaining a hardcoded allowlist.
|
|
627
|
+
let _originalScriptSrcs = null;
|
|
628
|
+
|
|
629
|
+
function buildOriginalScriptSrcSet(rootDir) {
|
|
630
|
+
if (_originalScriptSrcs) return _originalScriptSrcs;
|
|
631
|
+
_originalScriptSrcs = new Set();
|
|
632
|
+
const indexPath = join(rootDir, 'index.html');
|
|
633
|
+
if (!existsSync(indexPath)) return _originalScriptSrcs;
|
|
634
|
+
const html = readFileSync(indexPath, 'utf8');
|
|
635
|
+
const srcPattern = /<script[^>]*\ssrc=["']([^"']+)["'][^>]*>/gi;
|
|
636
|
+
let m;
|
|
637
|
+
while ((m = srcPattern.exec(html)) !== null) {
|
|
638
|
+
_originalScriptSrcs.add(m[1]);
|
|
639
|
+
}
|
|
640
|
+
return _originalScriptSrcs;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
function stripInjectedPluginScripts(html, rootDir) {
|
|
644
|
+
const originals = buildOriginalScriptSrcSet(rootDir);
|
|
645
|
+
// Remove every <script src="...">...</script> whose src is NOT in the
|
|
646
|
+
// original index.html. This catches all loader-injected plugins, Alpine,
|
|
647
|
+
// runtime libraries (js-yaml, marked, highlight, etc.), and any third-party
|
|
648
|
+
// scripts added dynamically during the render.
|
|
649
|
+
return html.replace(/<script[^>]*\ssrc=["']([^"']+)["'][^>]*>\s*<\/script>/gi,
|
|
650
|
+
(full, src) => originals.has(src) ? full : ''
|
|
651
|
+
);
|
|
632
652
|
}
|
|
633
653
|
|
|
634
654
|
function stripRuntimeTailwindArtifacts(html) {
|
|
@@ -1385,7 +1405,7 @@ function generateLocaleVariantHtml({
|
|
|
1385
1405
|
|
|
1386
1406
|
// Standard Node.js post-processing (same sequence as processPath)
|
|
1387
1407
|
html = stripDevOnlyContent(html);
|
|
1388
|
-
html = stripInjectedPluginScripts(html);
|
|
1408
|
+
html = stripInjectedPluginScripts(html, config.root);
|
|
1389
1409
|
if (tailwindBuilt) html = stripRuntimeTailwindArtifacts(html);
|
|
1390
1410
|
|
|
1391
1411
|
const pageUtilityBlocks = [];
|
|
@@ -2722,13 +2742,14 @@ async function runPrerender(config) {
|
|
|
2722
2742
|
runBatch(() => {
|
|
2723
2743
|
document.querySelectorAll('template[x-for][data-prerender-static-generated="1"]').forEach((tpl) => {
|
|
2724
2744
|
if (tpl.hasAttribute('data-hydrate') || tpl.closest('[data-hydrate]')) return;
|
|
2725
|
-
//
|
|
2726
|
-
//
|
|
2727
|
-
//
|
|
2728
|
-
//
|
|
2745
|
+
// $x-driven x-for: keep the template so Alpine can re-render the
|
|
2746
|
+
// list at runtime (locale switching, filtering, etc.), but remove
|
|
2747
|
+
// the static clones — Alpine creates fresh clones on init and does
|
|
2748
|
+
// NOT adopt existing DOM nodes, so leaving them produces duplicates.
|
|
2749
|
+
// Individual article/pricing pages still have full baked content
|
|
2750
|
+
// (via x-text/x-html); the x-for list is only the index/grid view.
|
|
2729
2751
|
const xFor = (tpl.getAttribute('x-for') || '');
|
|
2730
2752
|
if (xFor.includes('$x')) {
|
|
2731
|
-
// Remove static clones (siblings with same tag+class as template's first child)
|
|
2732
2753
|
const first = tpl.content?.firstElementChild;
|
|
2733
2754
|
if (first) {
|
|
2734
2755
|
const tag = first.tagName;
|
|
@@ -2860,7 +2881,7 @@ async function runPrerender(config) {
|
|
|
2860
2881
|
pushDebug({ path: displayPath, stage: 'pre-serialize', metrics: post });
|
|
2861
2882
|
}
|
|
2862
2883
|
html = stripDevOnlyContent(html);
|
|
2863
|
-
html = stripInjectedPluginScripts(html);
|
|
2884
|
+
html = stripInjectedPluginScripts(html, config.root);
|
|
2864
2885
|
if (tailwindBuilt) {
|
|
2865
2886
|
html = stripRuntimeTailwindArtifacts(html);
|
|
2866
2887
|
}
|