mnfst-render 0.5.15 → 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 +42 -58
- package/package.json +1 -1
package/manifest.render.mjs
CHANGED
|
@@ -609,32 +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
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
const
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
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
|
+
);
|
|
638
652
|
}
|
|
639
653
|
|
|
640
654
|
function stripRuntimeTailwindArtifacts(html) {
|
|
@@ -1391,7 +1405,7 @@ function generateLocaleVariantHtml({
|
|
|
1391
1405
|
|
|
1392
1406
|
// Standard Node.js post-processing (same sequence as processPath)
|
|
1393
1407
|
html = stripDevOnlyContent(html);
|
|
1394
|
-
html = stripInjectedPluginScripts(html);
|
|
1408
|
+
html = stripInjectedPluginScripts(html, config.root);
|
|
1395
1409
|
if (tailwindBuilt) html = stripRuntimeTailwindArtifacts(html);
|
|
1396
1410
|
|
|
1397
1411
|
const pageUtilityBlocks = [];
|
|
@@ -1999,22 +2013,6 @@ async function runPrerender(config) {
|
|
|
1999
2013
|
// (`hydratePrerenderedPage` in manifest.js) reads the contract and
|
|
2000
2014
|
// restores source attributes before Alpine starts.
|
|
2001
2015
|
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
|
-
|
|
2018
2016
|
// element -> { attrName: originalValue (null if attribute was absent) }
|
|
2019
2017
|
// Keyed by reference so detached elements drop out naturally.
|
|
2020
2018
|
const sourceAttrs = new Map();
|
|
@@ -2857,20 +2855,6 @@ async function runPrerender(config) {
|
|
|
2857
2855
|
toRemove.forEach((el) => { if (document.contains(el)) el.remove(); });
|
|
2858
2856
|
});
|
|
2859
2857
|
|
|
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
|
-
|
|
2874
2858
|
let html = await page.evaluate(() => document.documentElement.outerHTML);
|
|
2875
2859
|
// Inject the hydration contract blob into the raw HTML *before* caching
|
|
2876
2860
|
// it for locale variant generation, so every locale variant inherits the
|
|
@@ -2897,7 +2881,7 @@ async function runPrerender(config) {
|
|
|
2897
2881
|
pushDebug({ path: displayPath, stage: 'pre-serialize', metrics: post });
|
|
2898
2882
|
}
|
|
2899
2883
|
html = stripDevOnlyContent(html);
|
|
2900
|
-
html = stripInjectedPluginScripts(html);
|
|
2884
|
+
html = stripInjectedPluginScripts(html, config.root);
|
|
2901
2885
|
if (tailwindBuilt) {
|
|
2902
2886
|
html = stripRuntimeTailwindArtifacts(html);
|
|
2903
2887
|
}
|