mnfst-render 0.1.7 → 0.1.9

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.
Files changed (2) hide show
  1. package/manifest.render.mjs +78 -11
  2. package/package.json +1 -1
@@ -2,7 +2,7 @@
2
2
 
3
3
  /* Manifest Render */
4
4
 
5
- import { readFileSync, mkdirSync, writeFileSync, existsSync, rmSync, statSync, readdirSync, cpSync, unlinkSync } from 'node:fs';
5
+ import { readFileSync, readSync, mkdirSync, writeFileSync, existsSync, rmSync, statSync, readdirSync, cpSync, unlinkSync } from 'node:fs';
6
6
  import { spawnSync } from 'node:child_process';
7
7
  import { join, resolve, dirname, relative, basename, sep } from 'node:path';
8
8
  import { createServer } from 'node:http';
@@ -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 addFilePaths(value) {
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
- if (shouldIncludeDataPath(p)) paths.add('/' + p);
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
- if (shouldIncludeDataPath(normalized)) paths.add('/' + normalized);
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
- if (shouldIncludeDataPath(normalized)) paths.add('/' + normalized);
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.values(data)) {
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
  }
@@ -472,6 +496,38 @@ function indexHtmlUsesTailwind(rootDir) {
472
496
  return /\sdata-tailwind(?:=(["']).*?\1)?/i.test(html) && /<script[^>]*manifest\.min\.js/i.test(html);
473
497
  }
474
498
 
499
+ function promptContinueWithRuntimeTailwind(rootDir) {
500
+ const installMsg = [
501
+ 'prerender: tailwindcss package is not installed for this project.',
502
+ '',
503
+ 'To enable static Tailwind CSS compilation, install:',
504
+ ' npm i -D tailwindcss @tailwindcss/cli',
505
+ '',
506
+ `Project: ${rootDir}`,
507
+ '',
508
+ 'Continue prerender with runtime data-tailwind instead? [P]roceed/[E]nd (default: P): ',
509
+ ].join('\n');
510
+ process.stdout.write(`${installMsg}\n`);
511
+
512
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
513
+ process.stdout.write(
514
+ 'prerender: non-interactive terminal detected; continuing with runtime data-tailwind behavior.\n'
515
+ );
516
+ return true;
517
+ }
518
+ const buf = Buffer.alloc(1);
519
+ let answer = '';
520
+ while (true) {
521
+ const n = readSync(0, buf, 0, 1, null);
522
+ if (n <= 0) break;
523
+ const ch = buf.toString('utf8', 0, n);
524
+ if (ch === '\n' || ch === '\r') break;
525
+ answer += ch;
526
+ }
527
+ const normalized = answer.trim().toLowerCase();
528
+ return normalized === '' || normalized === 'p' || normalized === 'proceed' || normalized === 'y' || normalized === 'yes';
529
+ }
530
+
475
531
  /**
476
532
  * Build a static Tailwind stylesheet via @tailwindcss/cli (v4+), scanning project sources.
477
533
  * Only runs when the project opts in (data-tailwind on manifest script) or manifest.prerender.tailwind === true.
@@ -483,6 +539,16 @@ function runTailwindCliForPrerender(rootDir, outputDir, pre) {
483
539
  if (!usesTailwind) return false;
484
540
 
485
541
  const outCss = join(outputDir, 'prerender.tailwind.css');
542
+ try {
543
+ require.resolve('tailwindcss', { paths: [rootDir] });
544
+ } catch {
545
+ const proceed = promptContinueWithRuntimeTailwind(rootDir);
546
+ if (!proceed) {
547
+ throw new Error('prerender aborted: install tailwindcss/@tailwindcss/cli or disable prerender.tailwind.');
548
+ }
549
+ process.stdout.write('prerender: continuing with runtime data-tailwind behavior.\n');
550
+ return false;
551
+ }
486
552
  let inputPath = null;
487
553
  let createdTempInput = false;
488
554
  const userInput = pre?.tailwindInput;
@@ -498,9 +564,8 @@ function runTailwindCliForPrerender(rootDir, outputDir, pre) {
498
564
  const outputBasename = basename(outputDir);
499
565
  const defaultContent = [
500
566
  '**/*.html',
501
- '**/*.{js,mjs,css}',
502
- '**/*.json',
503
567
  '!**/node_modules/**',
568
+ '!**/dist/**',
504
569
  `!**/${outputBasename}/**`,
505
570
  ];
506
571
  const contentGlobs = Array.isArray(pre?.tailwindContent) && pre.tailwindContent.length > 0
@@ -1354,7 +1419,9 @@ async function runPrerender(config) {
1354
1419
  let html = await page.evaluate(() => document.documentElement.outerHTML);
1355
1420
  html = stripDevOnlyContent(html);
1356
1421
  html = stripInjectedPluginScripts(html);
1357
- html = stripRuntimeTailwindArtifacts(html);
1422
+ if (tailwindBuilt) {
1423
+ html = stripRuntimeTailwindArtifacts(html);
1424
+ }
1358
1425
  if (bundleUtilities) {
1359
1426
  const extracted = extractUtilityStyleBlocks(html);
1360
1427
  html = extracted.html;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mnfst-render",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Render Manifest sites to static HTML for SEO",
5
5
  "type": "module",
6
6
  "bin": {