mnfst-render 0.5.16 → 0.5.18
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 +53 -15
- package/package.json +1 -1
package/manifest.render.mjs
CHANGED
|
@@ -363,7 +363,7 @@ function parseYamlPaths(filePath) {
|
|
|
363
363
|
let currentGroup = '';
|
|
364
364
|
const lines = text.split(/\r?\n/);
|
|
365
365
|
for (const line of lines) {
|
|
366
|
-
const groupMatch = line.match(
|
|
366
|
+
const groupMatch = line.match(/^\s*-?\s*group:\s*["']?([^"'\n]+)["']?/);
|
|
367
367
|
if (groupMatch) {
|
|
368
368
|
currentGroup = groupMatch[1].trim().toLowerCase().replace(/\s+/g, '-');
|
|
369
369
|
continue;
|
|
@@ -372,12 +372,15 @@ function parseYamlPaths(filePath) {
|
|
|
372
372
|
if (pathMatch && currentGroup) {
|
|
373
373
|
const segment = pathMatch[1].trim();
|
|
374
374
|
paths.push(`${currentGroup}/${segment}`);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
const
|
|
379
|
-
if (
|
|
380
|
-
|
|
375
|
+
} else {
|
|
376
|
+
// No group context — fall back to a bare path/slug. Used by data files
|
|
377
|
+
// whose entries are flat (e.g. articles list with `path:` per item).
|
|
378
|
+
const genericPathMatch = line.match(/^\s*(?:-\s*)?(?:path|slug):\s*["']?([^"'\n#]+)["']?/);
|
|
379
|
+
if (genericPathMatch) {
|
|
380
|
+
const v = genericPathMatch[1].trim().replace(/^\/+|\/+$/g, '');
|
|
381
|
+
if (v && !v.includes('*') && !/\.[a-z0-9]+$/i.test(v)) {
|
|
382
|
+
paths.push(v);
|
|
383
|
+
}
|
|
381
384
|
}
|
|
382
385
|
}
|
|
383
386
|
}
|
|
@@ -1474,6 +1477,26 @@ function loadContentForPrerender(manifest, rootDir, locale) {
|
|
|
1474
1477
|
content = parseCsvToKeyValue(join(rootDir, data.slice(1)), loc);
|
|
1475
1478
|
} else if (data && typeof data === 'object' && data.locales && typeof data.locales === 'string') {
|
|
1476
1479
|
content = parseCsvToKeyValue(join(rootDir, data.locales.slice(1)), loc);
|
|
1480
|
+
} else if (data && typeof data === 'object' && !Array.isArray(data)) {
|
|
1481
|
+
// Per-locale files: { "en": "/data/content.en.yaml", "fr": "/data/content.fr.yaml", ... }
|
|
1482
|
+
const filePath = data[loc] || data[Object.keys(data)[0]];
|
|
1483
|
+
if (typeof filePath === 'string') {
|
|
1484
|
+
const fullPath = join(rootDir, filePath.startsWith('/') ? filePath.slice(1) : filePath);
|
|
1485
|
+
if (existsSync(fullPath)) {
|
|
1486
|
+
try {
|
|
1487
|
+
const raw = readFileSync(fullPath, 'utf8');
|
|
1488
|
+
if (filePath.endsWith('.yaml') || filePath.endsWith('.yml')) {
|
|
1489
|
+
let jsYaml = null;
|
|
1490
|
+
try { jsYaml = require('js-yaml'); } catch { /* skip */ }
|
|
1491
|
+
if (jsYaml) content = jsYaml.load(raw) || {};
|
|
1492
|
+
} else if (filePath.endsWith('.json')) {
|
|
1493
|
+
content = JSON.parse(raw);
|
|
1494
|
+
} else if (filePath.endsWith('.csv')) {
|
|
1495
|
+
content = parseCsvToKeyValue(fullPath, loc);
|
|
1496
|
+
}
|
|
1497
|
+
} catch { /* ignore parse errors */ }
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1477
1500
|
}
|
|
1478
1501
|
if (manifest.description !== undefined && content.description === undefined) {
|
|
1479
1502
|
content.description = manifest.description;
|
|
@@ -1546,15 +1569,27 @@ function resolveHeadXBindings(html, xData) {
|
|
|
1546
1569
|
.replace(/>/g, '>')
|
|
1547
1570
|
.replace(/"/g, '"');
|
|
1548
1571
|
return html.replace(/<head>([\s\S]*?)<\/head>/i, (_, headContent) => {
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1572
|
+
// Process each tag in <head> that has a :attr or x-bind:attr binding
|
|
1573
|
+
const out = headContent.replace(/<[^>]+>/g, (tag) => {
|
|
1574
|
+
// Find all :attr="$x...." or x-bind:attr="$x...." bindings in this tag
|
|
1575
|
+
const bindingRe = /\s(?::|x-bind:)(\w+)=["'](\$x\.[^"']+)["']/g;
|
|
1576
|
+
let m;
|
|
1577
|
+
let newTag = tag;
|
|
1578
|
+
while ((m = bindingRe.exec(tag)) !== null) {
|
|
1579
|
+
const attr = m[1];
|
|
1580
|
+
const expr = m[2];
|
|
1552
1581
|
const path = expr.replace(/^\$x\./, '').trim();
|
|
1553
1582
|
const value = getXPath(xData, path);
|
|
1554
|
-
if (value === undefined)
|
|
1555
|
-
|
|
1583
|
+
if (value === undefined) continue;
|
|
1584
|
+
// Remove the binding
|
|
1585
|
+
newTag = newTag.replace(m[0], '');
|
|
1586
|
+
// Remove existing static fallback for this attr
|
|
1587
|
+
newTag = newTag.replace(new RegExp(`\\s${attr}=["'][^"']*["']`), '');
|
|
1588
|
+
// Insert the resolved attr before the closing >
|
|
1589
|
+
newTag = newTag.replace(/>$/, ` ${attr}="${esc(value)}">`);
|
|
1556
1590
|
}
|
|
1557
|
-
|
|
1591
|
+
return newTag;
|
|
1592
|
+
});
|
|
1558
1593
|
return `<head>${out}</head>`;
|
|
1559
1594
|
});
|
|
1560
1595
|
}
|
|
@@ -1781,8 +1816,11 @@ async function runPrerender(config) {
|
|
|
1781
1816
|
paths.add(`${locale}/${seg}`);
|
|
1782
1817
|
}
|
|
1783
1818
|
}
|
|
1784
|
-
// Default locale also under its slug (e.g. /en/, /en/page-1) so linking is
|
|
1785
|
-
|
|
1819
|
+
// Default locale also under its slug (e.g. /en/, /en/page-1) so linking is
|
|
1820
|
+
// symmetric with other locales; canonical points to root. Skip this when
|
|
1821
|
+
// there's only one locale — the duplicates serve no purpose and bloat the
|
|
1822
|
+
// output (every page would be written twice: at root AND under /en/).
|
|
1823
|
+
if (defaultLocale && locales.length > 1) {
|
|
1786
1824
|
paths.add(defaultLocale);
|
|
1787
1825
|
for (const seg of localeNeutralSegments) {
|
|
1788
1826
|
if (seg !== '') paths.add(`${defaultLocale}/${seg}`);
|