basuicn 0.1.9 → 0.2.3

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.
@@ -5,7 +5,7 @@ const COMPONENTS_DIR = './src/components/ui';
5
5
  const OUTPUT_FILE = './registry.json';
6
6
 
7
7
  // Directories to exclude from registry (not reusable components)
8
- const EXCLUDE_DIRS = new Set(['icons', 'layout', 'vs-code', 'pretty-code', 'Showcase.tsx']);
8
+ const EXCLUDE_DIRS = new Set(['icons', 'layout', 'vs-code', 'Showcase.tsx']);
9
9
 
10
10
  interface RegistryFile {
11
11
  path: string;
package/scripts/ui-cli.ts CHANGED
@@ -6,7 +6,7 @@ import readline from 'readline';
6
6
 
7
7
  // ─── Constants ────────────────────────────────────────────────────────────────
8
8
 
9
- const VERSION = '0.1.9';
9
+ const VERSION = '0.2.3';
10
10
  const REGISTRY_LOCAL = './registry.json';
11
11
  const REGISTRY_REMOTE = 'https://raw.githubusercontent.com/Basuicn/basuicn-core/main/registry.json';
12
12
 
@@ -32,6 +32,40 @@ const error = (msg: string) => console.error(`${c.red}✖${c.reset} ${msg}`);
32
32
 
33
33
  const getTargetProjectDir = () => process.cwd();
34
34
 
35
+ // ─── Framework detection ──────────────────────────────────────────────────────
36
+
37
+ type Framework = 'vite' | 'nextjs-app' | 'nextjs-pages';
38
+
39
+ const detectFramework = (cwd: string): Framework => {
40
+ const hasNextConfig =
41
+ fs.existsSync(path.join(cwd, 'next.config.js')) ||
42
+ fs.existsSync(path.join(cwd, 'next.config.ts')) ||
43
+ fs.existsSync(path.join(cwd, 'next.config.mjs'));
44
+
45
+ const hasNextDep = (() => {
46
+ const pkgPath = path.join(cwd, 'package.json');
47
+ if (!fs.existsSync(pkgPath)) return false;
48
+ try {
49
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')) as Record<string, unknown>;
50
+ const all = { ...(pkg.dependencies as object || {}), ...(pkg.devDependencies as object || {}) } as Record<string, string>;
51
+ return !!all['next'];
52
+ } catch { return false; }
53
+ })();
54
+
55
+ if (hasNextConfig || hasNextDep) {
56
+ const hasAppDir =
57
+ fs.existsSync(path.join(cwd, 'src/app')) ||
58
+ fs.existsSync(path.join(cwd, 'app'));
59
+ const hasPagesDir =
60
+ fs.existsSync(path.join(cwd, 'src/pages')) ||
61
+ fs.existsSync(path.join(cwd, 'pages'));
62
+ if (hasPagesDir && !hasAppDir) return 'nextjs-pages';
63
+ return 'nextjs-app';
64
+ }
65
+
66
+ return 'vite';
67
+ };
68
+
35
69
  // ─── Interactive prompt ──────────────────────────────────────────────────────
36
70
 
37
71
  const ask = (question: string): Promise<string> => {
@@ -146,6 +180,21 @@ const RUNTIME_PACKAGES = [
146
180
  'lucide-react',
147
181
  ];
148
182
 
183
+ const NEXTJS_DEV_PACKAGES = [
184
+ 'tailwindcss',
185
+ '@tailwindcss/postcss',
186
+ '@types/node',
187
+ ];
188
+
189
+ const POSTCSS_CONFIG_TEMPLATE = `const config = {
190
+ plugins: {
191
+ '@tailwindcss/postcss': {},
192
+ },
193
+ };
194
+
195
+ export default config;
196
+ `;
197
+
149
198
  // ─── Vite config ──────────────────────────────────────────────────────────────
150
199
 
151
200
  const VITE_CONFIG_TEMPLATE = `import { defineConfig } from 'vite';
@@ -266,6 +315,28 @@ const setupViteConfig = (cwd: string) => {
266
315
  ok(`Updated ${path.basename(existingPath)} with Tailwind + path aliases.`);
267
316
  };
268
317
 
318
+ // ─── Next.js config ───────────────────────────────────────────────────────────
319
+
320
+ const setupNextConfig = (cwd: string) => {
321
+ installNpmPackages(NEXTJS_DEV_PACKAGES, cwd, true);
322
+
323
+ const postcssCandidates = ['postcss.config.mjs', 'postcss.config.js', 'postcss.config.cjs'];
324
+ const existingPostcss = postcssCandidates.map(f => path.join(cwd, f)).find(p => fs.existsSync(p));
325
+
326
+ if (!existingPostcss) {
327
+ fs.writeFileSync(path.join(cwd, 'postcss.config.mjs'), POSTCSS_CONFIG_TEMPLATE);
328
+ ok('Created postcss.config.mjs with @tailwindcss/postcss.');
329
+ return;
330
+ }
331
+
332
+ const content = fs.readFileSync(existingPostcss, 'utf-8');
333
+ if (!content.includes('@tailwindcss/postcss') && !content.includes('tailwindcss')) {
334
+ warn(`${path.basename(existingPostcss)} found but missing Tailwind plugin — add '@tailwindcss/postcss': {} to plugins manually.`);
335
+ } else {
336
+ ok(`${path.basename(existingPostcss)} already configured — skipping.`);
337
+ }
338
+ };
339
+
269
340
  // ─── tsconfig ─────────────────────────────────────────────────────────────────
270
341
 
271
342
  const setupTsConfig = (cwd: string) => {
@@ -339,6 +410,178 @@ const ensureCore = (
339
410
  }
340
411
  };
341
412
 
413
+ // ─── Next.js layout patching ─────────────────────────────────────────────────
414
+
415
+ const NEXT_APP_LAYOUT_CANDIDATES = [
416
+ 'src/app/layout.tsx', 'src/app/layout.jsx',
417
+ 'app/layout.tsx', 'app/layout.jsx',
418
+ ];
419
+
420
+ const NEXT_PAGES_APP_CANDIDATES = [
421
+ 'src/pages/_app.tsx', 'src/pages/_app.jsx',
422
+ 'pages/_app.tsx', 'pages/_app.jsx',
423
+ ];
424
+
425
+ const findNextLayoutFile = (cwd: string): string | null => {
426
+ for (const c of NEXT_APP_LAYOUT_CANDIDATES) {
427
+ const p = path.join(cwd, c);
428
+ if (fs.existsSync(p)) return p;
429
+ }
430
+ return null;
431
+ };
432
+
433
+ const findNextPagesAppFile = (cwd: string): string | null => {
434
+ for (const c of NEXT_PAGES_APP_CANDIDATES) {
435
+ const p = path.join(cwd, c);
436
+ if (fs.existsSync(p)) return p;
437
+ }
438
+ return null;
439
+ };
440
+
441
+ const patchNextLayout = (cwd: string) => {
442
+ const layoutPath = findNextLayoutFile(cwd);
443
+ if (!layoutPath) {
444
+ warn('Could not find app/layout.tsx — add ThemeProvider and CSS import manually.');
445
+ return;
446
+ }
447
+
448
+ let content = fs.readFileSync(layoutPath, 'utf-8');
449
+ let changed = false;
450
+
451
+ // CSS import
452
+ const cssImport = "import '@/styles/index.css';";
453
+ if (!content.includes('styles/index.css') && !content.includes('index.css')) {
454
+ const firstImport = content.match(/^import\s/m);
455
+ if (firstImport?.index !== undefined) {
456
+ content = content.slice(0, firstImport.index) + cssImport + '\n' + content.slice(firstImport.index);
457
+ } else {
458
+ content = cssImport + '\n' + content;
459
+ }
460
+ changed = true;
461
+ }
462
+
463
+ // ThemeProvider
464
+ if (!content.includes('ThemeProvider')) {
465
+ content = insertImport(content, "import { ThemeProvider } from '@/lib/theme/ThemeProvider';");
466
+ // Wrap {children} with ThemeProvider
467
+ const wrapped = content.replace(/\{children\}/, '<ThemeProvider>{children}</ThemeProvider>');
468
+ if (wrapped !== content) {
469
+ content = wrapped;
470
+ } else {
471
+ warn('Could not locate {children} in layout.tsx — add <ThemeProvider> wrapper manually.');
472
+ }
473
+ changed = true;
474
+ }
475
+
476
+ if (changed) {
477
+ fs.writeFileSync(layoutPath, content);
478
+ ok(`Patched ${path.relative(cwd, layoutPath)}.`);
479
+ } else {
480
+ ok(`${path.relative(cwd, layoutPath)} already configured — skipping.`);
481
+ }
482
+ };
483
+
484
+ const patchNextPagesApp = (cwd: string) => {
485
+ const appPath = findNextPagesAppFile(cwd);
486
+ if (!appPath) {
487
+ warn('Could not find pages/_app.tsx — add ThemeProvider and CSS import manually.');
488
+ return;
489
+ }
490
+
491
+ let content = fs.readFileSync(appPath, 'utf-8');
492
+ let changed = false;
493
+
494
+ // CSS import
495
+ const cssImport = "import '@/styles/index.css';";
496
+ if (!content.includes('styles/index.css') && !content.includes('index.css')) {
497
+ content = insertImport(content, cssImport);
498
+ changed = true;
499
+ }
500
+
501
+ // ThemeProvider
502
+ if (!content.includes('ThemeProvider')) {
503
+ content = insertImport(content, "import { ThemeProvider } from '@/lib/theme/ThemeProvider';");
504
+ const wrapped = content.replace(/(<Component\s[^/]*\/\s*>)/, '<ThemeProvider>\n $1\n </ThemeProvider>');
505
+ if (wrapped !== content) {
506
+ content = wrapped;
507
+ } else {
508
+ warn('Could not locate <Component .../> in _app.tsx — add <ThemeProvider> wrapper manually.');
509
+ }
510
+ changed = true;
511
+ }
512
+
513
+ if (changed) {
514
+ fs.writeFileSync(appPath, content);
515
+ ok(`Patched ${path.relative(cwd, appPath)}.`);
516
+ } else {
517
+ ok(`${path.relative(cwd, appPath)} already configured — skipping.`);
518
+ }
519
+ };
520
+
521
+ const patchNextLayoutComponent = (cwd: string, patch: { import: string; jsx: string }) => {
522
+ const layoutPath = findNextLayoutFile(cwd);
523
+ if (!layoutPath) {
524
+ warn(`Could not find Next.js layout — add ${patch.jsx} manually.`);
525
+ return;
526
+ }
527
+
528
+ let content = fs.readFileSync(layoutPath, 'utf-8');
529
+ const tagName = patch.jsx.match(/<(\w+)/)?.[1];
530
+ if (tagName && content.includes(`<${tagName}`)) return;
531
+
532
+ content = insertImport(content, patch.import);
533
+ // Insert before </body>
534
+ const updated = content.replace(/<\/body>/, ` ${patch.jsx}\n </body>`);
535
+ if (updated !== content) {
536
+ fs.writeFileSync(layoutPath, updated);
537
+ ok(`Added <${tagName}> to ${path.relative(cwd, layoutPath)}.`);
538
+ } else {
539
+ warn(`Could not auto-add <${tagName}> to layout.tsx — add it manually.`);
540
+ }
541
+ };
542
+
543
+ const patchNextPagesAppComponent = (cwd: string, patch: { import: string; jsx: string }) => {
544
+ const appPath = findNextPagesAppFile(cwd);
545
+ if (!appPath) {
546
+ warn(`Could not find pages/_app.tsx — add ${patch.jsx} manually.`);
547
+ return;
548
+ }
549
+
550
+ let content = fs.readFileSync(appPath, 'utf-8');
551
+ const tagName = patch.jsx.match(/<(\w+)/)?.[1];
552
+ if (tagName && content.includes(`<${tagName}`)) return;
553
+
554
+ content = insertImport(content, patch.import);
555
+ // Insert after <Component .../>
556
+ let updated = content.replace(
557
+ /(<Component\s[^/]*\/\s*>)(\s*\n\s*<\/ThemeProvider>)/,
558
+ `$1\n ${patch.jsx}$2`
559
+ );
560
+ if (updated === content) {
561
+ updated = content.replace(/(<Component\s[^/]*\/\s*>)/, `$1\n ${patch.jsx}`);
562
+ }
563
+ if (updated !== content) {
564
+ fs.writeFileSync(appPath, updated);
565
+ ok(`Added <${tagName}> to ${path.relative(cwd, appPath)}.`);
566
+ } else {
567
+ warn(`Could not auto-add <${tagName}> to _app.tsx — add it manually.`);
568
+ }
569
+ };
570
+
571
+ const patchEntryFile = (cwd: string, framework: Framework) => {
572
+ if (framework === 'nextjs-app') patchNextLayout(cwd);
573
+ else if (framework === 'nextjs-pages') patchNextPagesApp(cwd);
574
+ else patchMainTsx(cwd);
575
+ };
576
+
577
+ const patchEntryComponentFile = (cwd: string, componentName: string, framework: Framework) => {
578
+ const patch = MAIN_PATCH_COMPONENTS[componentName];
579
+ if (!patch) return;
580
+ if (framework === 'nextjs-app') patchNextLayoutComponent(cwd, patch);
581
+ else if (framework === 'nextjs-pages') patchNextPagesAppComponent(cwd, patch);
582
+ else patchMainTsxComponent(cwd, componentName);
583
+ };
584
+
342
585
  // ─── main.tsx patching ────────────────────────────────────────────────────────
343
586
 
344
587
  const MAIN_PATCH_COMPONENTS: Record<string, { import: string; jsx: string }> = {
@@ -448,7 +691,7 @@ const addComponent = (
448
691
  name: string,
449
692
  registry: { core?: unknown; components: Record<string, RegistryComponent> },
450
693
  cwd: string,
451
- options: { force: boolean },
694
+ options: { force: boolean; framework?: Framework },
452
695
  added: Set<string> = new Set()
453
696
  ) => {
454
697
  if (added.has(name)) return;
@@ -484,7 +727,15 @@ const addComponent = (
484
727
  continue;
485
728
  }
486
729
 
487
- fs.writeFileSync(targetPath, file.content);
730
+ // Add 'use client' for Next.js App Router TSX components that don't already have it
731
+ let content = file.content;
732
+ if (options.framework === 'nextjs-app' && file.path.endsWith('.tsx')) {
733
+ if (!content.startsWith("'use client'") && !content.startsWith('"use client"')) {
734
+ content = "'use client';\n" + content;
735
+ }
736
+ }
737
+
738
+ fs.writeFileSync(targetPath, content);
488
739
  ok(`Created: ${file.path}`);
489
740
  }
490
741
  };
@@ -564,13 +815,21 @@ const HELP_COMMANDS: Record<string, string> = {
564
815
  ${c.bold}basuicn init${c.reset}
565
816
 
566
817
  Initialize your project for basuicn components.
818
+ Auto-detects Vite or Next.js (App Router / Pages Router).
567
819
 
568
- ${c.bold}What it does:${c.reset}
820
+ ${c.bold}What it does (Vite):${c.reset}
569
821
  1. Installs runtime dependencies (@base-ui/react, tailwind-variants, etc.)
570
822
  2. Sets up vite.config.ts with Tailwind CSS + path aliases
571
823
  3. Patches tsconfig.json with path aliases (@/*, @lib/*, etc.)
572
824
  4. Copies core files (cn.ts, themes.ts, ThemeProvider.tsx, index.css)
573
- 5. Wraps your <App /> with <ThemeProvider> in the main entry
825
+ 5. Wraps your <App /> with <ThemeProvider> in src/main.tsx
826
+
827
+ ${c.bold}What it does (Next.js):${c.reset}
828
+ 1. Installs runtime dependencies (@base-ui/react, tailwind-variants, etc.)
829
+ 2. Sets up postcss.config.mjs with @tailwindcss/postcss
830
+ 3. Patches tsconfig.json with path aliases (@/*, @lib/*, etc.)
831
+ 4. Copies core files (cn.ts, themes.ts, ThemeProvider.tsx, index.css)
832
+ 5. Wraps {children} with <ThemeProvider> in app/layout.tsx (or pages/_app.tsx)
574
833
 
575
834
  ${c.bold}Usage:${c.reset}
576
835
  ${c.dim}$${c.reset} npx basuicn init
@@ -693,11 +952,17 @@ const main = async () => {
693
952
 
694
953
  case 'init': {
695
954
  log('Initializing project...');
696
- setupViteConfig(cwd);
955
+ const framework = detectFramework(cwd);
956
+ if (framework === 'vite') {
957
+ setupViteConfig(cwd);
958
+ } else {
959
+ log(`Detected Next.js (${framework === 'nextjs-app' ? 'App Router' : 'Pages Router'}).`);
960
+ setupNextConfig(cwd);
961
+ }
697
962
  setupTsConfig(cwd);
698
963
  installNpmPackages(RUNTIME_PACKAGES, cwd);
699
964
  ensureCore(registry, cwd, { force: true });
700
- patchMainTsx(cwd);
965
+ patchEntryFile(cwd, framework);
701
966
  console.log('');
702
967
  ok(`${c.bold}Initialization complete!${c.reset} Run ${c.cyan}npx basuicn add <component>${c.reset} to get started.`);
703
968
  break;
@@ -737,19 +1002,25 @@ const main = async () => {
737
1002
 
738
1003
  // Auto-init if project hasn't been initialized yet
739
1004
  const cnPath = path.join(cwd, 'src/lib/utils/cn.ts');
1005
+ const framework = detectFramework(cwd);
740
1006
  if (!fs.existsSync(cnPath)) {
741
1007
  log('Project not initialized — running init first...');
742
- setupViteConfig(cwd);
1008
+ if (framework === 'vite') {
1009
+ setupViteConfig(cwd);
1010
+ } else {
1011
+ log(`Detected Next.js (${framework === 'nextjs-app' ? 'App Router' : 'Pages Router'}).`);
1012
+ setupNextConfig(cwd);
1013
+ }
743
1014
  setupTsConfig(cwd);
744
1015
  installNpmPackages(RUNTIME_PACKAGES, cwd);
745
1016
  ensureCore(registry, cwd, { force: true });
746
- patchMainTsx(cwd);
1017
+ patchEntryFile(cwd, framework);
747
1018
  console.log('');
748
1019
  }
749
1020
 
750
1021
  for (const name of names) {
751
- addComponent(name, registry, cwd, { force: isForce });
752
- patchMainTsxComponent(cwd, name);
1022
+ addComponent(name, registry, cwd, { force: isForce, framework });
1023
+ patchEntryComponentFile(cwd, name, framework);
753
1024
  }
754
1025
  console.log('');
755
1026
  ok(`${c.bold}Done!${c.reset} Added ${names.length} component(s).`);
@@ -884,6 +1155,11 @@ const main = async () => {
884
1155
  if (!passed) { if (fix) console.log(` ${c.dim}→ ${fix}${c.reset}`); issues++; }
885
1156
  };
886
1157
 
1158
+ const docFramework = detectFramework(cwd);
1159
+ if (docFramework !== 'vite') {
1160
+ console.log(` ${c.cyan}ℹ${c.reset} Framework: Next.js (${docFramework === 'nextjs-app' ? 'App Router' : 'Pages Router'})\n`);
1161
+ }
1162
+
887
1163
  // Core files
888
1164
  check(fs.existsSync(path.join(cwd, 'src/lib/utils/cn.ts')),
889
1165
  'src/lib/utils/cn.ts', 'run: npx basuicn init');
@@ -894,28 +1170,52 @@ const main = async () => {
894
1170
  check(fs.existsSync(path.join(cwd, 'src/styles/index.css')),
895
1171
  'src/styles/index.css (theme variables)', 'run: npx basuicn init');
896
1172
 
897
- // Main entry
898
- const mainPath = findMainFile(cwd);
899
- if (mainPath) {
900
- const mainContent = fs.readFileSync(mainPath, 'utf-8');
901
- check(mainContent.includes('ThemeProvider'),
902
- 'ThemeProvider in main entry', 'run: npx basuicn init');
903
- check(mainContent.includes('styles/index.css') || mainContent.includes('index.css'),
904
- 'CSS import in main entry', 'run: npx basuicn init');
1173
+ // Entry file check (framework-aware)
1174
+ if (docFramework === 'nextjs-app') {
1175
+ const layoutPath = findNextLayoutFile(cwd);
1176
+ if (layoutPath) {
1177
+ const layoutContent = fs.readFileSync(layoutPath, 'utf-8');
1178
+ check(layoutContent.includes('ThemeProvider'), 'ThemeProvider in app/layout.tsx', 'run: npx basuicn init');
1179
+ check(layoutContent.includes('styles/index.css') || layoutContent.includes('index.css'), 'CSS import in app/layout.tsx', 'run: npx basuicn init');
1180
+ } else {
1181
+ check(false, 'app/layout.tsx', 'create src/app/layout.tsx');
1182
+ }
1183
+ } else if (docFramework === 'nextjs-pages') {
1184
+ const pagesAppPath = findNextPagesAppFile(cwd);
1185
+ if (pagesAppPath) {
1186
+ const pagesAppContent = fs.readFileSync(pagesAppPath, 'utf-8');
1187
+ check(pagesAppContent.includes('ThemeProvider'), 'ThemeProvider in pages/_app.tsx', 'run: npx basuicn init');
1188
+ check(pagesAppContent.includes('styles/index.css') || pagesAppContent.includes('index.css'), 'CSS import in pages/_app.tsx', 'run: npx basuicn init');
1189
+ } else {
1190
+ check(false, 'pages/_app.tsx', 'create pages/_app.tsx');
1191
+ }
905
1192
  } else {
906
- check(false, 'main entry file (src/main.tsx)', 'create src/main.tsx');
1193
+ const mainPath = findMainFile(cwd);
1194
+ if (mainPath) {
1195
+ const mainContent = fs.readFileSync(mainPath, 'utf-8');
1196
+ check(mainContent.includes('ThemeProvider'), 'ThemeProvider in main entry', 'run: npx basuicn init');
1197
+ check(mainContent.includes('styles/index.css') || mainContent.includes('index.css'), 'CSS import in main entry', 'run: npx basuicn init');
1198
+ } else {
1199
+ check(false, 'main entry file (src/main.tsx)', 'create src/main.tsx');
1200
+ }
907
1201
  }
908
1202
 
909
1203
  // Runtime packages
910
1204
  const pkgPath = path.join(cwd, 'package.json');
911
1205
  if (fs.existsSync(pkgPath)) {
912
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
913
- const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
1206
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')) as Record<string, unknown>;
1207
+ const allDeps = { ...(pkg.dependencies as object || {}), ...(pkg.devDependencies as object || {}) } as Record<string, string>;
914
1208
  for (const dep of RUNTIME_PACKAGES) {
915
1209
  check(!!allDeps[dep], `package: ${dep}`, `run: npm install ${dep}`);
916
1210
  }
917
- for (const dep of VITE_DEV_PACKAGES) {
918
- check(!!allDeps[dep], `package (dev): ${dep}`, `run: npm install -D ${dep}`);
1211
+ if (docFramework === 'vite') {
1212
+ for (const dep of VITE_DEV_PACKAGES) {
1213
+ check(!!allDeps[dep], `package (dev): ${dep}`, `run: npm install -D ${dep}`);
1214
+ }
1215
+ } else {
1216
+ for (const dep of NEXTJS_DEV_PACKAGES) {
1217
+ check(!!allDeps[dep], `package (dev): ${dep}`, `run: npm install -D ${dep}`);
1218
+ }
919
1219
  }
920
1220
  } else {
921
1221
  check(false, 'package.json found', 'run: npm init -y');
@@ -942,10 +1242,16 @@ const main = async () => {
942
1242
  });
943
1243
  check(hasAlias, 'TypeScript path aliases (@/*)', 'run: npx basuicn init');
944
1244
 
945
- const hasViteConfig =
946
- fs.existsSync(path.join(cwd, 'vite.config.ts')) ||
947
- fs.existsSync(path.join(cwd, 'vite.config.js'));
948
- check(hasViteConfig, 'vite.config.ts / vite.config.js', 'run: npx basuicn init');
1245
+ if (docFramework === 'vite') {
1246
+ const hasViteConfig =
1247
+ fs.existsSync(path.join(cwd, 'vite.config.ts')) ||
1248
+ fs.existsSync(path.join(cwd, 'vite.config.js'));
1249
+ check(hasViteConfig, 'vite.config.ts / vite.config.js', 'run: npx basuicn init');
1250
+ } else {
1251
+ const hasPostcss = ['postcss.config.mjs', 'postcss.config.js', 'postcss.config.cjs']
1252
+ .some(f => fs.existsSync(path.join(cwd, f)));
1253
+ check(hasPostcss, 'postcss.config.mjs (Tailwind CSS for Next.js)', 'run: npx basuicn init');
1254
+ }
949
1255
 
950
1256
  console.log('');
951
1257
  if (issues === 0) {