vercel 59.0.0 → 59.1.3

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.
Files changed (107) hide show
  1. package/dist/chunks/{add-SQ6ERCFZ.js → add-CUGPQYFW.js} +8 -8
  2. package/dist/chunks/{add-QZUQNL2C.js → add-PP3PJPJV.js} +5 -5
  3. package/dist/chunks/{add-3LO3UMYC.js → add-PRG4KKYH.js} +9 -11
  4. package/dist/chunks/{build-WMUKZEFH.js → build-T22GMLY6.js} +8 -8
  5. package/dist/chunks/{chunk-ELA5VN3A.js → chunk-2MF3ZAVW.js} +13 -4
  6. package/dist/chunks/{chunk-ZFTRB6AB.js → chunk-43DD2KNM.js} +1 -1
  7. package/dist/chunks/{chunk-N5DK7EMY.js → chunk-4HI7G6KG.js} +1 -1
  8. package/dist/chunks/{chunk-DQDJVHA5.js → chunk-4JKZ7SRX.js} +4 -4
  9. package/dist/chunks/{chunk-ZRWSSPW5.js → chunk-57G6HBJ4.js} +2 -2
  10. package/dist/chunks/{chunk-RWRSUGXY.js → chunk-5DVMXOFT.js} +2 -2
  11. package/dist/chunks/{chunk-SHR6DYA5.js → chunk-7NQLRE5V.js} +1 -1
  12. package/dist/chunks/{chunk-JLWQWYKO.js → chunk-BBSS3XIW.js} +2 -4
  13. package/dist/chunks/{chunk-V42UVZDX.js → chunk-BCZFZETZ.js} +5 -5
  14. package/dist/chunks/{chunk-MZ3L3SGZ.js → chunk-DAWJOQF6.js} +4 -6
  15. package/dist/chunks/{chunk-W34XLJ44.js → chunk-DEB45ND4.js} +3 -3
  16. package/dist/chunks/{chunk-QGQX7BYN.js → chunk-DOMLWKKZ.js} +38 -15
  17. package/dist/chunks/{chunk-TEQKKTJQ.js → chunk-F7OXZLY2.js} +2 -2
  18. package/dist/chunks/{chunk-J6LK45HT.js → chunk-GFCTRJI5.js} +16 -2
  19. package/dist/chunks/{chunk-O26N2UPI.js → chunk-GGPE5377.js} +1 -1
  20. package/dist/chunks/{chunk-4LDQIDRM.js → chunk-GMWRTZVZ.js} +9 -1
  21. package/dist/chunks/{chunk-LT5UQNTC.js → chunk-IKKUCZE7.js} +3 -3
  22. package/dist/chunks/{chunk-XIG4GH62.js → chunk-JYEO6OJS.js} +188 -102
  23. package/dist/chunks/{chunk-DMLU5KQE.js → chunk-KTLFN5FV.js} +1 -1
  24. package/dist/chunks/{chunk-L6NIIAB7.js → chunk-LNVZ3T7X.js} +6 -8
  25. package/dist/chunks/{chunk-UQVOFOP2.js → chunk-M34ZRNDQ.js} +1 -1
  26. package/dist/chunks/{chunk-2SFMOCWR.js → chunk-PD7ZUXE7.js} +1 -1
  27. package/dist/chunks/chunk-QCVKGY2A.js +151 -0
  28. package/dist/chunks/{chunk-S3H7ITUR.js → chunk-QD5NX7RG.js} +1 -1
  29. package/dist/chunks/{chunk-4N7XLXM2.js → chunk-QQ6PH4RY.js} +1 -0
  30. package/dist/chunks/{chunk-A5KP5HAI.js → chunk-RIB4S2IB.js} +10 -1
  31. package/dist/chunks/{chunk-ZX2FSPWV.js → chunk-SJQEGGH7.js} +4 -4
  32. package/dist/chunks/{chunk-5KI3SUJI.js → chunk-SOKQBFF7.js} +609 -73
  33. package/dist/chunks/{chunk-PSZQNYEV.js → chunk-SQGMTRZD.js} +91 -43
  34. package/dist/chunks/{chunk-TJJ562C5.js → chunk-TJLBCLEX.js} +15 -0
  35. package/dist/chunks/{chunk-WE3TFESA.js → chunk-TM3F7PLR.js} +2 -2
  36. package/dist/chunks/{chunk-SD2QZHZA.js → chunk-TNKRSTMB.js} +1 -1
  37. package/dist/chunks/{chunk-KT4XXKJK.js → chunk-VAFU7DXZ.js} +195 -3
  38. package/dist/chunks/{chunk-RQ7H43CJ.js → chunk-VDJN4WKH.js} +1 -1
  39. package/dist/chunks/{chunk-3R5JGMHV.js → chunk-VE545BR3.js} +4473 -2796
  40. package/dist/chunks/{chunk-B3JTF4CF.js → chunk-WC7IINFV.js} +10 -1
  41. package/dist/chunks/{chunk-SQAQED4U.js → chunk-XZLI27ES.js} +2 -2
  42. package/dist/chunks/{chunk-CMR5KQVR.js → chunk-ZUVJISHS.js} +3 -3
  43. package/dist/chunks/{clear-W2B4CHTP.js → clear-Z4IKUIN7.js} +5 -5
  44. package/dist/chunks/{compile-vercel-config-HTWOS7DZ.js → compile-vercel-config-PSVXFAZW.js} +3 -3
  45. package/dist/chunks/{config-EGJY4UUE.js → config-M5GMZ2X5.js} +8 -8
  46. package/dist/chunks/{constants-WEQVJSYS.js → constants-YMCRE6IG.js} +1 -1
  47. package/dist/chunks/{delete-BVMID4W2.js → delete-5GRRDJSF.js} +4 -4
  48. package/dist/chunks/{delete-6I7V5AGV.js → delete-P4XIBY7T.js} +5 -5
  49. package/dist/chunks/{disable-I54JNXJE.js → disable-NT5NYVO7.js} +4 -4
  50. package/dist/chunks/{discard-6TSJJCUQ.js → discard-TBNIEAJZ.js} +4 -4
  51. package/dist/chunks/{edit-YS2SWHFJ.js → edit-DJ6MT6HA.js} +5 -5
  52. package/dist/chunks/{edit-KCWYQ2YT.js → edit-UF3D2CCH.js} +5 -5
  53. package/dist/chunks/{enable-RYXRIGQN.js → enable-JTQBQMBO.js} +4 -4
  54. package/dist/chunks/{export-G3NYOADE.js → export-OKMRGRVM.js} +4 -4
  55. package/dist/chunks/{image-RJBLSUES.js → image-ARUJH4H5.js} +4 -4
  56. package/dist/chunks/{import-builders-M6F3F4DH.js → import-builders-3NTMZM64.js} +6 -4
  57. package/dist/chunks/{inspect-YTCVTZHY.js → inspect-5NETOGAI.js} +6 -7
  58. package/dist/chunks/{inspect-AE4YIJYS.js → inspect-CHFKZKKP.js} +5 -5
  59. package/dist/chunks/{inspect-Z74NZZ2A.js → inspect-FXZTQY62.js} +6 -6
  60. package/dist/chunks/{inspect-E7TBGK7D.js → inspect-PTXIBSYW.js} +8 -8
  61. package/dist/chunks/{inspect-7XUEVBDM.js → inspect-UXP2HDES.js} +5 -5
  62. package/dist/chunks/{list-SGPLL4KJ.js → list-2WMIVJ2R.js} +15 -10
  63. package/dist/chunks/{list-KJNC3NOE.js → list-ADI55WKW.js} +6 -7
  64. package/dist/chunks/{list-WXS2AFQD.js → list-DYCZA2VX.js} +3 -3
  65. package/dist/chunks/{login-XD3CFPBF.js → login-YCSIQG6B.js} +8 -8
  66. package/dist/chunks/{ls-5DSOTTYX.js → ls-2TKUIUK4.js} +11 -12
  67. package/dist/chunks/{ls-KE5I7BZO.js → ls-EOMIQLRH.js} +11 -12
  68. package/dist/chunks/{ls-SVSO3677.js → ls-PQFDGOJL.js} +7 -8
  69. package/dist/chunks/{ls-5A4327XJ.js → ls-RZVTIP5U.js} +11 -12
  70. package/dist/chunks/{ls-NBKSDTWX.js → ls-VIOMDUGW.js} +14 -15
  71. package/dist/chunks/{open-M2MOAE2K.js → open-JYPWOXQD.js} +5 -5
  72. package/dist/chunks/{permissions-ADPY554K.js → permissions-SGJWGEVD.js} +6 -6
  73. package/dist/chunks/{publish-OBIOOL7T.js → publish-ZUQHJAXL.js} +4 -4
  74. package/dist/chunks/{push-5237XBTO.js → push-XMVKG5A5.js} +8 -8
  75. package/dist/chunks/{query-ILL74HIA.js → query-ZLJZEGZB.js} +237 -49
  76. package/dist/chunks/{reorder-GAUAO4LW.js → reorder-NVHDWMRU.js} +4 -4
  77. package/dist/chunks/{reply-IZJHEULD.js → reply-PUUPUQ3V.js} +5 -5
  78. package/dist/chunks/{resolve-HTIT7CGL.js → resolve-5U2LJVE6.js} +5 -5
  79. package/dist/chunks/{restore-IKL74HSE.js → restore-OXIUY2UI.js} +4 -4
  80. package/dist/chunks/{rm-QAVNP5KX.js → rm-3IOCBNPE.js} +5 -5
  81. package/dist/chunks/{rm-5Y6BL47T.js → rm-BZNKJRXU.js} +5 -5
  82. package/dist/chunks/{rm-C37ZORQV.js → rm-OUI4ZB5R.js} +5 -5
  83. package/dist/chunks/{rm-XJU5SBGH.js → rm-Z2KYJ7XT.js} +8 -8
  84. package/dist/chunks/{rule-inspect-DYHWWROS.js → rule-inspect-ZBJJCDQV.js} +7 -8
  85. package/dist/chunks/{rules-M6EJ2Y6S.js → rules-HEGAXXUN.js} +8 -8
  86. package/dist/chunks/{schema-LLBDZNIC.js → schema-7GVL3BNP.js} +63 -31
  87. package/dist/chunks/{schema-ZNPIMQL5.js → schema-ZYIJYA2E.js} +2 -3
  88. package/dist/chunks/{tags-HR5RVQFA.js → tags-3SCWCMNQ.js} +3 -3
  89. package/dist/chunks/{types-UWI4HETD.js → types-AJHILHO5.js} +2 -4
  90. package/dist/chunks/{update-6A5V6HBH.js → update-YR2GCCJN.js} +7 -8
  91. package/dist/commands/build/index.js +10 -15
  92. package/dist/commands/deploy/index.js +310 -302
  93. package/dist/commands/dev/index.js +254 -181
  94. package/dist/commands/env/index.js +15 -16
  95. package/dist/commands/link/index.js +14 -20
  96. package/dist/commands/list/index.js +7 -9
  97. package/dist/commands-bulk.js +4408 -1246
  98. package/dist/help.js +1 -1
  99. package/dist/index.js +30 -22
  100. package/dist/version.mjs +1 -1
  101. package/package.json +41 -40
  102. package/dist/chunks/chunk-BGILSKZ5.js +0 -85
  103. package/dist/chunks/chunk-GFG6QDIE.js +0 -101
  104. package/dist/chunks/chunk-KBEX5MYS.js +0 -208
  105. package/dist/chunks/chunk-PBECT5BN.js +0 -107
  106. package/dist/chunks/chunk-PVCJIMVY.js +0 -1552
  107. /package/dist/{commands/dev/next-dev-websocket-shim-preload.cjs → runtime-assets/node-preloads/next-dev-websocket.cjs} +0 -0
@@ -1,1552 +0,0 @@
1
- import { createRequire as __createRequire } from 'node:module';
2
- import { fileURLToPath as __fileURLToPath } from 'node:url';
3
- import { dirname as __dirname_ } from 'node:path';
4
- const require = __createRequire(import.meta.url);
5
- const __filename = __fileURLToPath(import.meta.url);
6
- const __dirname = __dirname_(__filename);
7
- import {
8
- getLocalPathConfig,
9
- isPromptBackError,
10
- isPromptCanceledError
11
- } from "./chunk-BGILSKZ5.js";
12
- import {
13
- table
14
- } from "./chunk-KT4XXKJK.js";
15
- import {
16
- Separator,
17
- VERCEL_DIR_PROJECT,
18
- VERCEL_DIR_README,
19
- checkExistsAndConnect,
20
- compileVercelConfig,
21
- createDetectEntrypoint,
22
- createProject,
23
- detectProjects,
24
- fetchProjectsForRepoUrl,
25
- findProjectsFromPath,
26
- findRepoRoot,
27
- findSourceVercelConfigFile,
28
- getGitConfigPath,
29
- getLinkedProject,
30
- getProjectByNameOrId,
31
- getRemoteUrls,
32
- getServicesConfigWriteBlocker,
33
- getVercelDirectory,
34
- humanizePath,
35
- isDirectory,
36
- linkFolderToProject,
37
- linkRepoProject,
38
- parseGitConfig,
39
- parseRepoUrl,
40
- pluckRemoteUrls,
41
- printAlignedLabel,
42
- pull,
43
- readJSONFile,
44
- require_dist3 as require_dist,
45
- require_frameworks,
46
- require_lib,
47
- require_slugify,
48
- resolveGitRemote,
49
- selectAndParseRemoteUrl,
50
- selectOrg,
51
- writeServicesConfig
52
- } from "./chunk-3R5JGMHV.js";
53
- import {
54
- printError
55
- } from "./chunk-KBEX5MYS.js";
56
- import {
57
- CantParseJSONFile,
58
- ProjectNotFound,
59
- isAPIError
60
- } from "./chunk-SOFC4MLS.js";
61
- import {
62
- output_manager_default
63
- } from "./chunk-OX7KI3LF.js";
64
- import {
65
- require_source
66
- } from "./chunk-S7KYDPEM.js";
67
- import {
68
- __toESM
69
- } from "./chunk-TZ2YI2VH.js";
70
-
71
- // src/util/validate-paths.ts
72
- var import_fs_extra = __toESM(require_lib(), 1);
73
- var import_chalk = __toESM(require_source(), 1);
74
- import { homedir } from "os";
75
- import { resolve, sep } from "path";
76
- async function validateRootDirectory(cwd, path2, errorSuffix = "") {
77
- const pathStat = await (0, import_fs_extra.lstat)(path2).catch(() => null);
78
- const suffix = errorSuffix ? ` ${errorSuffix}` : "";
79
- if (!pathStat) {
80
- output_manager_default.error(
81
- `The provided path ${import_chalk.default.cyan(
82
- `\u201C${humanizePath(path2)}\u201D`
83
- )} does not exist.${suffix}`
84
- );
85
- return false;
86
- }
87
- if (!pathStat.isDirectory()) {
88
- output_manager_default.error(
89
- `The provided path ${import_chalk.default.cyan(
90
- `\u201C${humanizePath(path2)}\u201D`
91
- )} is a file, but expected a directory.${suffix}`
92
- );
93
- return false;
94
- }
95
- const base = resolve(cwd);
96
- const target = resolve(path2);
97
- if (target !== base && !target.startsWith(base + sep)) {
98
- output_manager_default.error(
99
- `The provided path ${import_chalk.default.cyan(
100
- `\u201C${humanizePath(path2)}\u201D`
101
- )} is outside of the project.${suffix}`
102
- );
103
- return false;
104
- }
105
- return true;
106
- }
107
- async function validatePaths(client, paths) {
108
- if (paths.length > 1) {
109
- output_manager_default.error(`Can't deploy more than one path.`);
110
- return { valid: false, exitCode: 1 };
111
- }
112
- const path2 = paths[0];
113
- const pathStat = await (0, import_fs_extra.lstat)(path2).catch(() => null);
114
- if (!pathStat) {
115
- output_manager_default.error(`Could not find ${import_chalk.default.cyan(`\u201C${humanizePath(path2)}\u201D`)}`);
116
- return { valid: false, exitCode: 1 };
117
- }
118
- if (!pathStat.isDirectory()) {
119
- output_manager_default.prettyError({
120
- message: "Support for single file deployments has been removed.",
121
- link: "https://vercel.link/no-single-file-deployments"
122
- });
123
- return { valid: false, exitCode: 1 };
124
- }
125
- if (path2 === homedir()) {
126
- const shouldDeployHomeDirectory = await client.input.confirm(
127
- `You are deploying your home directory. Do you want to continue?`,
128
- false
129
- );
130
- if (!shouldDeployHomeDirectory) {
131
- output_manager_default.print(`Canceled
132
- `);
133
- return { valid: false, exitCode: 0 };
134
- }
135
- }
136
- return { valid: true, path: path2 };
137
- }
138
-
139
- // src/util/config/read-config.ts
140
- async function readConfig(dir) {
141
- let pkgFilePath;
142
- try {
143
- const compileResult = await compileVercelConfig(dir);
144
- pkgFilePath = compileResult.configPath || getLocalPathConfig(dir);
145
- } catch (err) {
146
- if (err instanceof Error) {
147
- return err;
148
- }
149
- throw err;
150
- }
151
- const result = await readJSONFile(pkgFilePath);
152
- if (result instanceof CantParseJSONFile) {
153
- return result;
154
- }
155
- if (result) {
156
- return result;
157
- }
158
- return null;
159
- }
160
-
161
- // src/util/input/display-services.ts
162
- var import_frameworks = __toESM(require_frameworks(), 1);
163
- import {
164
- getServiceQueueTopics,
165
- isExperimentalServiceV2,
166
- isQueueTriggeredService,
167
- isScheduleTriggeredService,
168
- isWorkflowTriggeredService
169
- } from "@vercel/build-utils";
170
- var chalk2 = require_source();
171
- var frameworksBySlug = new Map(import_frameworks.frameworkList.map((f) => [f.slug, f]));
172
- var frameworkColors = {
173
- // JavaScript/TypeScript frameworks
174
- nextjs: chalk2.white,
175
- vite: chalk2.magenta,
176
- nuxtjs: chalk2.green,
177
- remix: chalk2.cyan,
178
- astro: chalk2.magenta,
179
- gatsby: chalk2.magenta,
180
- svelte: chalk2.red,
181
- sveltekit: chalk2.red,
182
- solidstart: chalk2.blue,
183
- angular: chalk2.red,
184
- vue: chalk2.green,
185
- ember: chalk2.red,
186
- preact: chalk2.magenta,
187
- // Python frameworks
188
- fastapi: chalk2.green,
189
- flask: chalk2.cyan,
190
- // Node frameworks
191
- express: chalk2.yellow,
192
- nest: chalk2.red,
193
- hono: chalk2.yellowBright
194
- };
195
- var runtimeColors = {
196
- node: chalk2.green,
197
- python: chalk2.blue,
198
- go: chalk2.cyan,
199
- ruby: chalk2.red,
200
- rust: chalk2.yellowBright
201
- };
202
- function getFrameworkName(slug) {
203
- if (!slug)
204
- return void 0;
205
- return frameworksBySlug.get(slug)?.name;
206
- }
207
- function formatRoutePrefix(routePrefix) {
208
- if (routePrefix === "/") {
209
- return "/";
210
- }
211
- const normalized = routePrefix.startsWith("/") ? routePrefix : `/${routePrefix}`;
212
- return `${normalized}/*`;
213
- }
214
- var jobTriggerLabels = {
215
- queue: "Job/Queue",
216
- schedule: "Job/Schedule",
217
- workflow: "Job/Workflow"
218
- };
219
- function getServiceDescriptionInfo(service) {
220
- if (service.type === "worker" || service.type === "job" || service.type === "cron") {
221
- const typeLabel = service.type === "worker" ? "Worker" : jobTriggerLabels[service.trigger ?? ""] ?? "Job";
222
- const typeColorFn = service.type === "worker" ? chalk2.magenta : chalk2.cyan;
223
- if (service.runtime) {
224
- const runtimeName = service.runtime.charAt(0).toUpperCase() + service.runtime.slice(1);
225
- const runtimeColorFn = runtimeColors[service.runtime] || chalk2.yellow;
226
- const label = `${typeLabel}${chalk2.white("/")}${runtimeColorFn(runtimeName)}`;
227
- return { label, colorFn: typeColorFn };
228
- }
229
- return { label: typeLabel, colorFn: typeColorFn };
230
- }
231
- return getFrameworkRuntimeBuilderInfo(service);
232
- }
233
- function getFrameworkRuntimeBuilderInfo(service) {
234
- const frameworkName = getFrameworkName(service.framework);
235
- if (frameworkName && service.framework) {
236
- const colorFn = frameworkColors[service.framework] || chalk2.cyan;
237
- return { label: frameworkName, colorFn };
238
- } else if (service.runtime) {
239
- const normalizedRuntime = service.runtime.toLowerCase().replace(/@.*$/, "");
240
- const colorFn = runtimeColors[normalizedRuntime] || chalk2.yellow;
241
- return { label: service.runtime, colorFn };
242
- } else if (service.builder?.use) {
243
- return { label: service.builder.use, colorFn: chalk2.magenta };
244
- }
245
- return { label: "unknown", colorFn: chalk2.dim };
246
- }
247
- function getServiceTarget(service) {
248
- if (isScheduleTriggeredService(service)) {
249
- return `schedule: ${service.schedule ?? "none"}`;
250
- }
251
- if (isQueueTriggeredService(service)) {
252
- const topics = getServiceQueueTopics(service);
253
- return `topics: ${topics.join(", ")}`;
254
- }
255
- if (isWorkflowTriggeredService(service)) {
256
- return "workflow";
257
- }
258
- return service.routePrefix ? formatRoutePrefix(service.routePrefix) : "no route";
259
- }
260
- function displayDetectedServices(services) {
261
- output_manager_default.print(`Detected services:
262
- `);
263
- const rows = services.some(isExperimentalServiceV2) ? buildServiceRowsV2(services.filter(isExperimentalServiceV2)) : buildServiceRowsV1(services);
264
- const tableOutput = table(rows, { align: ["l", "l", "l", "l"], hsep: 2 });
265
- output_manager_default.print(`${tableOutput}
266
- `);
267
- }
268
- function buildServiceRowsV1(services) {
269
- const outputOrder = {
270
- web: 0,
271
- cron: 1,
272
- job: 1,
273
- worker: 2
274
- };
275
- const sorted = [...services].sort(
276
- (a, b) => (outputOrder[a.type] ?? 3) - (outputOrder[b.type] ?? 3)
277
- );
278
- return sorted.map((service) => {
279
- const descInfo = getServiceDescriptionInfo(service);
280
- const target = getServiceTarget(service);
281
- return [
282
- `\u2022 ${service.name}`,
283
- descInfo.colorFn(`[${descInfo.label}]`),
284
- chalk2.dim("\u2192"),
285
- target
286
- ];
287
- });
288
- }
289
- function buildServiceRowsV2(services) {
290
- return services.map((service) => {
291
- const descInfo = getFrameworkRuntimeBuilderInfo(service);
292
- return [`\u2022 ${service.name}`, descInfo.colorFn(`[${descInfo.label}]`)];
293
- });
294
- }
295
- function displayServicesConfigNote(configFileName = "vercel.json") {
296
- output_manager_default.print(
297
- `
298
- ${chalk2.dim(`Services are configured via ${configFileName}.`)}
299
- `
300
- );
301
- }
302
- function displayServiceErrors(errors) {
303
- for (const error of errors) {
304
- output_manager_default.warn(error.message);
305
- }
306
- }
307
-
308
- // src/util/link/setup-and-link.ts
309
- var import_fs_extra2 = __toESM(require_lib(), 1);
310
- var import_fs_detectors2 = __toESM(require_dist(), 1);
311
- import { join as join3, basename } from "path";
312
- import { getPlatformEnv } from "@vercel/build-utils";
313
-
314
- // src/util/input/input-project.ts
315
- var import_chalk2 = __toESM(require_source(), 1);
316
- var import_slugify = __toESM(require_slugify(), 1);
317
- var SEARCH_ALL_PROJECTS = "search-all-projects";
318
- var CREATE_NEW_PROJECT = "create-new-project";
319
- var BACK_TO_PROJECT_SELECTION = Symbol("back-to-project-selection");
320
- var BACK_TO_TEAM_SELECTION = Symbol("back-to-team-selection");
321
- var SWITCH_GIT_REMOTE = Symbol("switch-git-remote");
322
- var NO_EXISTING_PROJECTS = Symbol("no-existing-projects");
323
- async function inputProjectDecision(client, defaultDecision) {
324
- const createChoice = {
325
- name: "Create new project",
326
- value: "create"
327
- };
328
- const existingChoice = {
329
- name: "Link existing project",
330
- value: "existing"
331
- };
332
- return await client.input.select({
333
- message: "Project?",
334
- choices: defaultDecision === "existing" ? [existingChoice, createChoice] : [createChoice, existingChoice]
335
- });
336
- }
337
- function randomNameSuffix() {
338
- return Math.random().toString(36).slice(2, 6).padEnd(4, "0");
339
- }
340
- async function suggestNewProjectName(client, org, slugifiedName, knownTaken) {
341
- let taken = knownTaken;
342
- if (!taken) {
343
- try {
344
- const existing = await getProjectByNameOrId(
345
- client,
346
- slugifiedName,
347
- org.id
348
- );
349
- taken = !(existing instanceof ProjectNotFound);
350
- } catch {
351
- taken = false;
352
- }
353
- }
354
- return taken ? `${slugifiedName}-${randomNameSuffix()}` : slugifiedName;
355
- }
356
- function promptForProjectName(client, org, defaultName, message = "Name?") {
357
- return client.input.text({
358
- message,
359
- default: defaultName,
360
- validate: async (val) => {
361
- if (!val) {
362
- return "Project name cannot be empty";
363
- }
364
- const project = await getProjectByNameOrId(client, val, org.id);
365
- if (!(project instanceof ProjectNotFound)) {
366
- return "Project already exists";
367
- }
368
- return true;
369
- }
370
- });
371
- }
372
- async function promptForProjectNameWithBack(client, org, defaultName) {
373
- try {
374
- return await client.withPromptBackNavigation(
375
- () => promptForProjectName(
376
- client,
377
- org,
378
- defaultName,
379
- `Name? ${import_chalk2.default.dim("Press \u2191 to return to project options")}`
380
- )
381
- );
382
- } catch (error) {
383
- if (isPromptBackError(error)) {
384
- return BACK_TO_PROJECT_SELECTION;
385
- }
386
- throw error;
387
- }
388
- }
389
- async function searchExistingProjects(client, org, allowBack) {
390
- const firstPage = await client.fetch(`/v9/projects?limit=100`, { accountId: org.id });
391
- const projects = firstPage.projects;
392
- const hasMoreProjects = firstPage.pagination.next != null;
393
- if (projects.length === 0) {
394
- output_manager_default.log(
395
- `No existing projects found under ${import_chalk2.default.bold(org.slug)}. Creating new project.`
396
- );
397
- return NO_EXISTING_PROJECTS;
398
- }
399
- const pageSize = 15;
400
- const countHint = projects.length > pageSize ? ` ${import_chalk2.default.dim(
401
- `(${hasMoreProjects ? "100+" : projects.length} projects)`
402
- )}` : "";
403
- return await client.input.search({
404
- message: `Which project?${countHint}`,
405
- pageSize,
406
- source: async (term, { signal }) => {
407
- const searchTerm = term?.trim();
408
- let matchingProjects = projects;
409
- if (searchTerm) {
410
- if (hasMoreProjects) {
411
- matchingProjects = (await client.fetch(
412
- `/v9/projects?search=${encodeURIComponent(searchTerm)}&limit=20`,
413
- {
414
- accountId: org.id,
415
- signal
416
- }
417
- )).projects;
418
- } else {
419
- const normalizedSearchTerm = searchTerm.toLowerCase();
420
- matchingProjects = projects.filter(
421
- (project) => project.name.toLowerCase().includes(normalizedSearchTerm) || project.id === searchTerm
422
- );
423
- }
424
- }
425
- const choices = matchingProjects.slice().sort((a, b) => b.updatedAt - a.updatedAt).map((project) => ({
426
- name: project.name,
427
- value: project
428
- }));
429
- if (!allowBack) {
430
- return choices;
431
- }
432
- const backChoice = {
433
- name: "Back to project options",
434
- value: BACK_TO_PROJECT_SELECTION
435
- };
436
- if (choices.length === 0) {
437
- return [backChoice];
438
- }
439
- if (searchTerm) {
440
- return choices;
441
- }
442
- return [...choices, new Separator(), backChoice];
443
- }
444
- });
445
- }
446
- async function inputProject(client, org, detectedProjectName, autoConfirm = false, skipAutoDetect = false, showProjectSuggestions = false, allowTeamSelectionBack = false, repoMatches = [], gitRemoteOptions) {
447
- const slugifiedName = (0, import_slugify.default)(detectedProjectName);
448
- const canSwitchGitRemote = (gitRemoteOptions?.remoteNames.length ?? 0) > 1;
449
- let detectedProject = null;
450
- if (!skipAutoDetect && repoMatches.length === 0) {
451
- output_manager_default.spinner("Searching for existing projects\u2026", 1e3);
452
- const [project, slugifiedProject] = await Promise.all([
453
- getProjectByNameOrId(client, detectedProjectName, org.id),
454
- slugifiedName !== detectedProjectName ? getProjectByNameOrId(client, slugifiedName, org.id) : null
455
- ]);
456
- detectedProject = !(project instanceof ProjectNotFound) ? project : !(slugifiedProject instanceof ProjectNotFound) ? slugifiedProject : null;
457
- if (detectedProject && !detectedProject.id) {
458
- throw new Error(`Detected linked project does not have "id".`);
459
- }
460
- output_manager_default.stopSpinner();
461
- }
462
- if (autoConfirm) {
463
- if (repoMatches.length === 1) {
464
- return repoMatches[0];
465
- }
466
- return detectedProject || detectedProjectName;
467
- }
468
- if (client.nonInteractive) {
469
- if (detectedProject) {
470
- return detectedProject;
471
- }
472
- const err = new Error("Confirmation required");
473
- err.code = "HEADLESS";
474
- throw err;
475
- }
476
- const slugifiedNameKnownFree = !skipAutoDetect && repoMatches.length === 0 && !detectedProject;
477
- let memoizedDefaultName;
478
- async function defaultNewProjectName() {
479
- if (memoizedDefaultName === void 0) {
480
- memoizedDefaultName = slugifiedNameKnownFree ? slugifiedName : await suggestNewProjectName(
481
- client,
482
- org,
483
- slugifiedName,
484
- detectedProject?.name === slugifiedName
485
- );
486
- }
487
- return memoizedDefaultName;
488
- }
489
- let shouldLinkProject;
490
- if (showProjectSuggestions && !skipAutoDetect) {
491
- for (; ; ) {
492
- const choices = [];
493
- if (repoMatches.length > 0) {
494
- choices.push(
495
- ...repoMatches.map((match) => ({
496
- name: `${match.project.name} ${import_chalk2.default.gray("(linked by git)")}`,
497
- value: match
498
- })),
499
- new Separator()
500
- );
501
- } else if (detectedProject) {
502
- choices.push(
503
- {
504
- name: `${detectedProject.name} ${import_chalk2.default.gray("(folder name)")}`,
505
- value: detectedProject
506
- },
507
- new Separator()
508
- );
509
- }
510
- choices.push(
511
- {
512
- name: "Search all projects",
513
- value: SEARCH_ALL_PROJECTS,
514
- description: "Browse or search every project in this team"
515
- },
516
- {
517
- name: "Create a new project",
518
- value: CREATE_NEW_PROJECT,
519
- description: `Create it under ${org.slug}`
520
- }
521
- );
522
- if (canSwitchGitRemote) {
523
- const current = gitRemoteOptions?.currentRemoteName;
524
- choices.push({
525
- name: "Switch Git remote",
526
- value: SWITCH_GIT_REMOTE,
527
- description: current ? `Suggestions are from ${current}` : "Search projects linked to a different remote"
528
- });
529
- }
530
- if (allowTeamSelectionBack) {
531
- choices.push({
532
- name: "Choose a different team",
533
- value: BACK_TO_TEAM_SELECTION,
534
- description: "Return to team selection"
535
- });
536
- }
537
- const selected = await client.input.select({
538
- message: "Which project?",
539
- choices
540
- });
541
- if (selected === BACK_TO_TEAM_SELECTION) {
542
- return BACK_TO_TEAM_SELECTION;
543
- }
544
- if (selected === SWITCH_GIT_REMOTE) {
545
- return SWITCH_GIT_REMOTE;
546
- }
547
- if (selected === CREATE_NEW_PROJECT) {
548
- const projectName = await promptForProjectNameWithBack(
549
- client,
550
- org,
551
- await defaultNewProjectName()
552
- );
553
- if (projectName === BACK_TO_PROJECT_SELECTION) {
554
- continue;
555
- }
556
- return projectName;
557
- }
558
- if (selected !== SEARCH_ALL_PROJECTS) {
559
- return selected;
560
- }
561
- const existingProject = await searchExistingProjects(client, org, true);
562
- if (existingProject === BACK_TO_PROJECT_SELECTION) {
563
- continue;
564
- }
565
- if (existingProject === NO_EXISTING_PROJECTS) {
566
- const projectName = await promptForProjectNameWithBack(
567
- client,
568
- org,
569
- slugifiedName
570
- );
571
- if (projectName === BACK_TO_PROJECT_SELECTION) {
572
- continue;
573
- }
574
- return projectName;
575
- }
576
- return existingProject;
577
- }
578
- } else if (!detectedProject) {
579
- const decision = await inputProjectDecision(
580
- client,
581
- skipAutoDetect ? "existing" : "create"
582
- );
583
- shouldLinkProject = decision === "existing";
584
- } else {
585
- output_manager_default.print(` ${import_chalk2.default.bold("Found existing project")}
586
- `);
587
- printAlignedLabel("Project", `${org.slug}/${detectedProject.name}`);
588
- if (await client.input.confirm(`Link directory to project?`, true)) {
589
- return detectedProject;
590
- }
591
- const decision = await inputProjectDecision(client, "existing");
592
- shouldLinkProject = decision === "existing";
593
- }
594
- if (shouldLinkProject) {
595
- const existingProject = await searchExistingProjects(client, org, false);
596
- if (existingProject !== NO_EXISTING_PROJECTS && existingProject !== BACK_TO_PROJECT_SELECTION) {
597
- return existingProject;
598
- }
599
- }
600
- return await promptForProjectName(client, org, await defaultNewProjectName());
601
- }
602
-
603
- // src/util/input/input-root-directory.ts
604
- var import_chalk3 = __toESM(require_source(), 1);
605
- import { normalizePath } from "@vercel/build-utils";
606
- import path from "path";
607
- async function inputRootDirectory(client, cwd, autoConfirm = false) {
608
- if (autoConfirm) {
609
- return null;
610
- }
611
- while (true) {
612
- const rootDirectory = await client.input.text({
613
- message: `Code directory?`,
614
- transformer: (input) => {
615
- return `${import_chalk3.default.dim(`./`)}${input}`;
616
- }
617
- });
618
- if (!rootDirectory) {
619
- return null;
620
- }
621
- const normal = path.normalize(rootDirectory);
622
- if (normal === "." || normal === "./") {
623
- return null;
624
- }
625
- const fullPath = path.join(cwd, normal);
626
- if (await validateRootDirectory(
627
- cwd,
628
- fullPath,
629
- "Please choose a different one."
630
- ) === false) {
631
- continue;
632
- }
633
- return normalizePath(normal);
634
- }
635
- }
636
-
637
- // src/util/input/edit-project-settings.ts
638
- var import_chalk4 = __toESM(require_source(), 1);
639
- var import_frameworks2 = __toESM(require_frameworks(), 1);
640
-
641
- // src/util/is-setting-value.ts
642
- function isSettingValue(setting) {
643
- return setting && typeof setting.value === "string";
644
- }
645
-
646
- // src/util/input/edit-project-settings.ts
647
- var settingMap = {
648
- buildCommand: "Build Command",
649
- devCommand: "Development Command",
650
- commandForIgnoringBuildStep: "Ignore Command",
651
- installCommand: "Install Command",
652
- outputDirectory: "Output Directory",
653
- framework: "Framework"
654
- };
655
- var settingKeys = Object.keys(settingMap).sort();
656
- async function editProjectSettings(client, projectSettings, framework, autoConfirm, localConfigurationOverrides, configFileName = "vercel.json") {
657
- const settings = Object.assign(
658
- {
659
- buildCommand: null,
660
- devCommand: null,
661
- framework: null,
662
- commandForIgnoringBuildStep: null,
663
- installCommand: null,
664
- outputDirectory: null
665
- },
666
- projectSettings
667
- );
668
- const hasLocalConfigurationOverrides = localConfigurationOverrides && Object.values(localConfigurationOverrides ?? {}).some(Boolean);
669
- if (hasLocalConfigurationOverrides) {
670
- for (const setting of settingKeys) {
671
- const localConfigValue = localConfigurationOverrides[setting];
672
- if (localConfigValue)
673
- settings[setting] = localConfigValue;
674
- }
675
- output_manager_default.print(` Local settings detected in ${configFileName}:
676
- `);
677
- for (const setting of settingKeys) {
678
- const override = localConfigurationOverrides[setting];
679
- if (override) {
680
- output_manager_default.print(
681
- ` ${import_chalk4.default.dim(
682
- `${import_chalk4.default.bold(`${settingMap[setting]}:`)} ${override}`
683
- )}
684
- `
685
- );
686
- }
687
- }
688
- if (localConfigurationOverrides.framework) {
689
- const overrideFramework = import_frameworks2.frameworkList.find(
690
- (f) => f.slug === localConfigurationOverrides.framework
691
- );
692
- if (overrideFramework) {
693
- framework = overrideFramework;
694
- output_manager_default.print(
695
- ` Merging default Project Settings for ${framework.name}. Previously listed overrides are prioritized.
696
- `
697
- );
698
- }
699
- }
700
- }
701
- if (!framework) {
702
- settings.framework = null;
703
- return settings;
704
- }
705
- output_manager_default.print("\n");
706
- if (!framework.slug) {
707
- output_manager_default.print(` No framework detected. Default Project Settings:
708
- `);
709
- } else {
710
- const buildCmd = framework.settings.buildCommand?.value ?? null;
711
- const outputSetting = framework.settings.outputDirectory;
712
- const outputDir = outputSetting ? isSettingValue(outputSetting) ? outputSetting.value : outputSetting.placeholder : null;
713
- const inline = [
714
- buildCmd ? `${settingMap.buildCommand}: ${buildCmd}` : null,
715
- outputDir ? `${settingMap.outputDirectory}: ${outputDir}` : null
716
- ].filter(Boolean);
717
- const detail = inline.length ? import_chalk4.default.dim(` (${inline.join(", ")})`) : "";
718
- output_manager_default.print(` ${import_chalk4.default.bold("Detected")} ${framework.name}${detail}
719
- `);
720
- }
721
- settings.framework = framework.slug;
722
- if (!framework.slug) {
723
- for (const setting of settingKeys) {
724
- if (setting === "framework" || setting === "commandForIgnoringBuildStep") {
725
- continue;
726
- }
727
- const defaultSetting = framework.settings[setting];
728
- const override = localConfigurationOverrides?.[setting];
729
- if (!override && defaultSetting) {
730
- output_manager_default.print(
731
- ` ${import_chalk4.default.dim(
732
- `${import_chalk4.default.bold(`${settingMap[setting]}:`)} ${isSettingValue(defaultSetting) ? defaultSetting.value : import_chalk4.default.italic(`${defaultSetting.placeholder}`)}`
733
- )}
734
- `
735
- );
736
- }
737
- }
738
- }
739
- if (autoConfirm || !await client.input.confirm("Customize settings?", false)) {
740
- return settings;
741
- }
742
- const choices = settingKeys.reduce(
743
- (acc, setting) => {
744
- const skip = setting === "framework" || setting === "commandForIgnoringBuildStep" || setting === "installCommand" || localConfigurationOverrides?.[setting];
745
- if (skip)
746
- return acc;
747
- return [...acc, { name: settingMap[setting], value: setting }];
748
- },
749
- []
750
- );
751
- const settingFields = await client.input.checkbox({
752
- message: "Which settings would you like to overwrite (select multiple)?",
753
- choices
754
- });
755
- for (const setting of settingFields) {
756
- const field = settingMap[setting];
757
- settings[setting] = await client.input.text({
758
- message: `${import_chalk4.default.bold(field)}?`
759
- });
760
- }
761
- return settings;
762
- }
763
-
764
- // src/util/link/setup-and-link.ts
765
- var import_frameworks3 = __toESM(require_frameworks(), 1);
766
-
767
- // src/util/input/vercel-auth.ts
768
- var import_chalk5 = __toESM(require_source(), 1);
769
- var DEFAULT_VERCEL_AUTH_SETTING = "standard";
770
- var OPTIONS = {
771
- message: `What setting do you want to use for Vercel Authentication?`,
772
- default: DEFAULT_VERCEL_AUTH_SETTING,
773
- choices: [
774
- {
775
- description: "Standard Protection (recommended)",
776
- name: "standard",
777
- value: "standard"
778
- },
779
- {
780
- description: "No Protection (all deployments will be public)",
781
- name: "none",
782
- value: "none"
783
- }
784
- ]
785
- };
786
- async function vercelAuth(client, {
787
- autoConfirm = false
788
- }) {
789
- if (autoConfirm || await client.input.confirm(
790
- `Want to use the default Deployment Protection settings? ${import_chalk5.default.dim(`(Vercel Authentication: Standard Protection)`)}`,
791
- true
792
- )) {
793
- return DEFAULT_VERCEL_AUTH_SETTING;
794
- }
795
- const vercelAuth2 = await client.input.select(OPTIONS);
796
- return vercelAuth2;
797
- }
798
-
799
- // src/util/link/services-setup.ts
800
- var import_fs_detectors = __toESM(require_dist(), 1);
801
- import { normalizePath as normalizePath2 } from "@vercel/build-utils";
802
- import { join, relative } from "path";
803
- var SERVICES_DOCS_URL = "https://vercel.com/docs/services";
804
- var INFERRED_SERVICES_PROMPT = "Multiple services were detected. How would you like to set up this project?";
805
- async function getServicesSetupState(workPath) {
806
- const detectServicesResult = await (0, import_fs_detectors.detectServices)({
807
- fs: new import_fs_detectors.LocalFileSystemDetector(workPath),
808
- detectEntrypoint: createDetectEntrypoint(workPath)
809
- });
810
- const hasConfiguredServices = detectServicesResult.resolved?.source === "configured";
811
- const inferredServices = hasConfiguredServices ? null : detectServicesResult.inferred;
812
- const inferredServicesWriteBlocker = inferredServices ? await getServicesConfigWriteBlocker(workPath, inferredServices.config) : null;
813
- return {
814
- detectServicesResult,
815
- hasConfiguredServices,
816
- inferredServices,
817
- inferredServicesWriteBlocker
818
- };
819
- }
820
- function displayConfiguredServicesSetup(detectServicesResult, configFileName = "vercel.json") {
821
- const v1Services = detectServicesResult.services.filter(
822
- import_fs_detectors.isExperimentalService
823
- );
824
- if (v1Services.length > 0) {
825
- displayDetectedServices(v1Services);
826
- }
827
- if (detectServicesResult.errors.length > 0) {
828
- displayServiceErrors(detectServicesResult.errors);
829
- }
830
- displayServicesConfigNote(configFileName);
831
- }
832
- function formatDetectedServicesSummary(services) {
833
- if (services.length === 0) {
834
- return "";
835
- }
836
- if (services.length === 1) {
837
- return `"${services[0].name}"`;
838
- }
839
- if (services.length === 2) {
840
- return `"${services[0].name}" + "${services[1].name}"`;
841
- }
842
- const othersCount = services.length - 2;
843
- return `"${services[0].name}" + "${services[1].name}" + ${othersCount} ${othersCount === 1 ? "other" : "others"}`;
844
- }
845
- function toProjectRootDirectory(projectPath, selectedPath) {
846
- const rootDirectory = normalizePath2(relative(projectPath, selectedPath));
847
- return rootDirectory === "" ? null : rootDirectory;
848
- }
849
- async function promptForInferredServicesSetup({
850
- client,
851
- autoConfirm,
852
- nonInteractive,
853
- workPath,
854
- inferred,
855
- inferredWriteBlocker,
856
- allowChooseDifferentProjectDirectory = false
857
- }) {
858
- if (!inferred) {
859
- return null;
860
- }
861
- if (inferredWriteBlocker) {
862
- output_manager_default.warn(
863
- `Multiple services were detected, but your existing project config uses \`${inferredWriteBlocker}\`. To deploy multiple services in one project, see ${output_manager_default.link("Services", SERVICES_DOCS_URL)}.`
864
- );
865
- return null;
866
- }
867
- displayDetectedServices(inferred.services);
868
- let choice = null;
869
- if (autoConfirm) {
870
- choice = { type: "services" };
871
- } else if (!nonInteractive) {
872
- const webServices = inferred.services.filter(
873
- (service) => (0, import_fs_detectors.isExperimentalService)(service) ? service.type === "web" : true
874
- );
875
- const choices = [
876
- {
877
- name: `Set up project with all detected services: ${formatDetectedServicesSummary(
878
- inferred.services
879
- )}`,
880
- value: "services"
881
- },
882
- ...webServices.map((service, index) => ({
883
- name: `Set up project with "${service.name}"`,
884
- value: `single-app:${index}`
885
- })),
886
- ...allowChooseDifferentProjectDirectory ? [
887
- {
888
- name: "Choose a different root directory",
889
- value: "project-directory"
890
- }
891
- ] : []
892
- ];
893
- const selected = await client.input.select({
894
- message: INFERRED_SERVICES_PROMPT,
895
- choices
896
- });
897
- if (selected === "services") {
898
- choice = { type: "services" };
899
- } else if (selected === "project-directory") {
900
- choice = { type: "project-directory" };
901
- } else if (typeof selected === "string" && selected.startsWith("single-app:")) {
902
- const index = Number.parseInt(selected.slice("single-app:".length), 10);
903
- const service = webServices[index];
904
- if (service) {
905
- const serviceRoot = (0, import_fs_detectors.isExperimentalServiceV2)(service) ? service.root : service.workspace;
906
- choice = {
907
- type: "single-app",
908
- selectedPath: serviceRoot === "." ? workPath : join(workPath, serviceRoot)
909
- };
910
- }
911
- }
912
- }
913
- if (choice?.type !== "services") {
914
- return choice;
915
- }
916
- const { configFileName } = await writeServicesConfig(
917
- workPath,
918
- inferred.config
919
- );
920
- output_manager_default.print(` Added services configuration to ${configFileName}.
921
- `);
922
- return { type: "services" };
923
- }
924
-
925
- // src/util/projects/search-project-across-teams.ts
926
- var import_slugify2 = __toESM(require_slugify(), 1);
927
- import { join as join2, relative as relative2 } from "path";
928
- async function searchProjectsByRepoRoot({
929
- client,
930
- cwd,
931
- gitProjectName,
932
- orgs,
933
- remoteName
934
- }) {
935
- const empty = { matches: [], remoteNames: [] };
936
- const rootPath = await findRepoRoot(cwd);
937
- if (!rootPath) {
938
- return empty;
939
- }
940
- const gitConfigPath = getGitConfigPath({ cwd: rootPath }) ?? join2(rootPath, ".git/config");
941
- const remoteUrls = await getRemoteUrls(gitConfigPath);
942
- const remoteNames = remoteUrls ? Object.keys(remoteUrls).sort() : [];
943
- let remote;
944
- try {
945
- remote = await resolveGitRemote(client, rootPath, {
946
- yes: true,
947
- existingRemoteName: remoteName
948
- });
949
- } catch (error) {
950
- if (isPromptCanceledError(error)) {
951
- throw error;
952
- }
953
- output_manager_default.debug(`Failed to resolve Git remote for project search: ${error}`);
954
- return { matches: [], remoteNames };
955
- }
956
- if (!remote) {
957
- return { matches: [], remoteNames };
958
- }
959
- const relativePath = relative2(rootPath, cwd);
960
- const results = await Promise.all(
961
- orgs.map(async (org) => {
962
- try {
963
- const projects = await fetchProjectsForRepoUrl(
964
- client,
965
- remote.repoUrl,
966
- org.id
967
- );
968
- const repoProjectConfigs = projects.filter(
969
- (project) => !gitProjectName || project.id === gitProjectName || project.name === gitProjectName
970
- ).map((project) => ({
971
- id: project.id,
972
- name: project.name,
973
- directory: project.rootDirectory || ".",
974
- orgId: org.id
975
- }));
976
- const matchingProjects = findProjectsFromPath(
977
- repoProjectConfigs,
978
- relativePath
979
- );
980
- return matchingProjects.map((match) => {
981
- const project = projects.find((p) => p.id === match.id);
982
- if (!project) {
983
- return null;
984
- }
985
- return {
986
- project,
987
- org,
988
- reason: "repo-root",
989
- repo: {
990
- ...remote,
991
- directory: match.directory
992
- }
993
- };
994
- }).filter(Boolean);
995
- } catch (error) {
996
- if (isPromptCanceledError(error)) {
997
- throw error;
998
- }
999
- output_manager_default.debug(
1000
- `Failed to search Git-linked projects under ${org.slug}: ${error}`
1001
- );
1002
- return [];
1003
- }
1004
- })
1005
- );
1006
- return {
1007
- matches: results.flat(),
1008
- remote,
1009
- remoteNames
1010
- };
1011
- }
1012
-
1013
- // src/util/link/setup-and-link.ts
1014
- function isCrossTeamMatch(value) {
1015
- return typeof value === "object" && value !== null && "project" in value && "org" in value && "reason" in value;
1016
- }
1017
- async function selectOrgForLink(client, autoConfirm, searchable = false, meta) {
1018
- try {
1019
- return await selectOrg(
1020
- client,
1021
- "Which team?",
1022
- autoConfirm,
1023
- searchable,
1024
- meta
1025
- );
1026
- } catch (err) {
1027
- if (isAPIError(err)) {
1028
- if (err.code === "NOT_AUTHORIZED") {
1029
- output_manager_default.prettyError(err);
1030
- return { status: "error", exitCode: 1, reason: "NOT_AUTHORIZED" };
1031
- }
1032
- if (err.code === "TEAM_DELETED") {
1033
- output_manager_default.prettyError(err);
1034
- return { status: "error", exitCode: 1, reason: "TEAM_DELETED" };
1035
- }
1036
- }
1037
- throw err;
1038
- }
1039
- }
1040
- function isErrnoException(err) {
1041
- return err instanceof Error && typeof err.code === "string";
1042
- }
1043
- async function hasWorkspaces(cwd) {
1044
- try {
1045
- const fs = new import_fs_detectors2.LocalFileSystemDetector(cwd);
1046
- const workspaces = await (0, import_fs_detectors2.getWorkspaces)({ fs });
1047
- return workspaces.length > 0;
1048
- } catch (err) {
1049
- if (isErrnoException(err) && err.code && ["ENOENT", "EACCES", "ENOTDIR"].includes(err.code)) {
1050
- output_manager_default.debug(`getWorkspaces failed for ${cwd}: ${err}`);
1051
- return false;
1052
- }
1053
- throw err;
1054
- }
1055
- }
1056
- async function shouldPromptForRootDirectory(opts) {
1057
- if (opts.servicesChoice?.type === "project-directory") {
1058
- return true;
1059
- }
1060
- if (await hasWorkspaces(opts.path)) {
1061
- return true;
1062
- }
1063
- try {
1064
- const detected = await detectProjects(opts.path);
1065
- const frameworksAtRoot = detected.get("") ?? [];
1066
- return frameworksAtRoot.length === 0;
1067
- } catch (err) {
1068
- output_manager_default.debug(`detectProjects failed at root: ${err}`);
1069
- return true;
1070
- }
1071
- }
1072
- async function maybePullEnvAfterLink(client, path2, autoConfirm, pullEnv) {
1073
- if (!pullEnv || !client.stdin.isTTY || client.nonInteractive) {
1074
- return;
1075
- }
1076
- output_manager_default.print("\n");
1077
- const pullEnvConfirmed = autoConfirm || await client.input.confirm(
1078
- "Pull development environment variables into .env.local?",
1079
- true
1080
- );
1081
- if (!pullEnvConfirmed) {
1082
- return;
1083
- }
1084
- const originalCwd = client.cwd;
1085
- try {
1086
- client.cwd = path2;
1087
- const args = autoConfirm ? ["--yes"] : [];
1088
- const exitCode = await pull(client, args, "vercel-cli:link");
1089
- if (exitCode !== 0) {
1090
- output_manager_default.error(
1091
- "Failed to pull environment variables. You can run `vc env pull` manually."
1092
- );
1093
- }
1094
- } catch (_error) {
1095
- output_manager_default.error(
1096
- "Failed to pull environment variables. You can run `vc env pull` manually."
1097
- );
1098
- } finally {
1099
- client.cwd = originalCwd;
1100
- }
1101
- }
1102
- async function linkCrossTeamMatch({
1103
- client,
1104
- path: path2,
1105
- match,
1106
- successEmoji,
1107
- autoConfirm,
1108
- pullEnv
1109
- }) {
1110
- client.config.currentTeam = match.org.type === "team" ? match.org.id : void 0;
1111
- if (match.reason === "repo-root" && match.repo) {
1112
- await linkRepoProject(client, path2, {
1113
- project: match.project,
1114
- orgId: match.org.id,
1115
- orgSlug: match.org.slug,
1116
- remoteName: match.repo.remoteName,
1117
- successEmoji
1118
- });
1119
- await maybePullEnvAfterLink(client, path2, autoConfirm, pullEnv);
1120
- return {
1121
- status: "linked",
1122
- org: match.org,
1123
- project: match.project,
1124
- repoRoot: match.repo.rootPath
1125
- };
1126
- }
1127
- await linkFolderToProject(
1128
- client,
1129
- path2,
1130
- { projectId: match.project.id, orgId: match.org.id },
1131
- match.project.name,
1132
- match.org.slug,
1133
- successEmoji,
1134
- autoConfirm,
1135
- pullEnv
1136
- );
1137
- return { status: "linked", org: match.org, project: match.project };
1138
- }
1139
- async function setupAndLink(client, path2, {
1140
- autoConfirm = false,
1141
- forceDelete = false,
1142
- link,
1143
- selectedOrg,
1144
- successEmoji = "link",
1145
- projectName,
1146
- nonInteractive = false,
1147
- pullEnv = true,
1148
- v0
1149
- }) {
1150
- const { config } = client;
1151
- const gitProjectName = projectName;
1152
- projectName = projectName ?? basename(path2);
1153
- if (!isDirectory(path2)) {
1154
- output_manager_default.error(`Expected directory but found file: ${path2}`);
1155
- return { status: "error", exitCode: 1, reason: "PATH_IS_FILE" };
1156
- }
1157
- if (!link) {
1158
- link = await getLinkedProject(client, { cwd: path2 });
1159
- }
1160
- const isTTY = client.stdin.isTTY;
1161
- let rootDirectory = null;
1162
- let newProjectName;
1163
- let org = selectedOrg;
1164
- if (!forceDelete && link.status === "linked") {
1165
- return link;
1166
- }
1167
- if (getPlatformEnv("ORG_ID") && getPlatformEnv("PROJECT_ID")) {
1168
- const envLink = await getLinkedProject(client, { cwd: path2 });
1169
- if (envLink.status === "error") {
1170
- return envLink;
1171
- }
1172
- if (envLink.status === "linked") {
1173
- config.currentTeam = envLink.org.type === "team" ? envLink.org.id : void 0;
1174
- output_manager_default.print("\n");
1175
- printAlignedLabel("Directory", humanizePath(path2));
1176
- printAlignedLabel("Source", "VERCEL_ORG_ID and VERCEL_PROJECT_ID");
1177
- output_manager_default.print("\n");
1178
- printAlignedLabel(
1179
- "Linked",
1180
- `${envLink.org.slug}/${envLink.project.name}`,
1181
- { gutter: "\u2713" }
1182
- );
1183
- return envLink;
1184
- }
1185
- }
1186
- if (!isTTY && !autoConfirm && !nonInteractive) {
1187
- return { status: "error", exitCode: 1, reason: "HEADLESS" };
1188
- }
1189
- if (!org && (nonInteractive || !isTTY)) {
1190
- const resolved = await selectOrgForLink(client, autoConfirm);
1191
- if ("status" in resolved) {
1192
- return resolved;
1193
- }
1194
- org = resolved;
1195
- }
1196
- if (forceDelete) {
1197
- const vercelDir = getVercelDirectory(path2);
1198
- (0, import_fs_extra2.remove)(join3(vercelDir, VERCEL_DIR_README));
1199
- (0, import_fs_extra2.remove)(join3(vercelDir, VERCEL_DIR_PROJECT));
1200
- }
1201
- output_manager_default.print("\n");
1202
- printAlignedLabel("Directory", humanizePath(path2));
1203
- output_manager_default.print("\n");
1204
- const interactive = isTTY && !nonInteractive;
1205
- const searchableTeamPicker = interactive;
1206
- const showProjectSuggestions = interactive && !gitProjectName;
1207
- let projectOrNewProjectName;
1208
- let teamAutoSelected = false;
1209
- let selectedGitRemoteName;
1210
- for (; ; ) {
1211
- if (!org) {
1212
- const orgMeta = {};
1213
- const resolved = await selectOrgForLink(
1214
- client,
1215
- autoConfirm,
1216
- searchableTeamPicker,
1217
- orgMeta
1218
- );
1219
- if ("status" in resolved) {
1220
- return resolved;
1221
- }
1222
- org = resolved;
1223
- teamAutoSelected = orgMeta.choiceCount === 1;
1224
- }
1225
- let repoMatches = [];
1226
- let gitRemoteNames = [];
1227
- let currentGitRemoteName;
1228
- if (showProjectSuggestions) {
1229
- output_manager_default.spinner("Searching for existing projects\u2026", 1e3);
1230
- try {
1231
- const repoSearch = await searchProjectsByRepoRoot({
1232
- client,
1233
- cwd: path2,
1234
- gitProjectName,
1235
- orgs: [org],
1236
- remoteName: selectedGitRemoteName
1237
- });
1238
- repoMatches = repoSearch.matches;
1239
- gitRemoteNames = repoSearch.remoteNames;
1240
- currentGitRemoteName = repoSearch.remote?.remoteName;
1241
- } catch (err) {
1242
- if (isPromptCanceledError(err)) {
1243
- throw err;
1244
- }
1245
- output_manager_default.debug(`Git-linked project search failed: ${err}`);
1246
- } finally {
1247
- output_manager_default.stopSpinner();
1248
- }
1249
- }
1250
- try {
1251
- projectOrNewProjectName = await inputProject(
1252
- client,
1253
- org,
1254
- projectName,
1255
- autoConfirm,
1256
- false,
1257
- showProjectSuggestions,
1258
- searchableTeamPicker && !selectedOrg && !teamAutoSelected,
1259
- repoMatches,
1260
- {
1261
- remoteNames: gitRemoteNames,
1262
- currentRemoteName: currentGitRemoteName
1263
- }
1264
- );
1265
- } catch (err) {
1266
- if (err instanceof Error && err.code === "HEADLESS") {
1267
- return { status: "error", exitCode: 1, reason: "HEADLESS" };
1268
- }
1269
- throw err;
1270
- }
1271
- if (projectOrNewProjectName === BACK_TO_TEAM_SELECTION) {
1272
- org = void 0;
1273
- selectedGitRemoteName = void 0;
1274
- continue;
1275
- }
1276
- if (projectOrNewProjectName === SWITCH_GIT_REMOTE) {
1277
- const rootPath = await findRepoRoot(path2);
1278
- if (!rootPath) {
1279
- continue;
1280
- }
1281
- try {
1282
- const remote = await resolveGitRemote(client, rootPath, {
1283
- yes: false
1284
- });
1285
- selectedGitRemoteName = remote?.remoteName;
1286
- } catch (err) {
1287
- if (isPromptCanceledError(err)) {
1288
- throw err;
1289
- }
1290
- output_manager_default.debug(`Failed to switch Git remote: ${err}`);
1291
- }
1292
- continue;
1293
- }
1294
- break;
1295
- }
1296
- if (typeof projectOrNewProjectName === "string") {
1297
- newProjectName = projectOrNewProjectName;
1298
- } else if (isCrossTeamMatch(projectOrNewProjectName)) {
1299
- return await linkCrossTeamMatch({
1300
- client,
1301
- path: path2,
1302
- match: projectOrNewProjectName,
1303
- successEmoji,
1304
- autoConfirm,
1305
- pullEnv
1306
- });
1307
- } else {
1308
- const project = projectOrNewProjectName;
1309
- await linkFolderToProject(
1310
- client,
1311
- path2,
1312
- {
1313
- projectId: project.id,
1314
- orgId: org.id
1315
- },
1316
- project.name,
1317
- org.slug,
1318
- successEmoji,
1319
- autoConfirm,
1320
- pullEnv
1321
- );
1322
- return { status: "linked", org, project };
1323
- }
1324
- config.currentTeam = org.type === "team" ? org.id : void 0;
1325
- const gitConnectIntent = await resolveGitConnectIntent(
1326
- client,
1327
- path2,
1328
- autoConfirm
1329
- );
1330
- const rootServicesSetup = await getServicesSetupState(path2);
1331
- const configFileName = await findSourceVercelConfigFile(path2) ?? "vercel.json";
1332
- try {
1333
- let settings = {};
1334
- let pathWithRootDirectory = path2;
1335
- let rootInferredServicesChoice = null;
1336
- if (!rootServicesSetup.hasConfiguredServices) {
1337
- rootInferredServicesChoice = await promptForInferredServicesSetup({
1338
- client,
1339
- autoConfirm,
1340
- nonInteractive,
1341
- workPath: path2,
1342
- inferred: rootServicesSetup.inferredServices,
1343
- inferredWriteBlocker: rootServicesSetup.inferredServicesWriteBlocker,
1344
- allowChooseDifferentProjectDirectory: true
1345
- });
1346
- }
1347
- if (rootServicesSetup.hasConfiguredServices) {
1348
- displayConfiguredServicesSetup(
1349
- rootServicesSetup.detectServicesResult,
1350
- configFileName
1351
- );
1352
- settings.framework = "services";
1353
- } else if (rootInferredServicesChoice?.type === "services") {
1354
- settings.framework = "services";
1355
- } else {
1356
- const skipSelectedRootInferredServicesPrompt = rootInferredServicesChoice?.type === "single-app";
1357
- if (rootInferredServicesChoice?.type === "single-app") {
1358
- rootDirectory = toProjectRootDirectory(
1359
- path2,
1360
- rootInferredServicesChoice.selectedPath
1361
- );
1362
- } else {
1363
- const shouldPromptRoot = await shouldPromptForRootDirectory({
1364
- path: path2,
1365
- servicesChoice: rootInferredServicesChoice
1366
- });
1367
- if (shouldPromptRoot) {
1368
- rootDirectory = await inputRootDirectory(client, path2, autoConfirm);
1369
- if (rootDirectory && !await validateRootDirectory(path2, join3(path2, rootDirectory))) {
1370
- return {
1371
- status: "error",
1372
- exitCode: 1,
1373
- reason: "INVALID_ROOT_DIRECTORY"
1374
- };
1375
- }
1376
- }
1377
- }
1378
- pathWithRootDirectory = rootDirectory ? join3(path2, rootDirectory) : path2;
1379
- const selectedRootServicesSetup = pathWithRootDirectory === path2 ? null : await getServicesSetupState(pathWithRootDirectory);
1380
- let selectedRootInferredServicesChoice = null;
1381
- if (!skipSelectedRootInferredServicesPrompt) {
1382
- selectedRootInferredServicesChoice = await promptForInferredServicesSetup({
1383
- client,
1384
- autoConfirm,
1385
- nonInteractive,
1386
- workPath: pathWithRootDirectory,
1387
- inferred: selectedRootServicesSetup?.inferredServices ?? null,
1388
- inferredWriteBlocker: selectedRootServicesSetup?.inferredServicesWriteBlocker ?? null
1389
- });
1390
- }
1391
- if (selectedRootServicesSetup?.hasConfiguredServices) {
1392
- displayConfiguredServicesSetup(
1393
- selectedRootServicesSetup.detectServicesResult,
1394
- configFileName
1395
- );
1396
- settings.framework = "services";
1397
- } else if (selectedRootInferredServicesChoice?.type === "services") {
1398
- settings.framework = "services";
1399
- } else {
1400
- if (selectedRootInferredServicesChoice?.type === "single-app") {
1401
- rootDirectory = toProjectRootDirectory(
1402
- path2,
1403
- selectedRootInferredServicesChoice.selectedPath
1404
- );
1405
- pathWithRootDirectory = rootDirectory ? join3(path2, rootDirectory) : path2;
1406
- }
1407
- const localConfig = await readConfig(pathWithRootDirectory);
1408
- if (localConfig instanceof CantParseJSONFile) {
1409
- output_manager_default.prettyError(localConfig);
1410
- return { status: "error", exitCode: 1 };
1411
- }
1412
- const isZeroConfig = !localConfig || !localConfig.builds || localConfig.builds.length === 0;
1413
- if (isZeroConfig) {
1414
- const localConfigurationOverrides = {
1415
- buildCommand: localConfig?.buildCommand,
1416
- devCommand: localConfig?.devCommand,
1417
- framework: localConfig?.framework,
1418
- commandForIgnoringBuildStep: localConfig?.ignoreCommand,
1419
- installCommand: localConfig?.installCommand,
1420
- outputDirectory: localConfig?.outputDirectory
1421
- };
1422
- const detectedProjectsForWorkspace = await detectProjects(
1423
- pathWithRootDirectory
1424
- );
1425
- const detectedProjects = detectedProjectsForWorkspace.get("") || [];
1426
- const framework = detectedProjects[0] ?? import_frameworks3.frameworkList.find((f) => f.slug === null);
1427
- settings = await editProjectSettings(
1428
- client,
1429
- {},
1430
- framework,
1431
- autoConfirm,
1432
- localConfigurationOverrides,
1433
- configFileName
1434
- );
1435
- }
1436
- }
1437
- }
1438
- const changeAdditionalSettings = false;
1439
- let vercelAuthSetting = DEFAULT_VERCEL_AUTH_SETTING;
1440
- if (changeAdditionalSettings) {
1441
- vercelAuthSetting = await vercelAuth(client, {
1442
- autoConfirm
1443
- });
1444
- }
1445
- if (rootDirectory) {
1446
- settings.rootDirectory = rootDirectory;
1447
- }
1448
- const project = await createProject(client, {
1449
- ...settings,
1450
- name: newProjectName,
1451
- vercelAuth: vercelAuthSetting,
1452
- v0
1453
- });
1454
- await linkFolderToProject(
1455
- client,
1456
- path2,
1457
- {
1458
- projectId: project.id,
1459
- orgId: org.id
1460
- },
1461
- project.name,
1462
- org.slug,
1463
- successEmoji,
1464
- autoConfirm,
1465
- false,
1466
- // don't prompt to pull env for newly created projects
1467
- "Created"
1468
- );
1469
- await applyGitConnectIntent(
1470
- client,
1471
- gitConnectIntent,
1472
- project,
1473
- autoConfirm,
1474
- org
1475
- );
1476
- return { status: "linked", org, project };
1477
- } catch (err) {
1478
- if (isPromptCanceledError(err)) {
1479
- throw err;
1480
- }
1481
- if (isAPIError(err) && err.code === "too_many_projects") {
1482
- output_manager_default.prettyError(err);
1483
- return { status: "error", exitCode: 1, reason: "TOO_MANY_PROJECTS" };
1484
- }
1485
- if (err instanceof Error && err.code === "HEADLESS") {
1486
- return { status: "error", exitCode: 1, reason: "HEADLESS" };
1487
- }
1488
- printError(err);
1489
- return { status: "error", exitCode: 1 };
1490
- }
1491
- }
1492
- async function resolveGitConnectIntent(client, path2, autoConfirm) {
1493
- try {
1494
- const gitConfig = await parseGitConfig(join3(path2, ".git/config"));
1495
- if (!gitConfig) {
1496
- return null;
1497
- }
1498
- const remoteUrls = pluckRemoteUrls(gitConfig);
1499
- if (!remoteUrls || Object.keys(remoteUrls).length === 0) {
1500
- return null;
1501
- }
1502
- const skipPrompts = autoConfirm || client.nonInteractive;
1503
- const shouldConnect = skipPrompts || await client.input.confirm(`Connect detected Git repository?`, false);
1504
- if (!shouldConnect) {
1505
- return null;
1506
- }
1507
- const repoInfo = skipPrompts ? parseRepoUrl(remoteUrls.origin ?? Object.values(remoteUrls)[0]) : await selectAndParseRemoteUrl(client, remoteUrls);
1508
- if (!repoInfo) {
1509
- return null;
1510
- }
1511
- return { repoInfo };
1512
- } catch (error) {
1513
- if (isPromptCanceledError(error)) {
1514
- throw error;
1515
- }
1516
- output_manager_default.debug(`Failed to detect git repository: ${error}`);
1517
- return null;
1518
- }
1519
- }
1520
- async function applyGitConnectIntent(client, intent, project, autoConfirm, org) {
1521
- if (!intent) {
1522
- return;
1523
- }
1524
- const { repoInfo } = intent;
1525
- try {
1526
- await checkExistsAndConnect({
1527
- client,
1528
- confirm: autoConfirm,
1529
- gitProviderLink: project.link,
1530
- org,
1531
- gitOrg: repoInfo.org,
1532
- project,
1533
- // Type assertion since we only need the id
1534
- provider: repoInfo.provider,
1535
- repo: repoInfo.repo,
1536
- repoPath: `${repoInfo.org}/${repoInfo.repo}`
1537
- });
1538
- } catch (error) {
1539
- if (isPromptCanceledError(error)) {
1540
- return;
1541
- }
1542
- output_manager_default.debug(`Failed to connect git repository: ${error}`);
1543
- }
1544
- }
1545
-
1546
- export {
1547
- validateRootDirectory,
1548
- validatePaths,
1549
- readConfig,
1550
- displayDetectedServices,
1551
- setupAndLink
1552
- };