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.
- package/dist/ui-cli.cjs +281 -29
- package/package.json +1 -1
- package/registry.json +91 -7
- package/scripts/build-registry.ts +1 -1
- package/scripts/ui-cli.ts +334 -28
|
@@ -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', '
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
'ThemeProvider in
|
|
903
|
-
|
|
904
|
-
|
|
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
|
-
|
|
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
|
-
|
|
918
|
-
|
|
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
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
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) {
|