bunkit-cli 1.0.2 → 1.1.0

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/dist/index.js +682 -440
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -11215,7 +11215,7 @@ function normalizeString(path, allowAboveRoot) {
11215
11215
  }
11216
11216
  return res;
11217
11217
  }
11218
- var _DRIVE_LETTER_START_RE, _UNC_REGEX, _IS_ABSOLUTE_RE, _DRIVE_LETTER_RE, normalize = function(path) {
11218
+ var _DRIVE_LETTER_START_RE, _UNC_REGEX, _IS_ABSOLUTE_RE, _DRIVE_LETTER_RE, _ROOT_FOLDER_RE, sep = "/", delimiter = ":", normalize = function(path) {
11219
11219
  if (path.length === 0) {
11220
11220
  return ".";
11221
11221
  }
@@ -11280,24 +11280,95 @@ var _DRIVE_LETTER_START_RE, _UNC_REGEX, _IS_ABSOLUTE_RE, _DRIVE_LETTER_RE, norma
11280
11280
  return resolvedPath.length > 0 ? resolvedPath : ".";
11281
11281
  }, isAbsolute = function(p2) {
11282
11282
  return _IS_ABSOLUTE_RE.test(p2);
11283
+ }, toNamespacedPath = function(p2) {
11284
+ return normalizeWindowsPath(p2);
11285
+ }, _EXTNAME_RE, extname = function(p2) {
11286
+ const match = _EXTNAME_RE.exec(normalizeWindowsPath(p2));
11287
+ return match && match[1] || "";
11288
+ }, relative = function(from, to) {
11289
+ const _from = resolve(from).replace(_ROOT_FOLDER_RE, "$1").split("/");
11290
+ const _to = resolve(to).replace(_ROOT_FOLDER_RE, "$1").split("/");
11291
+ if (_to[0][1] === ":" && _from[0][1] === ":" && _from[0] !== _to[0]) {
11292
+ return _to.join("/");
11293
+ }
11294
+ const _fromCopy = [..._from];
11295
+ for (const segment of _fromCopy) {
11296
+ if (_to[0] !== segment) {
11297
+ break;
11298
+ }
11299
+ _from.shift();
11300
+ _to.shift();
11301
+ }
11302
+ return [..._from.map(() => ".."), ..._to].join("/");
11283
11303
  }, dirname = function(p2) {
11284
11304
  const segments = normalizeWindowsPath(p2).replace(/\/$/, "").split("/").slice(0, -1);
11285
11305
  if (segments.length === 1 && _DRIVE_LETTER_RE.test(segments[0])) {
11286
11306
  segments[0] += "/";
11287
11307
  }
11288
11308
  return segments.join("/") || (isAbsolute(p2) ? "/" : ".");
11309
+ }, format = function(p2) {
11310
+ const segments = [p2.root, p2.dir, p2.base ?? p2.name + p2.ext].filter(Boolean);
11311
+ return normalizeWindowsPath(p2.root ? resolve(...segments) : segments.join("/"));
11289
11312
  }, basename = function(p2, extension) {
11290
11313
  const lastSegment = normalizeWindowsPath(p2).split("/").pop();
11291
11314
  return extension && lastSegment.endsWith(extension) ? lastSegment.slice(0, -extension.length) : lastSegment;
11292
- };
11315
+ }, parse = function(p2) {
11316
+ const root = normalizeWindowsPath(p2).split("/").shift() || "/";
11317
+ const base = basename(p2);
11318
+ const extension = extname(base);
11319
+ return {
11320
+ root,
11321
+ dir: dirname(p2),
11322
+ base,
11323
+ ext: extension,
11324
+ name: base.slice(0, base.length - extension.length)
11325
+ };
11326
+ }, path;
11293
11327
  var init_pathe_ff20891b = __esm(() => {
11294
11328
  _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
11295
11329
  _UNC_REGEX = /^[/\\]{2}/;
11296
11330
  _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
11297
11331
  _DRIVE_LETTER_RE = /^[A-Za-z]:$/;
11332
+ _ROOT_FOLDER_RE = /^\/([A-Za-z]:)?$/;
11333
+ _EXTNAME_RE = /.(\.[^./]+)$/;
11334
+ path = {
11335
+ __proto__: null,
11336
+ basename,
11337
+ delimiter,
11338
+ dirname,
11339
+ extname,
11340
+ format,
11341
+ isAbsolute,
11342
+ join,
11343
+ normalize,
11344
+ normalizeString,
11345
+ parse,
11346
+ relative,
11347
+ resolve,
11348
+ sep,
11349
+ toNamespacedPath
11350
+ };
11298
11351
  });
11299
11352
 
11300
11353
  // ../../node_modules/pathe/dist/index.mjs
11354
+ var exports_dist = {};
11355
+ __export(exports_dist, {
11356
+ toNamespacedPath: () => toNamespacedPath,
11357
+ sep: () => sep,
11358
+ resolve: () => resolve,
11359
+ relative: () => relative,
11360
+ parse: () => parse,
11361
+ normalizeString: () => normalizeString,
11362
+ normalize: () => normalize,
11363
+ join: () => join,
11364
+ isAbsolute: () => isAbsolute,
11365
+ format: () => format,
11366
+ extname: () => extname,
11367
+ dirname: () => dirname,
11368
+ delimiter: () => delimiter,
11369
+ default: () => path,
11370
+ basename: () => basename
11371
+ });
11301
11372
  var init_dist = __esm(() => {
11302
11373
  init_pathe_ff20891b();
11303
11374
  });
@@ -18563,7 +18634,7 @@ async function isGitAvailable() {
18563
18634
  }
18564
18635
  }
18565
18636
  async function initGit(cwd2) {
18566
- await execa("git", ["init"], { cwd: cwd2 });
18637
+ await execa("git", ["init", "--initial-branch=main"], { cwd: cwd2 });
18567
18638
  await execa("git", ["add", "-A"], { cwd: cwd2 });
18568
18639
  try {
18569
18640
  await execa("git", ["commit", "-m", "Initial commit from bunkit", "--no-verify"], { cwd: cwd2 });
@@ -26251,77 +26322,116 @@ var themes = {
26251
26322
  };
26252
26323
  function generateThemeCSS(theme, customRadius) {
26253
26324
  const radius = customRadius || theme.light.radius;
26254
- return `@layer base {
26255
- :root {
26256
- --radius: ${radius};
26257
- --background: ${theme.light.background};
26258
- --foreground: ${theme.light.foreground};
26259
- --card: ${theme.light.card};
26260
- --card-foreground: ${theme.light.cardForeground};
26261
- --popover: ${theme.light.popover};
26262
- --popover-foreground: ${theme.light.popoverForeground};
26263
- --primary: ${theme.light.primary};
26264
- --primary-foreground: ${theme.light.primaryForeground};
26265
- --secondary: ${theme.light.secondary};
26266
- --secondary-foreground: ${theme.light.secondaryForeground};
26267
- --muted: ${theme.light.muted};
26268
- --muted-foreground: ${theme.light.mutedForeground};
26269
- --accent: ${theme.light.accent};
26270
- --accent-foreground: ${theme.light.accentForeground};
26271
- --destructive: ${theme.light.destructive};
26272
- --destructive-foreground: ${theme.light.destructiveForeground};
26273
- --border: ${theme.light.border};
26274
- --input: ${theme.light.input};
26275
- --ring: ${theme.light.ring};
26276
- --chart-1: ${theme.light.chart1};
26277
- --chart-2: ${theme.light.chart2};
26278
- --chart-3: ${theme.light.chart3};
26279
- --chart-4: ${theme.light.chart4};
26280
- --chart-5: ${theme.light.chart5};
26281
- --sidebar: ${theme.light.sidebar};
26282
- --sidebar-foreground: ${theme.light.sidebarForeground};
26283
- --sidebar-primary: ${theme.light.sidebarPrimary};
26284
- --sidebar-primary-foreground: ${theme.light.sidebarPrimaryForeground};
26285
- --sidebar-accent: ${theme.light.sidebarAccent};
26286
- --sidebar-accent-foreground: ${theme.light.sidebarAccentForeground};
26287
- --sidebar-border: ${theme.light.sidebarBorder};
26288
- --sidebar-ring: ${theme.light.sidebarRing};
26289
- }
26290
-
26291
- .dark {
26292
- --background: ${theme.dark.background};
26293
- --foreground: ${theme.dark.foreground};
26294
- --card: ${theme.dark.card};
26295
- --card-foreground: ${theme.dark.cardForeground};
26296
- --popover: ${theme.dark.popover};
26297
- --popover-foreground: ${theme.dark.popoverForeground};
26298
- --primary: ${theme.dark.primary};
26299
- --primary-foreground: ${theme.dark.primaryForeground};
26300
- --secondary: ${theme.dark.secondary};
26301
- --secondary-foreground: ${theme.dark.secondaryForeground};
26302
- --muted: ${theme.dark.muted};
26303
- --muted-foreground: ${theme.dark.mutedForeground};
26304
- --accent: ${theme.dark.accent};
26305
- --accent-foreground: ${theme.dark.accentForeground};
26306
- --destructive: ${theme.dark.destructive};
26307
- --destructive-foreground: ${theme.dark.destructiveForeground};
26308
- --border: ${theme.dark.border};
26309
- --input: ${theme.dark.input};
26310
- --ring: ${theme.dark.ring};
26311
- --chart-1: ${theme.dark.chart1};
26312
- --chart-2: ${theme.dark.chart2};
26313
- --chart-3: ${theme.dark.chart3};
26314
- --chart-4: ${theme.dark.chart4};
26315
- --chart-5: ${theme.dark.chart5};
26316
- --sidebar: ${theme.dark.sidebar};
26317
- --sidebar-foreground: ${theme.dark.sidebarForeground};
26318
- --sidebar-primary: ${theme.dark.sidebarPrimary};
26319
- --sidebar-primary-foreground: ${theme.dark.sidebarPrimaryForeground};
26320
- --sidebar-accent: ${theme.dark.sidebarAccent};
26321
- --sidebar-accent-foreground: ${theme.dark.sidebarAccentForeground};
26322
- --sidebar-border: ${theme.dark.sidebarBorder};
26323
- --sidebar-ring: ${theme.dark.sidebarRing};
26324
- }
26325
+ return `@import "tailwindcss";
26326
+ @import "tw-animate-css";
26327
+
26328
+ :root {
26329
+ --radius: ${radius};
26330
+ --background: ${theme.light.background};
26331
+ --foreground: ${theme.light.foreground};
26332
+ --card: ${theme.light.card};
26333
+ --card-foreground: ${theme.light.cardForeground};
26334
+ --popover: ${theme.light.popover};
26335
+ --popover-foreground: ${theme.light.popoverForeground};
26336
+ --primary: ${theme.light.primary};
26337
+ --primary-foreground: ${theme.light.primaryForeground};
26338
+ --secondary: ${theme.light.secondary};
26339
+ --secondary-foreground: ${theme.light.secondaryForeground};
26340
+ --muted: ${theme.light.muted};
26341
+ --muted-foreground: ${theme.light.mutedForeground};
26342
+ --accent: ${theme.light.accent};
26343
+ --accent-foreground: ${theme.light.accentForeground};
26344
+ --destructive: ${theme.light.destructive};
26345
+ --destructive-foreground: ${theme.light.destructiveForeground};
26346
+ --border: ${theme.light.border};
26347
+ --input: ${theme.light.input};
26348
+ --ring: ${theme.light.ring};
26349
+ --chart-1: ${theme.light.chart1};
26350
+ --chart-2: ${theme.light.chart2};
26351
+ --chart-3: ${theme.light.chart3};
26352
+ --chart-4: ${theme.light.chart4};
26353
+ --chart-5: ${theme.light.chart5};
26354
+ --sidebar: ${theme.light.sidebar};
26355
+ --sidebar-foreground: ${theme.light.sidebarForeground};
26356
+ --sidebar-primary: ${theme.light.sidebarPrimary};
26357
+ --sidebar-primary-foreground: ${theme.light.sidebarPrimaryForeground};
26358
+ --sidebar-accent: ${theme.light.sidebarAccent};
26359
+ --sidebar-accent-foreground: ${theme.light.sidebarAccentForeground};
26360
+ --sidebar-border: ${theme.light.sidebarBorder};
26361
+ --sidebar-ring: ${theme.light.sidebarRing};
26362
+ }
26363
+
26364
+ .dark {
26365
+ --background: ${theme.dark.background};
26366
+ --foreground: ${theme.dark.foreground};
26367
+ --card: ${theme.dark.card};
26368
+ --card-foreground: ${theme.dark.cardForeground};
26369
+ --popover: ${theme.dark.popover};
26370
+ --popover-foreground: ${theme.dark.popoverForeground};
26371
+ --primary: ${theme.dark.primary};
26372
+ --primary-foreground: ${theme.dark.primaryForeground};
26373
+ --secondary: ${theme.dark.secondary};
26374
+ --secondary-foreground: ${theme.dark.secondaryForeground};
26375
+ --muted: ${theme.dark.muted};
26376
+ --muted-foreground: ${theme.dark.mutedForeground};
26377
+ --accent: ${theme.dark.accent};
26378
+ --accent-foreground: ${theme.dark.accentForeground};
26379
+ --destructive: ${theme.dark.destructive};
26380
+ --destructive-foreground: ${theme.dark.destructiveForeground};
26381
+ --border: ${theme.dark.border};
26382
+ --input: ${theme.dark.input};
26383
+ --ring: ${theme.dark.ring};
26384
+ --chart-1: ${theme.dark.chart1};
26385
+ --chart-2: ${theme.dark.chart2};
26386
+ --chart-3: ${theme.dark.chart3};
26387
+ --chart-4: ${theme.dark.chart4};
26388
+ --chart-5: ${theme.dark.chart5};
26389
+ --sidebar: ${theme.dark.sidebar};
26390
+ --sidebar-foreground: ${theme.dark.sidebarForeground};
26391
+ --sidebar-primary: ${theme.dark.sidebarPrimary};
26392
+ --sidebar-primary-foreground: ${theme.dark.sidebarPrimaryForeground};
26393
+ --sidebar-accent: ${theme.dark.sidebarAccent};
26394
+ --sidebar-accent-foreground: ${theme.dark.sidebarAccentForeground};
26395
+ --sidebar-border: ${theme.dark.sidebarBorder};
26396
+ --sidebar-ring: ${theme.dark.sidebarRing};
26397
+ }
26398
+
26399
+ @theme inline {
26400
+ --color-background: var(--background);
26401
+ --color-foreground: var(--foreground);
26402
+ --color-card: var(--card);
26403
+ --color-card-foreground: var(--card-foreground);
26404
+ --color-popover: var(--popover);
26405
+ --color-popover-foreground: var(--popover-foreground);
26406
+ --color-primary: var(--primary);
26407
+ --color-primary-foreground: var(--primary-foreground);
26408
+ --color-secondary: var(--secondary);
26409
+ --color-secondary-foreground: var(--secondary-foreground);
26410
+ --color-muted: var(--muted);
26411
+ --color-muted-foreground: var(--muted-foreground);
26412
+ --color-accent: var(--accent);
26413
+ --color-accent-foreground: var(--accent-foreground);
26414
+ --color-destructive: var(--destructive);
26415
+ --color-destructive-foreground: var(--destructive-foreground);
26416
+ --color-border: var(--border);
26417
+ --color-input: var(--input);
26418
+ --color-ring: var(--ring);
26419
+ --color-chart-1: var(--chart-1);
26420
+ --color-chart-2: var(--chart-2);
26421
+ --color-chart-3: var(--chart-3);
26422
+ --color-chart-4: var(--chart-4);
26423
+ --color-chart-5: var(--chart-5);
26424
+ --color-sidebar: var(--sidebar);
26425
+ --color-sidebar-foreground: var(--sidebar-foreground);
26426
+ --color-sidebar-primary: var(--sidebar-primary);
26427
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
26428
+ --color-sidebar-accent: var(--sidebar-accent);
26429
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
26430
+ --color-sidebar-border: var(--sidebar-border);
26431
+ --color-sidebar-ring: var(--sidebar-ring);
26432
+ --radius-lg: var(--radius);
26433
+ --radius-md: calc(var(--radius) - 2px);
26434
+ --radius-sm: calc(var(--radius) - 4px);
26325
26435
  }
26326
26436
 
26327
26437
  @layer base {
@@ -26339,7 +26449,6 @@ function generateThemeCSS(theme, customRadius) {
26339
26449
  init_execa();
26340
26450
  init_dist();
26341
26451
  init_src();
26342
- init_src();
26343
26452
  var DEFAULT_SHADCN_COMPONENTS = [
26344
26453
  "button",
26345
26454
  "card"
@@ -26347,50 +26456,107 @@ var DEFAULT_SHADCN_COMPONENTS = [
26347
26456
  async function installShadcnComponents(projectPath, components, options = {}) {
26348
26457
  const cwd2 = options.cwd || projectPath;
26349
26458
  const stdio = options.silent ? "pipe" : "inherit";
26459
+ const targetCwd = options.isMonorepo ? join(projectPath, "packages/ui") : cwd2;
26350
26460
  try {
26351
26461
  await execa("bunx", ["shadcn@latest", "add", ...components], {
26352
- cwd: cwd2,
26353
- stdio
26462
+ cwd: targetCwd,
26463
+ stdio,
26464
+ env: {
26465
+ ...process.env,
26466
+ BUN_INSTALL_LINKER: "isolated"
26467
+ }
26354
26468
  });
26469
+ if (options.isMonorepo) {
26470
+ await updateComponentsIndex(join(targetCwd, "src/components"));
26471
+ }
26355
26472
  } catch (error) {
26356
26473
  try {
26357
26474
  await execa("npx", ["shadcn@latest", "add", ...components], {
26358
- cwd: cwd2,
26475
+ cwd: targetCwd,
26359
26476
  stdio
26360
26477
  });
26478
+ if (options.isMonorepo) {
26479
+ await updateComponentsIndex(join(targetCwd, "src/components"));
26480
+ }
26361
26481
  } catch (fallbackError) {
26362
- logger.warn(`Could not install shadcn components automatically. You can install them manually with: bunx shadcn@latest add ${components.join(" ")}`);
26482
+ logger.warn(`Could not install shadcn components automatically. You can install them manually with: cd ${targetCwd} && bunx shadcn@latest add ${components.join(" ")}`);
26483
+ throw fallbackError;
26484
+ }
26485
+ }
26486
+ }
26487
+ async function updateComponentsIndex(componentsDir) {
26488
+ const indexPath = join(componentsDir, "index.ts");
26489
+ const uiDir = join(componentsDir, "ui");
26490
+ try {
26491
+ const { existsSync } = await import("fs");
26492
+ if (!existsSync(uiDir)) {
26493
+ logger.debug(`UI directory does not exist: ${uiDir}`);
26494
+ return;
26495
+ }
26496
+ const { readdir } = await import("fs/promises");
26497
+ const entries = await readdir(uiDir, { withFileTypes: true });
26498
+ const componentFiles = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".tsx")).map((entry) => entry.name.replace(".tsx", "")).sort();
26499
+ if (componentFiles.length === 0) {
26500
+ logger.debug(`No component files found in ${uiDir}`);
26501
+ return;
26363
26502
  }
26503
+ const exports = componentFiles.map((comp) => `export * from './ui/${comp}';`).join(`
26504
+ `);
26505
+ const newContent = `// Auto-generated exports for shadcn/ui components
26506
+ // Components are installed via: bunkit add component --components [name]
26507
+ // Or: bunx shadcn@latest add [name] (from packages/ui directory)
26508
+
26509
+ ${exports}
26510
+ `;
26511
+ await writeFile(indexPath, newContent);
26512
+ logger.debug(`Updated components index with ${componentFiles.length} components`);
26513
+ } catch (error) {
26514
+ logger.warn(`Could not update components index file: ${error.message}`);
26364
26515
  }
26365
26516
  }
26366
26517
  async function installDefaultShadcnComponents(projectPath, options = {}) {
26367
26518
  if (options.skipDefaults) {
26368
26519
  return;
26369
26520
  }
26521
+ const isMonorepo = options.isMonorepo ?? (await import("fs")).existsSync((await Promise.resolve().then(() => (init_dist(), exports_dist))).join(projectPath, "packages/ui/components.json"));
26370
26522
  logger.step("Installing default shadcn/ui components...");
26371
26523
  try {
26372
26524
  await installShadcnComponents(projectPath, [...DEFAULT_SHADCN_COMPONENTS], {
26373
26525
  silent: options.silent,
26374
- cwd: projectPath
26526
+ cwd: projectPath,
26527
+ isMonorepo
26375
26528
  });
26376
26529
  logger.success("Default components installed");
26377
26530
  } catch (error) {
26378
- logger.warn("Could not install default components automatically. Install them manually with: bunx shadcn@latest add button card");
26531
+ const manualCommand = isMonorepo ? "cd packages/ui && bunx shadcn@latest add button card" : "bunx shadcn@latest add button card";
26532
+ logger.warn(`Could not install default components automatically. Install them manually with: ${manualCommand}`);
26379
26533
  }
26380
26534
  }
26381
- async function createShadcnExample(projectPath, isMonorepo = false) {
26535
+ async function createShadcnExample(projectPath, isMonorepo = false, packageName) {
26382
26536
  const examplePath = isMonorepo ? join(projectPath, "apps/web/src/components/example.tsx") : join(projectPath, "src/components/example.tsx");
26383
- const exampleContent = `"use client"
26384
-
26385
- import { Button } from "@/components/ui/button"
26386
- import {
26537
+ const buttonImport = isMonorepo ? `import { Button } from "@workspace/ui/components/ui/button"` : `import { Button } from "@/components/ui/button"`;
26538
+ const cardImport = isMonorepo ? `import {
26387
26539
  Card,
26388
26540
  CardContent,
26389
26541
  CardDescription,
26390
26542
  CardFooter,
26391
26543
  CardHeader,
26392
26544
  CardTitle,
26393
- } from "@/components/ui/card"
26545
+ } from "@workspace/ui/components/ui/card"` : `import {
26546
+ Card,
26547
+ CardContent,
26548
+ CardDescription,
26549
+ CardFooter,
26550
+ CardHeader,
26551
+ CardTitle,
26552
+ } from "@/components/ui/card"`;
26553
+ const importHint = isMonorepo ? `// In monorepo, components are imported from @workspace/ui package
26554
+ // This ensures all apps share the same UI components and Tailwind CSS v4 config` : `// Components are imported using the @ alias configured in tsconfig.json`;
26555
+ const exampleContent = `"use client"
26556
+
26557
+ ${importHint}
26558
+ ${buttonImport}
26559
+ ${cardImport}
26394
26560
 
26395
26561
  /**
26396
26562
  * Example component showcasing shadcn/ui components
@@ -26410,7 +26576,7 @@ export function ExampleComponent() {
26410
26576
  </CardHeader>
26411
26577
  <CardContent>
26412
26578
  <p className="text-sm text-muted-foreground">
26413
- You can start building your UI by importing components from @/components/ui
26579
+ ${isMonorepo ? "Components are shared via @workspace/ui package. Add more components with: bunkit add component --components [name]" : "You can start building your UI by importing components from @/components/ui"}
26414
26580
  </p>
26415
26581
  </CardContent>
26416
26582
  <CardFooter className="flex gap-2">
@@ -26433,9 +26599,77 @@ init_dist();
26433
26599
  init_src();
26434
26600
  async function createShadcnDocs(projectPath, isMonorepo = false, context) {
26435
26601
  const docsPath = isMonorepo ? join(projectPath, "packages/ui/SHADCN.md") : join(projectPath, "SHADCN.md");
26602
+ const monorepoSection = isMonorepo ? `
26603
+ ## \uD83C\uDFD7\uFE0F Bun Monorepo Setup
26604
+
26605
+ This is a **Bun monorepo** with shared UI components in \`packages/ui\`. All shadcn/ui components are installed here and shared across all apps in the monorepo.
26606
+
26607
+ ### Monorepo Structure
26608
+
26609
+ \`\`\`
26610
+ packages/
26611
+ ui/ # Shared UI package
26612
+ src/
26613
+ components/ui/ # shadcn/ui components
26614
+ styles/ # Tailwind CSS v4 configuration
26615
+ lib/utils.ts # Utility functions (cn helper)
26616
+ components.json # shadcn/ui configuration
26617
+ \`\`\`
26618
+
26619
+ ### Adding Components (Monorepo)
26620
+
26621
+ Components are installed in \`packages/ui\` and automatically shared:
26622
+
26623
+ \`\`\`bash
26624
+ # From project root - bunkit handles monorepo detection
26625
+ bunkit add component --components button
26626
+
26627
+ # Or directly in packages/ui directory
26628
+ cd packages/ui
26629
+ bunx shadcn@latest add button
26630
+ \`\`\`
26631
+
26632
+ ### Using Components (Monorepo)
26633
+
26634
+ Import components using Bun workspace aliases:
26635
+
26636
+ \`\`\`tsx
26637
+ // Recommended: Use @workspace/ui alias (Bun workspace resolution)
26638
+ import { Button } from "@workspace/ui/components/ui/button"
26639
+ import { Card, CardContent, CardHeader, CardTitle } from "@workspace/ui/components/ui/card"
26640
+
26641
+ // Or use the package name directly
26642
+ import { Button } from "@${context?.packageName || "workspace"}/ui/components/ui/button"
26643
+ \`\`\`
26644
+
26645
+ ### Tailwind CSS v4 Configuration
26646
+
26647
+ Tailwind CSS v4 is configured in \`packages/ui/src/styles/globals.css\` using CSS-first configuration:
26648
+ - \u2705 No \`tailwind.config.ts\` needed (Tailwind v4 feature)
26649
+ - \u2705 Uses \`@theme inline\` directive for design tokens
26650
+ - \u2705 OKLCH color space (modern color format)
26651
+ - \u2705 Shared across all apps via workspace imports
26652
+
26653
+ All apps import the CSS from the UI package:
26654
+ \`\`\`css
26655
+ /* In apps/web/src/app/globals.css */
26656
+ @import "../../../packages/ui/src/styles/globals.css";
26657
+ \`\`\`
26658
+
26659
+ ### Bun Workspace Features
26660
+
26661
+ This setup leverages Bun 1.3 workspace features:
26662
+ - **Catalogs**: Dependency versions managed in root \`package.json\` catalog
26663
+ - **Isolated Installs**: Each package only sees its declared dependencies
26664
+ - **Workspace Aliases**: Use \`@workspace/ui\` for internal imports
26665
+ - **Fast Resolution**: Bun's native workspace resolution
26666
+
26667
+ ` : "";
26436
26668
  const docsContent = `# shadcn/ui Guide
26437
26669
 
26438
- This project uses [shadcn/ui](https://ui.shadcn.com) - a collection of re-usable components built with Radix UI and Tailwind CSS.
26670
+ This project uses [shadcn/ui](https://ui.shadcn.com) - a collection of re-usable components built with Radix UI and Tailwind CSS v4.
26671
+
26672
+ ${monorepoSection}
26439
26673
 
26440
26674
  ## \uD83D\uDE80 Quick Start
26441
26675
 
@@ -26457,14 +26691,40 @@ bunkit add component --all
26457
26691
  Or use the official shadcn CLI directly:
26458
26692
 
26459
26693
  \`\`\`bash
26460
- bunx shadcn@latest add button
26461
- bunx shadcn@latest add card
26462
- bunx shadcn@latest add input
26694
+ ${isMonorepo ? "cd packages/ui && " : ""}bunx shadcn@latest add button
26695
+ ${isMonorepo ? "cd packages/ui && " : ""}bunx shadcn@latest add card
26696
+ ${isMonorepo ? "cd packages/ui && " : ""}bunx shadcn@latest add input
26463
26697
  \`\`\`
26464
26698
 
26465
26699
  ### Using Components
26466
26700
 
26467
- Import components from the \`@/components/ui\` path:
26701
+ ${isMonorepo ? `Import components from the \`@workspace/ui\` package:
26702
+
26703
+ \`\`\`tsx
26704
+ import { Button } from "@workspace/ui/components/ui/button"
26705
+ import {
26706
+ Card,
26707
+ CardContent,
26708
+ CardDescription,
26709
+ CardFooter,
26710
+ CardHeader,
26711
+ CardTitle,
26712
+ } from "@workspace/ui/components/ui/card"
26713
+
26714
+ export function MyComponent() {
26715
+ return (
26716
+ <Card>
26717
+ <CardHeader>
26718
+ <CardTitle>Hello</CardTitle>
26719
+ <CardDescription>World</CardDescription>
26720
+ </CardHeader>
26721
+ <CardContent>
26722
+ <Button>Click me</Button>
26723
+ </CardContent>
26724
+ </Card>
26725
+ )
26726
+ }
26727
+ \`\`\`` : `Import components from the \`@/components/ui\` path:
26468
26728
 
26469
26729
  \`\`\`tsx
26470
26730
  import { Button } from "@/components/ui/button"
@@ -26490,7 +26750,7 @@ export function MyComponent() {
26490
26750
  </Card>
26491
26751
  )
26492
26752
  }
26493
- \`\`\`
26753
+ \`\`\``}
26494
26754
 
26495
26755
  ## \uD83D\uDCDA Available Components
26496
26756
 
@@ -26521,20 +26781,34 @@ Your project is configured with:
26521
26781
  Edit the CSS variables in \`${isMonorepo ? "packages/ui/src/styles/globals.css" : "src/app/globals.css"}\`:
26522
26782
 
26523
26783
  \`\`\`css
26524
- @layer base {
26525
- :root {
26526
- --radius: 0.625rem;
26527
- --background: oklch(...);
26528
- --foreground: oklch(...);
26529
- /* ... */
26530
- }
26784
+ /* Tailwind CSS v4 uses CSS-first configuration */
26785
+ @import "tailwindcss";
26786
+ @import "tw-animate-css";
26787
+
26788
+ :root {
26789
+ --radius: 0.625rem;
26790
+ --background: oklch(...);
26791
+ --foreground: oklch(...);
26792
+ /* ... */
26793
+ }
26794
+
26795
+ @theme inline {
26796
+ --color-background: var(--background);
26797
+ --color-foreground: var(--foreground);
26798
+ /* ... */
26531
26799
  }
26532
26800
  \`\`\`
26533
26801
 
26802
+ **Note**: Tailwind CSS v4 uses CSS-first configuration. No \`tailwind.config.ts\` is needed - all configuration is done via CSS using the \`@theme inline\` directive.
26803
+
26534
26804
  ### Component Customization
26535
26805
 
26536
26806
  Components are copied directly into your project at \`${isMonorepo ? "packages/ui/src/components/ui" : "src/components/ui"}\`. You can modify them directly - they're YOUR code!
26537
26807
 
26808
+ ${isMonorepo ? `
26809
+ **Monorepo Tip**: Since components are in \`packages/ui\`, changes automatically propagate to all apps that import them. This ensures consistent UI across your entire monorepo.
26810
+ ` : ""}
26811
+
26538
26812
  ## \uD83D\uDCD6 Documentation
26539
26813
 
26540
26814
  - [Official shadcn/ui Docs](https://ui.shadcn.com)
@@ -26644,119 +26918,6 @@ ${shadcnCss}`;
26644
26918
  }
26645
26919
  await writeFile(globalsCssPath, globalsCss);
26646
26920
  }
26647
- const tailwindConfigPath = join(projectPath, "tailwind.config.ts");
26648
- let tailwindConfig = "";
26649
- try {
26650
- tailwindConfig = await Bun.file(tailwindConfigPath).text();
26651
- } catch {}
26652
- if (tailwindConfig && !tailwindConfig.includes("hsl(var(--background))")) {
26653
- const shadcnTheme = ` theme: {
26654
- extend: {
26655
- colors: {
26656
- border: "hsl(var(--border))",
26657
- input: "hsl(var(--input))",
26658
- ring: "hsl(var(--ring))",
26659
- background: "hsl(var(--background))",
26660
- foreground: "hsl(var(--foreground))",
26661
- primary: {
26662
- DEFAULT: "hsl(var(--primary))",
26663
- foreground: "hsl(var(--primary-foreground))",
26664
- },
26665
- secondary: {
26666
- DEFAULT: "hsl(var(--secondary))",
26667
- foreground: "hsl(var(--secondary-foreground))",
26668
- },
26669
- destructive: {
26670
- DEFAULT: "hsl(var(--destructive))",
26671
- foreground: "hsl(var(--destructive-foreground))",
26672
- },
26673
- muted: {
26674
- DEFAULT: "hsl(var(--muted))",
26675
- foreground: "hsl(var(--muted-foreground))",
26676
- },
26677
- accent: {
26678
- DEFAULT: "hsl(var(--accent))",
26679
- foreground: "hsl(var(--accent-foreground))",
26680
- },
26681
- popover: {
26682
- DEFAULT: "hsl(var(--popover))",
26683
- foreground: "hsl(var(--popover-foreground))",
26684
- },
26685
- card: {
26686
- DEFAULT: "hsl(var(--card))",
26687
- foreground: "hsl(var(--card-foreground))",
26688
- },
26689
- },
26690
- borderRadius: {
26691
- lg: "var(--radius)",
26692
- md: "calc(var(--radius) - 2px)",
26693
- sm: "calc(var(--radius) - 4px)",
26694
- },
26695
- },
26696
- },`;
26697
- if (tailwindConfig.includes("theme:")) {
26698
- const updatedConfig = tailwindConfig.replace(/theme:\s*\{[^}]*\}/s, shadcnTheme.trim());
26699
- await writeFile(tailwindConfigPath, updatedConfig);
26700
- } else {
26701
- const updatedConfig = tailwindConfig.replace(/(\s*)(plugins:.*?)(\n\s*\};)/s, `$1$2$1${shadcnTheme}$3`);
26702
- await writeFile(tailwindConfigPath, updatedConfig);
26703
- }
26704
- } else if (!tailwindConfig) {
26705
- const newTailwindConfig = `import type { Config } from 'tailwindcss';
26706
-
26707
- const config: Config = {
26708
- content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
26709
- theme: {
26710
- extend: {
26711
- colors: {
26712
- border: "hsl(var(--border))",
26713
- input: "hsl(var(--input))",
26714
- ring: "hsl(var(--ring))",
26715
- background: "hsl(var(--background))",
26716
- foreground: "hsl(var(--foreground))",
26717
- primary: {
26718
- DEFAULT: "hsl(var(--primary))",
26719
- foreground: "hsl(var(--primary-foreground))",
26720
- },
26721
- secondary: {
26722
- DEFAULT: "hsl(var(--secondary))",
26723
- foreground: "hsl(var(--secondary-foreground))",
26724
- },
26725
- destructive: {
26726
- DEFAULT: "hsl(var(--destructive))",
26727
- foreground: "hsl(var(--destructive-foreground))",
26728
- },
26729
- muted: {
26730
- DEFAULT: "hsl(var(--muted))",
26731
- foreground: "hsl(var(--muted-foreground))",
26732
- },
26733
- accent: {
26734
- DEFAULT: "hsl(var(--accent))",
26735
- foreground: "hsl(var(--accent-foreground))",
26736
- },
26737
- popover: {
26738
- DEFAULT: "hsl(var(--popover))",
26739
- foreground: "hsl(var(--popover-foreground))",
26740
- },
26741
- card: {
26742
- DEFAULT: "hsl(var(--card))",
26743
- foreground: "hsl(var(--card-foreground))",
26744
- },
26745
- },
26746
- borderRadius: {
26747
- lg: "var(--radius)",
26748
- md: "calc(var(--radius) - 2px)",
26749
- sm: "calc(var(--radius) - 4px)",
26750
- },
26751
- },
26752
- },
26753
- plugins: [],
26754
- };
26755
-
26756
- export default config;
26757
- `;
26758
- await writeFile(tailwindConfigPath, newTailwindConfig);
26759
- }
26760
26921
  if (context.install !== false) {}
26761
26922
  await createShadcnExample(projectPath, false);
26762
26923
  await createShadcnDocs(projectPath, false, context);
@@ -26778,10 +26939,13 @@ async function setupShadcnMonorepo(projectPath, context) {
26778
26939
  main: "./src/index.ts",
26779
26940
  types: "./src/index.ts",
26780
26941
  exports: {
26942
+ ".": "./src/index.ts",
26781
26943
  "./components": "./src/components/index.ts",
26944
+ "./components/*": "./src/components/ui/*/index.ts",
26782
26945
  "./lib/utils": "./src/lib/utils.ts",
26783
26946
  "./hooks": "./src/hooks/index.ts",
26784
- "./styles": "./src/styles/globals.css"
26947
+ "./styles": "./src/styles/globals.css",
26948
+ "./components/ui/*": "./src/components/ui/*/index.ts"
26785
26949
  },
26786
26950
  dependencies: {
26787
26951
  "@radix-ui/react-slot": "catalog:",
@@ -26810,11 +26974,11 @@ async function setupShadcnMonorepo(projectPath, context) {
26810
26974
  },
26811
26975
  iconLibrary: "lucide",
26812
26976
  aliases: {
26813
- components: "@workspace/ui/components",
26814
- utils: "@workspace/ui/lib/utils",
26815
- hooks: "@workspace/ui/hooks",
26816
- lib: "@workspace/ui/lib",
26817
- ui: "@workspace/ui/components"
26977
+ components: "@/components",
26978
+ utils: "@/lib/utils",
26979
+ hooks: "@/hooks",
26980
+ lib: "@/lib",
26981
+ ui: "@/components/ui"
26818
26982
  }
26819
26983
  };
26820
26984
  await writeFile(join(projectPath, "packages/ui/components.json"), JSON.stringify(uiComponentsJson, null, 2));
@@ -26827,9 +26991,27 @@ export function cn(...inputs: ClassValue[]) {
26827
26991
  `;
26828
26992
  await writeFile(join(projectPath, "packages/ui/src/lib/utils.ts"), uiUtilsContent);
26829
26993
  const uiComponentsIndex = `// Export shadcn/ui components here
26830
- // Components will be added via: bunx shadcn@latest add [component]
26994
+ // Components are installed in src/components/ui/ and exported here
26995
+ // Add components using: bunkit add component --components button,card,input
26996
+ // Or directly: bunx shadcn@latest add [component] (from packages/ui directory)
26997
+
26998
+ // Components will be automatically exported here when added
26831
26999
  `;
26832
27000
  await writeFile(join(projectPath, "packages/ui/src/components/index.ts"), uiComponentsIndex);
27001
+ const uiIndexContent = `// Main entry point for @${packageName}/ui package
27002
+ // This package provides shared shadcn/ui components and Tailwind CSS v4 configuration
27003
+
27004
+ // Export utilities
27005
+ export * from './lib/utils';
27006
+
27007
+ // Export hooks (if any)
27008
+ export * from './hooks';
27009
+
27010
+ // Components are exported from individual files
27011
+ // Import like: import { Button } from '@${packageName}/ui/components/ui/button'
27012
+ // Or use the re-export: import { Button } from '@workspace/ui/components/ui/button'
27013
+ `;
27014
+ await writeFile(join(projectPath, "packages/ui/src/index.ts"), uiIndexContent);
26833
27015
  const uiHooksIndex = `// Export custom hooks here
26834
27016
  `;
26835
27017
  await writeFile(join(projectPath, "packages/ui/src/hooks/index.ts"), uiHooksIndex);
@@ -26850,212 +27032,81 @@ export function cn(...inputs: ClassValue[]) {
26850
27032
  resolveJsonModule: true,
26851
27033
  isolatedModules: true,
26852
27034
  jsx: "react-jsx",
26853
- types: ["react", "react-dom"]
27035
+ types: ["react", "react-dom"],
27036
+ paths: {
27037
+ "@/*": ["./src/*"]
27038
+ }
26854
27039
  },
26855
27040
  include: ["src/**/*"],
26856
27041
  exclude: ["node_modules"]
26857
27042
  };
26858
27043
  await writeFile(join(projectPath, "packages/ui/tsconfig.json"), JSON.stringify(uiTsconfig, null, 2));
26859
- await ensureDirectory(join(projectPath, "apps/web/src/components"));
26860
- await ensureDirectory(join(projectPath, "apps/web/src/lib"));
26861
- const webComponentsJson = {
26862
- $schema: "https://ui.shadcn.com/schema.json",
26863
- style,
26864
- rsc: true,
26865
- tsx: true,
26866
- tailwind: {
26867
- config: "",
26868
- css: "../../packages/ui/src/styles/globals.css",
26869
- baseColor,
26870
- cssVariables: true
26871
- },
26872
- iconLibrary: "lucide",
26873
- aliases: {
26874
- components: "@/components",
26875
- hooks: "@/hooks",
26876
- lib: "@/lib",
26877
- utils: "@workspace/ui/lib/utils",
26878
- ui: "@workspace/ui/components"
26879
- }
26880
- };
26881
- await writeFile(join(projectPath, "apps/web/components.json"), JSON.stringify(webComponentsJson, null, 2));
26882
- const webGlobalsCssPath = join(projectPath, "apps/web/src/app/globals.css");
26883
- const webGlobalsCss = `@import "../../../packages/ui/src/styles/globals.css";
26884
- `;
26885
- await writeFile(webGlobalsCssPath, webGlobalsCss);
26886
- const webPackageJsonPath = join(projectPath, "apps/web/package.json");
26887
- let webPackageJson = {};
26888
- try {
26889
- webPackageJson = JSON.parse(await Bun.file(webPackageJsonPath).text());
26890
- } catch {}
26891
- if (webPackageJson.dependencies) {
26892
- webPackageJson.dependencies[uiPackageName] = "workspace:*";
26893
- await writeFile(webPackageJsonPath, JSON.stringify(webPackageJson, null, 2));
26894
- }
26895
- await ensureDirectory(join(projectPath, "apps/platform/src/components"));
26896
- await ensureDirectory(join(projectPath, "apps/platform/src/lib"));
26897
- const platformComponentsJson = {
26898
- $schema: "https://ui.shadcn.com/schema.json",
26899
- style,
26900
- rsc: true,
26901
- tsx: true,
26902
- tailwind: {
26903
- config: "",
26904
- css: "../../packages/ui/src/styles/globals.css",
26905
- baseColor,
26906
- cssVariables: true
26907
- },
26908
- iconLibrary: "lucide",
26909
- aliases: {
26910
- components: "@/components",
26911
- hooks: "@/hooks",
26912
- lib: "@/lib",
26913
- utils: "@workspace/ui/lib/utils",
26914
- ui: "@workspace/ui/components"
27044
+ const configureNextJsApp = async (appName) => {
27045
+ const appPath = join(projectPath, `apps/${appName}`);
27046
+ if (!await directoryExists(appPath)) {
27047
+ return;
26915
27048
  }
26916
- };
26917
- await writeFile(join(projectPath, "apps/platform/components.json"), JSON.stringify(platformComponentsJson, null, 2));
26918
- const platformGlobalsCssPath = join(projectPath, "apps/platform/src/app/globals.css");
26919
- const platformGlobalsCss = `@import "../../../packages/ui/src/styles/globals.css";
26920
- `;
26921
- await writeFile(platformGlobalsCssPath, platformGlobalsCss);
26922
- const platformPackageJsonPath = join(projectPath, "apps/platform/package.json");
26923
- let platformPackageJson = {};
26924
- try {
26925
- platformPackageJson = JSON.parse(await Bun.file(platformPackageJsonPath).text());
26926
- } catch {}
26927
- if (platformPackageJson.dependencies) {
26928
- platformPackageJson.dependencies[uiPackageName] = "workspace:*";
26929
- await writeFile(platformPackageJsonPath, JSON.stringify(platformPackageJson, null, 2));
26930
- }
26931
- const updateAppTailwindConfig = async (appPath) => {
26932
- const tailwindConfigPath = join(projectPath, appPath, "tailwind.config.ts");
26933
- let tailwindConfig = "";
26934
- try {
26935
- tailwindConfig = await Bun.file(tailwindConfigPath).text();
26936
- } catch {}
26937
- const shadcnTheme = ` theme: {
26938
- extend: {
26939
- colors: {
26940
- border: "hsl(var(--border))",
26941
- input: "hsl(var(--input))",
26942
- ring: "hsl(var(--ring))",
26943
- background: "hsl(var(--background))",
26944
- foreground: "hsl(var(--foreground))",
26945
- primary: {
26946
- DEFAULT: "hsl(var(--primary))",
26947
- foreground: "hsl(var(--primary-foreground))",
26948
- },
26949
- secondary: {
26950
- DEFAULT: "hsl(var(--secondary))",
26951
- foreground: "hsl(var(--secondary-foreground))",
26952
- },
26953
- destructive: {
26954
- DEFAULT: "hsl(var(--destructive))",
26955
- foreground: "hsl(var(--destructive-foreground))",
26956
- },
26957
- muted: {
26958
- DEFAULT: "hsl(var(--muted))",
26959
- foreground: "hsl(var(--muted-foreground))",
26960
- },
26961
- accent: {
26962
- DEFAULT: "hsl(var(--accent))",
26963
- foreground: "hsl(var(--accent-foreground))",
26964
- },
26965
- popover: {
26966
- DEFAULT: "hsl(var(--popover))",
26967
- foreground: "hsl(var(--popover-foreground))",
26968
- },
26969
- card: {
26970
- DEFAULT: "hsl(var(--card))",
26971
- foreground: "hsl(var(--card-foreground))",
26972
- },
27049
+ await ensureDirectory(join(appPath, "src/components"));
27050
+ await ensureDirectory(join(appPath, "src/lib"));
27051
+ const appComponentsJson = {
27052
+ $schema: "https://ui.shadcn.com/schema.json",
27053
+ style,
27054
+ rsc: true,
27055
+ tsx: true,
27056
+ tailwind: {
27057
+ config: "",
27058
+ css: "../../packages/ui/src/styles/globals.css",
27059
+ baseColor,
27060
+ cssVariables: true
26973
27061
  },
26974
- borderRadius: {
26975
- lg: "var(--radius)",
26976
- md: "calc(var(--radius) - 2px)",
26977
- sm: "calc(var(--radius) - 4px)",
26978
- },
26979
- },
26980
- },`;
26981
- if (tailwindConfig && !tailwindConfig.includes("hsl(var(--background))")) {
26982
- if (tailwindConfig.includes("theme:")) {
26983
- const updatedConfig = tailwindConfig.replace(/theme:\s*\{[^}]*\}/s, shadcnTheme.trim());
26984
- await writeFile(tailwindConfigPath, updatedConfig);
26985
- } else {
26986
- const updatedConfig = tailwindConfig.replace(/(\s*)(plugins:.*?)(\n\s*\};)/s, `$1$2$1${shadcnTheme}$3`);
26987
- await writeFile(tailwindConfigPath, updatedConfig);
27062
+ iconLibrary: "lucide",
27063
+ aliases: {
27064
+ components: "@/components",
27065
+ hooks: "@/hooks",
27066
+ lib: "@/lib",
27067
+ utils: "@workspace/ui/lib/utils",
27068
+ ui: "@workspace/ui/components/ui"
26988
27069
  }
26989
- } else if (!tailwindConfig) {
26990
- const newTailwindConfig = `import type { Config } from 'tailwindcss';
26991
-
26992
- const config: Config = {
26993
- content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
26994
- theme: {
26995
- extend: {
26996
- colors: {
26997
- border: "hsl(var(--border))",
26998
- input: "hsl(var(--input))",
26999
- ring: "hsl(var(--ring))",
27000
- background: "hsl(var(--background))",
27001
- foreground: "hsl(var(--foreground))",
27002
- primary: {
27003
- DEFAULT: "hsl(var(--primary))",
27004
- foreground: "hsl(var(--primary-foreground))",
27005
- },
27006
- secondary: {
27007
- DEFAULT: "hsl(var(--secondary))",
27008
- foreground: "hsl(var(--secondary-foreground))",
27009
- },
27010
- destructive: {
27011
- DEFAULT: "hsl(var(--destructive))",
27012
- foreground: "hsl(var(--destructive-foreground))",
27013
- },
27014
- muted: {
27015
- DEFAULT: "hsl(var(--muted))",
27016
- foreground: "hsl(var(--muted-foreground))",
27017
- },
27018
- accent: {
27019
- DEFAULT: "hsl(var(--accent))",
27020
- foreground: "hsl(var(--accent-foreground))",
27021
- },
27022
- popover: {
27023
- DEFAULT: "hsl(var(--popover))",
27024
- foreground: "hsl(var(--popover-foreground))",
27025
- },
27026
- card: {
27027
- DEFAULT: "hsl(var(--card))",
27028
- foreground: "hsl(var(--card-foreground))",
27029
- },
27030
- },
27031
- borderRadius: {
27032
- lg: "var(--radius)",
27033
- md: "calc(var(--radius) - 2px)",
27034
- sm: "calc(var(--radius) - 4px)",
27035
- },
27036
- },
27037
- },
27038
- plugins: [],
27039
- };
27040
-
27041
- export default config;
27070
+ };
27071
+ await writeFile(join(appPath, "components.json"), JSON.stringify(appComponentsJson, null, 2));
27072
+ const globalsCssPath = join(appPath, "src/app/globals.css");
27073
+ const globalsCss = `@import "../../../packages/ui/src/styles/globals.css";
27042
27074
  `;
27043
- await writeFile(tailwindConfigPath, newTailwindConfig);
27075
+ await writeFile(globalsCssPath, globalsCss);
27076
+ const packageJsonPath = join(appPath, "package.json");
27077
+ let packageJson = {};
27078
+ try {
27079
+ packageJson = JSON.parse(await Bun.file(packageJsonPath).text());
27080
+ } catch {}
27081
+ if (packageJson.dependencies) {
27082
+ packageJson.dependencies[uiPackageName] = "workspace:*";
27083
+ await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
27044
27084
  }
27085
+ await createShadcnExample(appPath, true, packageName);
27045
27086
  };
27046
- await updateAppTailwindConfig("apps/web");
27047
- await updateAppTailwindConfig("apps/platform");
27087
+ await configureNextJsApp("web");
27088
+ await configureNextJsApp("platform");
27089
+ await configureNextJsApp("app");
27048
27090
  const rootPackageJsonPath = join(projectPath, "package.json");
27049
27091
  let rootPackageJson = {};
27050
27092
  try {
27051
27093
  rootPackageJson = JSON.parse(await Bun.file(rootPackageJsonPath).text());
27052
27094
  } catch {}
27053
- if (rootPackageJson.catalog) {
27054
- rootPackageJson.catalog["lucide-react"] = "^0.468.0";
27055
- await writeFile(rootPackageJsonPath, JSON.stringify(rootPackageJson, null, 2));
27056
- }
27057
- await createShadcnExample(join(projectPath, "apps/web"), true);
27058
- await createShadcnExample(join(projectPath, "apps/platform"), true);
27095
+ if (!rootPackageJson.catalog) {
27096
+ rootPackageJson.catalog = {};
27097
+ }
27098
+ const shadcnDependencies = {
27099
+ "@radix-ui/react-slot": "^1.1.0",
27100
+ "class-variance-authority": "^0.7.1",
27101
+ clsx: "^2.1.1",
27102
+ "tailwind-merge": "^2.5.5",
27103
+ "lucide-react": "^0.468.0",
27104
+ "@types/react": "^18.3.18",
27105
+ "@types/react-dom": "^18.3.5",
27106
+ typescript: "^5.7.2"
27107
+ };
27108
+ Object.assign(rootPackageJson.catalog, shadcnDependencies);
27109
+ await writeFile(rootPackageJsonPath, JSON.stringify(rootPackageJson, null, 2));
27059
27110
  await createShadcnDocs(join(projectPath, "packages/ui"), true, context);
27060
27111
  }
27061
27112
 
@@ -29926,6 +29977,7 @@ async function buildFullPreset(projectPath, context) {
29926
29977
  await ensureDirectory(join(projectPath, "apps/api"));
29927
29978
  await ensureDirectory(join(projectPath, "packages/types"));
29928
29979
  await ensureDirectory(join(projectPath, "packages/utils"));
29980
+ await ensureDirectory(join(projectPath, "packages/ui"));
29929
29981
  if (context.database && context.database !== "none") {
29930
29982
  await ensureDirectory(join(projectPath, "packages/db"));
29931
29983
  }
@@ -30000,7 +30052,8 @@ async function buildFullPreset(projectPath, context) {
30000
30052
  react: "catalog:",
30001
30053
  "react-dom": "catalog:",
30002
30054
  next: "catalog:",
30003
- [`@${context.packageName}/types`]: "workspace:*"
30055
+ [`@${context.packageName}/types`]: "workspace:*",
30056
+ [`@${context.packageName}/ui`]: "workspace:*"
30004
30057
  },
30005
30058
  devDependencies: {
30006
30059
  "@types/react": "catalog:",
@@ -30027,7 +30080,8 @@ async function buildFullPreset(projectPath, context) {
30027
30080
  react: "catalog:",
30028
30081
  "react-dom": "catalog:",
30029
30082
  next: "catalog:",
30030
- [`@${context.packageName}/types`]: "workspace:*"
30083
+ [`@${context.packageName}/types`]: "workspace:*",
30084
+ [`@${context.packageName}/ui`]: "workspace:*"
30031
30085
  },
30032
30086
  devDependencies: {
30033
30087
  "@types/react": "catalog:",
@@ -30825,8 +30879,15 @@ networks:
30825
30879
  if (context.cicd) {
30826
30880
  await setupGitHubActions(projectPath, context);
30827
30881
  }
30828
- if (context.uiLibrary === "shadcn" && context.cssFramework === "tailwind") {
30829
- await setupShadcnMonorepo(projectPath, context);
30882
+ if (context.cssFramework === "tailwind" && (context.uiLibrary === "shadcn" || context.uiLibrary === undefined)) {
30883
+ const fullContext = {
30884
+ ...context,
30885
+ uiLibrary: "shadcn",
30886
+ shadcnStyle: context.shadcnStyle || "new-york",
30887
+ shadcnBaseColor: context.shadcnBaseColor || "zinc",
30888
+ shadcnRadius: context.shadcnRadius || "0.625rem"
30889
+ };
30890
+ await setupShadcnMonorepo(projectPath, fullContext);
30830
30891
  }
30831
30892
  await setupTooling(projectPath, context);
30832
30893
  await setupVSCodeDebug(projectPath, context, "full");
@@ -31422,14 +31483,14 @@ async function buildEnterprisePreset(projectPath, context) {
31422
31483
  version: "0.0.0",
31423
31484
  private: true,
31424
31485
  scripts: {
31425
- dev: "next dev -p 3000",
31486
+ dev: "next dev -p ${PORT:-3000}",
31426
31487
  build: "next build",
31427
- start: "next start -p 3000",
31488
+ start: "next start -p ${PORT:-3000}",
31428
31489
  lint: "biome check .",
31429
31490
  format: "biome check --write .",
31430
- debug: "bun --inspect node_modules/.bin/next dev -p 3000",
31431
- "debug:brk": "bun --inspect-brk node_modules/.bin/next dev -p 3000",
31432
- "debug:wait": "bun --inspect-wait node_modules/.bin/next dev -p 3000"
31491
+ debug: "bun --inspect node_modules/.bin/next dev -p ${PORT:-3000}",
31492
+ "debug:brk": "bun --inspect-brk node_modules/.bin/next dev -p ${PORT:-3000}",
31493
+ "debug:wait": "bun --inspect-wait node_modules/.bin/next dev -p ${PORT:-3000}"
31433
31494
  },
31434
31495
  dependencies: {
31435
31496
  react: "catalog:",
@@ -31453,14 +31514,14 @@ async function buildEnterprisePreset(projectPath, context) {
31453
31514
  version: "0.0.0",
31454
31515
  private: true,
31455
31516
  scripts: {
31456
- dev: "next dev -p 3001",
31517
+ dev: "next dev -p ${PORT:-3001}",
31457
31518
  build: "next build",
31458
- start: "next start -p 3001",
31519
+ start: "next start -p ${PORT:-3001}",
31459
31520
  lint: "biome check .",
31460
31521
  format: "biome check --write .",
31461
- debug: "bun --inspect node_modules/.bin/next dev -p 3001",
31462
- "debug:brk": "bun --inspect-brk node_modules/.bin/next dev -p 3001",
31463
- "debug:wait": "bun --inspect-wait node_modules/.bin/next dev -p 3001"
31522
+ debug: "bun --inspect node_modules/.bin/next dev -p ${PORT:-3001}",
31523
+ "debug:brk": "bun --inspect-brk node_modules/.bin/next dev -p ${PORT:-3001}",
31524
+ "debug:wait": "bun --inspect-wait node_modules/.bin/next dev -p ${PORT:-3001}"
31464
31525
  },
31465
31526
  dependencies: {
31466
31527
  react: "catalog:",
@@ -31485,14 +31546,14 @@ async function buildEnterprisePreset(projectPath, context) {
31485
31546
  version: "0.0.0",
31486
31547
  private: true,
31487
31548
  scripts: {
31488
- dev: "next dev -p 3002",
31549
+ dev: "next dev -p ${PORT:-3002}",
31489
31550
  build: "next build",
31490
- start: "next start -p 3002",
31551
+ start: "next start -p ${PORT:-3002}",
31491
31552
  lint: "biome check .",
31492
31553
  format: "biome check --write .",
31493
- debug: "bun --inspect node_modules/.bin/next dev -p 3002",
31494
- "debug:brk": "bun --inspect-brk node_modules/.bin/next dev -p 3002",
31495
- "debug:wait": "bun --inspect-wait node_modules/.bin/next dev -p 3002"
31554
+ debug: "bun --inspect node_modules/.bin/next dev -p ${PORT:-3002}",
31555
+ "debug:brk": "bun --inspect-brk node_modules/.bin/next dev -p ${PORT:-3002}",
31556
+ "debug:wait": "bun --inspect-wait node_modules/.bin/next dev -p ${PORT:-3002}"
31496
31557
  },
31497
31558
  dependencies: {
31498
31559
  react: "catalog:",
@@ -31566,11 +31627,63 @@ async function buildEnterprisePreset(projectPath, context) {
31566
31627
  if (context.cicd) {
31567
31628
  await setupGitHubActions(projectPath, context);
31568
31629
  }
31569
- if (context.uiLibrary === "shadcn") {
31570
- await setupShadcnMonorepo(projectPath, context);
31630
+ if (context.cssFramework === "tailwind" && (context.uiLibrary === "shadcn" || context.uiLibrary === undefined)) {
31631
+ const enterpriseContext = {
31632
+ ...context,
31633
+ uiLibrary: "shadcn",
31634
+ shadcnStyle: context.shadcnStyle || "new-york",
31635
+ shadcnBaseColor: context.shadcnBaseColor || "zinc",
31636
+ shadcnRadius: context.shadcnRadius || "0.625rem"
31637
+ };
31638
+ await setupShadcnMonorepo(projectPath, enterpriseContext);
31571
31639
  }
31572
31640
  await setupTooling(projectPath, context);
31573
31641
  await setupVSCodeDebug(projectPath, context);
31642
+ const typesPackageJson = {
31643
+ name: `@${context.packageName}/types`,
31644
+ version: "0.0.0",
31645
+ private: true,
31646
+ main: "./src/index.ts",
31647
+ types: "./src/index.ts"
31648
+ };
31649
+ await writeFile(join(projectPath, "packages/types/package.json"), JSON.stringify(typesPackageJson, null, 2));
31650
+ const typesContent = `// Shared types for ${context.projectName}
31651
+
31652
+ export interface User {
31653
+ id: string;
31654
+ email: string;
31655
+ name?: string;
31656
+ createdAt: Date;
31657
+ }
31658
+
31659
+ export interface ApiResponse<T = unknown> {
31660
+ data?: T;
31661
+ error?: string;
31662
+ message?: string;
31663
+ }
31664
+ `;
31665
+ await ensureDirectory(join(projectPath, "packages/types/src"));
31666
+ await writeFile(join(projectPath, "packages/types/src/index.ts"), typesContent);
31667
+ const utilsPackageJson = {
31668
+ name: `@${context.packageName}/utils`,
31669
+ version: "0.0.0",
31670
+ private: true,
31671
+ main: "./src/index.ts",
31672
+ types: "./src/index.ts"
31673
+ };
31674
+ await writeFile(join(projectPath, "packages/utils/package.json"), JSON.stringify(utilsPackageJson, null, 2));
31675
+ const utilsContent = `// Shared utilities for ${context.projectName}
31676
+
31677
+ export function formatDate(date: Date): string {
31678
+ return date.toISOString();
31679
+ }
31680
+
31681
+ export function capitalize(str: string): string {
31682
+ return str.charAt(0).toUpperCase() + str.slice(1);
31683
+ }
31684
+ `;
31685
+ await ensureDirectory(join(projectPath, "packages/utils/src"));
31686
+ await writeFile(join(projectPath, "packages/utils/src/index.ts"), utilsContent);
31574
31687
  await ensureDirectory(join(projectPath, "apps/service-identity/src/routes"));
31575
31688
  await ensureDirectory(join(projectPath, "apps/service-identity/src/middleware"));
31576
31689
  const serviceIdentityIndex = `import { Hono } from 'hono';
@@ -31701,10 +31814,14 @@ bun install
31701
31814
  bun dev
31702
31815
 
31703
31816
  # Start individual apps
31704
- bun run dev:web # Start marketing site
31705
- bun run dev:app # Start main product app
31706
- bun run dev:platform # Start admin dashboard
31707
- bun run dev:identity # Start identity service
31817
+ bun run dev:web # Start marketing site (port 3000)
31818
+ bun run dev:app # Start main product app (port 3002)
31819
+ bun run dev:platform # Start admin dashboard (port 3001)
31820
+ bun run dev:identity # Start identity service (port 3003)
31821
+
31822
+ # Use custom ports if defaults are in use
31823
+ PORT=3004 bun run dev:app # Run app on port 3004
31824
+ PORT=3005 bun run dev:web # Run web on port 3005
31708
31825
 
31709
31826
  # Build all apps
31710
31827
  bun build
@@ -31714,6 +31831,26 @@ bun lint
31714
31831
  bun format
31715
31832
  \`\`\`
31716
31833
 
31834
+ ### Port Configuration
31835
+
31836
+ All apps support the \`PORT\` environment variable to override the default port:
31837
+
31838
+ - **apps/web**: Default port 3000
31839
+ - **apps/platform**: Default port 3001
31840
+ - **apps/app**: Default port 3002
31841
+ - **apps/service-identity**: Default port 3003
31842
+
31843
+ If a port is already in use, you can easily change it:
31844
+
31845
+ \`\`\`bash
31846
+ # Example: Run app on a different port
31847
+ PORT=4000 cd apps/app && bun dev
31848
+
31849
+ # Or set it globally for the session
31850
+ export PORT=4000
31851
+ cd apps/app && bun dev
31852
+ \`\`\`
31853
+
31717
31854
  ## Adding More Services
31718
31855
 
31719
31856
  You can easily add more services using bunkit:
@@ -32720,6 +32857,69 @@ async function createCommand2(preset, name, options) {
32720
32857
  }
32721
32858
  console.log("");
32722
32859
  const s = Y2();
32860
+ const isEnterprise = normalizedPreset === "enterprise-monorepo";
32861
+ let shadcnStyle = "new-york";
32862
+ let shadcnBaseColor = "zinc";
32863
+ let shadcnRadius = "0.625rem";
32864
+ if (isEnterprise) {
32865
+ Me(`Default theme: ${source_default.cyan("New York")} style, ${source_default.cyan("Zinc")} color, ${source_default.cyan("0.625rem")} radius`, "shadcn/ui defaults");
32866
+ const customize = await ye({
32867
+ message: `\uD83C\uDFA8 Customize shadcn/ui theme?`,
32868
+ initialValue: false
32869
+ });
32870
+ if (pD(customize)) {
32871
+ xe("Operation cancelled.");
32872
+ process.exit(0);
32873
+ }
32874
+ if (customize) {
32875
+ const styleChoice = await ve({
32876
+ message: "\uD83C\uDFA8 Component style",
32877
+ options: [
32878
+ { value: "new-york", label: "New York (Recommended)", hint: "Modern, rounded, subtle shadows" },
32879
+ { value: "default", label: "Default", hint: "Classic, sharper edges, higher contrast" }
32880
+ ],
32881
+ initialValue: "new-york"
32882
+ });
32883
+ if (pD(styleChoice)) {
32884
+ xe("Operation cancelled.");
32885
+ process.exit(0);
32886
+ }
32887
+ shadcnStyle = styleChoice;
32888
+ const colorChoice = await ve({
32889
+ message: "\uD83C\uDFA8 Base color theme",
32890
+ options: [
32891
+ { value: "zinc", label: "Zinc (Recommended)", hint: "Neutral gray - versatile, modern" },
32892
+ { value: "neutral", label: "Neutral", hint: "Pure neutral - no color cast" },
32893
+ { value: "gray", label: "Gray", hint: "Warm gray palette" },
32894
+ { value: "slate", label: "Slate", hint: "Cool gray - bluer tone" },
32895
+ { value: "stone", label: "Stone", hint: "Warm beige-gray - earthy feel" }
32896
+ ],
32897
+ initialValue: "zinc"
32898
+ });
32899
+ if (pD(colorChoice)) {
32900
+ xe("Operation cancelled.");
32901
+ process.exit(0);
32902
+ }
32903
+ shadcnBaseColor = colorChoice;
32904
+ const radiusInput = await he({
32905
+ message: "\uD83D\uDCD0 Border radius",
32906
+ placeholder: "0.625rem",
32907
+ initialValue: "0.625rem",
32908
+ validate: (value) => {
32909
+ if (!value.trim())
32910
+ return "Radius cannot be empty";
32911
+ if (!/^\d+(\.\d+)?(rem|px|em|%)$/.test(value.trim())) {
32912
+ return "Please enter a valid CSS value (e.g., 0.5rem, 8px)";
32913
+ }
32914
+ }
32915
+ });
32916
+ if (pD(radiusInput)) {
32917
+ xe("Operation cancelled.");
32918
+ process.exit(0);
32919
+ }
32920
+ shadcnRadius = radiusInput;
32921
+ }
32922
+ }
32723
32923
  s.start(`${source_default.cyan("\uD83D\uDD28")} Creating ${preset} project: ${source_default.bold(name)}`);
32724
32924
  try {
32725
32925
  const config = {
@@ -32731,13 +32931,18 @@ async function createCommand2(preset, name, options) {
32731
32931
  database: "none",
32732
32932
  redis: false,
32733
32933
  useBunSecrets: false,
32734
- codeQuality: "ultracite",
32934
+ codeQuality: isEnterprise ? "biome" : "ultracite",
32735
32935
  tsStrictness: "strict",
32736
32936
  testing: "bun-test",
32737
32937
  docker: false,
32738
32938
  cicd: false,
32739
32939
  envExample: true,
32740
- pathAliases: true
32940
+ pathAliases: true,
32941
+ cssFramework: isEnterprise ? "tailwind" : undefined,
32942
+ uiLibrary: isEnterprise ? "shadcn" : undefined,
32943
+ shadcnStyle: isEnterprise ? shadcnStyle : undefined,
32944
+ shadcnBaseColor: isEnterprise ? shadcnBaseColor : undefined,
32945
+ shadcnRadius: isEnterprise ? shadcnRadius : undefined
32741
32946
  };
32742
32947
  s.message(`${source_default.cyan("\uD83D\uDCC1")} Creating project structure...`);
32743
32948
  await createProject(config);
@@ -32771,6 +32976,19 @@ async function createCommand2(preset, name, options) {
32771
32976
  break;
32772
32977
  }
32773
32978
  s.message(`${source_default.cyan("\u2728")} Finalizing setup...`);
32979
+ if (isEnterprise && config.uiLibrary === "shadcn" && config.install !== false) {
32980
+ await new Promise((resolve2) => setTimeout(resolve2, 1000));
32981
+ s.message(`${source_default.cyan("\uD83E\uDDE9")} Installing default shadcn/ui components...`);
32982
+ try {
32983
+ await installDefaultShadcnComponents(projectPath, {
32984
+ silent: false,
32985
+ skipDefaults: false
32986
+ });
32987
+ } catch (error) {
32988
+ s.message(`${source_default.yellow("\u26A0\uFE0F")} Could not install default components automatically`);
32989
+ s.message(`${source_default.dim(" You can install them manually:")} ${source_default.cyan("cd packages/ui && bunx shadcn@latest add button card")}`);
32990
+ }
32991
+ }
32774
32992
  s.stop(`${source_default.green("\u2705")} Project ${source_default.bold(name)} created successfully!`);
32775
32993
  const getPresetEmoji = () => {
32776
32994
  switch (normalizedPreset) {
@@ -33268,6 +33486,20 @@ ${import_picocolors5.default.bold("Package info:")}
33268
33486
  // src/commands/add/component.ts
33269
33487
  init_dist();
33270
33488
  import { existsSync } from "fs";
33489
+ async function getPackageName(cwd2) {
33490
+ try {
33491
+ const packageJsonPath = join(cwd2, "package.json");
33492
+ if (existsSync(packageJsonPath)) {
33493
+ const packageJson = JSON.parse(await Bun.file(packageJsonPath).text());
33494
+ const name = packageJson.name;
33495
+ if (name && name.endsWith("-monorepo")) {
33496
+ return name.replace("-monorepo", "");
33497
+ }
33498
+ return name || null;
33499
+ }
33500
+ } catch {}
33501
+ return null;
33502
+ }
33271
33503
  async function addComponentCommand(options = {}) {
33272
33504
  const cwd2 = options.cwd || process.cwd();
33273
33505
  const spinner = Y2();
@@ -33279,6 +33511,12 @@ async function addComponentCommand(options = {}) {
33279
33511
  process.exit(1);
33280
33512
  }
33281
33513
  const targetPath = isMonorepo ? join(cwd2, "packages/ui") : cwd2;
33514
+ const targetComponentsJson = join(targetPath, "components.json");
33515
+ if (!existsSync(targetComponentsJson)) {
33516
+ M2.error(`shadcn/ui is not configured in ${isMonorepo ? "packages/ui" : "this project"}.`);
33517
+ M2.info("Run `bunkit init` with --ui-library shadcn to set up shadcn/ui first.");
33518
+ process.exit(1);
33519
+ }
33282
33520
  let components = [];
33283
33521
  if (options.all) {
33284
33522
  const popularComponents = [
@@ -33364,12 +33602,16 @@ async function addComponentCommand(options = {}) {
33364
33602
  try {
33365
33603
  await installShadcnComponents(targetPath, components, {
33366
33604
  silent: false,
33367
- cwd: targetPath
33605
+ cwd: cwd2,
33606
+ isMonorepo
33368
33607
  });
33369
33608
  spinner.stop(`\u2705 Installed: ${components.join(", ")}`);
33370
33609
  if (isMonorepo) {
33610
+ const packageName = await getPackageName(cwd2);
33371
33611
  Me(`Components installed in packages/ui. Import them using:
33372
- import { Button } from "@workspace/ui/components/ui/button"`, "Usage");
33612
+ ` + `import { Button } from "@workspace/ui/components/ui/button"
33613
+ ` + `// Or using workspace alias:
33614
+ ` + `import { Button } from "${packageName ? `@${packageName}/ui` : "@workspace/ui"}/components/ui/button"`, "Usage");
33373
33615
  } else {
33374
33616
  Me(`Import components using:
33375
33617
  import { Button } from "@/components/ui/button"`, "Usage");