mnfst-render 0.4.1 → 0.4.3
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 +54 -16
- package/package.json +1 -1
package/manifest.render.mjs
CHANGED
|
@@ -956,6 +956,7 @@ function markPrerenderedManifestComponents(html) {
|
|
|
956
956
|
return html.replace(/<(x-[a-z][\w-]*)([^>]*)>/gi, (full, tag, attrs) => {
|
|
957
957
|
const a = attrs || '';
|
|
958
958
|
if (/\bdata-pre-rendered\s*=/i.test(a) || /\bdata-processed\s*=/i.test(a)) return full;
|
|
959
|
+
if (/\bdata-prerender-hydrate\b/i.test(a)) return full; // Inside data-hydrate island — skip
|
|
959
960
|
const spacer = /\S/.test(a) ? ' ' : '';
|
|
960
961
|
return `<${tag}${a}${spacer}data-pre-rendered="1">`;
|
|
961
962
|
});
|
|
@@ -1161,14 +1162,24 @@ function loadAllLocaleContentData(manifest, rootDir, locales) {
|
|
|
1161
1162
|
*/
|
|
1162
1163
|
function buildSubstitutionPairs(defaultLocaleData, targetLocaleData) {
|
|
1163
1164
|
const pairs = [];
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1165
|
+
function collectPairs(defaultObj, targetObj) {
|
|
1166
|
+
if (!defaultObj || !targetObj) return;
|
|
1167
|
+
for (const key of Object.keys(defaultObj)) {
|
|
1168
|
+
const defaultVal = defaultObj[key];
|
|
1169
|
+
const targetVal = targetObj[key];
|
|
1170
|
+
if (defaultVal && typeof defaultVal === 'object') {
|
|
1171
|
+
// Recurse into nested objects (produced by setNestedKey for dotted CSV keys)
|
|
1172
|
+
collectPairs(defaultVal, targetVal && typeof targetVal === 'object' ? targetVal : {});
|
|
1173
|
+
} else {
|
|
1174
|
+
const from = String(defaultVal ?? '').trim();
|
|
1175
|
+
const to = String(targetVal ?? '').trim();
|
|
1176
|
+
if (!from || from === to) continue;
|
|
1177
|
+
pairs.push([from, to]);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1171
1180
|
}
|
|
1181
|
+
collectPairs(defaultLocaleData, targetLocaleData);
|
|
1182
|
+
// Sort longest-first so more specific strings are replaced before shorter substrings
|
|
1172
1183
|
pairs.sort((a, b) => b[0].length - a[0].length);
|
|
1173
1184
|
return pairs;
|
|
1174
1185
|
}
|
|
@@ -1266,6 +1277,9 @@ function generateLocaleVariantHtml({
|
|
|
1266
1277
|
html = stripRedundantImgSrcBindings(html);
|
|
1267
1278
|
html = stripEmptyInlineMaskStyles(html);
|
|
1268
1279
|
html = stripResolvedXIconDirectives(html);
|
|
1280
|
+
// markPrerenderedManifestComponents must run BEFORE stripPrerenderHydrateMarkers so it can
|
|
1281
|
+
// detect data-prerender-hydrate markers and skip components inside hydrate islands.
|
|
1282
|
+
html = markPrerenderedManifestComponents(html);
|
|
1269
1283
|
html = stripPrerenderHydrateMarkers(html);
|
|
1270
1284
|
|
|
1271
1285
|
const fileSegments = pathToFileSegments(pathSeg ? '/' + pathSeg : '/');
|
|
@@ -1290,7 +1304,6 @@ function generateLocaleVariantHtml({
|
|
|
1290
1304
|
'</head>',
|
|
1291
1305
|
`${canonicalHreflang}${injectOgLocale ? ogLocale : ''}${routeMeta}${baseMeta}<meta name="manifest:prerendered" content="1">\n<meta name="manifest:router-base-depth" content="${routeDepth}">\n</head>`
|
|
1292
1306
|
);
|
|
1293
|
-
html = markPrerenderedManifestComponents(html);
|
|
1294
1307
|
|
|
1295
1308
|
return { html, utilityBlocks: pageUtilityBlocks };
|
|
1296
1309
|
}
|
|
@@ -1683,16 +1696,39 @@ async function runPrerender(config) {
|
|
|
1683
1696
|
const puppeteerPaths = [];
|
|
1684
1697
|
const localeVariantPaths = []; // { pathSeg, basePathSeg, targetLocale }
|
|
1685
1698
|
|
|
1699
|
+
// Two-pass categorisation: locale substitution only applies when the locale-neutral base path
|
|
1700
|
+
// (e.g. 'about' for 'fr/about') is itself in the path list and will be Puppeteer-rendered.
|
|
1701
|
+
//
|
|
1702
|
+
// Paths whose data is inherently locale-specific (e.g. 'en/articles/slug', 'fr/articles/slug'
|
|
1703
|
+
// discovered from per-locale data sources) have no locale-neutral counterpart and must be
|
|
1704
|
+
// rendered by Puppeteer directly — their content differs per locale and substitution cannot
|
|
1705
|
+
// produce correct output. This mirrors the framework's own data model: locale-neutral paths
|
|
1706
|
+
// use a shared structure with CSV text overlay; locale-prefixed paths carry per-locale content.
|
|
1707
|
+
|
|
1708
|
+
// Pass 1: collect all locale-neutral path segments (no locale prefix in the first segment).
|
|
1709
|
+
const localeNeutralPathSet = new Set();
|
|
1710
|
+
for (const seg of pathList) {
|
|
1711
|
+
if (!seg || seg === NOT_FOUND_PATH) continue;
|
|
1712
|
+
if (!localeSet.has(seg.split('/')[0])) localeNeutralPathSet.add(seg);
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
// Pass 2: categorise.
|
|
1686
1716
|
for (const seg of pathList) {
|
|
1687
1717
|
if (!localeSubstEnabled || seg === NOT_FOUND_PATH || !seg) {
|
|
1688
1718
|
puppeteerPaths.push(seg);
|
|
1689
1719
|
continue;
|
|
1690
1720
|
}
|
|
1691
|
-
const
|
|
1692
|
-
if (localeSet.has(
|
|
1693
|
-
|
|
1694
|
-
|
|
1721
|
+
const fp = seg.split('/')[0];
|
|
1722
|
+
if (!localeSet.has(fp) || localeSubstExclude.has(fp)) {
|
|
1723
|
+
puppeteerPaths.push(seg);
|
|
1724
|
+
continue;
|
|
1725
|
+
}
|
|
1726
|
+
const basePathSeg = seg.slice(fp.length + 1) || '';
|
|
1727
|
+
if (localeNeutralPathSet.has(basePathSeg)) {
|
|
1728
|
+
// Locale-neutral base exists and will be Puppeteer-rendered → safe to substitute.
|
|
1729
|
+
localeVariantPaths.push({ pathSeg: seg, basePathSeg, targetLocale: fp });
|
|
1695
1730
|
} else {
|
|
1731
|
+
// No locale-neutral base — this path has per-locale content; Puppeteer required.
|
|
1696
1732
|
puppeteerPaths.push(seg);
|
|
1697
1733
|
}
|
|
1698
1734
|
}
|
|
@@ -2272,6 +2308,9 @@ async function runPrerender(config) {
|
|
|
2272
2308
|
html = stripRedundantImgSrcBindings(html);
|
|
2273
2309
|
html = stripEmptyInlineMaskStyles(html);
|
|
2274
2310
|
html = stripResolvedXIconDirectives(html);
|
|
2311
|
+
// markPrerenderedManifestComponents must run BEFORE stripPrerenderHydrateMarkers so it can
|
|
2312
|
+
// detect data-prerender-hydrate markers and skip components inside hydrate islands.
|
|
2313
|
+
html = markPrerenderedManifestComponents(html);
|
|
2275
2314
|
html = stripPrerenderHydrateMarkers(html);
|
|
2276
2315
|
html = rewriteHtmlAssetPaths(html, fileSegments.length);
|
|
2277
2316
|
const liveBase = config.liveUrl.replace(/\/$/, '');
|
|
@@ -2291,7 +2330,6 @@ async function runPrerender(config) {
|
|
|
2291
2330
|
'</head>',
|
|
2292
2331
|
`${canonicalHreflang}${injectOgLocale ? ogLocale : ''}${routeMeta}${baseMeta}${prerenderedMeta}<meta name="manifest:router-base-depth" content="${routeDepth}">\n</head>`
|
|
2293
2332
|
);
|
|
2294
|
-
html = markPrerenderedManifestComponents(html);
|
|
2295
2333
|
mkdirSync(outDir, { recursive: true });
|
|
2296
2334
|
writeFileSync(outFile, html, 'utf8');
|
|
2297
2335
|
pushDebug({
|
|
@@ -2344,9 +2382,9 @@ async function runPrerender(config) {
|
|
|
2344
2382
|
substIndex++;
|
|
2345
2383
|
const rawHtml = baseHtmlCache.get(basePathSeg);
|
|
2346
2384
|
if (!rawHtml) {
|
|
2347
|
-
// Base path
|
|
2348
|
-
failedPaths.push({ path: '/' + pathSeg, message: `
|
|
2349
|
-
process.stderr.write(`prerender:
|
|
2385
|
+
// Base path was expected to be Puppeteer-rendered but is absent — its render likely failed.
|
|
2386
|
+
failedPaths.push({ path: '/' + pathSeg, message: `base path "${basePathSeg || '/'}" missing from cache (did its Puppeteer render fail?)` });
|
|
2387
|
+
process.stderr.write(`prerender: skipped /${pathSeg} — base "${basePathSeg || '/'}" not in cache\n`);
|
|
2350
2388
|
continue;
|
|
2351
2389
|
}
|
|
2352
2390
|
|