@prisma/cli 3.0.0-dev.116.1 → 3.0.0-dev.117.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/cli2.js +2 -0
- package/dist/commands/init/index.js +32 -0
- package/dist/controllers/app.js +1 -1
- package/dist/controllers/init.js +491 -0
- package/dist/lib/agent/cli-command.js +4 -1
- package/dist/lib/agent/package-manager.js +1 -1
- package/dist/presenters/app.js +22 -7
- package/dist/presenters/init.js +29 -0
- package/dist/shell/command-meta.js +16 -0
- package/dist/shell/errors.js +7 -1
- package/dist/shell/prompt.js +5 -2
- package/package.json +1 -1
package/dist/cli2.js
CHANGED
|
@@ -12,6 +12,7 @@ import { createBranchCommand } from "./commands/branch/index.js";
|
|
|
12
12
|
import { createBuildCommand } from "./commands/build/index.js";
|
|
13
13
|
import { createDatabaseCommand } from "./commands/database/index.js";
|
|
14
14
|
import { createGitCommand } from "./commands/git/index.js";
|
|
15
|
+
import { createInitCommand } from "./commands/init/index.js";
|
|
15
16
|
import { createProjectCommand } from "./commands/project/index.js";
|
|
16
17
|
import { getCliName, getCliVersion } from "./lib/version.js";
|
|
17
18
|
import { runVersion } from "./controllers/version.js";
|
|
@@ -48,6 +49,7 @@ function createProgram(runtime) {
|
|
|
48
49
|
program.addOption(new Option("--version", "Print the CLI version and exit."));
|
|
49
50
|
program.name("prisma").showSuggestionAfterError();
|
|
50
51
|
program.addCommand(createVersionCommand(runtime));
|
|
52
|
+
program.addCommand(createInitCommand(runtime));
|
|
51
53
|
program.addCommand(createAgentCommand(runtime));
|
|
52
54
|
program.addCommand(createAuthCommand(runtime));
|
|
53
55
|
program.addCommand(createProjectCommand(runtime));
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { attachCommandDescriptor } from "../../shell/command-meta.js";
|
|
2
|
+
import { addGlobalFlags } from "../../shell/global-flags.js";
|
|
3
|
+
import { configureRuntimeCommand } from "../../shell/runtime.js";
|
|
4
|
+
import { runCommand } from "../../shell/command-runner.js";
|
|
5
|
+
import { runInit } from "../../controllers/init.js";
|
|
6
|
+
import { renderInit, serializeInit } from "../../presenters/init.js";
|
|
7
|
+
import { Command, Option } from "commander";
|
|
8
|
+
//#region src/commands/init/index.ts
|
|
9
|
+
function createInitCommand(runtime) {
|
|
10
|
+
const command = attachCommandDescriptor(configureRuntimeCommand(new Command("init"), runtime), "init");
|
|
11
|
+
command.addOption(new Option("--framework <framework>", "Framework override; detected when omitted")).addOption(new Option("--entry <path>", "Source entrypoint for entrypoint frameworks (Bun, Hono)")).addOption(new Option("--http-port <port>", "HTTP port the app listens on")).addOption(new Option("--region <region>", "Region used when deploy creates the app")).addOption(new Option("--name <app-name>", "App name")).addOption(new Option("--link", "Link this directory to a Project")).addOption(new Option("--no-link", "Skip the Project link step")).addOption(new Option("--project <id-or-name>", "Project to link this directory to")).addOption(new Option("--install", "Install @prisma/compute-sdk for config types")).addOption(new Option("--no-install", "Skip the types install step"));
|
|
12
|
+
addGlobalFlags(command);
|
|
13
|
+
command.action(async (options) => {
|
|
14
|
+
const flags = options;
|
|
15
|
+
await runCommand(runtime, "init", options, (context) => runInit(context, {
|
|
16
|
+
framework: flags.framework,
|
|
17
|
+
entry: flags.entry,
|
|
18
|
+
httpPort: flags.httpPort,
|
|
19
|
+
region: flags.region,
|
|
20
|
+
name: flags.name,
|
|
21
|
+
link: flags.link,
|
|
22
|
+
project: flags.project,
|
|
23
|
+
install: flags.install
|
|
24
|
+
}), {
|
|
25
|
+
renderHuman: (context, descriptor, result) => renderInit(context, descriptor, result),
|
|
26
|
+
renderJson: (result) => serializeInit(result)
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
return command;
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { createInitCommand };
|
package/dist/controllers/app.js
CHANGED
|
@@ -2528,4 +2528,4 @@ function toOptionalEnvVars(envVars) {
|
|
|
2528
2528
|
return Object.keys(envVars).length > 0 ? envVars : void 0;
|
|
2529
2529
|
}
|
|
2530
2530
|
//#endregion
|
|
2531
|
-
export { runAppBuild, runAppDeploy, runAppDomainAdd, runAppDomainRemove, runAppDomainRetry, runAppDomainShow, runAppDomainWait, runAppListDeploys, runAppLogs, runAppOpen, runAppPromote, runAppRemove, runAppRollback, runAppRun, runAppShow, runAppShowDeploy };
|
|
2531
|
+
export { detectDeployFramework, runAppBuild, runAppDeploy, runAppDomainAdd, runAppDomainRemove, runAppDomainRetry, runAppDomainShow, runAppDomainWait, runAppListDeploys, runAppLogs, runAppOpen, runAppPromote, runAppRemove, runAppRollback, runAppRun, runAppShow, runAppShowDeploy };
|
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
import { detectPackageManagerSync } from "../lib/agent/package-manager.js";
|
|
2
|
+
import { resolvePrismaCliPackageCommandFormatterSync } from "../lib/agent/cli-command.js";
|
|
3
|
+
import { CliError, usageError } from "../shell/errors.js";
|
|
4
|
+
import { canPrompt } from "../shell/runtime.js";
|
|
5
|
+
import { confirmPrompt, isPromptCancelError, selectPrompt, textPrompt } from "../shell/prompt.js";
|
|
6
|
+
import { readLocalResolutionPin } from "../lib/project/local-pin.js";
|
|
7
|
+
import { readBunPackageEntrypoint, readBunPackageJson } from "../lib/app/bun-project.js";
|
|
8
|
+
import { runProjectLink } from "./project.js";
|
|
9
|
+
import { detectDeployFramework } from "./app.js";
|
|
10
|
+
import { execa } from "execa";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
import { writeFile } from "node:fs/promises";
|
|
13
|
+
import { COMPUTE_CONFIG_FILENAME, COMPUTE_REGIONS, FRAMEWORKS, defaultHttpPortForBuildType, findComputeConfigCandidates, findComputeConfigDir, frameworkByKey, frameworkFromAlias, serializeComputeConfig } from "@prisma/compute-sdk/config";
|
|
14
|
+
//#region src/controllers/init.ts
|
|
15
|
+
async function runInit(context, flags) {
|
|
16
|
+
const cwd = context.runtime.cwd;
|
|
17
|
+
const signal = context.runtime.signal;
|
|
18
|
+
const formatCommand = resolvePrismaCliPackageCommandFormatterSync(cwd);
|
|
19
|
+
await requireNoExistingComputeConfig(cwd, signal);
|
|
20
|
+
const region = parseInitRegion(flags.region, formatCommand);
|
|
21
|
+
let framework = await resolveInitFramework(context, flags, formatCommand);
|
|
22
|
+
const name = await resolveInitAppName(cwd, flags.name, signal, formatCommand);
|
|
23
|
+
let httpPort = parseInitHttpPort(flags.httpPort, formatCommand) ?? {
|
|
24
|
+
value: defaultHttpPortForBuildType(frameworkByKey(framework.key).buildType),
|
|
25
|
+
source: "framework default"
|
|
26
|
+
};
|
|
27
|
+
const adjusted = await maybeAdjustSettings(context, framework, httpPort, { portExplicit: flags.httpPort !== void 0 });
|
|
28
|
+
framework = adjusted.framework;
|
|
29
|
+
httpPort = adjusted.httpPort;
|
|
30
|
+
const entry = await resolveInitEntry(cwd, framework, flags.entry, signal);
|
|
31
|
+
const settings = [
|
|
32
|
+
{
|
|
33
|
+
key: "app",
|
|
34
|
+
value: name.value,
|
|
35
|
+
source: name.source
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
key: "framework",
|
|
39
|
+
value: framework.displayName,
|
|
40
|
+
source: framework.source
|
|
41
|
+
},
|
|
42
|
+
...entry ? [{
|
|
43
|
+
key: "entry",
|
|
44
|
+
value: entry.value,
|
|
45
|
+
source: entry.source
|
|
46
|
+
}] : [],
|
|
47
|
+
{
|
|
48
|
+
key: "http port",
|
|
49
|
+
value: String(httpPort.value),
|
|
50
|
+
source: httpPort.source
|
|
51
|
+
},
|
|
52
|
+
...region ? [{
|
|
53
|
+
key: "region",
|
|
54
|
+
value: region,
|
|
55
|
+
source: "flag"
|
|
56
|
+
}] : []
|
|
57
|
+
];
|
|
58
|
+
renderInitSettingsPreview(context, settings);
|
|
59
|
+
const config = { app: {
|
|
60
|
+
name: name.value,
|
|
61
|
+
framework: framework.key,
|
|
62
|
+
httpPort: httpPort.value,
|
|
63
|
+
...entry ? { entry: entry.value } : {},
|
|
64
|
+
...region ? { region } : {}
|
|
65
|
+
} };
|
|
66
|
+
const configPath = path.join(cwd, COMPUTE_CONFIG_FILENAME);
|
|
67
|
+
let source = serializeComputeConfig(config);
|
|
68
|
+
if (framework.key === "custom") source += CUSTOM_BUILD_STUB;
|
|
69
|
+
signal.throwIfAborted();
|
|
70
|
+
try {
|
|
71
|
+
await writeFile(configPath, source, {
|
|
72
|
+
encoding: "utf8",
|
|
73
|
+
flag: "wx"
|
|
74
|
+
});
|
|
75
|
+
} catch (error) {
|
|
76
|
+
if (error.code === "EEXIST") throw initConfigExistsError(configPath);
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
const warnings = [];
|
|
80
|
+
const types = await resolveInitTypes(context, flags, { onWarning: (message) => warnings.push(message) });
|
|
81
|
+
const link = await resolveInitLink(context, flags, {
|
|
82
|
+
onWarning: (message) => warnings.push(message),
|
|
83
|
+
formatCommand
|
|
84
|
+
});
|
|
85
|
+
const unlinked = link.status !== "linked" && link.status !== "already-linked";
|
|
86
|
+
const typesMissing = types.status !== "installed" && types.status !== "already-installed";
|
|
87
|
+
return {
|
|
88
|
+
command: "init",
|
|
89
|
+
result: {
|
|
90
|
+
configPath: COMPUTE_CONFIG_FILENAME,
|
|
91
|
+
directory: formatInitDirectory(cwd),
|
|
92
|
+
app: {
|
|
93
|
+
name: name.value,
|
|
94
|
+
framework: framework.key,
|
|
95
|
+
httpPort: httpPort.value,
|
|
96
|
+
...entry ? { entry: entry.value } : {},
|
|
97
|
+
...region ? { region } : {}
|
|
98
|
+
},
|
|
99
|
+
settings,
|
|
100
|
+
types,
|
|
101
|
+
link
|
|
102
|
+
},
|
|
103
|
+
warnings,
|
|
104
|
+
nextSteps: [
|
|
105
|
+
...typesMissing && types.installCommand ? [types.installCommand] : [],
|
|
106
|
+
formatCommand(["app", "deploy"]),
|
|
107
|
+
...unlinked ? [formatCommand(["project", "link"])] : []
|
|
108
|
+
]
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/** Dev dependency that provides editor types for the generated config. */
|
|
112
|
+
const COMPUTE_SDK_PACKAGE = "@prisma/compute-sdk";
|
|
113
|
+
function packageAddCommand(packageManager) {
|
|
114
|
+
switch (packageManager) {
|
|
115
|
+
case "pnpm": return [
|
|
116
|
+
"pnpm",
|
|
117
|
+
"add",
|
|
118
|
+
"-D",
|
|
119
|
+
COMPUTE_SDK_PACKAGE
|
|
120
|
+
];
|
|
121
|
+
case "bun": return [
|
|
122
|
+
"bun",
|
|
123
|
+
"add",
|
|
124
|
+
"-d",
|
|
125
|
+
COMPUTE_SDK_PACKAGE
|
|
126
|
+
];
|
|
127
|
+
case "yarn": return [
|
|
128
|
+
"yarn",
|
|
129
|
+
"add",
|
|
130
|
+
"-D",
|
|
131
|
+
COMPUTE_SDK_PACKAGE
|
|
132
|
+
];
|
|
133
|
+
case "npm": return [
|
|
134
|
+
"npm",
|
|
135
|
+
"install",
|
|
136
|
+
"-D",
|
|
137
|
+
COMPUTE_SDK_PACKAGE
|
|
138
|
+
];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Offers to install `@prisma/compute-sdk` as a devDependency so the generated
|
|
143
|
+
* config's typed import resolves in the editor. Deploy resolves the import
|
|
144
|
+
* without a local install, so every outcome short of success is a hint, never
|
|
145
|
+
* a failure.
|
|
146
|
+
*/
|
|
147
|
+
async function resolveInitTypes(context, flags, hooks) {
|
|
148
|
+
const cwd = context.runtime.cwd;
|
|
149
|
+
let packageJson;
|
|
150
|
+
try {
|
|
151
|
+
packageJson = await readBunPackageJson(cwd, context.runtime.signal);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
if (context.runtime.signal.aborted) throw error;
|
|
154
|
+
hooks.onWarning(`Skipped the ${COMPUTE_SDK_PACKAGE} types install: package.json could not be read (${error instanceof Error ? error.message.split("\n")[0] : String(error)}).`);
|
|
155
|
+
return {
|
|
156
|
+
status: "skipped",
|
|
157
|
+
package: COMPUTE_SDK_PACKAGE,
|
|
158
|
+
installCommand: null
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
if (hasComputeSdkDependency(packageJson)) return {
|
|
162
|
+
status: "already-installed",
|
|
163
|
+
package: COMPUTE_SDK_PACKAGE,
|
|
164
|
+
installCommand: null
|
|
165
|
+
};
|
|
166
|
+
const installCommand = packageAddCommand(detectPackageManagerSync(cwd) ?? "npm");
|
|
167
|
+
const installCommandText = installCommand.join(" ");
|
|
168
|
+
const state = (status) => ({
|
|
169
|
+
status,
|
|
170
|
+
package: COMPUTE_SDK_PACKAGE,
|
|
171
|
+
installCommand: installCommandText
|
|
172
|
+
});
|
|
173
|
+
if (!packageJson) return state("skipped");
|
|
174
|
+
if (flags.install === false) return state("skipped");
|
|
175
|
+
let shouldInstall = flags.install === true;
|
|
176
|
+
if (!shouldInstall) {
|
|
177
|
+
if (!canPrompt(context) || context.flags.yes) return state("skipped");
|
|
178
|
+
try {
|
|
179
|
+
shouldInstall = await confirmPrompt({
|
|
180
|
+
input: context.runtime.stdin,
|
|
181
|
+
output: context.output.stderr,
|
|
182
|
+
signal: context.runtime.signal,
|
|
183
|
+
message: `Install ${COMPUTE_SDK_PACKAGE} for config types? (${installCommandText})`,
|
|
184
|
+
initialValue: true
|
|
185
|
+
});
|
|
186
|
+
} catch (error) {
|
|
187
|
+
if (isPromptCancelError(error)) return state("declined");
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
190
|
+
if (!shouldInstall) return state("declined");
|
|
191
|
+
}
|
|
192
|
+
const command = resolveInitInstallCommandOverride(context) ?? installCommand;
|
|
193
|
+
if (!context.flags.quiet && !context.flags.json) context.output.stderr.write(`Installing ${COMPUTE_SDK_PACKAGE}...\n`);
|
|
194
|
+
try {
|
|
195
|
+
const [executable, ...args] = command;
|
|
196
|
+
await execa(executable, args, {
|
|
197
|
+
cwd,
|
|
198
|
+
env: context.runtime.env,
|
|
199
|
+
cancelSignal: context.runtime.signal,
|
|
200
|
+
stdin: "ignore"
|
|
201
|
+
});
|
|
202
|
+
return state("installed");
|
|
203
|
+
} catch (error) {
|
|
204
|
+
if (context.runtime.signal.aborted) throw error;
|
|
205
|
+
const detail = error instanceof Error ? error.message.split("\n")[0] : String(error);
|
|
206
|
+
hooks.onWarning(`Installing ${COMPUTE_SDK_PACKAGE} failed: ${detail}. Install it later with ${installCommandText}.`);
|
|
207
|
+
return state("failed");
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function hasComputeSdkDependency(packageJson) {
|
|
211
|
+
for (const group of [packageJson?.dependencies, packageJson?.devDependencies]) if (group && typeof group === "object" && COMPUTE_SDK_PACKAGE in group) return true;
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
/** Test hook: JSON array command that replaces the real package-manager install. */
|
|
215
|
+
function resolveInitInstallCommandOverride(context) {
|
|
216
|
+
const raw = context.runtime.env.PRISMA_CLI_INIT_INSTALL_COMMAND;
|
|
217
|
+
if (!raw) return null;
|
|
218
|
+
try {
|
|
219
|
+
const parsed = JSON.parse(raw);
|
|
220
|
+
return Array.isArray(parsed) && parsed.every((p) => typeof p === "string") ? parsed : null;
|
|
221
|
+
} catch {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const CUSTOM_BUILD_STUB = `
|
|
226
|
+
// framework "custom" deploys a prebuilt artifact. Add its build settings:
|
|
227
|
+
// build: {
|
|
228
|
+
// command: "npm run build",
|
|
229
|
+
// outputDirectory: "dist",
|
|
230
|
+
// entrypoint: "server.js",
|
|
231
|
+
// },
|
|
232
|
+
`;
|
|
233
|
+
async function requireNoExistingComputeConfig(cwd, signal) {
|
|
234
|
+
const configDir = await findComputeConfigDir(cwd, signal);
|
|
235
|
+
if (!configDir) return;
|
|
236
|
+
throw initConfigExistsError((await findComputeConfigCandidates(configDir, signal))[0] ?? configDir);
|
|
237
|
+
}
|
|
238
|
+
function initConfigExistsError(existingPath) {
|
|
239
|
+
return new CliError({
|
|
240
|
+
code: "INIT_CONFIG_EXISTS",
|
|
241
|
+
domain: "app",
|
|
242
|
+
summary: "A compute config already exists",
|
|
243
|
+
why: `${existingPath} already defines this repository's compute config, and init never overwrites or merges.`,
|
|
244
|
+
fix: "Edit the existing config instead, or delete it first if you want init to regenerate it.",
|
|
245
|
+
exitCode: 1,
|
|
246
|
+
nextSteps: [],
|
|
247
|
+
meta: { existingConfigPath: existingPath }
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
async function resolveInitFramework(context, flags, formatCommand) {
|
|
251
|
+
if (flags.framework) {
|
|
252
|
+
const framework = frameworkFromAlias(flags.framework.trim());
|
|
253
|
+
if (!framework) throw usageError("Unknown framework", `"${flags.framework}" is not a supported framework.`, `Pass one of: ${FRAMEWORKS.map((candidate) => candidate.key).join(", ")}.`, [formatCommand([
|
|
254
|
+
"init",
|
|
255
|
+
"--framework",
|
|
256
|
+
"hono"
|
|
257
|
+
])], "app");
|
|
258
|
+
return {
|
|
259
|
+
key: framework.key,
|
|
260
|
+
displayName: framework.displayName,
|
|
261
|
+
source: "flag"
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
const detected = await detectDeployFramework(context.runtime.cwd, context.runtime.signal);
|
|
265
|
+
if (detected) return {
|
|
266
|
+
key: detected.key,
|
|
267
|
+
displayName: detected.displayName,
|
|
268
|
+
source: detected.annotation
|
|
269
|
+
};
|
|
270
|
+
if (canPrompt(context) && !context.flags.yes) {
|
|
271
|
+
const key = await selectPrompt({
|
|
272
|
+
input: context.runtime.stdin,
|
|
273
|
+
output: context.output.stderr,
|
|
274
|
+
signal: context.runtime.signal,
|
|
275
|
+
message: "Which framework does this app use?",
|
|
276
|
+
choices: FRAMEWORKS.map((framework) => ({
|
|
277
|
+
label: framework.displayName,
|
|
278
|
+
value: framework.key
|
|
279
|
+
}))
|
|
280
|
+
});
|
|
281
|
+
return {
|
|
282
|
+
key,
|
|
283
|
+
displayName: frameworkByKey(key).displayName,
|
|
284
|
+
source: "selected"
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
throw new CliError({
|
|
288
|
+
code: "INIT_DETECTION_FAILED",
|
|
289
|
+
domain: "app",
|
|
290
|
+
summary: "No supported framework detected",
|
|
291
|
+
why: "The directory has none of the framework signals init detects from, and no --framework was passed.",
|
|
292
|
+
fix: `Pass --framework with one of: ${FRAMEWORKS.map((framework) => framework.key).join(", ")}.`,
|
|
293
|
+
exitCode: 1,
|
|
294
|
+
nextSteps: FRAMEWORKS.slice(0, 3).map((framework) => formatCommand([
|
|
295
|
+
"init",
|
|
296
|
+
"--framework",
|
|
297
|
+
framework.key
|
|
298
|
+
])),
|
|
299
|
+
meta: { frameworks: FRAMEWORKS.map((framework) => framework.key) }
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
async function resolveInitAppName(cwd, explicitName, signal, formatCommand) {
|
|
303
|
+
const trimmed = explicitName?.trim();
|
|
304
|
+
if (explicitName !== void 0 && !trimmed) throw usageError("App name required", "--name needs a non-empty value.", "Pass a non-empty app name.", [formatCommand([
|
|
305
|
+
"init",
|
|
306
|
+
"--name",
|
|
307
|
+
"api"
|
|
308
|
+
])], "app");
|
|
309
|
+
if (trimmed) return {
|
|
310
|
+
value: trimmed,
|
|
311
|
+
source: "flag"
|
|
312
|
+
};
|
|
313
|
+
const packageJson = await readBunPackageJson(cwd, signal);
|
|
314
|
+
const packageName = typeof packageJson?.name === "string" ? packageJson.name.trim() : "";
|
|
315
|
+
if (packageName) return {
|
|
316
|
+
value: packageName,
|
|
317
|
+
source: "package.json"
|
|
318
|
+
};
|
|
319
|
+
return {
|
|
320
|
+
value: path.basename(cwd),
|
|
321
|
+
source: "directory name"
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
function parseInitHttpPort(value, formatCommand) {
|
|
325
|
+
if (value === void 0) return;
|
|
326
|
+
const port = Number(value.trim());
|
|
327
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) throw usageError("Invalid HTTP port", "--http-port must be an integer between 1 and 65535.", "Pass a valid port.", [formatCommand([
|
|
328
|
+
"init",
|
|
329
|
+
"--http-port",
|
|
330
|
+
"3000"
|
|
331
|
+
])], "app");
|
|
332
|
+
return {
|
|
333
|
+
value: port,
|
|
334
|
+
source: "flag"
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
function parseInitRegion(value, formatCommand) {
|
|
338
|
+
if (value === void 0) return;
|
|
339
|
+
const trimmed = value.trim();
|
|
340
|
+
if (COMPUTE_REGIONS.includes(trimmed)) return trimmed;
|
|
341
|
+
throw usageError("Unknown region", `"${value}" is not a supported Compute region.`, `Pass one of: ${COMPUTE_REGIONS.join(", ")}.`, [formatCommand([
|
|
342
|
+
"init",
|
|
343
|
+
"--region",
|
|
344
|
+
"us-east-1"
|
|
345
|
+
])], "app");
|
|
346
|
+
}
|
|
347
|
+
async function resolveInitEntry(cwd, resolvedFramework, explicitEntry, signal) {
|
|
348
|
+
const framework = frameworkByKey(resolvedFramework.key);
|
|
349
|
+
const trimmed = explicitEntry?.trim();
|
|
350
|
+
if (!framework.usesEntrypoint) {
|
|
351
|
+
if (trimmed) throw usageError("--entry is not supported for this framework", `${resolvedFramework.displayName} derives its entrypoint from build output; --entry applies only to frameworks that run a source entrypoint (Bun, Hono).`, "Drop --entry, or pass an entrypoint framework with --framework.", [], "app");
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
if (trimmed) return {
|
|
355
|
+
value: trimmed,
|
|
356
|
+
source: "flag"
|
|
357
|
+
};
|
|
358
|
+
const packageEntrypoint = readBunPackageEntrypoint(await readBunPackageJson(cwd, signal));
|
|
359
|
+
if (packageEntrypoint) return {
|
|
360
|
+
value: packageEntrypoint,
|
|
361
|
+
source: "package.json"
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
async function maybeAdjustSettings(context, framework, httpPort, options) {
|
|
365
|
+
if (!canPrompt(context) || context.flags.yes) return {
|
|
366
|
+
framework,
|
|
367
|
+
httpPort
|
|
368
|
+
};
|
|
369
|
+
if (!await confirmPrompt({
|
|
370
|
+
input: context.runtime.stdin,
|
|
371
|
+
output: context.output.stderr,
|
|
372
|
+
signal: context.runtime.signal,
|
|
373
|
+
message: `Adjust these settings? (${framework.displayName}, HTTP ${httpPort.value})`,
|
|
374
|
+
initialValue: false
|
|
375
|
+
})) return {
|
|
376
|
+
framework,
|
|
377
|
+
httpPort
|
|
378
|
+
};
|
|
379
|
+
const key = await selectPrompt({
|
|
380
|
+
input: context.runtime.stdin,
|
|
381
|
+
output: context.output.stderr,
|
|
382
|
+
signal: context.runtime.signal,
|
|
383
|
+
message: "Framework",
|
|
384
|
+
choices: FRAMEWORKS.map((candidate) => ({
|
|
385
|
+
label: candidate.key === framework.key ? `${candidate.displayName} (current)` : candidate.displayName,
|
|
386
|
+
value: candidate.key
|
|
387
|
+
}))
|
|
388
|
+
});
|
|
389
|
+
const nextFramework = key === framework.key ? framework : {
|
|
390
|
+
key,
|
|
391
|
+
displayName: frameworkByKey(key).displayName,
|
|
392
|
+
source: "selected"
|
|
393
|
+
};
|
|
394
|
+
const defaultPort = options.portExplicit ? httpPort.value : defaultHttpPortForBuildType(frameworkByKey(key).buildType);
|
|
395
|
+
const portText = await textPrompt({
|
|
396
|
+
input: context.runtime.stdin,
|
|
397
|
+
output: context.output.stderr,
|
|
398
|
+
signal: context.runtime.signal,
|
|
399
|
+
message: "HTTP port",
|
|
400
|
+
placeholder: String(defaultPort),
|
|
401
|
+
validate: (value) => {
|
|
402
|
+
if (!value?.trim()) return;
|
|
403
|
+
const port = Number(value.trim());
|
|
404
|
+
return Number.isInteger(port) && port >= 1 && port <= 65535 ? void 0 : "Enter a port between 1 and 65535.";
|
|
405
|
+
}
|
|
406
|
+
});
|
|
407
|
+
return {
|
|
408
|
+
framework: nextFramework,
|
|
409
|
+
httpPort: portText.trim() ? {
|
|
410
|
+
value: Number(portText.trim()),
|
|
411
|
+
source: "selected"
|
|
412
|
+
} : {
|
|
413
|
+
value: defaultPort,
|
|
414
|
+
source: options.portExplicit ? httpPort.source : "framework default"
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
function renderInitSettingsPreview(context, settings) {
|
|
419
|
+
if (context.flags.quiet || context.flags.json) return;
|
|
420
|
+
const keyWidth = Math.max(...settings.map((row) => row.key.length));
|
|
421
|
+
const valueWidth = Math.max(...settings.map((row) => row.value.length));
|
|
422
|
+
const lines = settings.map((row) => ` ${row.key.padEnd(keyWidth)} ${row.value.padEnd(valueWidth)} ${context.ui.dim(row.source)}`);
|
|
423
|
+
context.output.stderr.write(`${lines.join("\n")}\n\n`);
|
|
424
|
+
}
|
|
425
|
+
async function resolveInitLink(context, flags, hooks) {
|
|
426
|
+
const pin = await readLocalResolutionPin(context.runtime.cwd, context.runtime.signal);
|
|
427
|
+
if (pin.isOk() && pin.value.kind === "present") return {
|
|
428
|
+
status: "already-linked",
|
|
429
|
+
project: null
|
|
430
|
+
};
|
|
431
|
+
if (flags.link === false) return {
|
|
432
|
+
status: "skipped",
|
|
433
|
+
project: null
|
|
434
|
+
};
|
|
435
|
+
const explicitProject = flags.project?.trim();
|
|
436
|
+
let shouldLink = Boolean(explicitProject) || flags.link === true;
|
|
437
|
+
if (!shouldLink) {
|
|
438
|
+
if (!canPrompt(context) || context.flags.yes) return {
|
|
439
|
+
status: "skipped",
|
|
440
|
+
project: null
|
|
441
|
+
};
|
|
442
|
+
try {
|
|
443
|
+
shouldLink = await confirmPrompt({
|
|
444
|
+
input: context.runtime.stdin,
|
|
445
|
+
output: context.output.stderr,
|
|
446
|
+
signal: context.runtime.signal,
|
|
447
|
+
message: "Link this directory to a Prisma Project now?",
|
|
448
|
+
initialValue: true
|
|
449
|
+
});
|
|
450
|
+
} catch (error) {
|
|
451
|
+
if (isPromptCancelError(error)) return {
|
|
452
|
+
status: "declined",
|
|
453
|
+
project: null
|
|
454
|
+
};
|
|
455
|
+
throw error;
|
|
456
|
+
}
|
|
457
|
+
if (!shouldLink) return {
|
|
458
|
+
status: "declined",
|
|
459
|
+
project: null
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
try {
|
|
463
|
+
const linked = await runProjectLink(context, explicitProject || void 0);
|
|
464
|
+
return {
|
|
465
|
+
status: "linked",
|
|
466
|
+
project: {
|
|
467
|
+
id: linked.result.project.id,
|
|
468
|
+
name: linked.result.project.name
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
} catch (error) {
|
|
472
|
+
if (error instanceof CliError) {
|
|
473
|
+
if (isPromptCancelError(error)) return {
|
|
474
|
+
status: "declined",
|
|
475
|
+
project: null
|
|
476
|
+
};
|
|
477
|
+
hooks.onWarning(`Project link failed: ${error.summary}. Link later with ${hooks.formatCommand(["project", "link"])}.`);
|
|
478
|
+
return {
|
|
479
|
+
status: "failed",
|
|
480
|
+
project: null
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
throw error;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
function formatInitDirectory(cwd) {
|
|
487
|
+
const basename = path.basename(cwd);
|
|
488
|
+
return basename ? `./${basename}` : ".";
|
|
489
|
+
}
|
|
490
|
+
//#endregion
|
|
491
|
+
export { runInit };
|
|
@@ -10,8 +10,11 @@ async function resolvePrismaCliPackageCommand(options) {
|
|
|
10
10
|
function resolvePrismaCliPackageCommandFormatterSync(cwd) {
|
|
11
11
|
return createPrismaCliPackageCommandFormatter(resolvePackageRunnerSync(cwd));
|
|
12
12
|
}
|
|
13
|
+
function resolvePrismaCliPackageCommandSync(cwd, args) {
|
|
14
|
+
return resolvePrismaCliPackageCommandFormatterSync(cwd)(args);
|
|
15
|
+
}
|
|
13
16
|
function createPrismaCliPackageCommandFormatter(packageRunner) {
|
|
14
17
|
return (args) => formatPrismaCliCommand(args, { packageRunner });
|
|
15
18
|
}
|
|
16
19
|
//#endregion
|
|
17
|
-
export { resolvePrismaCliPackageCommand, resolvePrismaCliPackageCommandFormatterSync };
|
|
20
|
+
export { resolvePrismaCliPackageCommand, resolvePrismaCliPackageCommandFormatterSync, resolvePrismaCliPackageCommandSync };
|
|
@@ -96,4 +96,4 @@ function isMissingFileError(error) {
|
|
|
96
96
|
return code === "ENOENT" || code === "ENOTDIR";
|
|
97
97
|
}
|
|
98
98
|
//#endregion
|
|
99
|
-
export { resolvePackageRunner, resolvePackageRunnerSync, resolveSkillsPackageRunner };
|
|
99
|
+
export { detectPackageManagerSync, resolvePackageRunner, resolvePackageRunnerSync, resolveSkillsPackageRunner };
|
package/dist/presenters/app.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolvePrismaCliPackageCommandSync } from "../lib/agent/cli-command.js";
|
|
1
2
|
import { renderVerboseBlock } from "../shell/ui.js";
|
|
2
3
|
import { renderDeployOutputRows } from "../lib/app/deploy-output.js";
|
|
3
4
|
import { formatDomainFailureFix } from "../lib/app/domain-guidance.js";
|
|
@@ -38,17 +39,31 @@ function renderAppDeploy(context, descriptor, result, options) {
|
|
|
38
39
|
],
|
|
39
40
|
...renderBranchDatabaseDeploySummary(context, result),
|
|
40
41
|
"",
|
|
41
|
-
...renderDeployOutputRows(context.ui, [
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
...renderDeployOutputRows(context.ui, [
|
|
43
|
+
...result.promoted ? [] : [{
|
|
44
|
+
label: "Promote",
|
|
45
|
+
value: `prisma-cli app promote ${result.deployment.id}`
|
|
46
|
+
}],
|
|
47
|
+
{
|
|
48
|
+
label: "Logs",
|
|
49
|
+
value: logsCommand
|
|
50
|
+
},
|
|
51
|
+
...deployUsedComputeConfig(result) ? [] : [{
|
|
52
|
+
label: "Config",
|
|
53
|
+
value: resolvePrismaCliPackageCommandSync(context.runtime.cwd, ["init"])
|
|
54
|
+
}]
|
|
55
|
+
]),
|
|
48
56
|
...renderDeployResolvedContextBlock(context, result),
|
|
49
57
|
...renderDeploySettingsBlock(context, result)
|
|
50
58
|
];
|
|
51
59
|
}
|
|
60
|
+
function deployUsedComputeConfig(result) {
|
|
61
|
+
return result.deploySettings.config.path !== null || [
|
|
62
|
+
result.deploySettings.framework.source,
|
|
63
|
+
result.deploySettings.buildCommand.source,
|
|
64
|
+
result.deploySettings.outputDirectory.source
|
|
65
|
+
].some((source) => source?.includes("prisma.compute"));
|
|
66
|
+
}
|
|
52
67
|
function isAppDeployAllResult(result) {
|
|
53
68
|
return "deployments" in result;
|
|
54
69
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { resolvePrismaCliPackageCommandFormatterSync } from "../lib/agent/cli-command.js";
|
|
2
|
+
import { renderNextSteps, renderSummaryLine } from "../shell/ui.js";
|
|
3
|
+
//#region src/presenters/init.ts
|
|
4
|
+
function renderInit(context, _descriptor, result) {
|
|
5
|
+
const ui = context.ui;
|
|
6
|
+
const formatCommand = resolvePrismaCliPackageCommandFormatterSync(context.runtime.cwd);
|
|
7
|
+
const lines = [renderSummaryLine(ui, "success", `Wrote ${result.configPath}`)];
|
|
8
|
+
if (result.types.status === "installed") lines.push(renderSummaryLine(ui, "success", `Installed ${result.types.package} (config types)`));
|
|
9
|
+
else if ((result.types.status === "skipped" || result.types.status === "declined") && result.types.installCommand) lines.push(` ${ui.dim(`For editor types: ${result.types.installCommand}`)}`);
|
|
10
|
+
switch (result.link.status) {
|
|
11
|
+
case "linked":
|
|
12
|
+
lines.push(renderSummaryLine(ui, "success", `Linked "${result.directory}" to Project "${result.link.project?.name ?? ""}"`));
|
|
13
|
+
break;
|
|
14
|
+
case "already-linked": break;
|
|
15
|
+
case "skipped":
|
|
16
|
+
case "declined":
|
|
17
|
+
lines.push(` ${ui.dim(`Not linked to a Project yet; link with ${formatCommand(["project", "link"])}.`)}`);
|
|
18
|
+
break;
|
|
19
|
+
case "failed": break;
|
|
20
|
+
}
|
|
21
|
+
const linked = result.link.status === "linked" || result.link.status === "already-linked";
|
|
22
|
+
lines.push(...renderNextSteps([formatCommand(["app", "deploy"]), ...linked ? [] : [formatCommand(["project", "link"])]]));
|
|
23
|
+
return lines;
|
|
24
|
+
}
|
|
25
|
+
function serializeInit(result) {
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { renderInit, serializeInit };
|
|
@@ -20,6 +20,22 @@ const DESCRIPTORS = [
|
|
|
20
20
|
description: "Show CLI build and environment",
|
|
21
21
|
examples: ["prisma-cli version", "prisma-cli version --json"]
|
|
22
22
|
},
|
|
23
|
+
{
|
|
24
|
+
id: "init",
|
|
25
|
+
path: ["prisma", "init"],
|
|
26
|
+
description: "Write a committed prisma.compute.ts for this app",
|
|
27
|
+
examples: (runtime) => agentCommandExamples(runtime, [
|
|
28
|
+
["init"],
|
|
29
|
+
[
|
|
30
|
+
"init",
|
|
31
|
+
"--framework",
|
|
32
|
+
"hono",
|
|
33
|
+
"--entry",
|
|
34
|
+
"src/index.ts"
|
|
35
|
+
],
|
|
36
|
+
["init", "--no-link"]
|
|
37
|
+
])
|
|
38
|
+
},
|
|
23
39
|
{
|
|
24
40
|
id: "agent",
|
|
25
41
|
path: ["prisma", "agent"],
|
package/dist/shell/errors.js
CHANGED
|
@@ -44,6 +44,12 @@ function usageError(summary, why, fix, nextSteps = [], domain = "cli") {
|
|
|
44
44
|
nextSteps
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
|
+
function isUsageError(error, summary) {
|
|
48
|
+
return isErrorRecord(error) && error.code === "USAGE_ERROR" && (summary === void 0 || error.summary === summary);
|
|
49
|
+
}
|
|
50
|
+
function isErrorRecord(error) {
|
|
51
|
+
return typeof error === "object" && error !== null;
|
|
52
|
+
}
|
|
47
53
|
function authRequiredError(nextSteps = ["prisma-cli auth login"], options = {}) {
|
|
48
54
|
return new CliError({
|
|
49
55
|
code: "AUTH_REQUIRED",
|
|
@@ -131,4 +137,4 @@ function featureUnavailableError(summary, why, fix, nextSteps = [], domain = "cl
|
|
|
131
137
|
});
|
|
132
138
|
}
|
|
133
139
|
//#endregion
|
|
134
|
-
export { CliError, authConfigInvalidError, authRequiredError, commandCanceledError, featureUnavailableError, usageError, workspaceAmbiguousError, workspaceNotAuthenticatedError, workspaceRequiredError, workspaceSwitchUnavailableError };
|
|
140
|
+
export { CliError, authConfigInvalidError, authRequiredError, commandCanceledError, featureUnavailableError, isUsageError, usageError, workspaceAmbiguousError, workspaceNotAuthenticatedError, workspaceRequiredError, workspaceSwitchUnavailableError };
|
package/dist/shell/prompt.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { usageError } from "./errors.js";
|
|
1
|
+
import { isUsageError, usageError } from "./errors.js";
|
|
2
2
|
import { confirm, isCancel, select, text } from "@clack/prompts";
|
|
3
3
|
//#region src/shell/prompt.ts
|
|
4
4
|
const PROMPT_CANCELED_SUMMARY = "Interactive prompt canceled";
|
|
@@ -40,6 +40,9 @@ async function confirmPrompt(options) {
|
|
|
40
40
|
if (isCancel(response)) throw usageError(PROMPT_CANCELED_SUMMARY, "The command was canceled before a confirmation was made.", "Re-run the command and choose an option to continue.");
|
|
41
41
|
return response;
|
|
42
42
|
}
|
|
43
|
+
function isPromptCancelError(error) {
|
|
44
|
+
return isUsageError(error, PROMPT_CANCELED_SUMMARY);
|
|
45
|
+
}
|
|
43
46
|
function disposePromptState(_input) {}
|
|
44
47
|
//#endregion
|
|
45
|
-
export { confirmPrompt, disposePromptState, selectPrompt, textPrompt };
|
|
48
|
+
export { confirmPrompt, disposePromptState, isPromptCancelError, selectPrompt, textPrompt };
|