mnfst-render 0.3.3 → 0.3.5

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.
@@ -904,6 +904,7 @@ function stripPrerenderDynamicBindings(html) {
904
904
  return html.replace(/<(\w+)([^>]*)>/g, (match, tagName, attrsStr) => {
905
905
  if (tagName.toLowerCase() === 'script') return match;
906
906
  const isAnchor = tagName.toLowerCase() === 'a';
907
+ const isImg = tagName.toLowerCase() === 'img';
907
908
  let workAttrs = attrsStr;
908
909
  workAttrs = workAttrs.replace(/\s+:style=(?:"([^"]*)"|'([^']*)')/gi, (sub, d, s) => {
909
910
  const val = (d !== undefined ? d : s) || '';
@@ -919,8 +920,9 @@ function stripPrerenderDynamicBindings(html) {
919
920
  let m;
920
921
  while ((m = bindingRegex.exec(workAttrs)) !== null) {
921
922
  const attrName = (m[1] || '').toLowerCase();
922
- // Keep href on anchors so prerendered static navigation stays valid.
923
- if (attrName === 'class' || attrName === 'style' || (isAnchor && attrName === 'href')) continue;
923
+ // Keep href on anchors and src on images: :href / :src often reference x-for iterators (e.g.
924
+ // article?.banner). Stripping the baked literal leaves only :src/:href and breaks static HTML.
925
+ if (attrName === 'class' || attrName === 'style' || (isAnchor && attrName === 'href') || (isImg && attrName === 'src')) continue;
924
926
  const val = (m[2] !== undefined ? m[2] : m[3]) || '';
925
927
  if (val.indexOf('$x') === -1) toStrip.add(attrName);
926
928
  }
@@ -937,6 +939,32 @@ function stripPrerenderDynamicBindings(html) {
937
939
  });
938
940
  }
939
941
 
942
+ // Drop :src / x-bind:src when img already has a baked src= (x-for / iterator expressions break hydrate).
943
+ function stripRedundantImgSrcBindings(html) {
944
+ return html.replace(/<img\b([^>]*)>/gi, (full, attrs) => {
945
+ const srcM = attrs.match(/\ssrc=(["'])([\s\S]*?)\1/i);
946
+ if (!srcM || !String(srcM[2] || '').trim()) return full;
947
+ if (!/\s:src\s*=|\sx-bind:src\s*=/i.test(attrs)) return full;
948
+ let next = attrs.replace(/\s:src=(?:"[^"]*"|'[^']*')/gi, '');
949
+ next = next.replace(/\sx-bind:src=(?:"[^"]*"|'[^']*')/gi, '');
950
+ return `<img${next}>`;
951
+ });
952
+ }
953
+
954
+ /**
955
+ * Manifest runtime replaces <x-*> component placeholders by fetching source .html, which wipes
956
+ * prerender-baked markup (stripped :style, expanded lists, etc.). Tag opens with data-pre-rendered
957
+ * are skipped by manifest.components.processor — required for static prerender output to hydrate correctly.
958
+ */
959
+ function markPrerenderedManifestComponents(html) {
960
+ return html.replace(/<(x-[a-z][\w-]*)([^>]*)>/gi, (full, tag, attrs) => {
961
+ const a = attrs || '';
962
+ if (/\bdata-pre-rendered\s*=/i.test(a) || /\bdata-processed\s*=/i.test(a)) return full;
963
+ const spacer = /\S/.test(a) ? ' ' : '';
964
+ return `<${tag}${a}${spacer}data-pre-rendered="1">`;
965
+ });
966
+ }
967
+
940
968
  // Remove empty inline mask-image styles emitted before data resolves
941
969
  // (e.g. style="mask-image: url()"), while keeping any :style/x-bind:style bindings.
942
970
  function stripEmptyInlineMaskStyles(html) {
@@ -1993,6 +2021,7 @@ async function runPrerender(config) {
1993
2021
  const xData = { manifest, content };
1994
2022
  html = resolveHeadXBindings(html, xData);
1995
2023
  html = stripPrerenderDynamicBindings(html);
2024
+ html = stripRedundantImgSrcBindings(html);
1996
2025
  html = stripEmptyInlineMaskStyles(html);
1997
2026
  html = rewriteHtmlAssetPaths(html, fileSegments.length);
1998
2027
  const liveBase = config.liveUrl.replace(/\/$/, '');
@@ -2004,6 +2033,7 @@ async function runPrerender(config) {
2004
2033
  const routeDepth = fileSegments.length;
2005
2034
  const prerenderedMeta = `<meta name="manifest:prerendered" content="1">\n`;
2006
2035
  html = html.replace('</head>', `${canonicalHreflang}${injectOgLocale ? ogLocale : ''}${baseMeta}${prerenderedMeta}<meta name="manifest:router-base-depth" content="${routeDepth}">\n</head>`);
2036
+ html = markPrerenderedManifestComponents(html);
2007
2037
  mkdirSync(outDir, { recursive: true });
2008
2038
  writeFileSync(outFile, html, 'utf8');
2009
2039
  pushDebug({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mnfst-render",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "Render Manifest sites to static HTML for SEO",
5
5
  "type": "module",
6
6
  "bin": {