Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ Create a Prisma 8 app with Prisma Composer built in.
Use your package manager:

```bash
npx create-prisma@next my-app
pnpm dlx create-prisma@next my-app
yarn dlx create-prisma@next my-app
bunx create-prisma@next my-app
npx create-prisma@latest my-app
pnpm dlx create-prisma@latest my-app
yarn dlx create-prisma@latest my-app
bunx create-prisma@latest my-app
```

The CLI initializes Prisma 8 with `prisma@next`, installs dependencies, emits the contract, and generates a deployable Composer app. PostgreSQL projects use Composer's native Prisma Postgres provider, including migrations and a typed runtime client.
Expand Down Expand Up @@ -41,13 +41,21 @@ workspace. Choosing another workspace also updates the Prisma CLI's active works

PostgreSQL and MongoDB are supported with PSL or TypeScript contract authoring. npm, pnpm, Yarn, and Bun are supported.

Deno is supported for local minimal PostgreSQL apps:

```bash
deno run -A npm:create-prisma@latest my-deno-app --template minimal --provider postgres --package-manager deno --no-deploy
```

Prisma Compute does not support Deno deployments yet.

## Options

- positional project name or `--name`
- `--template`
- `--provider postgres|postgresql|mongo|mongodb`
- `--authoring psl|typescript`
- `--package-manager npm|pnpm|yarn|bun`
- `--package-manager npm|pnpm|yarn|bun|deno`
- `--deploy` / `--no-deploy`
- `--workspace <id-or-name>`
- `--yes`
Expand Down
1 change: 1 addition & 0 deletions src/commands/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ async function collectCreateContext(

const prismaSetupContext = await collectPrismaSetupContext(input, {
projectDir: targetDirectory,
template,
});
if (!prismaSetupContext) {
return;
Expand Down
4 changes: 4 additions & 0 deletions src/constants/dependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export const dependencyVersionMap = {
"@types/node": "^25.6.2",
alchemy: "2.0.0-beta.67",
arktype: "^2.2.3",
dotenv: "^17.4.2",
esbuild: "^0.28.1",
effect: "4.0.0-beta.103",
mongodb: "^7.1.0",
Expand All @@ -22,6 +23,9 @@ export const dependencyVersionMap = {
} as const;

export const PRISMA_PLATFORM_CLI_PACKAGE = "prisma@next";
// The consolidated CLI currently imports Node-only credential storage when Deno starts it.
// Keep Deno on Prisma 8's ORM-only CLI entrypoint until that upstream path is Deno-compatible.
export const PRISMA_DENO_CLI_PACKAGE = "prisma-next";

export type AvailableDependency = keyof typeof dependencyVersionMap;

Expand Down
36 changes: 35 additions & 1 deletion src/tasks/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import path from "node:path";
import {
getCreateTemplateDependencies,
getDependencyVersion,
PRISMA_DENO_CLI_PACKAGE,
PRISMA_PLATFORM_CLI_PACKAGE,
} from "../constants/dependencies";
import { getDbPackages } from "../constants/db-packages";
Expand All @@ -16,6 +17,27 @@ import {
} from "../utils/package-manager";

function getPrismaScriptMap(packageManager: PackageManager): Record<string, string> {
if (packageManager === "deno") {
const prismaCommand = (needsDatabase: boolean, ...args: string[]) =>
[
"deno run -A",
...(needsDatabase ? ["--env-file=.env"] : []),
`npm:${PRISMA_DENO_CLI_PACKAGE}`,
...args,
].join(" ");

return {
"contract:emit": prismaCommand(false, "contract", "emit"),
"db:init": prismaCommand(true, "db", "init"),
"db:update": prismaCommand(true, "db", "update"),
"db:verify": prismaCommand(true, "db", "verify"),
"migration:plan": prismaCommand(true, "migration", "plan"),
migrate: prismaCommand(true, "migrate"),
"migration:status": prismaCommand(true, "migration", "status"),
"migration:show": prismaCommand(true, "migration", "show"),
};
}

const prismaCommand = (...args: string[]) =>
getPackageExecutionCommand(packageManager, [PRISMA_PLATFORM_CLI_PACKAGE, ...args]);

Expand All @@ -32,6 +54,10 @@ function getPrismaScriptMap(packageManager: PackageManager): Record<string, stri
}

export function getComposerScriptMap(packageManager: PackageManager): Record<string, string> {
if (packageManager === "deno") {
return {};
}

const composerCommand = (subcommand: string, extraArgs: string[] = []) =>
getPackageExecutionCommand(packageManager, [
PRISMA_PLATFORM_CLI_PACKAGE,
Expand Down Expand Up @@ -131,10 +157,14 @@ export async function writePrismaDependencies(
): Promise<void> {
const dependencies = [getDbPackages(provider)];
if (provider === "mongo") dependencies.push("arktype", "mongodb");
if (packageManager === "deno") dependencies.push("dotenv");

const devDependencies =
packageManager === "deno" ? ["@types/node"] : ["@prisma/cli-engine", "@types/node"];

await addPackageDependency({
dependencies,
devDependencies: ["@prisma/cli-engine", "@types/node"],
devDependencies,
scripts: getPrismaScriptMap(packageManager),
projectDir,
});
Expand All @@ -147,6 +177,10 @@ export async function writeCreateTemplateDependencies(opts: {
}): Promise<void> {
const { template, packageManager, projectDir = process.cwd() } = opts;

if (packageManager === "deno") {
return;
}

for (const target of getCreateTemplateDependencies(template, packageManager)) {
await addPackageDependency({
dependencies: target.dependencies,
Expand Down
84 changes: 63 additions & 21 deletions src/tasks/setup-prisma.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import { execa } from "execa";
import fs from "fs-extra";
import path from "node:path";

import { PRISMA_PLATFORM_CLI_PACKAGE } from "../constants/dependencies";
import { PRISMA_DENO_CLI_PACKAGE, PRISMA_PLATFORM_CLI_PACKAGE } from "../constants/dependencies";
import { scaffoldCreateSharedTemplates } from "../templates/render-create-template";
import {
AuthoringStyleSchema,
DatabaseProviderSchema,
PackageManagerSchema,
packageManagers,
type AuthoringStyle,
type CreateTemplate,
type DatabaseProvider,
Expand Down Expand Up @@ -91,6 +92,7 @@ function getPackageManagerHint(option: PackageManager, detected: PackageManager)
pnpm: "Fast, disk-efficient package manager",
yarn: "Yarn package manager",
bun: "Fast runtime and package manager",
deno: "Deno runtime (minimal PostgreSQL apps)",
} satisfies Record<PackageManager, string>;
return option === detected ? `Detected; ${hints[option]}` : hints[option];
}
Expand All @@ -101,7 +103,7 @@ async function promptForPackageManager(
const packageManager = await select({
message: "Choose package manager",
initialValue: detected,
options: (["npm", "pnpm", "yarn", "bun"] as const).map((value) => ({
options: packageManagers.map((value) => ({
value,
label: value,
hint: getPackageManagerHint(value, detected),
Expand All @@ -128,7 +130,7 @@ async function promptForDeployment(): Promise<boolean | undefined> {

export async function collectPrismaSetupContext(
input: PrismaSetupCommandInput,
options: { projectDir?: string } = {},
options: { projectDir?: string; template?: CreateTemplate } = {},
): Promise<PrismaSetupContext | undefined> {
const projectDir = path.resolve(options.projectDir ?? process.cwd());
const useDefaults = input.yes === true;
Expand All @@ -147,7 +149,20 @@ export async function collectPrismaSetupContext(
(useDefaults ? detectedPackageManager : await promptForPackageManager(detectedPackageManager));
if (!packageManager) return;

const shouldDeploy = input.deploy ?? (useDefaults ? false : await promptForDeployment());
if (packageManager === "deno" && databaseProvider !== "postgres") {
throw new Error("Deno support currently requires PostgreSQL.");
}
if (packageManager === "deno" && options.template && options.template !== "minimal") {
throw new Error("Deno support currently requires the minimal template.");
}
if (packageManager === "deno" && input.deploy === true) {
throw new Error("Prisma Compute does not support Deno deployments yet. Use --no-deploy.");
}

const shouldDeploy =
packageManager === "deno"
? false
: (input.deploy ?? (useDefaults ? false : await promptForDeployment()));
if (shouldDeploy === undefined) return;

return {
Expand Down Expand Up @@ -179,31 +194,49 @@ function getInitTarget(provider: DatabaseProvider): "postgres" | "mongodb" {
}

function getPrismaCliInvocation(packageManager: PackageManager, args: string[]) {
return getPackageExecutionArgs(packageManager, [PRISMA_PLATFORM_CLI_PACKAGE, ...args]);
const packageName =
packageManager === "deno" ? PRISMA_DENO_CLI_PACKAGE : PRISMA_PLATFORM_CLI_PACKAGE;
return getPackageExecutionArgs(packageManager, [packageName, ...args]);
}

async function runPrismaInit(context: PrismaSetupContext, projectDir: string): Promise<void> {
const args = [
"orm",
"init",
"--yes",
"--no-interactive",
"--target",
getInitTarget(context.databaseProvider),
"--authoring",
context.authoring,
"--schema-path",
getContractPath(context.authoring),
"--skip-install",
"--skip-skills",
];
const args =
context.packageManager === "deno"
? [
"init",
"--yes",
"--no-interactive",
"--target",
getInitTarget(context.databaseProvider),
"--authoring",
context.authoring,
"--schema-path",
getContractPath(context.authoring),
"--no-install",
"--no-skill",
]
: [
"orm",
"init",
"--yes",
"--no-interactive",
"--target",
getInitTarget(context.databaseProvider),
"--authoring",
context.authoring,
"--schema-path",
getContractPath(context.authoring),
"--skip-install",
"--skip-skills",
];
const invocation = getPrismaCliInvocation(context.packageManager, args);
if (context.verbose) log.step(`Running ${[invocation.command, ...invocation.args].join(" ")}`);
await execa(invocation.command, invocation.args, {
cwd: projectDir,
stdio: context.verbose ? "inherit" : "pipe",
env: { ...process.env, CI: "1" },
});
if (context.packageManager === "deno") await fs.remove(path.join(projectDir, "prisma-next.md"));
}

async function ensureGitignoreEntry(projectDir: string, entry: string): Promise<void> {
Expand Down Expand Up @@ -312,10 +345,19 @@ function buildNextSteps(context: PrismaSetupContext, options: PrismaSetupRunOpti
}
if (options.includeDevNextStep) {
nextSteps.push({
command: getRunScriptCommand(context.packageManager, "dev:composer"),
description: "Build and start the app with Prisma Composer locally.",
command: getRunScriptCommand(
context.packageManager,
context.packageManager === "deno" ? "dev" : "dev:composer",
),
description:
context.packageManager === "deno"
? "Start the Deno app after setting DATABASE_URL in .env."
: "Build and start the app with Prisma Composer locally.",
});
}
if (context.packageManager === "deno") {
return nextSteps;
}
nextSteps.push({
command: getRunScriptCommand(context.packageManager, "deploy"),
description: "Build and deploy the app with Prisma Composer.",
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { z } from "zod";
export const databaseProviders = ["postgres", "mongo"] as const;
export const databaseProviderInputs = ["postgres", "postgresql", "mongo", "mongodb"] as const;

export const packageManagers = ["npm", "pnpm", "yarn", "bun"] as const;
export const packageManagers = ["npm", "pnpm", "yarn", "bun", "deno"] as const;
export const authoringStyles = ["psl", "typescript"] as const;
export const createTemplates = [
"minimal",
Expand Down
Loading
Loading