@prisma/cli 3.0.0-dev.87.1 → 3.0.0-dev.90.1
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/commands/app/index.js +65 -52
- package/dist/controllers/app-env.js +1 -1
- package/dist/controllers/app.js +580 -294
- package/dist/controllers/project.js +3 -3
- package/dist/lib/app/app-interaction.js +5 -0
- package/dist/lib/app/{preview-provider.js → app-provider.js} +9 -9
- package/dist/lib/app/{preview-branch-database.js → branch-database-api.js} +1 -1
- package/dist/lib/app/branch-database-deploy.js +45 -104
- package/dist/lib/app/branch-database.js +22 -184
- package/dist/lib/app/build-settings.js +93 -0
- package/dist/lib/app/build.js +80 -0
- package/dist/lib/app/bun-project.js +2 -3
- package/dist/lib/app/compute-config.js +147 -0
- package/dist/lib/app/deploy-plan.js +58 -0
- package/dist/lib/app/{preview-progress.js → deploy-progress.js} +5 -5
- package/dist/lib/app/local-dev.js +3 -60
- package/dist/lib/app/production-deploy-gate.js +2 -2
- package/dist/lib/diagnostics.js +1 -1
- package/dist/lib/fs/home-path.js +24 -0
- package/dist/lib/git/local-branch.js +15 -3
- package/dist/lib/project/resolution.js +1 -1
- package/dist/lib/project/setup.js +10 -8
- package/dist/presenters/app.js +45 -40
- package/dist/presenters/project.js +2 -8
- package/dist/shell/diagnostics-output.js +2 -8
- package/dist/shell/runtime.js +7 -3
- package/package.json +5 -3
- package/dist/lib/app/preview-build-settings.js +0 -385
- package/dist/lib/app/preview-build.js +0 -522
- package/dist/lib/app/preview-interaction.js +0 -5
package/dist/presenters/app.js
CHANGED
|
@@ -28,7 +28,8 @@ function renderAppBuild(context, descriptor, result) {
|
|
|
28
28
|
function serializeAppBuild(result) {
|
|
29
29
|
return result;
|
|
30
30
|
}
|
|
31
|
-
function renderAppDeploy(context, descriptor, result) {
|
|
31
|
+
function renderAppDeploy(context, descriptor, result, options) {
|
|
32
|
+
const logsCommand = options?.logsTarget ? `prisma-cli app logs ${options.logsTarget}` : "prisma-cli app logs";
|
|
32
33
|
return [
|
|
33
34
|
`Live in ${formatDuration(result.durationMs)}`,
|
|
34
35
|
...result.deployment.url ? [context.ui.link(result.deployment.url)] : [],
|
|
@@ -36,12 +37,37 @@ function renderAppDeploy(context, descriptor, result) {
|
|
|
36
37
|
"",
|
|
37
38
|
...renderDeployOutputRows(context.ui, [{
|
|
38
39
|
label: "Logs",
|
|
39
|
-
value:
|
|
40
|
+
value: logsCommand
|
|
40
41
|
}]),
|
|
41
42
|
...renderDeployResolvedContextBlock(context, result),
|
|
42
43
|
...renderDeploySettingsBlock(context, result)
|
|
43
44
|
];
|
|
44
45
|
}
|
|
46
|
+
function isAppDeployAllResult(result) {
|
|
47
|
+
return "deployments" in result;
|
|
48
|
+
}
|
|
49
|
+
function renderAppDeployAll(context, descriptor, result) {
|
|
50
|
+
const lines = [];
|
|
51
|
+
for (const deployment of result.deployments) {
|
|
52
|
+
lines.push(deployment.target);
|
|
53
|
+
lines.push(...renderAppDeploy(context, descriptor, deployment.result, { logsTarget: deployment.target }).map((line) => line ? ` ${line}` : line));
|
|
54
|
+
lines.push("");
|
|
55
|
+
}
|
|
56
|
+
lines.push(...renderDeployOutputRows(context.ui, result.deployments.map((deployment) => ({
|
|
57
|
+
label: deployment.target,
|
|
58
|
+
value: deployment.result.deployment.url ?? deployment.result.deployment.id
|
|
59
|
+
}))));
|
|
60
|
+
return lines;
|
|
61
|
+
}
|
|
62
|
+
function serializeAppDeployAll(result) {
|
|
63
|
+
return {
|
|
64
|
+
count: result.deployments.length,
|
|
65
|
+
deployments: result.deployments.map((deployment) => ({
|
|
66
|
+
target: deployment.target,
|
|
67
|
+
...serializeAppDeploy(deployment.result)
|
|
68
|
+
}))
|
|
69
|
+
};
|
|
70
|
+
}
|
|
45
71
|
function serializeAppDeploy(result) {
|
|
46
72
|
const { deploySettings, localPin: _localPin, ...serialized } = result;
|
|
47
73
|
const { id: _branchId, ...branch } = serialized.branch;
|
|
@@ -56,28 +82,14 @@ function serializeAppDeploy(result) {
|
|
|
56
82
|
};
|
|
57
83
|
}
|
|
58
84
|
function renderBranchDatabaseDeploySummary(context, result) {
|
|
59
|
-
if (result.branchDatabase
|
|
60
|
-
return ["", ...renderDeployOutputRows(context.ui, [
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
value: result.branchDatabase.envVars.join(", ")
|
|
68
|
-
},
|
|
69
|
-
...result.branchDatabase.schema ? [{
|
|
70
|
-
label: "Schema",
|
|
71
|
-
value: formatBranchDatabaseSchemaCommand(result.branchDatabase.schema.command)
|
|
72
|
-
}] : []
|
|
73
|
-
])];
|
|
74
|
-
}
|
|
75
|
-
function formatBranchDatabaseSchemaCommand(command) {
|
|
76
|
-
switch (command) {
|
|
77
|
-
case "migrate-deploy": return "prisma migrate deploy";
|
|
78
|
-
case "db-push": return "prisma db push";
|
|
79
|
-
case "prisma-next-db-init": return "prisma-next db init";
|
|
80
|
-
}
|
|
85
|
+
if (!result.branchDatabase || result.branchDatabase.status !== "created") return [];
|
|
86
|
+
return ["", ...renderDeployOutputRows(context.ui, [{
|
|
87
|
+
label: "Database",
|
|
88
|
+
value: result.branchDatabase.database?.name ?? "created"
|
|
89
|
+
}, {
|
|
90
|
+
label: "Env",
|
|
91
|
+
value: result.branchDatabase.envVars.join(", ")
|
|
92
|
+
}])];
|
|
81
93
|
}
|
|
82
94
|
function formatDuration(durationMs) {
|
|
83
95
|
if (durationMs < 1e3) return `${durationMs}ms`;
|
|
@@ -163,21 +175,14 @@ function branchDatabaseRows(branchDatabase) {
|
|
|
163
175
|
value: "not configured",
|
|
164
176
|
tone: "dim"
|
|
165
177
|
}];
|
|
166
|
-
return [
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
value: branchDatabase.envVars.join(", ")
|
|
175
|
-
}] : [],
|
|
176
|
-
...branchDatabase.schema ? [{
|
|
177
|
-
key: "branch db schema",
|
|
178
|
-
value: `${formatBranchDatabaseSchemaCommand(branchDatabase.schema.command)} (${branchDatabase.schema.source}, ${branchDatabase.schema.path})`
|
|
179
|
-
}] : []
|
|
180
|
-
];
|
|
178
|
+
return [{
|
|
179
|
+
key: "branch db",
|
|
180
|
+
value: branchDatabase.status === "created" ? `created${branchDatabase.database ? ` (${branchDatabase.database.name})` : ""}` : `skipped${branchDatabase.reason ? ` (${branchDatabase.reason})` : ""}`,
|
|
181
|
+
tone: branchDatabase.status === "created" ? "success" : "dim"
|
|
182
|
+
}, ...branchDatabase.envVars.length > 0 ? [{
|
|
183
|
+
key: "branch db env",
|
|
184
|
+
value: branchDatabase.envVars.join(", ")
|
|
185
|
+
}] : []];
|
|
181
186
|
}
|
|
182
187
|
function formatEnvVarNames(envVars) {
|
|
183
188
|
return envVars.length > 0 ? envVars.join(", ") : "none";
|
|
@@ -638,4 +643,4 @@ function formatRecentDeployments(deployments) {
|
|
|
638
643
|
return deployments.map((deployment) => `${deployment.id}${deployment.live ? " (live)" : ""}`).join(", ");
|
|
639
644
|
}
|
|
640
645
|
//#endregion
|
|
641
|
-
export { renderAppBuild, renderAppDeploy, renderAppDomainAdd, renderAppDomainRemove, renderAppDomainRetry, renderAppDomainShow, renderAppListDeploys, renderAppOpen, renderAppPromote, renderAppRemove, renderAppRollback, renderAppRun, renderAppShow, renderAppShowDeploy, serializeAppBuild, serializeAppDeploy, serializeAppDomainAdd, serializeAppDomainRemove, serializeAppDomainRetry, serializeAppDomainShow, serializeAppListDeploys, serializeAppOpen, serializeAppPromote, serializeAppRemove, serializeAppRollback, serializeAppRun, serializeAppShow, serializeAppShowDeploy };
|
|
646
|
+
export { isAppDeployAllResult, renderAppBuild, renderAppDeploy, renderAppDeployAll, renderAppDomainAdd, renderAppDomainRemove, renderAppDomainRetry, renderAppDomainShow, renderAppListDeploys, renderAppOpen, renderAppPromote, renderAppRemove, renderAppRollback, renderAppRun, renderAppShow, renderAppShowDeploy, serializeAppBuild, serializeAppDeploy, serializeAppDeployAll, serializeAppDomainAdd, serializeAppDomainRemove, serializeAppDomainRetry, serializeAppDomainShow, serializeAppListDeploys, serializeAppOpen, serializeAppPromote, serializeAppRemove, serializeAppRollback, serializeAppRun, serializeAppShow, serializeAppShowDeploy };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { formatDescriptorLabel } from "../shell/command-meta.js";
|
|
2
2
|
import { padDisplay, renderNextSteps, renderSummaryLine, renderVerboseBlock } from "../shell/ui.js";
|
|
3
|
+
import { shortenHomePath } from "../lib/fs/home-path.js";
|
|
3
4
|
import { formatCommandArgument } from "../shell/command-arguments.js";
|
|
4
5
|
import { renderMutate, renderShow, serializeList } from "../output/patterns.js";
|
|
5
6
|
import { renderResolvedProjectContextBlock } from "./verbose-context.js";
|
|
6
|
-
import path from "node:path";
|
|
7
7
|
import stringWidth from "string-width";
|
|
8
8
|
//#region src/presenters/project.ts
|
|
9
9
|
function renderProjectList(context, descriptor, result) {
|
|
@@ -160,13 +160,7 @@ function renderBoundProjectShow(context, descriptor, result) {
|
|
|
160
160
|
return lines;
|
|
161
161
|
}
|
|
162
162
|
function formatLocalRepoPath(cwd, env) {
|
|
163
|
-
|
|
164
|
-
const home = env.HOME ? path.resolve(env.HOME) : null;
|
|
165
|
-
if (home && (resolved === home || resolved.startsWith(`${home}${path.sep}`))) {
|
|
166
|
-
const relative = path.relative(home, resolved);
|
|
167
|
-
return relative ? `~/${relative}` : "~";
|
|
168
|
-
}
|
|
169
|
-
return resolved;
|
|
163
|
+
return shortenHomePath(cwd, env);
|
|
170
164
|
}
|
|
171
165
|
function formatGitConnectionDetail(status) {
|
|
172
166
|
switch (status) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { renderVerboseBlock } from "./ui.js";
|
|
2
|
-
import
|
|
2
|
+
import { shortenHomePath } from "../lib/fs/home-path.js";
|
|
3
3
|
//#region src/shell/diagnostics-output.ts
|
|
4
4
|
function renderCommandDiagnostics(context, diagnostics, rows = [], options = {}) {
|
|
5
5
|
if (!diagnostics) return [];
|
|
@@ -43,13 +43,7 @@ function renderCommandDiagnostics(context, diagnostics, rows = [], options = {})
|
|
|
43
43
|
], { title: options.title ?? "Local context" });
|
|
44
44
|
}
|
|
45
45
|
function formatLocalPath(value, env) {
|
|
46
|
-
|
|
47
|
-
const home = env.HOME ? path.resolve(env.HOME) : null;
|
|
48
|
-
if (home && (resolved === home || resolved.startsWith(`${home}${path.sep}`))) {
|
|
49
|
-
const relative = path.relative(home, resolved);
|
|
50
|
-
return relative ? `~/${relative}` : "~";
|
|
51
|
-
}
|
|
52
|
-
return resolved;
|
|
46
|
+
return shortenHomePath(value, env);
|
|
53
47
|
}
|
|
54
48
|
function formatDirtyState(dirty) {
|
|
55
49
|
if (dirty === null) return "unknown";
|
package/dist/shell/runtime.js
CHANGED
|
@@ -2,6 +2,7 @@ import { LocalStateStore } from "../adapters/local-state.js";
|
|
|
2
2
|
import { MockApi } from "../adapters/mock-api.js";
|
|
3
3
|
import { createShellUi } from "./ui.js";
|
|
4
4
|
import { renderHelp } from "./help.js";
|
|
5
|
+
import { findComputeConfigDir } from "@prisma/compute-sdk/config";
|
|
5
6
|
import path from "node:path";
|
|
6
7
|
//#region src/shell/runtime.ts
|
|
7
8
|
const DEFAULT_STATE_DIR_NAME = path.join(".prisma", "cli");
|
|
@@ -20,7 +21,7 @@ function configureRuntimeCommand(command, runtime) {
|
|
|
20
21
|
}
|
|
21
22
|
async function createCommandContext(runtime, flags) {
|
|
22
23
|
const fixturePath = runtime.fixturePath ?? runtime.env.PRISMA_CLI_MOCK_FIXTURE_PATH;
|
|
23
|
-
const stateDir = resolveStateDir(runtime);
|
|
24
|
+
const stateDir = await resolveStateDir(runtime);
|
|
24
25
|
let loadedApi;
|
|
25
26
|
if (fixturePath) loadedApi = await MockApi.load(fixturePath, runtime.signal);
|
|
26
27
|
return {
|
|
@@ -38,8 +39,11 @@ async function createCommandContext(runtime, flags) {
|
|
|
38
39
|
ui: createShellUi(runtime, flags)
|
|
39
40
|
};
|
|
40
41
|
}
|
|
41
|
-
function resolveStateDir(runtime) {
|
|
42
|
-
|
|
42
|
+
async function resolveStateDir(runtime) {
|
|
43
|
+
const explicitStateDir = runtime.stateDir ?? runtime.env.PRISMA_CLI_STATE_DIR;
|
|
44
|
+
if (explicitStateDir) return explicitStateDir;
|
|
45
|
+
const projectDir = await findComputeConfigDir(runtime.cwd, runtime.signal);
|
|
46
|
+
return path.join(projectDir ?? runtime.cwd, DEFAULT_STATE_DIR_NAME);
|
|
43
47
|
}
|
|
44
48
|
function canPrompt(context) {
|
|
45
49
|
if (context.flags.json) return false;
|
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma/cli",
|
|
3
|
-
"version": "3.0.0-dev.
|
|
3
|
+
"version": "3.0.0-dev.90.1",
|
|
4
4
|
"description": "Command-line interface for the Prisma Developer Platform.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"prisma-cli": "./dist/cli.js"
|
|
8
8
|
},
|
|
9
|
+
"exports": {
|
|
10
|
+
"./package.json": "./package.json"
|
|
11
|
+
},
|
|
9
12
|
"files": [
|
|
10
13
|
"dist",
|
|
11
14
|
"README.md",
|
|
@@ -41,11 +44,10 @@
|
|
|
41
44
|
},
|
|
42
45
|
"dependencies": {
|
|
43
46
|
"@clack/prompts": "^1.5.0",
|
|
44
|
-
"@prisma/compute-sdk": "^0.
|
|
47
|
+
"@prisma/compute-sdk": "^0.28.0",
|
|
45
48
|
"@prisma/credentials-store": "^7.8.0",
|
|
46
49
|
"@prisma/management-api-sdk": "^1.37.0",
|
|
47
50
|
"better-result": "^2.9.2",
|
|
48
|
-
"c12": "4.0.0-beta.5",
|
|
49
51
|
"colorette": "^2.0.20",
|
|
50
52
|
"commander": "^14.0.3",
|
|
51
53
|
"dotenv": "^17.4.2",
|
|
@@ -1,385 +0,0 @@
|
|
|
1
|
-
import { CliError } from "../../shell/errors.js";
|
|
2
|
-
import { readBunPackageJson } from "./bun-project.js";
|
|
3
|
-
import { readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import { exec } from "node:child_process";
|
|
6
|
-
import { parseModule } from "magicast";
|
|
7
|
-
//#region src/lib/app/preview-build-settings.ts
|
|
8
|
-
const PRISMA_APP_CONFIG_FILENAME = "prisma.app.json";
|
|
9
|
-
const PRISMA_APP_CONFIG_SCHEMA_URL = "https://pris.ly/schemas/prisma-app-config.v1.json";
|
|
10
|
-
async function resolveOrCreatePreviewBuildSettings(options) {
|
|
11
|
-
const configPath = path.join(options.appPath, PRISMA_APP_CONFIG_FILENAME);
|
|
12
|
-
const existing = await readPreviewBuildSettingsConfig(configPath, options.signal);
|
|
13
|
-
if (existing) return {
|
|
14
|
-
status: "used",
|
|
15
|
-
configPath,
|
|
16
|
-
relativeConfigPath: PRISMA_APP_CONFIG_FILENAME,
|
|
17
|
-
settings: {
|
|
18
|
-
buildCommand: existing.buildCommand,
|
|
19
|
-
buildCommandSource: null,
|
|
20
|
-
outputDirectory: existing.outputDirectory,
|
|
21
|
-
outputDirectorySource: null
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
const settings = await resolvePreviewBuildSettings(options);
|
|
25
|
-
const config = {
|
|
26
|
-
$schema: PRISMA_APP_CONFIG_SCHEMA_URL,
|
|
27
|
-
buildCommand: settings.buildCommand,
|
|
28
|
-
outputDirectory: settings.outputDirectory
|
|
29
|
-
};
|
|
30
|
-
try {
|
|
31
|
-
await writeFile(configPath, `${JSON.stringify(config, null, 2)}\n`, {
|
|
32
|
-
encoding: "utf8",
|
|
33
|
-
flag: "wx",
|
|
34
|
-
signal: options.signal
|
|
35
|
-
});
|
|
36
|
-
} catch (error) {
|
|
37
|
-
if (error.code === "EEXIST") {
|
|
38
|
-
const raced = await readPreviewBuildSettingsConfig(configPath, options.signal);
|
|
39
|
-
if (raced) return {
|
|
40
|
-
status: "used",
|
|
41
|
-
configPath,
|
|
42
|
-
relativeConfigPath: PRISMA_APP_CONFIG_FILENAME,
|
|
43
|
-
settings: {
|
|
44
|
-
buildCommand: raced.buildCommand,
|
|
45
|
-
buildCommandSource: null,
|
|
46
|
-
outputDirectory: raced.outputDirectory,
|
|
47
|
-
outputDirectorySource: null
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
throw error;
|
|
52
|
-
}
|
|
53
|
-
return {
|
|
54
|
-
status: "created",
|
|
55
|
-
configPath,
|
|
56
|
-
relativeConfigPath: PRISMA_APP_CONFIG_FILENAME,
|
|
57
|
-
settings
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
async function resolvePreviewBuildSettings(options) {
|
|
61
|
-
switch (options.buildType) {
|
|
62
|
-
case "nextjs": {
|
|
63
|
-
const packageJson = await readBunPackageJson(options.appPath, options.signal);
|
|
64
|
-
const buildCommand = await resolveFrameworkBuildCommand(options.appPath, packageJson, {
|
|
65
|
-
command: "next build",
|
|
66
|
-
source: "Next.js default",
|
|
67
|
-
signal: options.signal
|
|
68
|
-
});
|
|
69
|
-
const outputRoot = await resolveNextOutputRoot(options.appPath, options.signal);
|
|
70
|
-
return {
|
|
71
|
-
buildCommand: buildCommand.command,
|
|
72
|
-
buildCommandSource: buildCommand.source,
|
|
73
|
-
outputDirectory: joinPosix(outputRoot, "standalone"),
|
|
74
|
-
outputDirectorySource: outputRoot === ".next" ? "Next.js output" : "next.config distDir"
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
case "tanstack-start": {
|
|
78
|
-
const packageJson = await readBunPackageJson(options.appPath, options.signal);
|
|
79
|
-
const buildCommand = await resolveFrameworkBuildCommand(options.appPath, packageJson, {
|
|
80
|
-
command: "vite build",
|
|
81
|
-
source: "TanStack Start default",
|
|
82
|
-
signal: options.signal
|
|
83
|
-
});
|
|
84
|
-
return {
|
|
85
|
-
buildCommand: buildCommand.command,
|
|
86
|
-
buildCommandSource: buildCommand.source,
|
|
87
|
-
outputDirectory: ".output",
|
|
88
|
-
outputDirectorySource: "TanStack Start output"
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
case "bun": {
|
|
92
|
-
const packageJson = await readBunPackageJson(options.appPath, options.signal);
|
|
93
|
-
const buildCommand = await resolveFrameworkBuildCommand(options.appPath, packageJson, {
|
|
94
|
-
command: null,
|
|
95
|
-
source: null,
|
|
96
|
-
signal: options.signal
|
|
97
|
-
});
|
|
98
|
-
return {
|
|
99
|
-
buildCommand: buildCommand.command,
|
|
100
|
-
buildCommandSource: buildCommand.source,
|
|
101
|
-
outputDirectory: ".",
|
|
102
|
-
outputDirectorySource: "app root"
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
async function readPreviewBuildSettingsConfig(configPath, signal) {
|
|
108
|
-
let content;
|
|
109
|
-
try {
|
|
110
|
-
content = await readFile(configPath, {
|
|
111
|
-
encoding: "utf8",
|
|
112
|
-
signal
|
|
113
|
-
});
|
|
114
|
-
} catch (error) {
|
|
115
|
-
if (error.code === "ENOENT") return null;
|
|
116
|
-
throw error;
|
|
117
|
-
}
|
|
118
|
-
let parsed;
|
|
119
|
-
try {
|
|
120
|
-
parsed = JSON.parse(content);
|
|
121
|
-
} catch (error) {
|
|
122
|
-
throw invalidPrismaAppConfigError(configPath, `The file is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
123
|
-
}
|
|
124
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw invalidPrismaAppConfigError(configPath, "The file must contain a JSON object.");
|
|
125
|
-
const raw = parsed;
|
|
126
|
-
if ("$schema" in raw && raw.$schema !== void 0 && typeof raw.$schema !== "string") throw invalidPrismaAppConfigError(configPath, "The $schema field must be a string when present.");
|
|
127
|
-
if (raw.buildCommand !== null && typeof raw.buildCommand !== "string") throw invalidPrismaAppConfigError(configPath, "The buildCommand field must be a string or null.");
|
|
128
|
-
let buildCommand = null;
|
|
129
|
-
if (typeof raw.buildCommand === "string") {
|
|
130
|
-
buildCommand = raw.buildCommand.trim();
|
|
131
|
-
if (buildCommand.length === 0) throw invalidPrismaAppConfigError(configPath, "The buildCommand field must not be an empty string. Use null to skip the build step.");
|
|
132
|
-
}
|
|
133
|
-
const outputDirectory = normalizeConfigOutputDirectory(configPath, raw.outputDirectory);
|
|
134
|
-
return {
|
|
135
|
-
buildCommand,
|
|
136
|
-
outputDirectory
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
function normalizeConfigOutputDirectory(configPath, value) {
|
|
140
|
-
if (typeof value !== "string" || value.trim().length === 0) throw invalidPrismaAppConfigError(configPath, "The outputDirectory field must be a non-empty string.");
|
|
141
|
-
const normalized = normalizeRelativePath(value);
|
|
142
|
-
if (!normalized) throw invalidPrismaAppConfigError(configPath, "The outputDirectory field must be a relative path inside the app directory.");
|
|
143
|
-
return normalized;
|
|
144
|
-
}
|
|
145
|
-
function invalidPrismaAppConfigError(configPath, why) {
|
|
146
|
-
return new CliError({
|
|
147
|
-
code: "APP_CONFIG_INVALID",
|
|
148
|
-
domain: "app",
|
|
149
|
-
summary: `Invalid ${PRISMA_APP_CONFIG_FILENAME}`,
|
|
150
|
-
why,
|
|
151
|
-
fix: `Edit ${PRISMA_APP_CONFIG_FILENAME} so buildCommand is a string or null and outputDirectory is a relative path inside the app root. Delete the file and rerun prisma-cli app deploy to regenerate defaults.`,
|
|
152
|
-
where: configPath,
|
|
153
|
-
meta: { configPath },
|
|
154
|
-
exitCode: 2,
|
|
155
|
-
nextSteps: ["prisma-cli app deploy"]
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
async function hasRootFile(appPath, filenames, signal) {
|
|
159
|
-
let entries;
|
|
160
|
-
try {
|
|
161
|
-
signal?.throwIfAborted();
|
|
162
|
-
entries = await readdir(appPath);
|
|
163
|
-
signal?.throwIfAborted();
|
|
164
|
-
} catch (error) {
|
|
165
|
-
if (signal?.aborted) throw error;
|
|
166
|
-
return false;
|
|
167
|
-
}
|
|
168
|
-
return entries.some((entry) => filenames.includes(entry));
|
|
169
|
-
}
|
|
170
|
-
function hasPackageDependency(packageJson, dependencyName) {
|
|
171
|
-
return hasAnyPackageDependency(packageJson, [dependencyName]);
|
|
172
|
-
}
|
|
173
|
-
function hasAnyPackageDependency(packageJson, dependencyNames) {
|
|
174
|
-
if (!packageJson) return false;
|
|
175
|
-
return [packageJson.dependencies, packageJson.devDependencies].some((group) => {
|
|
176
|
-
if (!group || typeof group !== "object") return false;
|
|
177
|
-
return dependencyNames.some((dependencyName) => dependencyName in group);
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
async function resolveFrameworkBuildCommand(appPath, packageJson, fallback) {
|
|
181
|
-
const buildScript = readBuildScript(packageJson);
|
|
182
|
-
if (buildScript) {
|
|
183
|
-
const packageManager = await resolvePackageManager(appPath, packageJson, fallback.signal);
|
|
184
|
-
if (!packageManager) return {
|
|
185
|
-
command: buildScript,
|
|
186
|
-
source: "package.json scripts.build"
|
|
187
|
-
};
|
|
188
|
-
return {
|
|
189
|
-
command: `${packageManager} run build`,
|
|
190
|
-
source: "package.json scripts.build"
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
return {
|
|
194
|
-
command: fallback.command,
|
|
195
|
-
source: fallback.source
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
function readBuildScript(packageJson) {
|
|
199
|
-
if (!packageJson?.scripts || typeof packageJson.scripts !== "object") return null;
|
|
200
|
-
const scripts = packageJson.scripts;
|
|
201
|
-
if (typeof scripts.build !== "string") return null;
|
|
202
|
-
const buildScript = scripts.build.trim();
|
|
203
|
-
return buildScript.length > 0 ? buildScript : null;
|
|
204
|
-
}
|
|
205
|
-
async function resolvePackageManager(appPath, packageJson, signal) {
|
|
206
|
-
const fromPackageManager = packageManagerFromPackageJson(packageJson?.packageManager);
|
|
207
|
-
if (fromPackageManager) return fromPackageManager;
|
|
208
|
-
if (await pathExists(path.join(appPath, "bun.lock"), signal) || await pathExists(path.join(appPath, "bun.lockb"), signal)) return "bun";
|
|
209
|
-
if (await pathExists(path.join(appPath, "pnpm-lock.yaml"), signal)) return "pnpm";
|
|
210
|
-
if (await pathExists(path.join(appPath, "yarn.lock"), signal)) return "yarn";
|
|
211
|
-
if (await pathExists(path.join(appPath, "package-lock.json"), signal)) return "npm";
|
|
212
|
-
}
|
|
213
|
-
function packageManagerFromPackageJson(value) {
|
|
214
|
-
if (typeof value !== "string") return null;
|
|
215
|
-
const name = value.split("@")[0];
|
|
216
|
-
return name === "bun" || name === "pnpm" || name === "yarn" || name === "npm" ? name : null;
|
|
217
|
-
}
|
|
218
|
-
async function runResolvedBuildCommand(appPath, settings, failurePrefix, signal) {
|
|
219
|
-
if (!settings.buildCommand) return;
|
|
220
|
-
await execBuildCommand(settings.buildCommand, appPath, failurePrefix, signal);
|
|
221
|
-
}
|
|
222
|
-
function execBuildCommand(command, cwd, failurePrefix, signal) {
|
|
223
|
-
return new Promise((resolve, reject) => {
|
|
224
|
-
const child = exec(command, {
|
|
225
|
-
cwd,
|
|
226
|
-
env: {
|
|
227
|
-
...process.env,
|
|
228
|
-
PATH: [path.join(cwd, "node_modules", ".bin"), process.env.PATH].filter(Boolean).join(path.delimiter)
|
|
229
|
-
},
|
|
230
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
231
|
-
signal
|
|
232
|
-
}, (error, stdout, stderr) => {
|
|
233
|
-
if (error) {
|
|
234
|
-
const output = [stderr.trim(), stdout.trim()].filter(Boolean).join("\n");
|
|
235
|
-
reject(/* @__PURE__ */ new Error(`${failurePrefix} failed:\n${output || error.message}`));
|
|
236
|
-
return;
|
|
237
|
-
}
|
|
238
|
-
resolve();
|
|
239
|
-
});
|
|
240
|
-
if (signal?.aborted) child.kill();
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
async function resolveNextOutputRoot(appPath, signal) {
|
|
244
|
-
return (await readNextConfig(appPath, signal)).distDir ?? ".next";
|
|
245
|
-
}
|
|
246
|
-
async function readNextConfig(appPath, signal) {
|
|
247
|
-
for (const fileName of NEXT_CONFIG_FILENAMES) {
|
|
248
|
-
const filePath = path.join(appPath, fileName);
|
|
249
|
-
let content;
|
|
250
|
-
try {
|
|
251
|
-
content = await readFile(filePath, {
|
|
252
|
-
encoding: "utf8",
|
|
253
|
-
signal
|
|
254
|
-
});
|
|
255
|
-
} catch (error) {
|
|
256
|
-
if (error.code === "ENOENT") continue;
|
|
257
|
-
throw error;
|
|
258
|
-
}
|
|
259
|
-
return readStaticNextConfig(content);
|
|
260
|
-
}
|
|
261
|
-
return {};
|
|
262
|
-
}
|
|
263
|
-
const NEXT_CONFIG_FILENAMES = [
|
|
264
|
-
"next.config.js",
|
|
265
|
-
"next.config.mjs",
|
|
266
|
-
"next.config.ts",
|
|
267
|
-
"next.config.mts"
|
|
268
|
-
];
|
|
269
|
-
function readStaticNextConfig(content) {
|
|
270
|
-
try {
|
|
271
|
-
const program = asAstNode(parseModule(content).$ast);
|
|
272
|
-
const bindings = program ? collectStaticBindings(program) : /* @__PURE__ */ new Map();
|
|
273
|
-
const configObject = program ? findExportedConfigObject(program, bindings) : null;
|
|
274
|
-
if (!configObject) return {};
|
|
275
|
-
const rawDistDir = readStaticStringProperty(configObject, "distDir");
|
|
276
|
-
const output = readStaticStringProperty(configObject, "output");
|
|
277
|
-
return {
|
|
278
|
-
distDir: rawDistDir ? normalizeRelativePath(rawDistDir) : void 0,
|
|
279
|
-
output: output === "standalone" || output === "export" ? output : void 0
|
|
280
|
-
};
|
|
281
|
-
} catch {
|
|
282
|
-
return {};
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
function joinPosix(...parts) {
|
|
286
|
-
return parts.join("/").replace(/\/+/g, "/");
|
|
287
|
-
}
|
|
288
|
-
function nextOutputRootFromStandaloneDirectory(outputDirectory) {
|
|
289
|
-
const normalized = outputDirectory.replace(/\/+$/g, "");
|
|
290
|
-
if (normalized === "standalone") return ".";
|
|
291
|
-
if (normalized.endsWith("/standalone")) {
|
|
292
|
-
const outputRoot = normalized.slice(0, -11);
|
|
293
|
-
return outputRoot.length > 0 ? outputRoot : ".";
|
|
294
|
-
}
|
|
295
|
-
const dirname = path.posix.dirname(normalized);
|
|
296
|
-
return dirname === "." ? "." : dirname;
|
|
297
|
-
}
|
|
298
|
-
function asAstNode(value) {
|
|
299
|
-
if (!value || typeof value !== "object") return null;
|
|
300
|
-
return typeof value.type === "string" ? value : null;
|
|
301
|
-
}
|
|
302
|
-
function astNodes(value) {
|
|
303
|
-
if (!Array.isArray(value)) return [];
|
|
304
|
-
return value.map(asAstNode).filter((node) => Boolean(node));
|
|
305
|
-
}
|
|
306
|
-
function collectStaticBindings(program) {
|
|
307
|
-
const bindings = /* @__PURE__ */ new Map();
|
|
308
|
-
for (const statement of astNodes(program.body)) {
|
|
309
|
-
if (statement.type !== "VariableDeclaration") continue;
|
|
310
|
-
for (const declaration of astNodes(statement.declarations)) {
|
|
311
|
-
const id = asAstNode(declaration.id);
|
|
312
|
-
const init = asAstNode(declaration.init);
|
|
313
|
-
if (id?.type === "Identifier" && typeof id.name === "string" && init) bindings.set(id.name, init);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
return bindings;
|
|
317
|
-
}
|
|
318
|
-
function findExportedConfigObject(program, bindings) {
|
|
319
|
-
for (const statement of astNodes(program.body)) {
|
|
320
|
-
if (statement.type === "ExportDefaultDeclaration") return resolveConfigObject(statement.declaration, bindings);
|
|
321
|
-
if (statement.type !== "ExpressionStatement") continue;
|
|
322
|
-
const expression = asAstNode(statement.expression);
|
|
323
|
-
if (expression?.type !== "AssignmentExpression" || expression.operator !== "=") continue;
|
|
324
|
-
if (isModuleExports(expression.left)) return resolveConfigObject(expression.right, bindings);
|
|
325
|
-
}
|
|
326
|
-
return null;
|
|
327
|
-
}
|
|
328
|
-
function resolveConfigObject(value, bindings, depth = 0) {
|
|
329
|
-
if (depth > 4) return null;
|
|
330
|
-
const node = unwrapStaticExpression(asAstNode(value));
|
|
331
|
-
if (!node) return null;
|
|
332
|
-
if (node.type === "ObjectExpression") return node;
|
|
333
|
-
if (node.type === "Identifier" && typeof node.name === "string") return resolveConfigObject(bindings.get(node.name), bindings, depth + 1);
|
|
334
|
-
if (node.type === "CallExpression") return resolveConfigObject(astNodes(node.arguments)[0], bindings, depth + 1);
|
|
335
|
-
return null;
|
|
336
|
-
}
|
|
337
|
-
function unwrapStaticExpression(node) {
|
|
338
|
-
let current = node;
|
|
339
|
-
while (current?.type === "TSAsExpression" || current?.type === "TSSatisfiesExpression" || current?.type === "TSNonNullExpression") current = asAstNode(current.expression);
|
|
340
|
-
return current;
|
|
341
|
-
}
|
|
342
|
-
function isModuleExports(value) {
|
|
343
|
-
const node = asAstNode(value);
|
|
344
|
-
if (node?.type !== "MemberExpression" || node.computed === true) return false;
|
|
345
|
-
const object = asAstNode(node.object);
|
|
346
|
-
const property = asAstNode(node.property);
|
|
347
|
-
return object?.type === "Identifier" && object.name === "module" && property?.type === "Identifier" && property.name === "exports";
|
|
348
|
-
}
|
|
349
|
-
function readStaticStringProperty(objectExpression, propertyName) {
|
|
350
|
-
for (const property of astNodes(objectExpression.properties)) {
|
|
351
|
-
if (property.type !== "ObjectProperty" || property.computed === true) continue;
|
|
352
|
-
if (propertyKeyName(property.key) !== propertyName) continue;
|
|
353
|
-
const value = unwrapStaticExpression(asAstNode(property.value));
|
|
354
|
-
if (value?.type === "StringLiteral" && typeof value.value === "string") {
|
|
355
|
-
const trimmed = value.value.trim();
|
|
356
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
function propertyKeyName(value) {
|
|
361
|
-
const key = asAstNode(value);
|
|
362
|
-
if (key?.type === "Identifier" && typeof key.name === "string") return key.name;
|
|
363
|
-
if (key?.type === "StringLiteral" && typeof key.value === "string") return key.value;
|
|
364
|
-
}
|
|
365
|
-
function normalizeRelativePath(value) {
|
|
366
|
-
const raw = value.trim().replace(/\\/g, "/");
|
|
367
|
-
if (raw.length === 0 || raw.split("/").includes("..")) return;
|
|
368
|
-
const normalized = path.posix.normalize(raw);
|
|
369
|
-
const segments = normalized.split("/");
|
|
370
|
-
if (path.win32.isAbsolute(value) || path.posix.isAbsolute(normalized) || segments.includes("..")) return;
|
|
371
|
-
return normalized === "." ? "." : normalized;
|
|
372
|
-
}
|
|
373
|
-
async function pathExists(targetPath, signal) {
|
|
374
|
-
try {
|
|
375
|
-
signal?.throwIfAborted();
|
|
376
|
-
await stat(targetPath);
|
|
377
|
-
signal?.throwIfAborted();
|
|
378
|
-
return true;
|
|
379
|
-
} catch (error) {
|
|
380
|
-
if (signal?.aborted) throw error;
|
|
381
|
-
return false;
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
//#endregion
|
|
385
|
-
export { PRISMA_APP_CONFIG_FILENAME, PRISMA_APP_CONFIG_SCHEMA_URL, hasAnyPackageDependency, hasPackageDependency, hasRootFile, joinPosix, nextOutputRootFromStandaloneDirectory, resolveOrCreatePreviewBuildSettings, resolvePreviewBuildSettings, runResolvedBuildCommand };
|