mnfst-render 0.1.8 → 0.2.0
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 +59 -12
- package/package.json +1 -1
package/manifest.render.mjs
CHANGED
|
@@ -313,31 +313,55 @@ function discoverDataPaths(manifest, rootDir, wildcardBases = [], locales = [])
|
|
|
313
313
|
return wildcardBases.some((base) => rest.startsWith(base + '/'));
|
|
314
314
|
}
|
|
315
315
|
|
|
316
|
-
function
|
|
316
|
+
function expandCandidates(rawPath, sourceKey) {
|
|
317
|
+
const p = String(rawPath || '').replace(/^\/+|\/+$/g, '');
|
|
318
|
+
if (!p) return [];
|
|
319
|
+
const candidates = [p];
|
|
320
|
+
if (wildcardBases.length === 0) return candidates;
|
|
321
|
+
if (!sourceKey || !wildcardBases.includes(sourceKey)) return candidates;
|
|
322
|
+
const parts = p.split('/');
|
|
323
|
+
const hasLocalePrefix = parts.length > 1 && localeSet.has(parts[0].toLowerCase());
|
|
324
|
+
if (hasLocalePrefix) {
|
|
325
|
+
const locale = parts[0];
|
|
326
|
+
const rest = parts.slice(1).join('/');
|
|
327
|
+
if (rest && !rest.startsWith(sourceKey + '/')) candidates.push(`${locale}/${sourceKey}/${rest}`);
|
|
328
|
+
} else if (!p.startsWith(sourceKey + '/')) {
|
|
329
|
+
candidates.push(`${sourceKey}/${p}`);
|
|
330
|
+
}
|
|
331
|
+
return candidates;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function addFilePaths(value, sourceKey) {
|
|
317
335
|
if (typeof value !== 'string' || !value.startsWith('/')) return;
|
|
318
336
|
const filePath = join(rootDir, value.slice(1));
|
|
319
337
|
if (filePath.endsWith('.yaml') || filePath.endsWith('.yml')) {
|
|
320
338
|
parseYamlPaths(filePath).forEach((p) => {
|
|
321
|
-
|
|
339
|
+
for (const c of expandCandidates(p, sourceKey)) {
|
|
340
|
+
if (shouldIncludeDataPath(c)) paths.add('/' + c);
|
|
341
|
+
}
|
|
322
342
|
});
|
|
323
343
|
} else if (filePath.endsWith('.json')) {
|
|
324
344
|
parseJsonPaths(filePath).forEach((p) => {
|
|
325
345
|
const normalized = p.startsWith('/') ? p.slice(1) : p;
|
|
326
|
-
|
|
346
|
+
for (const c of expandCandidates(normalized, sourceKey)) {
|
|
347
|
+
if (shouldIncludeDataPath(c)) paths.add('/' + c);
|
|
348
|
+
}
|
|
327
349
|
});
|
|
328
350
|
} else if (filePath.endsWith('.csv')) {
|
|
329
351
|
parseCsvPaths(filePath).forEach((p) => {
|
|
330
352
|
const normalized = p.startsWith('/') ? p.slice(1) : p;
|
|
331
|
-
|
|
353
|
+
for (const c of expandCandidates(normalized, sourceKey)) {
|
|
354
|
+
if (shouldIncludeDataPath(c)) paths.add('/' + c);
|
|
355
|
+
}
|
|
332
356
|
});
|
|
333
357
|
}
|
|
334
358
|
}
|
|
335
359
|
|
|
336
|
-
for (const value of Object.
|
|
337
|
-
if (typeof value === 'string') addFilePaths(value);
|
|
360
|
+
for (const [sourceKey, value] of Object.entries(data)) {
|
|
361
|
+
if (typeof value === 'string') addFilePaths(value, sourceKey);
|
|
338
362
|
else if (value && typeof value === 'object') {
|
|
339
363
|
for (const v of Object.values(value)) {
|
|
340
|
-
if (typeof v === 'string') addFilePaths(v);
|
|
364
|
+
if (typeof v === 'string') addFilePaths(v, sourceKey);
|
|
341
365
|
}
|
|
342
366
|
}
|
|
343
367
|
}
|
|
@@ -540,9 +564,8 @@ function runTailwindCliForPrerender(rootDir, outputDir, pre) {
|
|
|
540
564
|
const outputBasename = basename(outputDir);
|
|
541
565
|
const defaultContent = [
|
|
542
566
|
'**/*.html',
|
|
543
|
-
'**/*.{js,mjs,css}',
|
|
544
|
-
'**/*.json',
|
|
545
567
|
'!**/node_modules/**',
|
|
568
|
+
'!**/dist/**',
|
|
546
569
|
`!**/${outputBasename}/**`,
|
|
547
570
|
];
|
|
548
571
|
const contentGlobs = Array.isArray(pre?.tailwindContent) && pre.tailwindContent.length > 0
|
|
@@ -679,12 +702,14 @@ function stripPrerenderedXDataDirectives(html) {
|
|
|
679
702
|
function stripPrerenderDynamicBindings(html) {
|
|
680
703
|
return html.replace(/<(\w+)([^>]*)>/g, (match, tagName, attrsStr) => {
|
|
681
704
|
if (tagName.toLowerCase() === 'script') return match;
|
|
705
|
+
const isAnchor = tagName.toLowerCase() === 'a';
|
|
682
706
|
const toStrip = new Set();
|
|
683
707
|
const bindingRegex = /(?:^|\s)(?::|x-bind:)(\w+)=(?:"([^"]*)"|'([^']*)')/g;
|
|
684
708
|
let m;
|
|
685
709
|
while ((m = bindingRegex.exec(attrsStr)) !== null) {
|
|
686
710
|
const attrName = (m[1] || '').toLowerCase();
|
|
687
|
-
|
|
711
|
+
// Keep href on anchors so prerendered static navigation stays valid.
|
|
712
|
+
if (attrName === 'class' || attrName === 'style' || (isAnchor && attrName === 'href')) continue;
|
|
688
713
|
const val = (m[2] !== undefined ? m[2] : m[3]) || '';
|
|
689
714
|
if (val.indexOf('$x') === -1) toStrip.add(attrName);
|
|
690
715
|
}
|
|
@@ -1084,8 +1109,12 @@ async function main() {
|
|
|
1084
1109
|
await new Promise((res) => staticServer.close(res));
|
|
1085
1110
|
}
|
|
1086
1111
|
}
|
|
1087
|
-
const
|
|
1088
|
-
|
|
1112
|
+
const elapsedMs = Date.now() - startedAt;
|
|
1113
|
+
const totalSeconds = Math.floor(elapsedMs / 1000);
|
|
1114
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
1115
|
+
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
1116
|
+
const seconds = totalSeconds % 60;
|
|
1117
|
+
process.stdout.write(`prerender: total time ${hours}h ${minutes}m ${seconds}s\n`);
|
|
1089
1118
|
}
|
|
1090
1119
|
|
|
1091
1120
|
async function runPrerender(config) {
|
|
@@ -1258,6 +1287,24 @@ async function runPrerender(config) {
|
|
|
1258
1287
|
});
|
|
1259
1288
|
}).catch(() => { });
|
|
1260
1289
|
|
|
1290
|
+
// Ensure $route-dependent expressions are recalculated after locale/data stores settle.
|
|
1291
|
+
// This helps localized dynamic pages (e.g. /ko/articles/slug) compute prev/next links correctly.
|
|
1292
|
+
await page.evaluate(() => {
|
|
1293
|
+
try {
|
|
1294
|
+
const to = window.ManifestRoutingNavigation?.getCurrentRoute?.() ?? window.location.pathname;
|
|
1295
|
+
window.dispatchEvent(new CustomEvent('manifest:route-change', {
|
|
1296
|
+
detail: {
|
|
1297
|
+
from: to,
|
|
1298
|
+
to,
|
|
1299
|
+
normalizedPath: (typeof to === 'string' && to !== '/') ? to.replace(/^\/|\/$/g, '') : '/'
|
|
1300
|
+
}
|
|
1301
|
+
}));
|
|
1302
|
+
window.dispatchEvent(new PopStateEvent('popstate'));
|
|
1303
|
+
} catch {
|
|
1304
|
+
// no-op
|
|
1305
|
+
}
|
|
1306
|
+
}).catch(() => { });
|
|
1307
|
+
|
|
1261
1308
|
// Optional extra delay so in-page async (e.g. fetch() in x-init for client logos) can complete before snapshot.
|
|
1262
1309
|
if (config.waitAfterIdle > 0) {
|
|
1263
1310
|
await new Promise((r) => setTimeout(r, config.waitAfterIdle));
|