create-prisma 0.1.3 → 0.1.4

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/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import "./create-fJECj1B0.mjs";
2
+ import "./create-Dz9GFGFQ.mjs";
3
3
  import { createCreatePrismaCli } from "./index.mjs";
4
4
 
5
5
  //#region src/cli.ts
@@ -7,8 +7,10 @@ import { existsSync } from "node:fs";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import { z } from "zod";
9
9
  import { execa } from "execa";
10
+ import { styleText } from "node:util";
10
11
 
11
12
  //#region src/templates/shared.ts
13
+ Handlebars.registerHelper("eq", (left, right) => left === right);
12
14
  function findPackageRoot(startDir) {
13
15
  let currentDir = startDir;
14
16
  while (true) {
@@ -41,19 +43,25 @@ function stripHbsExtension(filePath) {
41
43
  function ensureTrailingNewline(content) {
42
44
  return content.endsWith("\n") ? content : `${content}\n`;
43
45
  }
46
+ async function renderTemplateFile(opts) {
47
+ const { templateFilePath, outputPath, context } = opts;
48
+ const templateContent = await fs.readFile(templateFilePath, "utf8");
49
+ const outputContent = templateFilePath.endsWith(".hbs") ? Handlebars.compile(templateContent, {
50
+ noEscape: true,
51
+ strict: true
52
+ })(context) : templateContent;
53
+ await fs.outputFile(outputPath, ensureTrailingNewline(outputContent), "utf8");
54
+ }
44
55
  async function renderTemplateTree(opts) {
45
56
  const { templateRoot, outputDir, context } = opts;
46
57
  const templateFiles = await getTemplateFilesRecursively(templateRoot);
47
58
  for (const templateFilePath of templateFiles) {
48
- const relativeTemplatePath = path.relative(templateRoot, templateFilePath);
49
- const relativeOutputPath = stripHbsExtension(relativeTemplatePath);
50
- const outputPath = path.join(outputDir, relativeOutputPath);
51
- const templateContent = await fs.readFile(templateFilePath, "utf8");
52
- const outputContent = relativeTemplatePath.endsWith(".hbs") ? Handlebars.compile(templateContent, {
53
- noEscape: true,
54
- strict: true
55
- })(context) : templateContent;
56
- await fs.outputFile(outputPath, ensureTrailingNewline(outputContent), "utf8");
59
+ const relativeOutputPath = stripHbsExtension(path.relative(templateRoot, templateFilePath));
60
+ await renderTemplateFile({
61
+ templateFilePath,
62
+ outputPath: path.join(outputDir, relativeOutputPath),
63
+ context
64
+ });
57
65
  }
58
66
  }
59
67
 
@@ -62,19 +70,19 @@ async function renderTemplateTree(opts) {
62
70
  function getCreateTemplateDir(template) {
63
71
  return resolveTemplatesDir(`templates/create/${template}`);
64
72
  }
65
- function createTemplateContext$1(projectName, schemaPreset) {
73
+ function createTemplateContext$1(projectName, schemaPreset, packageManager) {
66
74
  return {
67
75
  projectName,
68
76
  schemaPreset,
69
- useBasicSchema: schemaPreset === "basic"
77
+ packageManager
70
78
  };
71
79
  }
72
80
  async function scaffoldCreateTemplate(opts) {
73
- const { projectDir, projectName, template, schemaPreset } = opts;
81
+ const { projectDir, projectName, template, schemaPreset, packageManager } = opts;
74
82
  await renderTemplateTree({
75
83
  templateRoot: getCreateTemplateDir(template),
76
84
  outputDir: projectDir,
77
- context: createTemplateContext$1(projectName, schemaPreset)
85
+ context: createTemplateContext$1(projectName, schemaPreset, packageManager)
78
86
  });
79
87
  }
80
88
 
@@ -112,13 +120,13 @@ const PrismaSetupOptionsSchema = z.object({
112
120
  generate: z.boolean().optional().describe("Generate Prisma Client after scaffolding"),
113
121
  schemaPreset: SchemaPresetSchema.optional().describe("Schema preset to scaffold in prisma/schema.prisma")
114
122
  });
115
- const InitCommandInputSchema = CommonCommandOptionsSchema.merge(PrismaSetupOptionsSchema);
123
+ const InitCommandInputSchema = CommonCommandOptionsSchema.extend(PrismaSetupOptionsSchema.shape);
116
124
  const CreateScaffoldOptionsSchema = z.object({
117
125
  name: z.string().trim().min(1, "Please enter a valid project name").optional().describe("Project name / directory"),
118
126
  template: CreateTemplateSchema.optional().describe("Project template"),
119
127
  force: z.boolean().optional().describe("Allow scaffolding into a non-empty target directory")
120
128
  });
121
- const CreateCommandInputSchema = CommonCommandOptionsSchema.merge(CreateScaffoldOptionsSchema).merge(PrismaSetupOptionsSchema);
129
+ const CreateCommandInputSchema = CommonCommandOptionsSchema.extend(CreateScaffoldOptionsSchema.shape).extend(PrismaSetupOptionsSchema.shape);
122
130
 
123
131
  //#endregion
124
132
  //#region src/utils/package-manager.ts
@@ -171,7 +179,7 @@ async function detectPackageManager(projectDir = process.cwd()) {
171
179
  if (fromLockfile) return fromLockfile;
172
180
  const fromUserAgent = parseUserAgent(process.env.npm_config_user_agent);
173
181
  if (fromUserAgent) return fromUserAgent;
174
- return "bun";
182
+ return "npm";
175
183
  }
176
184
  function getInstallCommand(packageManager) {
177
185
  return `${packageManager} install`;
@@ -293,12 +301,7 @@ function createTemplateContext(provider, envVar, schemaPreset) {
293
301
  return {
294
302
  envVar,
295
303
  provider,
296
- schemaPreset,
297
- usePgAdapter: provider === "postgresql" || provider === "cockroachdb",
298
- useMariaDbAdapter: provider === "mysql",
299
- useSqliteAdapter: provider === "sqlite",
300
- useMssqlAdapter: provider === "sqlserver",
301
- useBasicSchema: schemaPreset === "basic"
304
+ schemaPreset
302
305
  };
303
306
  }
304
307
  async function scaffoldInitTemplates(projectDir, provider, envVar = "DATABASE_URL", schemaPreset = "empty") {
@@ -489,7 +492,7 @@ const dependencyVersionMap = {
489
492
  };
490
493
 
491
494
  //#endregion
492
- //#region src/db/config.ts
495
+ //#region src/constants/db-packages.ts
493
496
  function getDbPackages(provider) {
494
497
  switch (provider) {
495
498
  case "postgresql":
@@ -591,6 +594,13 @@ async function installProjectDependencies(packageManager, projectDir = process.c
591
594
  });
592
595
  }
593
596
 
597
+ //#endregion
598
+ //#region src/ui/branding.ts
599
+ const prismaTitle = `${styleText(["bold", "cyan"], "Create")} ${styleText(["bold", "magenta"], "Prisma")}`;
600
+ function getCreatePrismaIntro() {
601
+ return prismaTitle;
602
+ }
603
+
594
604
  //#endregion
595
605
  //#region src/commands/init.ts
596
606
  const DEFAULT_DATABASE_PROVIDER = "postgresql";
@@ -627,7 +637,7 @@ async function promptForDatabaseProvider() {
627
637
  ]
628
638
  });
629
639
  if (isCancel(databaseProvider)) {
630
- cancel("Cancelled.");
640
+ cancel("Operation cancelled.");
631
641
  return;
632
642
  }
633
643
  return DatabaseProviderSchema.parse(databaseProvider);
@@ -659,7 +669,7 @@ async function promptForPackageManager(detectedPackageManager) {
659
669
  ]
660
670
  });
661
671
  if (isCancel(packageManager)) {
662
- cancel("Cancelled.");
672
+ cancel("Operation cancelled.");
663
673
  return;
664
674
  }
665
675
  return PackageManagerSchema.parse(packageManager);
@@ -670,7 +680,7 @@ async function promptForDependencyInstall(packageManager) {
670
680
  initialValue: true
671
681
  });
672
682
  if (isCancel(shouldInstall)) {
673
- cancel("Cancelled.");
683
+ cancel("Operation cancelled.");
674
684
  return;
675
685
  }
676
686
  return Boolean(shouldInstall);
@@ -681,22 +691,11 @@ async function promptForPrismaPostgres() {
681
691
  initialValue: true
682
692
  });
683
693
  if (isCancel(shouldUsePrismaPostgres)) {
684
- cancel("Cancelled.");
694
+ cancel("Operation cancelled.");
685
695
  return;
686
696
  }
687
697
  return Boolean(shouldUsePrismaPostgres);
688
698
  }
689
- async function promptContinueWithDefaultPostgresUrl() {
690
- const shouldContinue = await confirm({
691
- message: "Continue with default local PostgreSQL DATABASE_URL instead?",
692
- initialValue: true
693
- });
694
- if (isCancel(shouldContinue)) {
695
- cancel("Cancelled.");
696
- return;
697
- }
698
- return Boolean(shouldContinue);
699
- }
700
699
  async function promptForPrismaFilesMode(existingFiles, canReuseExistingPrismaFiles, baseDir) {
701
700
  const mode = await select({
702
701
  message: `Prisma already exists (${existingFiles.map((filePath) => formatCreatedPath(filePath, baseDir)).join(", ")}). How should we continue?`,
@@ -724,49 +723,23 @@ async function promptForPrismaFilesMode(existingFiles, canReuseExistingPrismaFil
724
723
  label: "Cancel"
725
724
  }]
726
725
  });
727
- if (isCancel(mode) || mode === "cancel") {
728
- cancel("Cancelled.");
726
+ if (isCancel(mode)) {
727
+ cancel("Operation cancelled.");
728
+ return;
729
+ }
730
+ if (mode === "cancel") {
731
+ cancel("Operation cancelled.");
729
732
  return;
730
733
  }
731
734
  if (mode !== "reuse" && mode !== "overwrite") {
732
- cancel("Cancelled.");
735
+ cancel("Operation cancelled.");
733
736
  return;
734
737
  }
735
738
  return mode;
736
739
  }
737
- function formatEnvStatus(status, envPath, envVarName, baseDir) {
738
- const relativeEnvPath = path.relative(baseDir, envPath) || ".env";
739
- switch (status) {
740
- case "created": return `Created ${relativeEnvPath} with ${envVarName}`;
741
- case "appended": return `Appended ${envVarName} to ${relativeEnvPath}`;
742
- case "existing": return `Kept existing ${envVarName} in ${relativeEnvPath}`;
743
- case "updated": return `Updated ${envVarName} in ${relativeEnvPath}`;
744
- default: return `Updated ${relativeEnvPath}`;
745
- }
746
- }
747
- function formatGitignoreStatus(status, gitignorePath, baseDir) {
748
- const relativePath = path.relative(baseDir, gitignorePath) || ".gitignore";
749
- switch (status) {
750
- case "created": return `Created ${relativePath} with prisma/generated`;
751
- case "appended": return `Added prisma/generated to ${relativePath}`;
752
- case "existing": return `Kept existing prisma/generated ignore in ${relativePath}`;
753
- default: return `Updated ${relativePath}`;
754
- }
755
- }
756
740
  function formatCreatedPath(filePath, baseDir) {
757
741
  return path.relative(baseDir, filePath);
758
742
  }
759
- function formatFileAction(action) {
760
- switch (action) {
761
- case "create": return "Created";
762
- case "overwrite": return "Wrote";
763
- case "reuse": return "Kept existing";
764
- default: {
765
- const exhaustiveCheck = action;
766
- throw new Error(`Unsupported file action: ${String(exhaustiveCheck)}`);
767
- }
768
- }
769
- }
770
743
  function getCommandErrorMessage(error) {
771
744
  if (error instanceof Error && "stderr" in error) {
772
745
  const stderr = String(error.stderr ?? "").trim();
@@ -774,13 +747,13 @@ function getCommandErrorMessage(error) {
774
747
  }
775
748
  return error instanceof Error ? error.message : String(error);
776
749
  }
777
- async function runInitCommand(rawInput = {}, options = {}) {
750
+ async function collectInitContext(rawInput = {}, options = {}) {
778
751
  const projectDir = path.resolve(options.projectDir ?? process.cwd());
779
752
  const input = InitCommandInputSchema.parse(rawInput);
780
753
  const useDefaults = input.yes === true;
781
754
  const verbose = input.verbose === true;
782
755
  const shouldGenerate = input.generate ?? DEFAULT_GENERATE;
783
- if (!options.skipIntro) intro("Create Prisma");
756
+ if (!options.skipIntro) intro(getCreatePrismaIntro());
784
757
  let prismaFilesMode = "create";
785
758
  const existingPrismaFiles = findExistingPrismaFiles(projectDir);
786
759
  const canReuseExistingPrismaFiles = canReusePrismaFiles(projectDir);
@@ -793,134 +766,176 @@ async function runInitCommand(rawInput = {}, options = {}) {
793
766
  const databaseProvider = input.provider ?? (useDefaults ? DEFAULT_DATABASE_PROVIDER : await promptForDatabaseProvider());
794
767
  if (!databaseProvider) return;
795
768
  const schemaPreset = input.schemaPreset ?? DEFAULT_SCHEMA_PRESET$1;
796
- let databaseUrl = input.databaseUrl;
769
+ const databaseUrl = input.databaseUrl;
797
770
  let shouldUsePrismaPostgres = false;
798
- let claimUrl;
799
- let prismaPostgresWarning;
800
771
  if (databaseProvider === "postgresql" && !databaseUrl) {
801
772
  const prismaPostgresChoice = input.prismaPostgres ?? (useDefaults ? DEFAULT_PRISMA_POSTGRES : await promptForPrismaPostgres());
802
773
  if (prismaPostgresChoice === void 0) return;
803
774
  shouldUsePrismaPostgres = prismaPostgresChoice;
804
775
  }
805
776
  const detectedPackageManager = await detectPackageManager(projectDir);
806
- const finalPackageManager = input.packageManager ?? (useDefaults ? detectedPackageManager : await promptForPackageManager(detectedPackageManager));
807
- if (!finalPackageManager) return;
808
- const installCommand = getInstallCommand(finalPackageManager);
809
- if (shouldUsePrismaPostgres) {
810
- const createDbCommand = getCreateDbCommand(finalPackageManager);
811
- const prismaPostgresSpinner = spinner();
812
- prismaPostgresSpinner.start(`Provisioning Prisma Postgres with ${createDbCommand}...`);
813
- try {
814
- const prismaPostgresResult = await provisionPrismaPostgres(finalPackageManager);
815
- databaseUrl = prismaPostgresResult.databaseUrl;
816
- claimUrl = prismaPostgresResult.claimUrl;
817
- prismaPostgresSpinner.stop("Prisma Postgres database provisioned.");
818
- } catch (error) {
819
- const errorMessage = error instanceof Error ? error.message : String(error);
820
- prismaPostgresSpinner.stop("Could not provision Prisma Postgres.");
821
- prismaPostgresWarning = `Prisma Postgres provisioning failed: ${errorMessage}`;
822
- const shouldContinue = useDefaults ? true : await promptContinueWithDefaultPostgresUrl();
823
- if (shouldContinue === void 0) return;
824
- if (!shouldContinue) {
825
- cancel("Cancelled.");
826
- return;
827
- }
828
- }
829
- }
830
- const dependencyWriteResult = await writePrismaDependencies(databaseProvider, projectDir);
831
- const shouldInstall = input.install ?? (useDefaults ? DEFAULT_INSTALL : await promptForDependencyInstall(finalPackageManager));
777
+ const packageManager = input.packageManager ?? (useDefaults ? detectedPackageManager : await promptForPackageManager(detectedPackageManager));
778
+ if (!packageManager) return;
779
+ const shouldInstall = input.install ?? (useDefaults ? DEFAULT_INSTALL : await promptForDependencyInstall(packageManager));
832
780
  if (shouldInstall === void 0) return;
833
- if (shouldInstall) if (verbose) {
781
+ return {
782
+ projectDir,
783
+ verbose,
784
+ shouldGenerate,
785
+ prismaFilesMode,
786
+ databaseProvider,
787
+ schemaPreset,
788
+ databaseUrl,
789
+ shouldUsePrismaPostgres,
790
+ packageManager,
791
+ shouldInstall
792
+ };
793
+ }
794
+ async function provisionPrismaPostgresIfNeeded(context, projectDir) {
795
+ if (!context.shouldUsePrismaPostgres) return { databaseUrl: context.databaseUrl };
796
+ const createDbCommand = getCreateDbCommand(context.packageManager);
797
+ const prismaPostgresSpinner = spinner();
798
+ prismaPostgresSpinner.start(`Provisioning Prisma Postgres with ${createDbCommand}...`);
799
+ try {
800
+ const prismaPostgresResult = await provisionPrismaPostgres(context.packageManager, projectDir);
801
+ prismaPostgresSpinner.stop("Prisma Postgres database provisioned.");
802
+ return {
803
+ databaseUrl: prismaPostgresResult.databaseUrl,
804
+ claimUrl: prismaPostgresResult.claimUrl
805
+ };
806
+ } catch (error) {
807
+ const errorMessage = error instanceof Error ? error.message : String(error);
808
+ prismaPostgresSpinner.stop("Could not provision Prisma Postgres.");
809
+ return {
810
+ databaseUrl: context.databaseUrl,
811
+ warning: `Prisma Postgres provisioning failed: ${errorMessage}`
812
+ };
813
+ }
814
+ }
815
+ async function writeDependenciesForContext(context, projectDir) {
816
+ try {
817
+ return await writePrismaDependencies(context.databaseProvider, projectDir);
818
+ } catch (error) {
819
+ cancel(getCommandErrorMessage(error));
820
+ return;
821
+ }
822
+ }
823
+ async function installDependenciesForContext(context, projectDir) {
824
+ if (!context.shouldInstall) return true;
825
+ const installCommand = getInstallCommand(context.packageManager);
826
+ if (context.verbose) {
834
827
  log.step(`Running ${installCommand}`);
835
828
  try {
836
- await installProjectDependencies(finalPackageManager, projectDir, { verbose });
829
+ await installProjectDependencies(context.packageManager, projectDir, { verbose: context.verbose });
837
830
  log.success("Dependencies installed.");
831
+ return true;
838
832
  } catch (error) {
839
833
  cancel(`Failed to run ${installCommand}: ${getCommandErrorMessage(error)}`);
840
- return;
841
- }
842
- } else {
843
- const installSpinner = spinner();
844
- installSpinner.start(`Running ${installCommand}...`);
845
- try {
846
- await installProjectDependencies(finalPackageManager, projectDir, { verbose });
847
- installSpinner.stop("Dependencies installed.");
848
- } catch (error) {
849
- installSpinner.stop("Could not install dependencies.");
850
- cancel(`Failed to run ${installCommand}: ${getCommandErrorMessage(error)}`);
851
- return;
834
+ return false;
852
835
  }
853
836
  }
837
+ const installSpinner = spinner();
838
+ installSpinner.start(`Running ${installCommand}...`);
839
+ try {
840
+ await installProjectDependencies(context.packageManager, projectDir, { verbose: context.verbose });
841
+ installSpinner.stop("Dependencies installed.");
842
+ return true;
843
+ } catch (error) {
844
+ installSpinner.stop("Could not install dependencies.");
845
+ cancel(`Failed to run ${installCommand}: ${getCommandErrorMessage(error)}`);
846
+ return false;
847
+ }
848
+ }
849
+ async function initializePrismaFilesForContext(context, projectDir, provisionResult) {
854
850
  const initSpinner = spinner();
855
851
  initSpinner.start("Preparing Prisma files...");
856
- let initResult;
857
852
  try {
858
- initResult = await initializePrismaFiles({
859
- provider: databaseProvider,
860
- databaseUrl,
861
- claimUrl,
862
- schemaPreset,
863
- prismaFilesMode,
853
+ const initResult = await initializePrismaFiles({
854
+ provider: context.databaseProvider,
855
+ databaseUrl: provisionResult.databaseUrl,
856
+ claimUrl: provisionResult.claimUrl,
857
+ schemaPreset: context.schemaPreset,
858
+ prismaFilesMode: context.prismaFilesMode,
864
859
  projectDir
865
860
  });
861
+ if (initResult.prismaFilesMode === "overwrite") initSpinner.stop("Prisma files updated.");
862
+ else if (initResult.prismaFilesMode === "reuse") initSpinner.stop("Using existing Prisma files.");
863
+ else initSpinner.stop("Prisma files ready.");
864
+ return initResult;
866
865
  } catch (error) {
867
866
  initSpinner.stop("Could not prepare Prisma files.");
868
867
  cancel(getCommandErrorMessage(error));
869
868
  return;
870
869
  }
871
- if (initResult.prismaFilesMode === "overwrite") initSpinner.stop("Prisma files updated.");
872
- else if (initResult.prismaFilesMode === "reuse") initSpinner.stop("Using existing Prisma files.");
873
- else initSpinner.stop("Prisma files ready.");
874
- let generateWarning;
875
- let didGenerateClient = false;
876
- if (shouldGenerate) {
877
- const generateCommand = getPrismaCliCommand(finalPackageManager, ["generate"]);
878
- if (verbose) log.step(`Running ${generateCommand}`);
879
- const generateSpinner = verbose ? void 0 : spinner();
880
- generateSpinner?.start("Generating Prisma Client...");
881
- try {
882
- const generateArgs = getPrismaCliArgs(finalPackageManager, ["generate"]);
883
- await execa(generateArgs.command, generateArgs.args, {
884
- cwd: projectDir,
885
- stdio: verbose ? "inherit" : "pipe"
886
- });
887
- didGenerateClient = true;
888
- if (verbose) log.success("Prisma Client generated.");
889
- else generateSpinner?.stop("Prisma Client generated.");
890
- } catch (error) {
891
- if (verbose) log.warn("Could not generate Prisma Client.");
892
- else generateSpinner?.stop("Could not generate Prisma Client.");
893
- generateWarning = `Prisma generate failed: ${getCommandErrorMessage(error)}`;
894
- }
870
+ }
871
+ async function generatePrismaClientForContext(context, projectDir) {
872
+ if (!context.shouldGenerate) return { didGenerateClient: false };
873
+ const generateCommand = getPrismaCliCommand(context.packageManager, ["generate"]);
874
+ if (context.verbose) log.step(`Running ${generateCommand}`);
875
+ const generateSpinner = context.verbose ? void 0 : spinner();
876
+ generateSpinner?.start("Generating Prisma Client...");
877
+ try {
878
+ const generateArgs = getPrismaCliArgs(context.packageManager, ["generate"]);
879
+ await execa(generateArgs.command, generateArgs.args, {
880
+ cwd: projectDir,
881
+ stdio: context.verbose ? "inherit" : "pipe"
882
+ });
883
+ if (context.verbose) log.success("Prisma Client generated.");
884
+ else generateSpinner?.stop("Prisma Client generated.");
885
+ return { didGenerateClient: true };
886
+ } catch (error) {
887
+ if (context.verbose) log.warn("Could not generate Prisma Client.");
888
+ else generateSpinner?.stop("Could not generate Prisma Client.");
889
+ return {
890
+ didGenerateClient: false,
891
+ warning: `Prisma generate failed: ${getCommandErrorMessage(error)}`
892
+ };
895
893
  }
896
- const fileActionLabel = formatFileAction(initResult.prismaFilesMode);
897
- const summaryLines = [
898
- `- ${fileActionLabel} ${formatCreatedPath(initResult.schemaPath, projectDir)}`,
899
- `- ${fileActionLabel} ${formatCreatedPath(initResult.configPath, projectDir)}`,
900
- `- ${fileActionLabel} ${formatCreatedPath(initResult.singletonPath, projectDir)}`
901
- ];
902
- if (initResult.envStatus !== "existing") summaryLines.push(`- ${formatEnvStatus(initResult.envStatus, initResult.envPath, "DATABASE_URL", projectDir)}`);
903
- if (initResult.claimEnvStatus) summaryLines.push(`- ${formatEnvStatus(initResult.claimEnvStatus, initResult.envPath, "CLAIM_URL", projectDir)}`);
904
- if (initResult.gitignoreStatus !== "existing") summaryLines.push(`- ${formatGitignoreStatus(initResult.gitignoreStatus, initResult.gitignorePath, projectDir)}`);
905
- if (dependencyWriteResult.addedScripts.length > 0) summaryLines.push(`- Added package.json scripts: ${dependencyWriteResult.addedScripts.join(", ")}`);
906
- else if (dependencyWriteResult.scripts.length > 0) summaryLines.push(`- Kept existing package.json scripts: ${dependencyWriteResult.scripts.join(", ")}`);
907
- if (!shouldInstall) summaryLines.push(`- Skipped ${installCommand}.`);
908
- else summaryLines.push(`- Installed dependencies with ${installCommand}.`);
909
- if (!shouldGenerate) summaryLines.push("- Skipped Prisma Client generation.");
910
- else if (didGenerateClient) summaryLines.push("- Prisma Client generated.");
911
- const postgresLines = [];
912
- if (prismaPostgresWarning) postgresLines.push(`- ${prismaPostgresWarning}`);
913
- if (generateWarning) postgresLines.push(`- ${generateWarning}`);
894
+ }
895
+ function buildWarningLines(provisionWarning, generateWarning) {
896
+ const warningLines = [];
897
+ if (provisionWarning) warningLines.push(`- ${provisionWarning}`);
898
+ if (generateWarning) warningLines.push(`- ${generateWarning}`);
899
+ return warningLines;
900
+ }
901
+ function buildNextStepsForContext(opts) {
902
+ const { context, options, didGenerateClient } = opts;
914
903
  const nextSteps = [...options.prependNextSteps ?? []];
915
- if (!shouldInstall) nextSteps.push(`- ${installCommand}`);
916
- if (!didGenerateClient || !shouldGenerate) nextSteps.push(`- ${getRunScriptCommand(finalPackageManager, "db:generate")}`);
917
- nextSteps.push(`- ${getRunScriptCommand(finalPackageManager, "db:migrate")}`);
918
- outro(`Setup complete.
919
- ${summaryLines.join("\n")}
920
- ${postgresLines.length > 0 ? `\n${postgresLines.join("\n")}` : ""}
904
+ if (!context.shouldInstall) nextSteps.push(`- ${getInstallCommand(context.packageManager)}`);
905
+ if (!didGenerateClient || !context.shouldGenerate) nextSteps.push(`- ${getRunScriptCommand(context.packageManager, "db:generate")}`);
906
+ nextSteps.push(`- ${getRunScriptCommand(context.packageManager, "db:migrate")}`);
907
+ if (options.includeDevNextStep) nextSteps.push(`- ${getRunScriptCommand(context.packageManager, "dev")}`);
908
+ return nextSteps;
909
+ }
910
+ async function executeInitContext(context, options = {}) {
911
+ const projectDir = path.resolve(options.projectDir ?? context.projectDir);
912
+ const provisionResult = await provisionPrismaPostgresIfNeeded(context, projectDir);
913
+ if (!provisionResult) return;
914
+ if (!await writeDependenciesForContext(context, projectDir)) return;
915
+ if (!await installDependenciesForContext(context, projectDir)) return;
916
+ if (!await initializePrismaFilesForContext(context, projectDir, provisionResult)) return;
917
+ const generateResult = await generatePrismaClientForContext(context, projectDir);
918
+ const warningLines = buildWarningLines(provisionResult.warning, generateResult.warning);
919
+ const nextSteps = buildNextStepsForContext({
920
+ context,
921
+ options,
922
+ didGenerateClient: generateResult.didGenerateClient
923
+ });
924
+ outro(`Setup complete.${warningLines.length > 0 ? `\n\n${warningLines.join("\n")}` : ""}
921
925
 
922
926
  Next steps:
923
927
  ${nextSteps.join("\n")}`);
928
+ return { packageManager: context.packageManager };
929
+ }
930
+ async function runInitCommand(rawInput = {}, options = {}) {
931
+ try {
932
+ const context = await collectInitContext(rawInput, options);
933
+ if (!context) return;
934
+ return executeInitContext(context, options);
935
+ } catch (error) {
936
+ cancel(`Init command failed: ${error instanceof Error ? error.message : String(error)}`);
937
+ return;
938
+ }
924
939
  }
925
940
 
926
941
  //#endregion
@@ -934,17 +949,21 @@ function toPackageName(projectName) {
934
949
  function formatPathForDisplay(filePath) {
935
950
  return path.relative(process.cwd(), filePath) || ".";
936
951
  }
952
+ function validateProjectName(value) {
953
+ const trimmed = String(value ?? "").trim();
954
+ if (trimmed.length === 0) return "Please enter a project name.";
955
+ if (trimmed === "." || trimmed === "..") return "Project name cannot be '.' or '..'.";
956
+ if (path.isAbsolute(trimmed)) return "Use a relative project name instead of an absolute path.";
957
+ }
937
958
  async function promptForProjectName() {
938
959
  const projectName = await text({
939
960
  message: "Project name",
940
961
  placeholder: DEFAULT_PROJECT_NAME,
941
962
  initialValue: DEFAULT_PROJECT_NAME,
942
- validate: (value) => {
943
- return String(value ?? "").trim().length > 0 ? void 0 : "Please enter a valid project name.";
944
- }
963
+ validate: validateProjectName
945
964
  });
946
965
  if (isCancel(projectName)) {
947
- cancel("Cancelled.");
966
+ cancel("Operation cancelled.");
948
967
  return;
949
968
  }
950
969
  return String(projectName).trim();
@@ -964,17 +983,40 @@ async function promptForCreateTemplate() {
964
983
  }]
965
984
  });
966
985
  if (isCancel(template)) {
967
- cancel("Cancelled.");
986
+ cancel("Operation cancelled.");
968
987
  return;
969
988
  }
970
989
  return CreateTemplateSchema.parse(template);
971
990
  }
972
- async function isDirectoryEmpty(directoryPath) {
973
- if (!await fs.pathExists(directoryPath)) return true;
974
- return (await fs.readdir(directoryPath)).length === 0;
991
+ async function inspectTargetPath(targetPath) {
992
+ if (!await fs.pathExists(targetPath)) return {
993
+ exists: false,
994
+ isDirectory: true,
995
+ isEmptyDirectory: true
996
+ };
997
+ if (!(await fs.stat(targetPath)).isDirectory()) return {
998
+ exists: true,
999
+ isDirectory: false,
1000
+ isEmptyDirectory: false
1001
+ };
1002
+ return {
1003
+ exists: true,
1004
+ isDirectory: true,
1005
+ isEmptyDirectory: (await fs.readdir(targetPath)).length === 0
1006
+ };
975
1007
  }
976
1008
  async function runCreateCommand(rawInput = {}) {
977
- const input = CreateCommandInputSchema.parse(rawInput);
1009
+ try {
1010
+ const input = CreateCommandInputSchema.parse(rawInput);
1011
+ intro(getCreatePrismaIntro());
1012
+ const context = await collectCreateContext(input);
1013
+ if (!context) return;
1014
+ await executeCreateContext(context);
1015
+ } catch (error) {
1016
+ cancel(`Create command failed: ${error instanceof Error ? error.message : String(error)}`);
1017
+ }
1018
+ }
1019
+ async function collectCreateContext(input) {
978
1020
  const useDefaults = input.yes === true;
979
1021
  const force = input.force === true;
980
1022
  const projectName = input.name ?? (useDefaults ? DEFAULT_PROJECT_NAME : await promptForProjectName());
@@ -983,29 +1025,16 @@ async function runCreateCommand(rawInput = {}) {
983
1025
  if (!template) return;
984
1026
  const schemaPreset = input.schemaPreset ?? DEFAULT_SCHEMA_PRESET;
985
1027
  const targetDirectory = path.resolve(process.cwd(), projectName);
986
- const targetExists = await fs.pathExists(targetDirectory);
987
- const targetIsEmpty = await isDirectoryEmpty(targetDirectory);
988
- if (targetExists && !targetIsEmpty && !force) {
989
- cancel(`Target directory ${formatPathForDisplay(targetDirectory)} is not empty. Use --force to continue.`);
1028
+ const targetPathState = await inspectTargetPath(targetDirectory);
1029
+ if (targetPathState.exists && !targetPathState.isDirectory) {
1030
+ cancel(`Target path ${formatPathForDisplay(targetDirectory)} already exists and is not a directory. Choose a different project name.`);
990
1031
  return;
991
1032
  }
992
- const scaffoldSpinner = spinner();
993
- scaffoldSpinner.start(`Scaffolding ${template} project...`);
994
- try {
995
- await scaffoldCreateTemplate({
996
- projectDir: targetDirectory,
997
- projectName: toPackageName(path.basename(targetDirectory)),
998
- template,
999
- schemaPreset
1000
- });
1001
- scaffoldSpinner.stop("Project files scaffolded.");
1002
- } catch (error) {
1003
- scaffoldSpinner.stop("Could not scaffold project files.");
1004
- cancel(error instanceof Error ? error.message : String(error));
1033
+ if (targetPathState.exists && !targetPathState.isEmptyDirectory && !force) {
1034
+ cancel(`Target directory ${formatPathForDisplay(targetDirectory)} is not empty. Use --force to continue.`);
1005
1035
  return;
1006
1036
  }
1007
- if (targetExists && !targetIsEmpty && force) log.warn(`Used --force in non-empty directory ${formatPathForDisplay(targetDirectory)}.`);
1008
- await runInitCommand({
1037
+ const initContext = await collectInitContext({
1009
1038
  yes: input.yes,
1010
1039
  verbose: input.verbose,
1011
1040
  provider: input.provider,
@@ -1017,9 +1046,44 @@ async function runCreateCommand(rawInput = {}) {
1017
1046
  schemaPreset
1018
1047
  }, {
1019
1048
  skipIntro: true,
1020
- prependNextSteps: [`- cd ${formatPathForDisplay(targetDirectory)}`],
1021
1049
  projectDir: targetDirectory
1022
1050
  });
1051
+ if (!initContext) return;
1052
+ return {
1053
+ targetDirectory,
1054
+ targetPathState,
1055
+ force,
1056
+ template,
1057
+ schemaPreset,
1058
+ projectPackageName: toPackageName(path.basename(targetDirectory)),
1059
+ initContext
1060
+ };
1061
+ }
1062
+ async function executeCreateContext(context) {
1063
+ const scaffoldSpinner = spinner();
1064
+ scaffoldSpinner.start(`Scaffolding ${context.template} project...`);
1065
+ try {
1066
+ await scaffoldCreateTemplate({
1067
+ projectDir: context.targetDirectory,
1068
+ projectName: context.projectPackageName,
1069
+ template: context.template,
1070
+ schemaPreset: context.schemaPreset,
1071
+ packageManager: context.initContext.packageManager
1072
+ });
1073
+ scaffoldSpinner.stop("Project files scaffolded.");
1074
+ } catch (error) {
1075
+ scaffoldSpinner.stop("Could not scaffold project files.");
1076
+ cancel(error instanceof Error ? error.message : String(error));
1077
+ return;
1078
+ }
1079
+ if (context.targetPathState.exists && !context.targetPathState.isEmptyDirectory && context.force) log.warn(`Used --force in non-empty directory ${formatPathForDisplay(context.targetDirectory)}.`);
1080
+ const cdStep = `- cd ${formatPathForDisplay(context.targetDirectory)}`;
1081
+ await executeInitContext(context.initContext, {
1082
+ skipIntro: true,
1083
+ prependNextSteps: [cdStep],
1084
+ projectDir: context.targetDirectory,
1085
+ includeDevNextStep: true
1086
+ });
1023
1087
  }
1024
1088
 
1025
1089
  //#endregion
package/dist/index.mjs CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { a as DatabaseProviderSchema, c as PackageManagerSchema, i as CreateTemplateSchema, l as SchemaPresetSchema, n as runInitCommand, o as DatabaseUrlSchema, r as CreateCommandInputSchema, s as InitCommandInputSchema, t as runCreateCommand } from "./create-fJECj1B0.mjs";
2
+ import { a as DatabaseProviderSchema, c as PackageManagerSchema, i as CreateTemplateSchema, l as SchemaPresetSchema, n as runInitCommand, o as DatabaseUrlSchema, r as CreateCommandInputSchema, s as InitCommandInputSchema, t as runCreateCommand } from "./create-Dz9GFGFQ.mjs";
3
3
  import { os } from "@orpc/server";
4
4
  import { createCli } from "trpc-cli";
5
5
 
6
6
  //#region src/index.ts
7
- const CLI_VERSION = "0.1.3";
7
+ const CLI_VERSION = "0.1.4";
8
8
  const router = os.router({
9
9
  create: os.meta({
10
10
  description: "Create a new project with Prisma setup",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-prisma",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Create and initialize Prisma 7 projects with first-party templates and great DX.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -4,25 +4,63 @@ Generated by `create-prisma` with the Hono template.
4
4
 
5
5
  ## Scripts
6
6
 
7
+ {{#if (eq packageManager "bun")}}
7
8
  - `bun run dev` - start local dev server
8
9
  - `bun run build` - typecheck and compile
9
10
  - `bun run start` - run compiled server from `dist/`
11
+ {{else}}
12
+ {{#if (eq packageManager "pnpm")}}
13
+ - `pnpm dev` - start local dev server
14
+ - `pnpm build` - typecheck and compile
15
+ - `pnpm start` - run compiled server from `dist/`
16
+ {{else}}
17
+ {{#if (eq packageManager "npm")}}
18
+ - `npm run dev` - start local dev server
19
+ - `npm run build` - typecheck and compile
20
+ - `npm run start` - run compiled server from `dist/`
21
+ {{else}}
22
+ - `npm run dev` or `pnpm dev` or `bun run dev` - start local dev server
23
+ - `npm run build` or `pnpm build` or `bun run build` - typecheck and compile
24
+ - `npm run start` or `pnpm start` or `bun run start` - run compiled server from `dist/`
25
+ {{/if}}
26
+ {{/if}}
27
+ {{/if}}
10
28
 
11
29
  ## Prisma
12
30
 
13
31
  1. Make sure dependencies are installed.
14
32
  2. Generate Prisma Client:
15
33
 
16
- ```bash
17
- bunx --bun prisma generate
18
- ```
34
+ {{#if (eq packageManager "bun")}}
35
+ `bun run db:generate`
36
+ {{else}}
37
+ {{#if (eq packageManager "pnpm")}}
38
+ `pnpm db:generate`
39
+ {{else}}
40
+ {{#if (eq packageManager "npm")}}
41
+ `npm run db:generate`
42
+ {{else}}
43
+ `npm run db:generate` or `pnpm db:generate` or `bun run db:generate`
44
+ {{/if}}
45
+ {{/if}}
46
+ {{/if}}
19
47
 
20
48
  3. Run your first migration:
21
49
 
22
- ```bash
23
- bunx --bun prisma migrate dev
24
- ```
25
- {{#if useBasicSchema}}
50
+ {{#if (eq packageManager "bun")}}
51
+ `bun run db:migrate`
52
+ {{else}}
53
+ {{#if (eq packageManager "pnpm")}}
54
+ `pnpm db:migrate`
55
+ {{else}}
56
+ {{#if (eq packageManager "npm")}}
57
+ `npm run db:migrate`
58
+ {{else}}
59
+ `npm run db:migrate` or `pnpm db:migrate` or `bun run db:migrate`
60
+ {{/if}}
61
+ {{/if}}
62
+ {{/if}}
63
+ {{#if (eq schemaPreset "basic")}}
26
64
 
27
65
  The template includes a basic `User` model and a sample `GET /users` endpoint.
28
66
  {{/if}}
@@ -5,7 +5,7 @@
5
5
  "scripts": {
6
6
  "dev": "tsx watch src/index.ts",
7
7
  "build": "tsc",
8
- "start": "node dist/src/index.js"
8
+ "start": "tsx dist/src/index.js"
9
9
  },
10
10
  "dependencies": {
11
11
  "@hono/node-server": "^1.19.9",
@@ -1,6 +1,6 @@
1
1
  import { serve } from "@hono/node-server";
2
2
  import { Hono } from "hono";
3
- {{#if useBasicSchema}}
3
+ {{#if (eq schemaPreset "basic")}}
4
4
  import { prisma } from "../prisma";
5
5
  {{/if}}
6
6
 
@@ -11,7 +11,7 @@ app.get("/", (c) => {
11
11
  message: "hello from create-prisma + hono",
12
12
  });
13
13
  });
14
- {{#if useBasicSchema}}
14
+ {{#if (eq schemaPreset "basic")}}
15
15
 
16
16
  app.get("/users", async (c) => {
17
17
  const users = await prisma.user.findMany({
@@ -4,9 +4,27 @@ Generated by `create-prisma` with the Next.js template.
4
4
 
5
5
  ## Scripts
6
6
 
7
+ {{#if (eq packageManager "bun")}}
8
+ - `bun dev` - start local dev server
9
+ - `bun run build` - production build
10
+ - `bun run start` - run production server
11
+ {{else}}
12
+ {{#if (eq packageManager "pnpm")}}
13
+ - `pnpm dev` - start local dev server
14
+ - `pnpm build` - production build
15
+ - `pnpm start` - run production server
16
+ {{else}}
17
+ {{#if (eq packageManager "npm")}}
18
+ - `npm run dev` - start local dev server
19
+ - `npm run build` - production build
20
+ - `npm run start` - run production server
21
+ {{else}}
7
22
  - `npm run dev` or `pnpm dev` or `bun dev` - start local dev server
8
23
  - `npm run build` or `pnpm build` or `bun run build` - production build
9
24
  - `npm run start` or `pnpm start` or `bun run start` - run production server
25
+ {{/if}}
26
+ {{/if}}
27
+ {{/if}}
10
28
 
11
29
  ## Prisma
12
30
 
@@ -21,7 +39,7 @@ Database helper scripts are added to `package.json`:
21
39
  - `db:generate`
22
40
  - `db:push`
23
41
  - `db:migrate`
24
- {{#if useBasicSchema}}
42
+ {{#if (eq schemaPreset "basic")}}
25
43
 
26
44
  The starter page reads from a basic `User` model so you can verify queries quickly.
27
45
  {{/if}}
@@ -1,25 +1,31 @@
1
- {{#if useBasicSchema}}
1
+ {{#if (eq schemaPreset "basic")}}
2
2
  import { prisma } from "../prisma";
3
3
  {{/if}}
4
4
 
5
5
  export default async function Home() {
6
- {{#if useBasicSchema}}
7
- const users = await prisma.user.findMany({
8
- take: 10,
9
- orderBy: {
10
- createdAt: "desc",
11
- },
12
- });
6
+ {{#if (eq schemaPreset "basic")}}
7
+ const users = await prisma.user
8
+ .findMany({
9
+ take: 10,
10
+ orderBy: {
11
+ createdAt: "desc",
12
+ },
13
+ })
14
+ .catch(() => undefined);
13
15
  {{/if}}
14
16
 
15
17
  return (
16
18
  <main>
17
19
  <h1>create-prisma + next</h1>
18
20
  <p>Project is ready with Next.js App Router and Prisma 7.</p>
19
- {{#if useBasicSchema}}
21
+ {{#if (eq schemaPreset "basic")}}
20
22
  <h2>Users</h2>
21
- {users.length === 0 ? (
22
- <p>No users yet. Run the <code>db:migrate</code> script, then create one.</p>
23
+ {!users ? (
24
+ <p>
25
+ Could not query users yet. Run the <code>db:migrate</code> script, then refresh.
26
+ </p>
27
+ ) : users.length === 0 ? (
28
+ <p>No users yet.</p>
23
29
  ) : (
24
30
  <ul>
25
31
  {users.map((user) => (
@@ -1,15 +1,18 @@
1
1
  import "dotenv/config";
2
2
  import { PrismaClient } from "./generated/client";
3
- {{#if usePgAdapter}}
3
+ {{#if (eq provider "postgresql")}}
4
4
  import { PrismaPg } from "@prisma/adapter-pg";
5
5
  {{/if}}
6
- {{#if useMariaDbAdapter}}
6
+ {{#if (eq provider "cockroachdb")}}
7
+ import { PrismaPg } from "@prisma/adapter-pg";
8
+ {{/if}}
9
+ {{#if (eq provider "mysql")}}
7
10
  import { PrismaMariaDb } from "@prisma/adapter-mariadb";
8
11
  {{/if}}
9
- {{#if useSqliteAdapter}}
12
+ {{#if (eq provider "sqlite")}}
10
13
  import { PrismaBetterSqlite3 } from "@prisma/adapter-better-sqlite3";
11
14
  {{/if}}
12
- {{#if useMssqlAdapter}}
15
+ {{#if (eq provider "sqlserver")}}
13
16
  import { PrismaMssql } from "@prisma/adapter-mssql";
14
17
  {{/if}}
15
18
 
@@ -17,20 +20,25 @@ declare global {
17
20
  var prisma: PrismaClient | undefined;
18
21
  }
19
22
 
20
- {{#if usePgAdapter}}
23
+ {{#if (eq provider "postgresql")}}
24
+ const adapter = new PrismaPg({
25
+ connectionString: process.env.{{envVar}} ?? "",
26
+ });
27
+ {{/if}}
28
+ {{#if (eq provider "cockroachdb")}}
21
29
  const adapter = new PrismaPg({
22
30
  connectionString: process.env.{{envVar}} ?? "",
23
31
  });
24
32
  {{/if}}
25
- {{#if useMariaDbAdapter}}
33
+ {{#if (eq provider "mysql")}}
26
34
  const adapter = new PrismaMariaDb(process.env.{{envVar}} ?? "");
27
35
  {{/if}}
28
- {{#if useSqliteAdapter}}
36
+ {{#if (eq provider "sqlite")}}
29
37
  const adapter = new PrismaBetterSqlite3({
30
38
  url: process.env.{{envVar}} ?? "file:./dev.db",
31
39
  });
32
40
  {{/if}}
33
- {{#if useMssqlAdapter}}
41
+ {{#if (eq provider "sqlserver")}}
34
42
  const adapter = new PrismaMssql(process.env.{{envVar}} ?? "");
35
43
  {{/if}}
36
44
 
@@ -6,7 +6,7 @@ generator client {
6
6
  datasource db {
7
7
  provider = "{{provider}}"
8
8
  }
9
- {{#if useBasicSchema}}
9
+ {{#if (eq schemaPreset "basic")}}
10
10
 
11
11
  model User {
12
12
  id String @id @default(cuid())