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
13 changes: 10 additions & 3 deletions docs/product/command-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ prisma branch use
prisma branch use production
```

## `prisma app build --entry <path> --build-type <auto|bun|nextjs>`
## `prisma app build --entry <path> --build-type <auto|bun|nextjs|nuxt|astro|tanstack-start>`

Purpose:

Expand All @@ -308,13 +308,16 @@ Purpose:
Behavior:

- detects supported project shapes when `--build-type auto` is used
- supports Bun and Next.js app builds in the preview package
- supports Bun, Next.js, Nuxt, Astro, and TanStack Start app builds in the preview package
- fails with `USAGE_ERROR` when framework detection is ambiguous

Examples:

```bash
prisma app build --build-type nextjs
prisma app build --build-type nuxt
prisma app build --build-type astro
prisma app build --build-type tanstack-start
prisma app build --build-type bun --entry server.ts
```

Expand All @@ -337,7 +340,7 @@ prisma app run --build-type nextjs
prisma app run --build-type bun --entry server.ts --port 3000
```

## `prisma app deploy --app <name> --entry <path> --build-type <auto|bun|nextjs> --http-port <port> --env <name=value>`
## `prisma app deploy --app <name> --entry <path> --build-type <auto|bun|nextjs|nuxt|astro|tanstack-start> --http-port <port> --env <name=value>`

Purpose:

Expand All @@ -349,6 +352,7 @@ Behavior:
- resolves or creates project context
- resolves or creates app context when required
- accepts repeated `--env NAME=VALUE` flags
- uses the same supported build strategies as `app build`
- does not print secret values
- returns app, deployment id, URL, and next steps

Expand All @@ -358,6 +362,9 @@ Examples:
prisma app deploy
prisma app deploy --app hello-world --env DATABASE_URL=postgresql://example
prisma app deploy --app hello-world --build-type nextjs --http-port 3000
prisma app deploy --app hello-world --build-type nuxt
prisma app deploy --app hello-world --build-type astro
prisma app deploy --app hello-world --build-type tanstack-start
```

## `prisma app update-env --app <name> --env <name=value>`
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
},
"dependencies": {
"@clack/prompts": "^1.2.0",
"@prisma/compute-sdk": "^0.14.0",
"@prisma/compute-sdk": "^0.17.0",
"c12": "4.0.0-beta.4",
"@prisma/credentials-store": "^7.7.0",
"@prisma/management-api-sdk": "^1.24.0",
Expand Down
5 changes: 3 additions & 2 deletions packages/cli/src/commands/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import { attachCommandDescriptor } from "../../shell/command-meta";
import { addGlobalFlags } from "../../shell/global-flags";
import { runCommand } from "../../shell/command-runner";
import { configureRuntimeCommand, type CliRuntime } from "../../shell/runtime";
import { PREVIEW_BUILD_TYPES } from "../../lib/app/preview-build";
import type {
AppBuildResult,
AppDeployResult,
Expand Down Expand Up @@ -90,7 +91,7 @@ function createBuildCommand(runtime: CliRuntime): Command {
.addOption(new Option("--entry <path>", "Entrypoint path for Bun or auto builds"))
.addOption(
new Option("--build-type <type>", "Local build type")
.choices(["auto", "bun", "nextjs"])
.choices([...PREVIEW_BUILD_TYPES])
.default("auto"),
);
addGlobalFlags(command);
Expand Down Expand Up @@ -161,7 +162,7 @@ function createDeployCommand(runtime: CliRuntime): Command {
.addOption(new Option("--entry <path>", "Entrypoint path for Bun or auto deploys"))
.addOption(
new Option("--build-type <type>", "Deploy build type")
.choices(["auto", "bun", "nextjs"])
.choices([...PREVIEW_BUILD_TYPES])
.default("auto"),
)
.addOption(new Option("--http-port <port>", "HTTP port override for the deployed app"))
Expand Down
76 changes: 63 additions & 13 deletions packages/cli/src/controllers/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ import {
runLocalApp,
} from "../lib/app/local-dev";
import { projectNotFoundError } from "../use-cases/project";
import { executePreviewBuild, type PreviewBuildType } from "../lib/app/preview-build";
import {
executePreviewBuild,
PREVIEW_BUILD_TYPES,
RESOLVED_PREVIEW_BUILD_TYPES,
type PreviewBuildType,
} from "../lib/app/preview-build";
import {
createPreviewDeployInteraction,
PREVIEW_DEFAULT_REGION,
Expand All @@ -56,13 +61,11 @@ export async function runAppBuild(
const buildType = normalizeBuildType(requestedBuildType);
assertSupportedEntrypoint(buildType, entrypoint, "build");

const resolvedBuildType = await requireLocalBuildType(context, buildType, "build");

try {
const { artifact, buildType: actualBuildType } = await executePreviewBuild({
appPath: context.runtime.cwd,
entrypoint,
buildType: resolvedBuildType,
buildType,
});

return {
Expand All @@ -76,6 +79,16 @@ export async function runAppBuild(
nextSteps: ["prisma app deploy"],
};
} catch (error) {
if (buildType === "auto" && isAutoBuildDetectionError(error)) {
throw usageError(
"App build requires an explicit framework when detection is ambiguous",
`This preview auto-detects clear project shapes for ${RESOLVED_PREVIEW_BUILD_TYPES.map(formatBuildTypeName).join(", ")}.`,
"Pass a supported --build-type value, or pass --entry <path> for a Bun app.",
getBuildTypeExamples("build"),
"app",
);
}
Comment thread
AmanVarshney01 marked this conversation as resolved.

throw buildFailedError("Local app build failed", error);
}
}
Expand Down Expand Up @@ -1217,31 +1230,44 @@ function normalizeBuildType(requestedBuildType: string | undefined): PreviewBuil
return "auto";
}

if (requestedBuildType === "auto" || requestedBuildType === "bun" || requestedBuildType === "nextjs") {
if (isPreviewBuildType(requestedBuildType)) {
return requestedBuildType;
}

throw usageError(
`Unsupported build type "${requestedBuildType}"`,
"Only auto, bun, and nextjs are supported in the current preview.",
"Pass --build-type auto, --build-type bun, or --build-type nextjs.",
["prisma app build --build-type nextjs", "prisma app build --build-type bun --entry server.ts"],
`Only ${PREVIEW_BUILD_TYPES.join(", ")} are supported in the current preview.`,
"Pass a supported --build-type value.",
getBuildTypeExamples("build"),
"app",
);
}

function isPreviewBuildType(value: string): value is PreviewBuildType {
return (PREVIEW_BUILD_TYPES as readonly string[]).includes(value);
}

function getBuildTypeExamples(commandName: "build" | "deploy"): string[] {
return RESOLVED_PREVIEW_BUILD_TYPES.map((buildType) => {
const entrypoint = buildType === "bun" ? " --entry server.ts" : "";
return `prisma app ${commandName} --build-type ${buildType}${entrypoint}`;
});
}

function assertSupportedEntrypoint(
buildType: PreviewBuildType,
entrypoint: string | undefined,
commandName: "build" | "run" | "deploy",
) {
if (buildType === "nextjs" && entrypoint) {
// Framework strategies derive their runtime entrypoints from build output.
// Only Bun consumes a user-provided source entrypoint; auto may fall back to Bun.
if (buildType !== "auto" && buildType !== "bun" && entrypoint) {
throw usageError(
`App ${commandName} does not accept --entry with --build-type nextjs`,
"Next.js apps do not use an entrypoint flag in the current preview.",
`App ${commandName} does not accept --entry with --build-type ${buildType}`,
`${formatBuildTypeName(buildType)} apps do not use an entrypoint flag in the current preview.`,
`Remove --entry, or rerun prisma app ${commandName} with --build-type bun when you want to target a Bun entrypoint directly.`,
[
`prisma app ${commandName} --build-type nextjs`,
`prisma app ${commandName} --build-type ${buildType}`,
`prisma app ${commandName} --build-type bun --entry server.ts`,
],
"app",
Expand All @@ -1254,14 +1280,17 @@ async function requireLocalBuildType(
buildType: PreviewBuildType,
commandName: "build" | "run",
) {
// Local dev server support is intentionally narrower than deploy build support.
// Nuxt, Astro, and TanStack Start can deploy via SDK strategies, but app run
// only starts the local dev servers currently documented for the preview.
const resolvedBuildType = await resolveLocalBuildType(context.runtime.cwd, buildType);
if (resolvedBuildType) {
return resolvedBuildType;
}

throw usageError(
`App ${commandName} requires an explicit framework when detection is ambiguous`,
"This preview only auto-detects clear Next.js or Bun project shapes.",
"This preview only starts local dev servers for clear Next.js or Bun project shapes.",
"Pass --build-type nextjs for a Next.js app, or pass --build-type bun with --entry <path> for a Bun app.",
[
`prisma app ${commandName} --build-type nextjs`,
Expand Down Expand Up @@ -1387,6 +1416,27 @@ function formatFrameworkName(framework: AppRunResult["framework"]): string {
return framework === "nextjs" ? "Next.js" : "Bun";
}

function isAutoBuildDetectionError(error: unknown): boolean {
return error instanceof Error && error.message.startsWith("Entrypoint is required.");
}

function formatBuildTypeName(buildType: PreviewBuildType): string {
switch (buildType) {
case "nextjs":
return "Next.js";
case "nuxt":
return "Nuxt";
case "astro":
return "Astro";
case "tanstack-start":
return "TanStack Start";
case "bun":
return "Bun";
case "auto":
return "Auto";
}
}

function removeFailedError(summary: string, error: unknown, nextSteps: string[]): CliError {
return new CliError({
code: "REMOVE_FAILED",
Expand Down
14 changes: 10 additions & 4 deletions packages/cli/src/lib/app/local-dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import path from "node:path";
import type { PreviewBuildType, ResolvedPreviewBuildType } from "./preview-build";
import { readBunPackageEntrypoint, readBunPackageJson, resolveBunEntrypoint } from "./bun-project";

export type LocalBuildType = Extract<ResolvedPreviewBuildType, "bun" | "nextjs">;

const NEXT_CONFIG_FILENAMES = [
"next.config.js",
"next.config.mjs",
Expand All @@ -15,7 +17,7 @@ const NEXT_CONFIG_FILENAMES = [
export const DEFAULT_LOCAL_DEV_PORT = 3000;

export interface LocalRunResult {
framework: ResolvedPreviewBuildType;
framework: LocalBuildType;
entrypoint: string | null;
port: number;
command: string;
Expand All @@ -32,15 +34,19 @@ interface CommandCandidate {
export async function resolveLocalBuildType(
appPath: string,
buildType: PreviewBuildType,
): Promise<ResolvedPreviewBuildType | null> {
): Promise<LocalBuildType | null> {
if (buildType === "bun" || buildType === "nextjs") {
return buildType;
}

if (buildType !== "auto") {
return null;
}

return detectLocalBuildType(appPath);
}

export async function detectLocalBuildType(appPath: string): Promise<ResolvedPreviewBuildType | null> {
export async function detectLocalBuildType(appPath: string): Promise<LocalBuildType | null> {
if (await isNextProject(appPath)) {
return "nextjs";
}
Expand All @@ -54,7 +60,7 @@ export async function detectLocalBuildType(appPath: string): Promise<ResolvedPre

export async function runLocalApp(options: {
appPath: string;
buildType: ResolvedPreviewBuildType;
buildType: LocalBuildType;
entrypoint?: string;
port: number;
env: NodeJS.ProcessEnv;
Expand Down
Loading
Loading