create-kofi-stack 2.1.0 → 2.1.2

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 +99 -24
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -409,17 +409,54 @@ async function generateProject(config, options = {}) {
409
409
  p2.log.info(`Creating project in ${pc2.cyan(config.targetDir)}`);
410
410
  console.log();
411
411
  try {
412
- spinner.start("Generating project files...");
413
- const result = await generateVirtualProject(config);
414
- if (!result.success) {
415
- spinner.fail("Failed to generate project");
416
- console.error(pc2.red("Errors:"), result.errors?.join("\n"));
417
- process.exit(1);
412
+ const presetUrl = buildShadcnPresetUrl(config);
413
+ if (config.structure === "standalone") {
414
+ spinner.start("Creating project with shadcn/ui...");
415
+ try {
416
+ await execa("pnpm", ["dlx", "shadcn@latest", "create", config.projectName, "--template", "next", "--preset", presetUrl, "--src-dir"], {
417
+ cwd: path2.dirname(config.targetDir),
418
+ stdio: "pipe"
419
+ });
420
+ spinner.succeed("Base project created with shadcn/ui");
421
+ } catch {
422
+ if (await fs.pathExists(config.targetDir)) {
423
+ spinner.succeed("Base project created with shadcn/ui");
424
+ } else {
425
+ spinner.fail("Failed to create project with shadcn");
426
+ throw new Error("shadcn create failed and project directory was not created");
427
+ }
428
+ }
429
+ const workspaceFile = path2.join(config.targetDir, "pnpm-workspace.yaml");
430
+ if (await fs.pathExists(workspaceFile)) {
431
+ await fs.remove(workspaceFile);
432
+ }
433
+ spinner.start("Adding Convex and authentication...");
434
+ const result = await generateVirtualProject(config);
435
+ if (!result.success) {
436
+ spinner.fail("Failed to generate project files");
437
+ console.error(pc2.red("Errors:"), result.errors?.join("\n"));
438
+ process.exit(1);
439
+ }
440
+ await writeNodeToDisk(result.tree.root, config.targetDir, {
441
+ skipExisting: ["src/app/globals.css", "tailwind.config.ts", "components.json", "next.config.ts", "postcss.config.mjs"]
442
+ });
443
+ spinner.succeed("Convex and authentication added");
444
+ spinner.start("Merging dependencies...");
445
+ await mergePackageJson(config.targetDir, result.tree.root);
446
+ spinner.succeed("Dependencies merged");
447
+ } else {
448
+ spinner.start("Generating project files...");
449
+ const result = await generateVirtualProject(config);
450
+ if (!result.success) {
451
+ spinner.fail("Failed to generate project");
452
+ console.error(pc2.red("Errors:"), result.errors?.join("\n"));
453
+ process.exit(1);
454
+ }
455
+ spinner.succeed("Project files generated");
456
+ spinner.start("Writing files to disk...");
457
+ await writeNodeToDisk(result.tree.root, config.targetDir);
458
+ spinner.succeed("Files written to disk");
418
459
  }
419
- spinner.succeed("Project files generated");
420
- spinner.start("Writing files to disk...");
421
- await writeNodeToDisk(result.tree.root, config.targetDir);
422
- spinner.succeed("Files written to disk");
423
460
  spinner.start("Generating secrets...");
424
461
  const backendEnvPath = config.structure === "monorepo" ? path2.join(config.targetDir, "packages/backend/.env.local") : path2.join(config.targetDir, ".env.local");
425
462
  await updateEnvWithSecrets(backendEnvPath, {
@@ -449,18 +486,18 @@ async function generateProject(config, options = {}) {
449
486
  } catch {
450
487
  spinner.warn("Failed to install dependencies. Run pnpm install manually.");
451
488
  }
452
- const shadcnDir = config.structure === "monorepo" ? path2.join(config.targetDir, "packages/ui") : config.targetDir;
453
- const presetUrl = buildShadcnPresetUrl(config);
454
- spinner.start("Initializing shadcn/ui...");
455
- try {
456
- await execa("pnpm", ["dlx", "shadcn@latest", "init", "--yes", "--preset", presetUrl], {
457
- cwd: shadcnDir,
458
- stdio: "pipe"
459
- });
460
- spinner.succeed("shadcn/ui initialized");
461
- } catch {
462
- spinner.warn(`Failed to initialize shadcn. Run manually:
463
- pnpm dlx shadcn@latest init --preset "${presetUrl}"`);
489
+ const shadcnDir = config.structure === "monorepo" ? path2.join(config.targetDir, "apps/web") : config.targetDir;
490
+ if (config.structure === "monorepo") {
491
+ spinner.start("Initializing shadcn/ui in apps/web...");
492
+ try {
493
+ await execa("pnpm", ["dlx", "shadcn@latest", "init", "--defaults", "--force"], {
494
+ cwd: shadcnDir,
495
+ stdio: "pipe"
496
+ });
497
+ spinner.succeed("shadcn/ui initialized");
498
+ } catch {
499
+ spinner.warn("Failed to initialize shadcn. Run manually in apps/web: pnpm dlx shadcn@latest init");
500
+ }
464
501
  }
465
502
  spinner.start("Installing shadcn/ui components...");
466
503
  try {
@@ -630,8 +667,13 @@ async function setupPayload(config) {
630
667
  }
631
668
  p2.log.success("Payload CMS configured successfully!");
632
669
  }
633
- async function writeNodeToDisk(node, targetDir) {
670
+ async function writeNodeToDisk(node, targetDir, options = {}) {
671
+ const { skipExisting = [], basePath = "" } = options;
634
672
  if (node.type === "file") {
673
+ const relativePath = basePath ? `${basePath}/${node.name}` : node.name;
674
+ if (skipExisting.includes(relativePath)) {
675
+ return;
676
+ }
635
677
  const filePath = path2.join(targetDir, node.name);
636
678
  await fs.ensureDir(path2.dirname(filePath));
637
679
  if (Buffer.isBuffer(node.content)) {
@@ -642,7 +684,11 @@ async function writeNodeToDisk(node, targetDir) {
642
684
  } else {
643
685
  await fs.ensureDir(targetDir);
644
686
  for (const child of node.children) {
687
+ const childRelativePath = basePath ? `${basePath}/${child.name}` : child.name;
645
688
  if (child.type === "file") {
689
+ if (skipExisting.includes(childRelativePath)) {
690
+ continue;
691
+ }
646
692
  const filePath = path2.join(targetDir, child.name);
647
693
  await fs.ensureDir(path2.dirname(filePath));
648
694
  if (Buffer.isBuffer(child.content)) {
@@ -651,11 +697,40 @@ async function writeNodeToDisk(node, targetDir) {
651
697
  await fs.writeFile(filePath, child.content, "utf-8");
652
698
  }
653
699
  } else {
654
- await writeNodeToDisk(child, path2.join(targetDir, child.name));
700
+ await writeNodeToDisk(child, path2.join(targetDir, child.name), {
701
+ skipExisting,
702
+ basePath: childRelativePath
703
+ });
655
704
  }
656
705
  }
657
706
  }
658
707
  }
708
+ async function mergePackageJson(targetDir, virtualRoot) {
709
+ const packageJsonPath = path2.join(targetDir, "package.json");
710
+ const existingPkg = await fs.readJson(packageJsonPath);
711
+ let ourPkgContent;
712
+ if (virtualRoot.type === "directory") {
713
+ const pkgFile = virtualRoot.children.find((c) => c.name === "package.json");
714
+ if (pkgFile?.type === "file" && typeof pkgFile.content === "string") {
715
+ ourPkgContent = pkgFile.content;
716
+ }
717
+ }
718
+ if (!ourPkgContent) return;
719
+ const ourPkg = JSON.parse(ourPkgContent);
720
+ existingPkg.dependencies = {
721
+ ...existingPkg.dependencies,
722
+ ...ourPkg.dependencies
723
+ };
724
+ existingPkg.devDependencies = {
725
+ ...existingPkg.devDependencies,
726
+ ...ourPkg.devDependencies
727
+ };
728
+ existingPkg.scripts = {
729
+ ...existingPkg.scripts,
730
+ ...ourPkg.scripts
731
+ };
732
+ await fs.writeJson(packageJsonPath, existingPkg, { spaces: 2 });
733
+ }
659
734
 
660
735
  // src/index.ts
661
736
  import { readFileSync } from "fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-kofi-stack",
3
- "version": "2.1.0",
3
+ "version": "2.1.2",
4
4
  "description": "Scaffold opinionated full-stack projects with Next.js, Convex, Better-Auth, and more",
5
5
  "type": "module",
6
6
  "bin": {