mnfst-render 0.1.6 → 0.1.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 +294 -9
- package/package.json +2 -2
package/manifest.render.mjs
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
/* Manifest Render */
|
|
4
4
|
|
|
5
|
-
import { readFileSync, mkdirSync, writeFileSync, existsSync, rmSync, statSync, readdirSync, cpSync } from 'node:fs';
|
|
6
|
-
import {
|
|
5
|
+
import { readFileSync, readSync, mkdirSync, writeFileSync, existsSync, rmSync, statSync, readdirSync, cpSync, unlinkSync } from 'node:fs';
|
|
6
|
+
import { spawnSync } from 'node:child_process';
|
|
7
|
+
import { join, resolve, dirname, relative, basename, sep } from 'node:path';
|
|
7
8
|
import { createServer } from 'node:http';
|
|
8
9
|
import { createRequire } from 'node:module';
|
|
9
10
|
import { fileURLToPath } from 'node:url';
|
|
@@ -179,6 +180,15 @@ function conditionsToPaths(conditions) {
|
|
|
179
180
|
return paths;
|
|
180
181
|
}
|
|
181
182
|
|
|
183
|
+
function getWildcardBasesFromConditions(conditions) {
|
|
184
|
+
const bases = new Set();
|
|
185
|
+
for (const c of conditions) {
|
|
186
|
+
const parsed = normalizeRouteCondition(c);
|
|
187
|
+
if (parsed.kind === 'wildcard-prefix' && parsed.path) bases.add(parsed.path);
|
|
188
|
+
}
|
|
189
|
+
return [...bases];
|
|
190
|
+
}
|
|
191
|
+
|
|
182
192
|
// --- Discovery: data-driven paths (docs-style YAML group/items[].path) ------
|
|
183
193
|
|
|
184
194
|
function parseYamlPaths(filePath) {
|
|
@@ -288,20 +298,38 @@ function parseCsvPaths(filePath) {
|
|
|
288
298
|
return paths;
|
|
289
299
|
}
|
|
290
300
|
|
|
291
|
-
function discoverDataPaths(manifest, rootDir) {
|
|
301
|
+
function discoverDataPaths(manifest, rootDir, wildcardBases = [], locales = []) {
|
|
292
302
|
const paths = new Set();
|
|
293
303
|
const data = manifest.data;
|
|
294
304
|
if (!data || typeof data !== 'object') return paths;
|
|
305
|
+
const localeSet = new Set((locales || []).map((l) => String(l).toLowerCase()));
|
|
306
|
+
|
|
307
|
+
function shouldIncludeDataPath(rawPath) {
|
|
308
|
+
const p = String(rawPath || '').replace(/^\/+|\/+$/g, '');
|
|
309
|
+
if (!p || p.includes('#') || p.includes('?') || p.includes('*')) return false;
|
|
310
|
+
if (wildcardBases.length === 0) return true;
|
|
311
|
+
const segs = p.split('/');
|
|
312
|
+
const rest = segs.length > 1 && localeSet.has(segs[0].toLowerCase()) ? segs.slice(1).join('/') : p;
|
|
313
|
+
return wildcardBases.some((base) => rest.startsWith(base + '/'));
|
|
314
|
+
}
|
|
295
315
|
|
|
296
316
|
function addFilePaths(value) {
|
|
297
317
|
if (typeof value !== 'string' || !value.startsWith('/')) return;
|
|
298
318
|
const filePath = join(rootDir, value.slice(1));
|
|
299
319
|
if (filePath.endsWith('.yaml') || filePath.endsWith('.yml')) {
|
|
300
|
-
parseYamlPaths(filePath).forEach((p) =>
|
|
320
|
+
parseYamlPaths(filePath).forEach((p) => {
|
|
321
|
+
if (shouldIncludeDataPath(p)) paths.add('/' + p);
|
|
322
|
+
});
|
|
301
323
|
} else if (filePath.endsWith('.json')) {
|
|
302
|
-
parseJsonPaths(filePath).forEach((p) =>
|
|
324
|
+
parseJsonPaths(filePath).forEach((p) => {
|
|
325
|
+
const normalized = p.startsWith('/') ? p.slice(1) : p;
|
|
326
|
+
if (shouldIncludeDataPath(normalized)) paths.add('/' + normalized);
|
|
327
|
+
});
|
|
303
328
|
} else if (filePath.endsWith('.csv')) {
|
|
304
|
-
parseCsvPaths(filePath).forEach((p) =>
|
|
329
|
+
parseCsvPaths(filePath).forEach((p) => {
|
|
330
|
+
const normalized = p.startsWith('/') ? p.slice(1) : p;
|
|
331
|
+
if (shouldIncludeDataPath(normalized)) paths.add('/' + normalized);
|
|
332
|
+
});
|
|
305
333
|
}
|
|
306
334
|
}
|
|
307
335
|
|
|
@@ -321,11 +349,14 @@ function discoverDataPaths(manifest, rootDir) {
|
|
|
321
349
|
function discoverRoutes(manifest, rootDir) {
|
|
322
350
|
const pathSet = new Set();
|
|
323
351
|
pathSet.add('/');
|
|
352
|
+
const allConditions = new Set();
|
|
353
|
+
const locales = discoverLocales(manifest, rootDir);
|
|
324
354
|
|
|
325
355
|
const indexPath = join(rootDir, 'index.html');
|
|
326
356
|
if (existsSync(indexPath)) {
|
|
327
357
|
const indexHtml = readFileSync(indexPath, 'utf8');
|
|
328
358
|
const conditions = extractXRouteConditions(indexHtml);
|
|
359
|
+
conditions.forEach((c) => allConditions.add(c));
|
|
329
360
|
conditionsToPaths(conditions).forEach((p) => pathSet.add(p));
|
|
330
361
|
}
|
|
331
362
|
|
|
@@ -338,11 +369,13 @@ function discoverRoutes(manifest, rootDir) {
|
|
|
338
369
|
if (existsSync(compPath)) {
|
|
339
370
|
const html = readFileSync(compPath, 'utf8');
|
|
340
371
|
const conditions = extractXRouteConditions(html);
|
|
372
|
+
conditions.forEach((c) => allConditions.add(c));
|
|
341
373
|
conditionsToPaths(conditions).forEach((p) => pathSet.add(p));
|
|
342
374
|
}
|
|
343
375
|
}
|
|
344
376
|
|
|
345
|
-
|
|
377
|
+
const wildcardBases = getWildcardBasesFromConditions(allConditions);
|
|
378
|
+
discoverDataPaths(manifest, rootDir, wildcardBases, locales).forEach((p) => pathSet.add(p));
|
|
346
379
|
|
|
347
380
|
const arr = [...pathSet].map((p) => (p === '/' ? '' : p.replace(/^\//, '').replace(/\/$/, '') || ''));
|
|
348
381
|
return arr.includes('') ? arr : ['', ...arr.filter(Boolean)];
|
|
@@ -399,6 +432,223 @@ function stripInjectedPluginScripts(html) {
|
|
|
399
432
|
return out;
|
|
400
433
|
}
|
|
401
434
|
|
|
435
|
+
function stripRuntimeTailwindArtifacts(html) {
|
|
436
|
+
let out = html.replace(/\sdata-tailwind(?:=(["']).*?\1)?/gi, '');
|
|
437
|
+
// Remove PlayCDN-injected runtime Tailwind stylesheet from snapshots.
|
|
438
|
+
out = out.replace(/<style>\s*\/\*!\s*tailwindcss[\s\S]*?<\/style>/gi, '');
|
|
439
|
+
return out;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/** Manifest utilities plugin: <style id="utility-styles"> and <style id="utility-styles-critical"> */
|
|
443
|
+
function extractUtilityStyleBlocks(html) {
|
|
444
|
+
const blocks = [];
|
|
445
|
+
let out = html.replace(
|
|
446
|
+
/<style[^>]*\bid=["']utility-styles-critical["'][^>]*>([\s\S]*?)<\/style>/gi,
|
|
447
|
+
(_, css) => {
|
|
448
|
+
const t = (css || '').trim();
|
|
449
|
+
if (t) blocks.push({ kind: 'critical', css: t });
|
|
450
|
+
return '';
|
|
451
|
+
}
|
|
452
|
+
);
|
|
453
|
+
out = out.replace(/<style[^>]*\bid=["']utility-styles["'][^>]*>([\s\S]*?)<\/style>/gi, (_, css) => {
|
|
454
|
+
const t = (css || '').trim();
|
|
455
|
+
if (t) blocks.push({ kind: 'main', css: t });
|
|
456
|
+
return '';
|
|
457
|
+
});
|
|
458
|
+
return { html: out, blocks };
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function injectAfterHeadOpen(html, snippet) {
|
|
462
|
+
if (!snippet) return html;
|
|
463
|
+
const hrefMatch = snippet.match(/href=["']([^"']+)["']/);
|
|
464
|
+
if (hrefMatch && html.includes(hrefMatch[1])) return html;
|
|
465
|
+
return html.replace(/<head([^>]*)>/i, `<head$1>\n${snippet}\n`);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function indexHtmlUsesTailwind(rootDir) {
|
|
469
|
+
const indexPath = join(rootDir, 'index.html');
|
|
470
|
+
if (!existsSync(indexPath)) return false;
|
|
471
|
+
const html = readFileSync(indexPath, 'utf8');
|
|
472
|
+
return /\sdata-tailwind(?:=(["']).*?\1)?/i.test(html) && /<script[^>]*manifest\.min\.js/i.test(html);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function promptContinueWithRuntimeTailwind(rootDir) {
|
|
476
|
+
const installMsg = [
|
|
477
|
+
'prerender: tailwindcss package is not installed for this project.',
|
|
478
|
+
'',
|
|
479
|
+
'To enable static Tailwind CSS compilation, install:',
|
|
480
|
+
' npm i -D tailwindcss @tailwindcss/cli',
|
|
481
|
+
'',
|
|
482
|
+
`Project: ${rootDir}`,
|
|
483
|
+
'',
|
|
484
|
+
'Continue prerender with runtime data-tailwind instead? [P]roceed/[E]nd (default: P): ',
|
|
485
|
+
].join('\n');
|
|
486
|
+
process.stdout.write(`${installMsg}\n`);
|
|
487
|
+
|
|
488
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
489
|
+
process.stdout.write(
|
|
490
|
+
'prerender: non-interactive terminal detected; continuing with runtime data-tailwind behavior.\n'
|
|
491
|
+
);
|
|
492
|
+
return true;
|
|
493
|
+
}
|
|
494
|
+
const buf = Buffer.alloc(1);
|
|
495
|
+
let answer = '';
|
|
496
|
+
while (true) {
|
|
497
|
+
const n = readSync(0, buf, 0, 1, null);
|
|
498
|
+
if (n <= 0) break;
|
|
499
|
+
const ch = buf.toString('utf8', 0, n);
|
|
500
|
+
if (ch === '\n' || ch === '\r') break;
|
|
501
|
+
answer += ch;
|
|
502
|
+
}
|
|
503
|
+
const normalized = answer.trim().toLowerCase();
|
|
504
|
+
return normalized === '' || normalized === 'p' || normalized === 'proceed' || normalized === 'y' || normalized === 'yes';
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Build a static Tailwind stylesheet via @tailwindcss/cli (v4+), scanning project sources.
|
|
509
|
+
* Only runs when the project opts in (data-tailwind on manifest script) or manifest.prerender.tailwind === true.
|
|
510
|
+
*/
|
|
511
|
+
function runTailwindCliForPrerender(rootDir, outputDir, pre) {
|
|
512
|
+
const explicit = pre?.tailwind;
|
|
513
|
+
if (explicit === false) return false;
|
|
514
|
+
const usesTailwind = explicit === true || indexHtmlUsesTailwind(rootDir);
|
|
515
|
+
if (!usesTailwind) return false;
|
|
516
|
+
|
|
517
|
+
const outCss = join(outputDir, 'prerender.tailwind.css');
|
|
518
|
+
try {
|
|
519
|
+
require.resolve('tailwindcss', { paths: [rootDir] });
|
|
520
|
+
} catch {
|
|
521
|
+
const proceed = promptContinueWithRuntimeTailwind(rootDir);
|
|
522
|
+
if (!proceed) {
|
|
523
|
+
throw new Error('prerender aborted: install tailwindcss/@tailwindcss/cli or disable prerender.tailwind.');
|
|
524
|
+
}
|
|
525
|
+
process.stdout.write('prerender: continuing with runtime data-tailwind behavior.\n');
|
|
526
|
+
return false;
|
|
527
|
+
}
|
|
528
|
+
let inputPath = null;
|
|
529
|
+
let createdTempInput = false;
|
|
530
|
+
const userInput = pre?.tailwindInput;
|
|
531
|
+
if (typeof userInput === 'string' && userInput.trim()) {
|
|
532
|
+
inputPath = resolve(rootDir, userInput.trim());
|
|
533
|
+
}
|
|
534
|
+
if (!inputPath || !existsSync(inputPath)) {
|
|
535
|
+
inputPath = join(rootDir, '.mnfst-prerender-tailwind-input.css');
|
|
536
|
+
writeFileSync(inputPath, '@import "tailwindcss";\n', 'utf8');
|
|
537
|
+
createdTempInput = true;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const outputBasename = basename(outputDir);
|
|
541
|
+
const defaultContent = [
|
|
542
|
+
'**/*.html',
|
|
543
|
+
'**/*.{js,mjs,css}',
|
|
544
|
+
'**/*.json',
|
|
545
|
+
'!**/node_modules/**',
|
|
546
|
+
`!**/${outputBasename}/**`,
|
|
547
|
+
];
|
|
548
|
+
const contentGlobs = Array.isArray(pre?.tailwindContent) && pre.tailwindContent.length > 0
|
|
549
|
+
? pre.tailwindContent
|
|
550
|
+
: defaultContent;
|
|
551
|
+
|
|
552
|
+
const args = [
|
|
553
|
+
'--yes',
|
|
554
|
+
'@tailwindcss/cli@4',
|
|
555
|
+
'-i',
|
|
556
|
+
inputPath,
|
|
557
|
+
'-o',
|
|
558
|
+
outCss,
|
|
559
|
+
];
|
|
560
|
+
for (const g of contentGlobs) {
|
|
561
|
+
args.push('--content', g);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
process.stdout.write('prerender: compiling Tailwind CSS (this may take a minute)...\n');
|
|
565
|
+
const r = spawnSync('npx', args, {
|
|
566
|
+
cwd: rootDir,
|
|
567
|
+
encoding: 'utf8',
|
|
568
|
+
shell: process.platform === 'win32',
|
|
569
|
+
});
|
|
570
|
+
if (createdTempInput) {
|
|
571
|
+
try {
|
|
572
|
+
unlinkSync(inputPath);
|
|
573
|
+
} catch {
|
|
574
|
+
// ignore
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
if (r.status !== 0) {
|
|
578
|
+
console.error('prerender: Tailwind CLI failed; install with `npm i -D tailwindcss @tailwindcss/cli` or fix tailwindInput/tailwindContent in manifest.prerender.');
|
|
579
|
+
if (r.stderr) console.error(r.stderr);
|
|
580
|
+
if (r.stdout) console.error(r.stdout);
|
|
581
|
+
return false;
|
|
582
|
+
}
|
|
583
|
+
if (!existsSync(outCss)) {
|
|
584
|
+
console.error('prerender: Tailwind CLI did not produce prerender.tailwind.css');
|
|
585
|
+
return false;
|
|
586
|
+
}
|
|
587
|
+
process.stdout.write(`prerender: wrote ${relative(rootDir, outCss)}\n`);
|
|
588
|
+
return true;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function mergeUtilityCssBlocks(allBlocks) {
|
|
592
|
+
const critical = [];
|
|
593
|
+
const main = [];
|
|
594
|
+
const seenC = new Set();
|
|
595
|
+
const seenM = new Set();
|
|
596
|
+
for (const b of allBlocks) {
|
|
597
|
+
if (b.kind === 'critical') {
|
|
598
|
+
if (!seenC.has(b.css)) {
|
|
599
|
+
seenC.add(b.css);
|
|
600
|
+
critical.push(b.css);
|
|
601
|
+
}
|
|
602
|
+
} else {
|
|
603
|
+
if (!seenM.has(b.css)) {
|
|
604
|
+
seenM.add(b.css);
|
|
605
|
+
main.push(b.css);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
const parts = [];
|
|
610
|
+
if (critical.length) parts.push('/* manifest utilities: critical */\n', critical.join('\n\n'));
|
|
611
|
+
if (main.length) parts.push('/* manifest utilities */\n', main.join('\n\n'));
|
|
612
|
+
return parts.join('\n');
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function walkHtmlFiles(dir, out = []) {
|
|
616
|
+
for (const ent of readdirSync(dir, { withFileTypes: true })) {
|
|
617
|
+
if (ent.name.startsWith('.')) continue;
|
|
618
|
+
const p = join(dir, ent.name);
|
|
619
|
+
if (ent.isDirectory()) {
|
|
620
|
+
if (ent.name === 'node_modules') continue;
|
|
621
|
+
walkHtmlFiles(p, out);
|
|
622
|
+
} else if (ent.name.endsWith('.html')) out.push(p);
|
|
623
|
+
}
|
|
624
|
+
return out;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
function depthFromOutputRoot(outputDir, filePath) {
|
|
628
|
+
const rel = relative(outputDir, dirname(filePath));
|
|
629
|
+
if (!rel || rel === '.') return 0;
|
|
630
|
+
return rel.split(sep).filter(Boolean).length;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/** Inject stylesheet link with correct relative href for static hosting (after prerender wrote files). */
|
|
634
|
+
function postProcessInjectStylesheetLink(outputDir, filename) {
|
|
635
|
+
const cssPath = join(outputDir, filename);
|
|
636
|
+
if (!existsSync(cssPath)) return;
|
|
637
|
+
const stat = statSync(cssPath);
|
|
638
|
+
if (stat.size === 0) return;
|
|
639
|
+
|
|
640
|
+
const files = walkHtmlFiles(outputDir);
|
|
641
|
+
for (const file of files) {
|
|
642
|
+
let html = readFileSync(file, 'utf8');
|
|
643
|
+
if (html.includes(filename)) continue;
|
|
644
|
+
const depth = depthFromOutputRoot(outputDir, file);
|
|
645
|
+
const prefix = depth ? '../'.repeat(depth) : '';
|
|
646
|
+
const tag = `<link rel="stylesheet" href="${prefix}${filename}">`;
|
|
647
|
+
html = injectAfterHeadOpen(html, tag);
|
|
648
|
+
writeFileSync(file, html, 'utf8');
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
402
652
|
// --- (Removed) We used to strip x-text containing product. / feature. to avoid wrong-scope errors
|
|
403
653
|
// on duplicated x-for output, but that also stripped legitimate loop body bindings (e.g. product
|
|
404
654
|
// search results), breaking reactivity. If "product/feature is not defined" appears again, fix
|
|
@@ -820,6 +1070,7 @@ function copyProjectIntoDist(rootResolved, outputResolved) {
|
|
|
820
1070
|
|
|
821
1071
|
async function main() {
|
|
822
1072
|
const config = resolveConfig();
|
|
1073
|
+
const startedAt = Date.now();
|
|
823
1074
|
let staticServer = null;
|
|
824
1075
|
if (config.serve) {
|
|
825
1076
|
const { server, url } = await startStaticServer(config.root);
|
|
@@ -833,6 +1084,8 @@ async function main() {
|
|
|
833
1084
|
await new Promise((res) => staticServer.close(res));
|
|
834
1085
|
}
|
|
835
1086
|
}
|
|
1087
|
+
const secs = ((Date.now() - startedAt) / 1000).toFixed(1);
|
|
1088
|
+
process.stdout.write(`prerender: total time ${secs}s\n`);
|
|
836
1089
|
}
|
|
837
1090
|
|
|
838
1091
|
async function runPrerender(config) {
|
|
@@ -851,6 +1104,12 @@ async function runPrerender(config) {
|
|
|
851
1104
|
|
|
852
1105
|
const defaultLocale = locales[0] ?? null;
|
|
853
1106
|
const routeSegments = discoverRoutes(manifest, config.root);
|
|
1107
|
+
const localeSet = new Set(locales.map((l) => String(l).toLowerCase()));
|
|
1108
|
+
const localeNeutralSegments = routeSegments.filter((seg) => {
|
|
1109
|
+
if (!seg) return true;
|
|
1110
|
+
const first = seg.split('/')[0].toLowerCase();
|
|
1111
|
+
return !localeSet.has(first);
|
|
1112
|
+
});
|
|
854
1113
|
const paths = new Set();
|
|
855
1114
|
paths.add('');
|
|
856
1115
|
|
|
@@ -859,14 +1118,15 @@ async function runPrerender(config) {
|
|
|
859
1118
|
}
|
|
860
1119
|
for (const locale of locales.slice(1)) {
|
|
861
1120
|
paths.add(locale);
|
|
862
|
-
for (const seg of
|
|
1121
|
+
for (const seg of localeNeutralSegments) {
|
|
1122
|
+
if (!seg) continue;
|
|
863
1123
|
paths.add(`${locale}/${seg}`);
|
|
864
1124
|
}
|
|
865
1125
|
}
|
|
866
1126
|
// Default locale also under its slug (e.g. /en/, /en/page-1) so linking is symmetric; canonical points to root
|
|
867
1127
|
if (defaultLocale) {
|
|
868
1128
|
paths.add(defaultLocale);
|
|
869
|
-
for (const seg of
|
|
1129
|
+
for (const seg of localeNeutralSegments) {
|
|
870
1130
|
if (seg !== '') paths.add(`${defaultLocale}/${seg}`);
|
|
871
1131
|
}
|
|
872
1132
|
}
|
|
@@ -895,6 +1155,11 @@ async function runPrerender(config) {
|
|
|
895
1155
|
mkdirSync(outputResolved, { recursive: true });
|
|
896
1156
|
copyProjectIntoDist(rootResolved, outputResolved);
|
|
897
1157
|
|
|
1158
|
+
const pre = manifest.prerender ?? {};
|
|
1159
|
+
const bundleUtilities = pre.utilitiesBundle !== false;
|
|
1160
|
+
const tailwindBuilt = runTailwindCliForPrerender(rootResolved, outputResolved, pre);
|
|
1161
|
+
const utilityBlocks = [];
|
|
1162
|
+
|
|
898
1163
|
let browser;
|
|
899
1164
|
try {
|
|
900
1165
|
const chromium = await importFromProject('@sparticuz/chromium');
|
|
@@ -1131,6 +1396,17 @@ async function runPrerender(config) {
|
|
|
1131
1396
|
let html = await page.evaluate(() => document.documentElement.outerHTML);
|
|
1132
1397
|
html = stripDevOnlyContent(html);
|
|
1133
1398
|
html = stripInjectedPluginScripts(html);
|
|
1399
|
+
if (tailwindBuilt) {
|
|
1400
|
+
html = stripRuntimeTailwindArtifacts(html);
|
|
1401
|
+
}
|
|
1402
|
+
if (bundleUtilities) {
|
|
1403
|
+
const extracted = extractUtilityStyleBlocks(html);
|
|
1404
|
+
html = extracted.html;
|
|
1405
|
+
for (const b of extracted.blocks) utilityBlocks.push(b);
|
|
1406
|
+
}
|
|
1407
|
+
if (tailwindBuilt) {
|
|
1408
|
+
html = injectAfterHeadOpen(html, '<link rel="stylesheet" href="/prerender.tailwind.css">');
|
|
1409
|
+
}
|
|
1134
1410
|
html = stripDuplicatedLoopDirectives(html);
|
|
1135
1411
|
html = stripPrerenderedXDataDirectives(html);
|
|
1136
1412
|
const currentLocale =
|
|
@@ -1178,6 +1454,15 @@ async function runPrerender(config) {
|
|
|
1178
1454
|
await browser.close();
|
|
1179
1455
|
}
|
|
1180
1456
|
|
|
1457
|
+
if (bundleUtilities) {
|
|
1458
|
+
const utilMerged = mergeUtilityCssBlocks(utilityBlocks);
|
|
1459
|
+
if (utilMerged.trim()) {
|
|
1460
|
+
writeFileSync(join(outputResolved, 'prerender.utilities.css'), `${utilMerged}\n`, 'utf8');
|
|
1461
|
+
process.stdout.write('prerender: wrote prerender.utilities.css (Manifest custom utilities)\n');
|
|
1462
|
+
postProcessInjectStylesheetLink(outputResolved, 'prerender.utilities.css');
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1181
1466
|
writeSeoFiles(
|
|
1182
1467
|
config.output,
|
|
1183
1468
|
pathList.filter((p) => p !== NOT_FOUND_PATH),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mnfst-render",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "Render Manifest sites to static HTML for SEO",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -32,4 +32,4 @@
|
|
|
32
32
|
"url": "git+https://github.com/andrewmatlock/Manifest.git",
|
|
33
33
|
"directory": "packages/render"
|
|
34
34
|
}
|
|
35
|
-
}
|
|
35
|
+
}
|