mnfst-render 0.3.7 → 0.3.8
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 +56 -10
- package/package.json +1 -1
package/manifest.render.mjs
CHANGED
|
@@ -252,6 +252,13 @@ function loadConfig(rootDir) {
|
|
|
252
252
|
return manifest;
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
+
function normalizeLocaleRouteExclude(val) {
|
|
256
|
+
if (val == null) return [];
|
|
257
|
+
if (Array.isArray(val)) return val.map((s) => String(s).trim()).filter(Boolean);
|
|
258
|
+
if (typeof val === 'string') return val.split(',').map((s) => s.trim()).filter(Boolean);
|
|
259
|
+
return [];
|
|
260
|
+
}
|
|
261
|
+
|
|
255
262
|
function resolveConfig() {
|
|
256
263
|
const cli = parseArgs();
|
|
257
264
|
const cwd = process.cwd();
|
|
@@ -274,6 +281,10 @@ function resolveConfig() {
|
|
|
274
281
|
output: resolve(root, cli.output ?? pre.output ?? 'website'),
|
|
275
282
|
root,
|
|
276
283
|
routerBase: pre.routerBase ?? null,
|
|
284
|
+
/** Logical path prefixes (after locale) that skip sticky locale prefix; see manifest:locale-route-exclude */
|
|
285
|
+
localeRouteExclude: normalizeLocaleRouteExclude(
|
|
286
|
+
pre.localeRouteExclude ?? pre.localeStickyExclude
|
|
287
|
+
),
|
|
277
288
|
locales: pre.locales,
|
|
278
289
|
redirects: Array.isArray(pre.redirects) ? pre.redirects : [],
|
|
279
290
|
wait: cli.wait ?? pre.wait ?? null,
|
|
@@ -853,19 +864,26 @@ function depthFromOutputRoot(outputDir, filePath) {
|
|
|
853
864
|
return rel.split(sep).filter(Boolean).length;
|
|
854
865
|
}
|
|
855
866
|
|
|
856
|
-
/**
|
|
857
|
-
function
|
|
867
|
+
/** Root-absolute path for prerender bundles (same URL from every page depth; supports manifest:router-base). */
|
|
868
|
+
function buildRootAssetPath(routerBasePath, filename) {
|
|
869
|
+
const base = String(routerBasePath || '').replace(/^\/+|\/+$/g, '');
|
|
870
|
+
const name = String(filename || '').replace(/^\/+/, '');
|
|
871
|
+
const path = base ? `${base}/${name}` : name;
|
|
872
|
+
return '/' + path.replace(/\/{2,}/g, '/');
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/** Inject stylesheet link with root-absolute href (avoids ../ resolving under locale segments like /en/page/). */
|
|
876
|
+
function postProcessInjectStylesheetLink(outputDir, filename, routerBasePath) {
|
|
858
877
|
const cssPath = join(outputDir, filename);
|
|
859
878
|
if (!existsSync(cssPath)) return;
|
|
860
879
|
const stat = statSync(cssPath);
|
|
861
880
|
if (stat.size === 0) return;
|
|
862
881
|
|
|
882
|
+
const href = buildRootAssetPath(routerBasePath, filename);
|
|
883
|
+
const tag = `<link rel="stylesheet" href="${href}">`;
|
|
863
884
|
const files = walkHtmlFiles(outputDir);
|
|
864
885
|
for (const file of files) {
|
|
865
886
|
let html = readFileSync(file, 'utf8');
|
|
866
|
-
const depth = depthFromOutputRoot(outputDir, file);
|
|
867
|
-
const prefix = depth ? '../'.repeat(depth) : '';
|
|
868
|
-
const tag = `<link rel="stylesheet" href="${prefix}${filename}">`;
|
|
869
887
|
html = injectBeforeHeadClose(html, tag);
|
|
870
888
|
writeFileSync(file, html, 'utf8');
|
|
871
889
|
}
|
|
@@ -1007,6 +1025,10 @@ function stripEmptyInlineMaskStyles(html) {
|
|
|
1007
1025
|
// All project assets are copied into output, so root-relative paths become relative within output.
|
|
1008
1026
|
// Do NOT rewrite href on <a> tags (navigation links); only rewrite link/script/img so router gets clean paths.
|
|
1009
1027
|
|
|
1028
|
+
function isPrerenderBundleAssetPath(pathAfterSlash) {
|
|
1029
|
+
return /(^|\/)prerender\.(tailwind|utilities)\.css$/.test(pathAfterSlash);
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1010
1032
|
function rewriteHtmlAssetPaths(html, depthWithinOutput) {
|
|
1011
1033
|
const prefix = depthWithinOutput > 0 ? '../'.repeat(depthWithinOutput) : '';
|
|
1012
1034
|
if (!prefix) return html;
|
|
@@ -1016,9 +1038,10 @@ function rewriteHtmlAssetPaths(html, depthWithinOutput) {
|
|
|
1016
1038
|
const tag = htmlBeforeMatch.slice(lastOpen + 1).match(/^(\w+)/);
|
|
1017
1039
|
return tag && tag[1].toLowerCase() === 'a';
|
|
1018
1040
|
}
|
|
1019
|
-
let out = html.replace(/(\s(href|src)=["'])\/(?!\/)/g, (match, lead,
|
|
1041
|
+
let out = html.replace(/(\s(href|src)=["'])\/(?!\/)([^'"]*)/g, (match, lead, _attr, rest, offset, fullString) => {
|
|
1020
1042
|
if (isAnchorTag(fullString.slice(0, offset))) return match;
|
|
1021
|
-
|
|
1043
|
+
if (isPrerenderBundleAssetPath(rest)) return match;
|
|
1044
|
+
return lead + prefix + rest;
|
|
1022
1045
|
});
|
|
1023
1046
|
out = out.replace(/(\s(href|src)=["'])(\.\.\/)+/g, (match, lead, attr, dots, offset, fullString) => {
|
|
1024
1047
|
if (isAnchorTag(fullString.slice(0, offset))) return match;
|
|
@@ -1027,6 +1050,17 @@ function rewriteHtmlAssetPaths(html, depthWithinOutput) {
|
|
|
1027
1050
|
return out;
|
|
1028
1051
|
}
|
|
1029
1052
|
|
|
1053
|
+
// Alpine x-data drives radio state; baked checked="" from the live DOM (e.g. yearly) fights monthly defaults.
|
|
1054
|
+
function stripPrerenderBakedRadioCheckedForXModel(html) {
|
|
1055
|
+
return html.replace(/<input\b([^>]*)>/gi, (full, attrs) => {
|
|
1056
|
+
if (!/\btype\s*=\s*["']radio["']/i.test(attrs)) return full;
|
|
1057
|
+
if (!/\bx-model\s*=/i.test(attrs)) return full;
|
|
1058
|
+
const next = attrs.replace(/\s+checked(?:\s*=\s*["'][^"']*["']|\s*=\s*[^\s>]+)?/gi, '');
|
|
1059
|
+
if (next === attrs) return full;
|
|
1060
|
+
return `<input${next}>`;
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1030
1064
|
// --- Canonical and hreflang (per-page injection) ---
|
|
1031
1065
|
|
|
1032
1066
|
function buildCanonicalAndHreflang(pathSeg, locales, defaultLocale, base) {
|
|
@@ -2032,7 +2066,10 @@ async function runPrerender(config) {
|
|
|
2032
2066
|
for (const b of extracted.blocks) utilityBlocks.push(b);
|
|
2033
2067
|
}
|
|
2034
2068
|
if (tailwindBuilt) {
|
|
2035
|
-
html = injectBeforeHeadClose(
|
|
2069
|
+
html = injectBeforeHeadClose(
|
|
2070
|
+
html,
|
|
2071
|
+
`<link rel="stylesheet" href="${buildRootAssetPath(routerBasePath, 'prerender.tailwind.css')}">`
|
|
2072
|
+
);
|
|
2036
2073
|
}
|
|
2037
2074
|
html = stripDuplicatedLoopDirectives(html);
|
|
2038
2075
|
html = stripPrerenderedXDataDirectives(html);
|
|
@@ -2040,6 +2077,7 @@ async function runPrerender(config) {
|
|
|
2040
2077
|
const xData = { manifest, content };
|
|
2041
2078
|
html = resolveHeadXBindings(html, xData);
|
|
2042
2079
|
html = stripPrerenderDynamicBindings(html);
|
|
2080
|
+
html = stripPrerenderBakedRadioCheckedForXModel(html);
|
|
2043
2081
|
html = stripRedundantImgSrcBindings(html);
|
|
2044
2082
|
html = stripEmptyInlineMaskStyles(html);
|
|
2045
2083
|
html = stripResolvedXIconDirectives(html);
|
|
@@ -2050,9 +2088,17 @@ async function runPrerender(config) {
|
|
|
2050
2088
|
const injectOgLocale = ogLocale && hasOtherOgMeta(html);
|
|
2051
2089
|
if (injectOgLocale) html = stripOgLocaleFromHead(html);
|
|
2052
2090
|
const baseMeta = routerBasePath !== null ? `<meta name="manifest:router-base" content="${String(routerBasePath).replace(/"/g, '"')}">\n` : '';
|
|
2091
|
+
const routeEx = config.localeRouteExclude || [];
|
|
2092
|
+
const routeMeta =
|
|
2093
|
+
routeEx.length > 0
|
|
2094
|
+
? `<meta name="manifest:locale-route-exclude" content="${JSON.stringify(routeEx).replace(/"/g, '"')}">\n`
|
|
2095
|
+
: '';
|
|
2053
2096
|
const routeDepth = fileSegments.length;
|
|
2054
2097
|
const prerenderedMeta = `<meta name="manifest:prerendered" content="1">\n`;
|
|
2055
|
-
html = html.replace(
|
|
2098
|
+
html = html.replace(
|
|
2099
|
+
'</head>',
|
|
2100
|
+
`${canonicalHreflang}${injectOgLocale ? ogLocale : ''}${routeMeta}${baseMeta}${prerenderedMeta}<meta name="manifest:router-base-depth" content="${routeDepth}">\n</head>`
|
|
2101
|
+
);
|
|
2056
2102
|
html = markPrerenderedManifestComponents(html);
|
|
2057
2103
|
mkdirSync(outDir, { recursive: true });
|
|
2058
2104
|
writeFileSync(outFile, html, 'utf8');
|
|
@@ -2113,7 +2159,7 @@ async function runPrerender(config) {
|
|
|
2113
2159
|
if (utilMerged.trim()) {
|
|
2114
2160
|
writeFileSync(join(outputResolved, 'prerender.utilities.css'), `${utilMerged}\n`, 'utf8');
|
|
2115
2161
|
process.stdout.write('prerender: wrote prerender.utilities.css (Manifest custom utilities)\n');
|
|
2116
|
-
postProcessInjectStylesheetLink(outputResolved, 'prerender.utilities.css');
|
|
2162
|
+
postProcessInjectStylesheetLink(outputResolved, 'prerender.utilities.css', routerBasePath || '');
|
|
2117
2163
|
}
|
|
2118
2164
|
}
|
|
2119
2165
|
|