@ruan-cat/vercel-deploy-tool 1.0.0 → 1.1.0
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/README.md +14 -0
- package/dist/cli.js +113 -58
- package/dist/index.d.ts +11 -18
- package/dist/index.js +114 -62
- package/package.json +1 -1
- package/src/cli.ts +9 -2
- package/src/commands/deploy.ts +24 -13
- package/src/config/loader.ts +31 -21
- package/src/core/tasks/alias.ts +2 -5
- package/src/core/tasks/build.ts +12 -5
- package/src/core/tasks/deploy.ts +12 -4
- package/src/core/tasks/index.ts +22 -4
- package/src/core/tasks/link.ts +12 -5
- package/src/core/vercel.ts +19 -1
- package/src/index.ts +1 -1
package/README.md
CHANGED
|
@@ -99,6 +99,13 @@ pnpm run deploy-vercel
|
|
|
99
99
|
npx vercel-deploy-tool deploy
|
|
100
100
|
# 或使用短别名
|
|
101
101
|
npx vdt deploy
|
|
102
|
+
|
|
103
|
+
# 如需指定自定义 dotenv 文件
|
|
104
|
+
npx vdt deploy --env-path .env.production
|
|
105
|
+
# 等价:设置环境变量再运行
|
|
106
|
+
VERCEL_DEPLOY_TOOL_ENV_PATH=.env.production npx vdt deploy
|
|
107
|
+
# 多文件场景(依赖 dotenvx)
|
|
108
|
+
dotenvx run -f .env.test -f .env.test-2 -- vdt deploy
|
|
102
109
|
```
|
|
103
110
|
|
|
104
111
|
### 方式二:使用 API
|
|
@@ -256,6 +263,13 @@ export default defineConfig({
|
|
|
256
263
|
- 部署到 Vercel
|
|
257
264
|
- 设置自定义域名别名
|
|
258
265
|
|
|
266
|
+
### 环境变量优先级
|
|
267
|
+
|
|
268
|
+
- `--env-path` / `VERCEL_DEPLOY_TOOL_ENV_PATH` 指定的 dotenv(如 `.env.production`)
|
|
269
|
+
- 现有 `process.env`
|
|
270
|
+
- c12 自动加载的 `.env*`
|
|
271
|
+
- 配置默认值
|
|
272
|
+
|
|
259
273
|
## 📋 .gitignore 配置
|
|
260
274
|
|
|
261
275
|
添加以下内容到 `.gitignore`:
|
package/dist/cli.js
CHANGED
|
@@ -9,12 +9,13 @@ import { Command as Command3 } from "commander";
|
|
|
9
9
|
// src/commands/deploy.ts
|
|
10
10
|
import { Command } from "commander";
|
|
11
11
|
import { consola as consola10 } from "consola";
|
|
12
|
+
import { config as dotenvxConfig2 } from "@dotenvx/dotenvx";
|
|
12
13
|
|
|
13
14
|
// src/config/loader.ts
|
|
14
15
|
import { loadConfig as c12LoadConfig } from "c12";
|
|
15
|
-
import { resolve } from "pathe";
|
|
16
16
|
import { consola } from "consola";
|
|
17
17
|
import { isUndefined } from "lodash-es";
|
|
18
|
+
import { config as dotenvxConfig } from "@dotenvx/dotenvx";
|
|
18
19
|
import { printFormat } from "@ruan-cat/utils";
|
|
19
20
|
var CONFIG_NAME = "vercel-deploy-tool";
|
|
20
21
|
var DEFAULT_CONFIG = {
|
|
@@ -25,32 +26,40 @@ var DEFAULT_CONFIG = {
|
|
|
25
26
|
deployTargets: []
|
|
26
27
|
};
|
|
27
28
|
async function loadConfig() {
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
consola.start("\u5F00\u59CB\u8BFB\u53D6\u914D\u7F6E\u6587\u4EF6 vercel-deploy-tool.config.* ...");
|
|
30
|
+
const envPath = process.env.VERCEL_DEPLOY_TOOL_ENV_PATH;
|
|
31
|
+
if (envPath) {
|
|
32
|
+
dotenvxConfig({ path: envPath });
|
|
33
|
+
consola.info(`\u5DF2\u4ECE VERCEL_DEPLOY_TOOL_ENV_PATH \u52A0\u8F7D dotenv: ${envPath}`);
|
|
34
|
+
}
|
|
35
|
+
let config;
|
|
36
|
+
const loaded = await c12LoadConfig({
|
|
37
|
+
cwd: process.cwd(),
|
|
30
38
|
name: CONFIG_NAME,
|
|
31
39
|
dotenv: true,
|
|
32
40
|
defaults: DEFAULT_CONFIG
|
|
33
41
|
});
|
|
42
|
+
config = loaded.config;
|
|
34
43
|
const vercelOrgId = process.env.VERCEL_ORG_ID;
|
|
35
44
|
const vercelProjectId = process.env.VERCEL_PROJECT_ID;
|
|
36
45
|
const vercelToken = process.env.VERCEL_TOKEN;
|
|
37
46
|
if (!isUndefined(vercelOrgId)) {
|
|
38
|
-
|
|
47
|
+
config.vercelOrgId = vercelOrgId;
|
|
39
48
|
}
|
|
40
49
|
if (!isUndefined(vercelProjectId)) {
|
|
41
|
-
|
|
50
|
+
config.vercelProjectId = vercelProjectId;
|
|
42
51
|
}
|
|
43
52
|
if (!isUndefined(vercelToken)) {
|
|
44
|
-
|
|
53
|
+
config.vercelToken = vercelToken;
|
|
45
54
|
}
|
|
46
55
|
consola.success("\u914D\u7F6E\u52A0\u8F7D\u5B8C\u6210");
|
|
47
|
-
consola.box(printFormat(
|
|
48
|
-
return
|
|
56
|
+
consola.box(printFormat(config));
|
|
57
|
+
return config;
|
|
49
58
|
}
|
|
50
|
-
var config = await loadConfig();
|
|
51
59
|
|
|
52
60
|
// src/core/tasks/index.ts
|
|
53
|
-
import
|
|
61
|
+
import fs4 from "fs";
|
|
62
|
+
import { resolve as resolve5 } from "path";
|
|
54
63
|
import { consola as consola9 } from "consola";
|
|
55
64
|
|
|
56
65
|
// src/core/executor.ts
|
|
@@ -105,19 +114,21 @@ var VERCEL_NULL_CONFIG_PATH = "./vercel.null.def.json";
|
|
|
105
114
|
var VERCEL_OUTPUT_STATIC = ".vercel/output/static";
|
|
106
115
|
|
|
107
116
|
// src/core/tasks/link.ts
|
|
117
|
+
import fs from "fs";
|
|
118
|
+
import { resolve } from "path";
|
|
108
119
|
import { spawnSync } from "child_process";
|
|
109
120
|
import { concat } from "lodash-es";
|
|
110
121
|
import { consola as consola2 } from "consola";
|
|
111
122
|
|
|
112
123
|
// src/core/vercel.ts
|
|
113
|
-
function getVercelProjectNameArg(
|
|
114
|
-
return ["--
|
|
124
|
+
function getVercelProjectNameArg(config) {
|
|
125
|
+
return ["--project", config.vercelProjectName];
|
|
115
126
|
}
|
|
116
|
-
function getVercelScopeArg(
|
|
117
|
-
return ["--scope",
|
|
127
|
+
function getVercelScopeArg(config) {
|
|
128
|
+
return ["--scope", config.vercelOrgId];
|
|
118
129
|
}
|
|
119
|
-
function getVercelTokenArg(
|
|
120
|
-
return ["--token",
|
|
130
|
+
function getVercelTokenArg(config) {
|
|
131
|
+
return ["--token", config.vercelToken];
|
|
121
132
|
}
|
|
122
133
|
function getVercelLocalConfigArg() {
|
|
123
134
|
return ["--local-config", VERCEL_NULL_CONFIG_PATH];
|
|
@@ -125,24 +136,37 @@ function getVercelLocalConfigArg() {
|
|
|
125
136
|
function getTargetCWDArg(target) {
|
|
126
137
|
return ["--cwd", target.targetCWD];
|
|
127
138
|
}
|
|
139
|
+
function createVercelSpawnOptions(stdoutMode = "inherit") {
|
|
140
|
+
const base = {
|
|
141
|
+
encoding: "utf8",
|
|
142
|
+
shell: process.platform === "win32"
|
|
143
|
+
};
|
|
144
|
+
if (stdoutMode === "pipe") {
|
|
145
|
+
return { ...base, stdio: ["inherit", "pipe", "inherit"] };
|
|
146
|
+
}
|
|
147
|
+
return { ...base, stdio: "inherit" };
|
|
148
|
+
}
|
|
128
149
|
|
|
129
150
|
// src/core/tasks/link.ts
|
|
130
|
-
function createLinkTask(
|
|
151
|
+
function createLinkTask(config, target) {
|
|
131
152
|
return {
|
|
132
153
|
name: `Link: ${target.targetCWD}`,
|
|
133
154
|
fn: async () => {
|
|
155
|
+
const targetPath = resolve(target.targetCWD);
|
|
156
|
+
if (!fs.existsSync(targetPath)) {
|
|
157
|
+
const err = new Error(`\u76EE\u6807\u76EE\u5F55\u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6784\u5EFA: ${target.targetCWD}`);
|
|
158
|
+
consola2.error(err.message);
|
|
159
|
+
throw err;
|
|
160
|
+
}
|
|
134
161
|
const args = concat(
|
|
135
162
|
["link"],
|
|
136
163
|
["--yes"],
|
|
137
164
|
getTargetCWDArg(target),
|
|
138
|
-
getVercelProjectNameArg(
|
|
139
|
-
getVercelTokenArg(
|
|
165
|
+
getVercelProjectNameArg(config),
|
|
166
|
+
getVercelTokenArg(config)
|
|
140
167
|
);
|
|
141
168
|
consola2.start(`\u5F00\u59CB link \u4EFB\u52A1: ${target.targetCWD}`);
|
|
142
|
-
const result = spawnSync("vercel", args,
|
|
143
|
-
encoding: "utf-8",
|
|
144
|
-
stdio: "inherit"
|
|
145
|
-
});
|
|
169
|
+
const result = spawnSync("vercel", args, createVercelSpawnOptions());
|
|
146
170
|
if (result.error) {
|
|
147
171
|
consola2.error(`link \u4EFB\u52A1\u5931\u8D25: ${target.targetCWD}`);
|
|
148
172
|
throw result.error;
|
|
@@ -154,26 +178,31 @@ function createLinkTask(config2, target) {
|
|
|
154
178
|
}
|
|
155
179
|
|
|
156
180
|
// src/core/tasks/build.ts
|
|
181
|
+
import fs2 from "fs";
|
|
182
|
+
import { resolve as resolve2 } from "path";
|
|
157
183
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
158
184
|
import { concat as concat2 } from "lodash-es";
|
|
159
185
|
import { consola as consola3 } from "consola";
|
|
160
|
-
function createBuildTask(
|
|
186
|
+
function createBuildTask(config, target) {
|
|
161
187
|
return {
|
|
162
188
|
name: `Build: ${target.targetCWD}`,
|
|
163
189
|
fn: async () => {
|
|
190
|
+
const targetPath = resolve2(target.targetCWD);
|
|
191
|
+
if (!fs2.existsSync(targetPath)) {
|
|
192
|
+
const err = new Error(`\u76EE\u6807\u76EE\u5F55\u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6784\u5EFA: ${target.targetCWD}`);
|
|
193
|
+
consola3.error(err.message);
|
|
194
|
+
throw err;
|
|
195
|
+
}
|
|
164
196
|
const args = concat2(
|
|
165
197
|
["build"],
|
|
166
198
|
["--yes"],
|
|
167
199
|
["--prod"],
|
|
168
200
|
getTargetCWDArg(target),
|
|
169
201
|
getVercelLocalConfigArg(),
|
|
170
|
-
getVercelTokenArg(
|
|
202
|
+
getVercelTokenArg(config)
|
|
171
203
|
);
|
|
172
204
|
consola3.start(`\u5F00\u59CB build \u4EFB\u52A1: ${target.targetCWD}`);
|
|
173
|
-
const result = spawnSync2("vercel", args,
|
|
174
|
-
encoding: "utf-8",
|
|
175
|
-
stdio: "inherit"
|
|
176
|
-
});
|
|
205
|
+
const result = spawnSync2("vercel", args, createVercelSpawnOptions());
|
|
177
206
|
if (result.error) {
|
|
178
207
|
consola3.error(`build \u4EFB\u52A1\u5931\u8D25: ${target.targetCWD}`);
|
|
179
208
|
throw result.error;
|
|
@@ -188,8 +217,8 @@ function createBuildTask(config2, target) {
|
|
|
188
217
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
189
218
|
import { consola as consola4 } from "consola";
|
|
190
219
|
import { isUndefined as isUndefined2, isEmpty } from "lodash-es";
|
|
191
|
-
function createAfterBuildTasks(
|
|
192
|
-
const afterBuildTasks =
|
|
220
|
+
function createAfterBuildTasks(config) {
|
|
221
|
+
const afterBuildTasks = config.afterBuildTasks;
|
|
193
222
|
if (isUndefined2(afterBuildTasks) || isEmpty(afterBuildTasks)) {
|
|
194
223
|
return [
|
|
195
224
|
{
|
|
@@ -244,14 +273,14 @@ function createUserCommandTasks(target) {
|
|
|
244
273
|
}
|
|
245
274
|
|
|
246
275
|
// src/core/tasks/copy-dist.ts
|
|
247
|
-
import { resolve as
|
|
276
|
+
import { resolve as resolve3 } from "path";
|
|
248
277
|
import { rmSync, mkdirSync, cpSync } from "fs";
|
|
249
278
|
import { consola as consola6 } from "consola";
|
|
250
279
|
function createCopyDistTasks(target) {
|
|
251
280
|
const targetCWD = target.targetCWD;
|
|
252
281
|
const outputDirectory = target.outputDirectory;
|
|
253
282
|
function joinPath(dir) {
|
|
254
|
-
return
|
|
283
|
+
return resolve3(process.cwd(), targetCWD, dir);
|
|
255
284
|
}
|
|
256
285
|
const pathVercelOutputStatic = joinPath(VERCEL_OUTPUT_STATIC);
|
|
257
286
|
const pathOutputDirectory = joinPath(outputDirectory);
|
|
@@ -286,25 +315,31 @@ function createCopyDistTasks(target) {
|
|
|
286
315
|
}
|
|
287
316
|
|
|
288
317
|
// src/core/tasks/deploy.ts
|
|
318
|
+
import fs3 from "fs";
|
|
319
|
+
import { resolve as resolve4 } from "path";
|
|
289
320
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
290
321
|
import { concat as concat3 } from "lodash-es";
|
|
291
322
|
import { consola as consola7 } from "consola";
|
|
292
|
-
function createDeployTask(
|
|
323
|
+
function createDeployTask(config, target) {
|
|
293
324
|
return {
|
|
294
325
|
name: `Deploy: ${target.targetCWD}`,
|
|
295
326
|
fn: async () => {
|
|
327
|
+
const targetPath = resolve4(target.targetCWD);
|
|
328
|
+
if (!fs3.existsSync(targetPath)) {
|
|
329
|
+
const err = new Error(`\u76EE\u6807\u76EE\u5F55\u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6784\u5EFA: ${target.targetCWD}`);
|
|
330
|
+
consola7.error(err.message);
|
|
331
|
+
throw err;
|
|
332
|
+
}
|
|
296
333
|
const args = concat3(
|
|
297
334
|
["deploy"],
|
|
298
335
|
["--yes"],
|
|
299
336
|
["--prebuilt"],
|
|
300
337
|
["--prod"],
|
|
301
338
|
getTargetCWDArg(target),
|
|
302
|
-
getVercelTokenArg(
|
|
339
|
+
getVercelTokenArg(config)
|
|
303
340
|
);
|
|
304
341
|
consola7.start(`\u5F00\u59CB\u90E8\u7F72\u4EFB\u52A1: ${target.targetCWD}`);
|
|
305
|
-
const result = spawnSync5("vercel", args,
|
|
306
|
-
encoding: "utf-8"
|
|
307
|
-
});
|
|
342
|
+
const result = spawnSync5("vercel", args, createVercelSpawnOptions("pipe"));
|
|
308
343
|
if (result.error) {
|
|
309
344
|
consola7.error(`\u90E8\u7F72\u5931\u8D25\u4E86: ${target.targetCWD}`);
|
|
310
345
|
consola7.error(result.error);
|
|
@@ -322,16 +357,13 @@ function createDeployTask(config2, target) {
|
|
|
322
357
|
import { spawnSync as spawnSync6 } from "child_process";
|
|
323
358
|
import { concat as concat4 } from "lodash-es";
|
|
324
359
|
import { consola as consola8 } from "consola";
|
|
325
|
-
function createAliasTask(
|
|
360
|
+
function createAliasTask(config, vercelUrl, userUrl) {
|
|
326
361
|
return {
|
|
327
362
|
name: `Alias: ${userUrl}`,
|
|
328
363
|
fn: async () => {
|
|
329
|
-
const args = concat4(["alias", "set", vercelUrl, userUrl], getVercelTokenArg(
|
|
364
|
+
const args = concat4(["alias", "set", vercelUrl, userUrl], getVercelTokenArg(config), getVercelScopeArg(config));
|
|
330
365
|
consola8.start(`\u5F00\u59CB\u522B\u540D\u4EFB\u52A1: ${userUrl}`);
|
|
331
|
-
const result = spawnSync6("vercel", args,
|
|
332
|
-
encoding: "utf-8",
|
|
333
|
-
stdio: "inherit"
|
|
334
|
-
});
|
|
366
|
+
const result = spawnSync6("vercel", args, createVercelSpawnOptions());
|
|
335
367
|
if (result.error) {
|
|
336
368
|
consola8.error(`\u522B\u540D\u4EFB\u52A1\u5931\u8D25: ${userUrl}`);
|
|
337
369
|
throw result.error;
|
|
@@ -345,19 +377,31 @@ function createAliasTask(config2, vercelUrl, userUrl) {
|
|
|
345
377
|
|
|
346
378
|
// src/core/tasks/index.ts
|
|
347
379
|
async function generateVercelNullConfig() {
|
|
348
|
-
|
|
380
|
+
fs4.writeFileSync(VERCEL_NULL_CONFIG_PATH, JSON.stringify(VERCEL_NULL_CONFIG, null, 2));
|
|
349
381
|
consola9.success(`\u751F\u6210 Vercel \u7A7A\u914D\u7F6E\u6587\u4EF6: ${VERCEL_NULL_CONFIG_PATH}`);
|
|
350
382
|
}
|
|
351
|
-
async function executeDeploymentWorkflow(
|
|
383
|
+
async function executeDeploymentWorkflow(config) {
|
|
352
384
|
await generateVercelNullConfig();
|
|
353
|
-
const { deployTargets } =
|
|
385
|
+
const { deployTargets } = config;
|
|
386
|
+
const availableTargets = deployTargets.filter((target) => {
|
|
387
|
+
const targetPath = resolve5(target.targetCWD);
|
|
388
|
+
if (!fs4.existsSync(targetPath)) {
|
|
389
|
+
consola9.warn(`\u76EE\u6807\u76EE\u5F55\u4E0D\u5B58\u5728\uFF0C\u5DF2\u8DF3\u8FC7: ${target.targetCWD}`);
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
return true;
|
|
393
|
+
});
|
|
394
|
+
if (availableTargets.length === 0) {
|
|
395
|
+
consola9.error("\u6CA1\u6709\u53EF\u7528\u7684\u90E8\u7F72\u76EE\u6807\uFF0C\u8BF7\u5148\u6784\u5EFA\u4EA7\u7269");
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
354
398
|
await task("Vercel \u90E8\u7F72\u5DE5\u4F5C\u6D41", async ({ task: task2 }) => {
|
|
355
399
|
await task2("1. Link \u9879\u76EE", async () => {
|
|
356
|
-
const linkTasks =
|
|
400
|
+
const linkTasks = availableTargets.map((target) => createLinkTask(config, target));
|
|
357
401
|
await task2.group((task3) => linkTasks.map((t) => task3(t.name, t.fn)));
|
|
358
402
|
});
|
|
359
403
|
await task2("2. \u6784\u5EFA\u9879\u76EE", async () => {
|
|
360
|
-
const buildTasks =
|
|
404
|
+
const buildTasks = availableTargets.filter(isNeedVercelBuild).map((target) => createBuildTask(config, target));
|
|
361
405
|
if (buildTasks.length === 0) {
|
|
362
406
|
consola9.warn("\u6CA1\u6709\u9700\u8981\u6267\u884C build \u7684\u76EE\u6807");
|
|
363
407
|
return;
|
|
@@ -365,11 +409,11 @@ async function executeDeploymentWorkflow(config2) {
|
|
|
365
409
|
await task2.group((task3) => buildTasks.map((t) => task3(t.name, t.fn)));
|
|
366
410
|
});
|
|
367
411
|
await task2("3. \u6267\u884C AfterBuild \u4EFB\u52A1", async () => {
|
|
368
|
-
const afterBuildTasks = createAfterBuildTasks(
|
|
412
|
+
const afterBuildTasks = createAfterBuildTasks(config);
|
|
369
413
|
await executeSequential("AfterBuild", afterBuildTasks);
|
|
370
414
|
});
|
|
371
415
|
await task2("4. \u6267\u884C\u7528\u6237\u547D\u4EE4\u4E0E\u6587\u4EF6\u590D\u5236", async () => {
|
|
372
|
-
const targetTasks =
|
|
416
|
+
const targetTasks = availableTargets.map((target) => ({
|
|
373
417
|
name: `\u5904\u7406\u76EE\u6807: ${target.targetCWD}`,
|
|
374
418
|
fn: async () => {
|
|
375
419
|
if (!isDeployTargetWithUserCommands(target)) {
|
|
@@ -389,14 +433,14 @@ async function executeDeploymentWorkflow(config2) {
|
|
|
389
433
|
await task2.group((task3) => targetTasks.map((t) => task3(t.name, t.fn)));
|
|
390
434
|
});
|
|
391
435
|
await task2("5. \u90E8\u7F72\u4E0E\u8BBE\u7F6E\u522B\u540D", async () => {
|
|
392
|
-
const deployAliasTasks =
|
|
436
|
+
const deployAliasTasks = availableTargets.map((target) => ({
|
|
393
437
|
name: `\u90E8\u7F72\u4E0E\u522B\u540D: ${target.targetCWD}`,
|
|
394
438
|
fn: async () => {
|
|
395
|
-
const deployTask = createDeployTask(
|
|
439
|
+
const deployTask = createDeployTask(config, target);
|
|
396
440
|
const deployResult = await task2(deployTask.name, deployTask.fn);
|
|
397
441
|
const vercelUrl = deployResult.result;
|
|
398
442
|
if (target.url && target.url.length > 0) {
|
|
399
|
-
const aliasTasks = target.url.map((userUrl) => createAliasTask(
|
|
443
|
+
const aliasTasks = target.url.map((userUrl) => createAliasTask(config, vercelUrl, userUrl));
|
|
400
444
|
await task2.group((task3) => aliasTasks.map((t) => task3(t.name, t.fn)));
|
|
401
445
|
} else {
|
|
402
446
|
consola9.warn(`\u76EE\u6807 ${target.targetCWD} \u6CA1\u6709\u914D\u7F6E\u522B\u540D`);
|
|
@@ -412,12 +456,17 @@ async function executeDeploymentWorkflow(config2) {
|
|
|
412
456
|
// src/commands/deploy.ts
|
|
413
457
|
function createDeployCommand() {
|
|
414
458
|
const command = new Command("deploy");
|
|
415
|
-
command.description("\u90E8\u7F72\u9879\u76EE\u5230 Vercel").action(async () => {
|
|
459
|
+
command.description("\u90E8\u7F72\u9879\u76EE\u5230 Vercel").option("--env-path <path>", "\u6307\u5B9A dotenv \u6587\u4EF6\u8DEF\u5F84\uFF0C\u7528\u4E8E\u8986\u76D6\u9ED8\u8BA4\u73AF\u5883\u53D8\u91CF").action(async (options) => {
|
|
416
460
|
try {
|
|
461
|
+
if (options?.envPath) {
|
|
462
|
+
process.env.VERCEL_DEPLOY_TOOL_ENV_PATH = options.envPath;
|
|
463
|
+
dotenvxConfig2({ path: options.envPath });
|
|
464
|
+
consola10.info(`\u5DF2\u4ECE --env-path \u52A0\u8F7D\u73AF\u5883\u53D8\u91CF: ${options.envPath}`);
|
|
465
|
+
}
|
|
417
466
|
consola10.start("\u5F00\u59CB\u52A0\u8F7D\u914D\u7F6E...");
|
|
418
|
-
const
|
|
467
|
+
const config = await loadConfig();
|
|
419
468
|
consola10.start("\u5F00\u59CB\u6267\u884C\u90E8\u7F72\u5DE5\u4F5C\u6D41...");
|
|
420
|
-
await executeDeploymentWorkflow(
|
|
469
|
+
await executeDeploymentWorkflow(config);
|
|
421
470
|
consola10.success("\u90E8\u7F72\u5B8C\u6210\uFF01");
|
|
422
471
|
} catch (error) {
|
|
423
472
|
consola10.error("\u90E8\u7F72\u5931\u8D25:");
|
|
@@ -520,4 +569,10 @@ program.on("--help", () => {
|
|
|
520
569
|
console.log(" VERCEL_PROJECT_ID - Vercel \u9879\u76EE ID");
|
|
521
570
|
console.log("");
|
|
522
571
|
});
|
|
523
|
-
|
|
572
|
+
async function main() {
|
|
573
|
+
await program.parseAsync();
|
|
574
|
+
}
|
|
575
|
+
main().catch((error) => {
|
|
576
|
+
console.error(error);
|
|
577
|
+
process.exit(1);
|
|
578
|
+
});
|
package/dist/index.d.ts
CHANGED
|
@@ -112,7 +112,12 @@ declare function defineConfig(config: VercelDeployConfig): VercelDeployConfig;
|
|
|
112
112
|
/**
|
|
113
113
|
* 异步加载配置(工厂函数模式)
|
|
114
114
|
* @description
|
|
115
|
-
*
|
|
115
|
+
* 从约定俗成的配置处,获得用户配置文件。不会在模块导入时自动执行,避免 top-level await 引起的执行时警告。
|
|
116
|
+
*
|
|
117
|
+
* 环境变量优先级:
|
|
118
|
+
* 1) 命令行传入的 env-path(通过 VERCEL_DEPLOY_TOOL_ENV_PATH 或 deploy 命令参数注入)
|
|
119
|
+
* 2) Node 进程已有的 process.env
|
|
120
|
+
* 3) c12 默认加载的 .env* 文件
|
|
116
121
|
*
|
|
117
122
|
* @example
|
|
118
123
|
* ```ts
|
|
@@ -121,29 +126,17 @@ declare function defineConfig(config: VercelDeployConfig): VercelDeployConfig;
|
|
|
121
126
|
*/
|
|
122
127
|
declare function loadConfig(): Promise<VercelDeployConfig>;
|
|
123
128
|
/**
|
|
124
|
-
*
|
|
125
|
-
* @description
|
|
126
|
-
* 这是混合模式的一部分,提供默认的配置实例
|
|
127
|
-
*
|
|
128
|
-
* @example
|
|
129
|
-
* ```ts
|
|
130
|
-
* import { config } from "@ruan-cat/vercel-deploy-tool/config/loader";
|
|
131
|
-
* console.log(config.vercelProjectName);
|
|
132
|
-
* ```
|
|
133
|
-
*/
|
|
134
|
-
declare const config: VercelDeployConfig;
|
|
135
|
-
/**
|
|
136
|
-
* 导出配置获取函数
|
|
129
|
+
* 导出配置获取函数(带缓存)
|
|
137
130
|
* @description
|
|
138
|
-
*
|
|
131
|
+
* 首次调用会触发加载,后续复用结果。避免 top-level await。
|
|
139
132
|
*
|
|
140
133
|
* @example
|
|
141
134
|
* ```ts
|
|
142
135
|
* import { getConfig } from "@ruan-cat/vercel-deploy-tool";
|
|
143
|
-
* const config = getConfig();
|
|
136
|
+
* const config = await getConfig();
|
|
144
137
|
* ```
|
|
145
138
|
*/
|
|
146
|
-
declare function getConfig(): VercelDeployConfig
|
|
139
|
+
declare function getConfig(): Promise<VercelDeployConfig>;
|
|
147
140
|
|
|
148
141
|
/**
|
|
149
142
|
* 执行 Vercel 部署工作流
|
|
@@ -251,4 +244,4 @@ declare function createDeployCommand(): Command;
|
|
|
251
244
|
*/
|
|
252
245
|
declare function createInitCommand(): Command;
|
|
253
246
|
|
|
254
|
-
export { type DeployTarget, type DeployTargetBase, type DeployTargetType, type DeployTargetWithUserCommands, VERCEL_NULL_CONFIG, VERCEL_NULL_CONFIG_PATH, VERCEL_OUTPUT_STATIC, type VercelDeployConfig,
|
|
247
|
+
export { type DeployTarget, type DeployTargetBase, type DeployTargetType, type DeployTargetWithUserCommands, VERCEL_NULL_CONFIG, VERCEL_NULL_CONFIG_PATH, VERCEL_OUTPUT_STATIC, type VercelDeployConfig, createDeployCommand, createInitCommand, defineConfig, executeDeploymentWorkflow, getConfig, getIsCopyDist, isDeployTargetBase, isDeployTargetWithUserCommands, isNeedVercelBuild, loadConfig };
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// src/config/define-config.ts
|
|
2
|
-
function defineConfig(
|
|
3
|
-
return
|
|
2
|
+
function defineConfig(config) {
|
|
3
|
+
return config;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
// src/config/loader.ts
|
|
7
7
|
import { loadConfig as c12LoadConfig } from "c12";
|
|
8
|
-
import { resolve } from "pathe";
|
|
9
8
|
import { consola } from "consola";
|
|
10
9
|
import { isUndefined } from "lodash-es";
|
|
10
|
+
import { config as dotenvxConfig } from "@dotenvx/dotenvx";
|
|
11
11
|
import { printFormat } from "@ruan-cat/utils";
|
|
12
12
|
var CONFIG_NAME = "vercel-deploy-tool";
|
|
13
13
|
var DEFAULT_CONFIG = {
|
|
@@ -18,35 +18,47 @@ var DEFAULT_CONFIG = {
|
|
|
18
18
|
deployTargets: []
|
|
19
19
|
};
|
|
20
20
|
async function loadConfig() {
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
consola.start("\u5F00\u59CB\u8BFB\u53D6\u914D\u7F6E\u6587\u4EF6 vercel-deploy-tool.config.* ...");
|
|
22
|
+
const envPath = process.env.VERCEL_DEPLOY_TOOL_ENV_PATH;
|
|
23
|
+
if (envPath) {
|
|
24
|
+
dotenvxConfig({ path: envPath });
|
|
25
|
+
consola.info(`\u5DF2\u4ECE VERCEL_DEPLOY_TOOL_ENV_PATH \u52A0\u8F7D dotenv: ${envPath}`);
|
|
26
|
+
}
|
|
27
|
+
let config;
|
|
28
|
+
const loaded = await c12LoadConfig({
|
|
29
|
+
cwd: process.cwd(),
|
|
23
30
|
name: CONFIG_NAME,
|
|
24
31
|
dotenv: true,
|
|
25
32
|
defaults: DEFAULT_CONFIG
|
|
26
33
|
});
|
|
34
|
+
config = loaded.config;
|
|
27
35
|
const vercelOrgId = process.env.VERCEL_ORG_ID;
|
|
28
36
|
const vercelProjectId = process.env.VERCEL_PROJECT_ID;
|
|
29
37
|
const vercelToken = process.env.VERCEL_TOKEN;
|
|
30
38
|
if (!isUndefined(vercelOrgId)) {
|
|
31
|
-
|
|
39
|
+
config.vercelOrgId = vercelOrgId;
|
|
32
40
|
}
|
|
33
41
|
if (!isUndefined(vercelProjectId)) {
|
|
34
|
-
|
|
42
|
+
config.vercelProjectId = vercelProjectId;
|
|
35
43
|
}
|
|
36
44
|
if (!isUndefined(vercelToken)) {
|
|
37
|
-
|
|
45
|
+
config.vercelToken = vercelToken;
|
|
38
46
|
}
|
|
39
47
|
consola.success("\u914D\u7F6E\u52A0\u8F7D\u5B8C\u6210");
|
|
40
|
-
consola.box(printFormat(
|
|
41
|
-
return config2;
|
|
42
|
-
}
|
|
43
|
-
var config = await loadConfig();
|
|
44
|
-
function getConfig() {
|
|
48
|
+
consola.box(printFormat(config));
|
|
45
49
|
return config;
|
|
46
50
|
}
|
|
51
|
+
var cachedConfigPromise = null;
|
|
52
|
+
async function getConfig() {
|
|
53
|
+
if (!cachedConfigPromise) {
|
|
54
|
+
cachedConfigPromise = loadConfig();
|
|
55
|
+
}
|
|
56
|
+
return cachedConfigPromise;
|
|
57
|
+
}
|
|
47
58
|
|
|
48
59
|
// src/core/tasks/index.ts
|
|
49
|
-
import
|
|
60
|
+
import fs4 from "fs";
|
|
61
|
+
import { resolve as resolve5 } from "path";
|
|
50
62
|
import { consola as consola9 } from "consola";
|
|
51
63
|
|
|
52
64
|
// src/core/executor.ts
|
|
@@ -104,19 +116,21 @@ var VERCEL_NULL_CONFIG_PATH = "./vercel.null.def.json";
|
|
|
104
116
|
var VERCEL_OUTPUT_STATIC = ".vercel/output/static";
|
|
105
117
|
|
|
106
118
|
// src/core/tasks/link.ts
|
|
119
|
+
import fs from "fs";
|
|
120
|
+
import { resolve } from "path";
|
|
107
121
|
import { spawnSync } from "child_process";
|
|
108
122
|
import { concat } from "lodash-es";
|
|
109
123
|
import { consola as consola2 } from "consola";
|
|
110
124
|
|
|
111
125
|
// src/core/vercel.ts
|
|
112
|
-
function getVercelProjectNameArg(
|
|
113
|
-
return ["--
|
|
126
|
+
function getVercelProjectNameArg(config) {
|
|
127
|
+
return ["--project", config.vercelProjectName];
|
|
114
128
|
}
|
|
115
|
-
function getVercelScopeArg(
|
|
116
|
-
return ["--scope",
|
|
129
|
+
function getVercelScopeArg(config) {
|
|
130
|
+
return ["--scope", config.vercelOrgId];
|
|
117
131
|
}
|
|
118
|
-
function getVercelTokenArg(
|
|
119
|
-
return ["--token",
|
|
132
|
+
function getVercelTokenArg(config) {
|
|
133
|
+
return ["--token", config.vercelToken];
|
|
120
134
|
}
|
|
121
135
|
function getVercelLocalConfigArg() {
|
|
122
136
|
return ["--local-config", VERCEL_NULL_CONFIG_PATH];
|
|
@@ -124,24 +138,37 @@ function getVercelLocalConfigArg() {
|
|
|
124
138
|
function getTargetCWDArg(target) {
|
|
125
139
|
return ["--cwd", target.targetCWD];
|
|
126
140
|
}
|
|
141
|
+
function createVercelSpawnOptions(stdoutMode = "inherit") {
|
|
142
|
+
const base = {
|
|
143
|
+
encoding: "utf8",
|
|
144
|
+
shell: process.platform === "win32"
|
|
145
|
+
};
|
|
146
|
+
if (stdoutMode === "pipe") {
|
|
147
|
+
return { ...base, stdio: ["inherit", "pipe", "inherit"] };
|
|
148
|
+
}
|
|
149
|
+
return { ...base, stdio: "inherit" };
|
|
150
|
+
}
|
|
127
151
|
|
|
128
152
|
// src/core/tasks/link.ts
|
|
129
|
-
function createLinkTask(
|
|
153
|
+
function createLinkTask(config, target) {
|
|
130
154
|
return {
|
|
131
155
|
name: `Link: ${target.targetCWD}`,
|
|
132
156
|
fn: async () => {
|
|
157
|
+
const targetPath = resolve(target.targetCWD);
|
|
158
|
+
if (!fs.existsSync(targetPath)) {
|
|
159
|
+
const err = new Error(`\u76EE\u6807\u76EE\u5F55\u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6784\u5EFA: ${target.targetCWD}`);
|
|
160
|
+
consola2.error(err.message);
|
|
161
|
+
throw err;
|
|
162
|
+
}
|
|
133
163
|
const args = concat(
|
|
134
164
|
["link"],
|
|
135
165
|
["--yes"],
|
|
136
166
|
getTargetCWDArg(target),
|
|
137
|
-
getVercelProjectNameArg(
|
|
138
|
-
getVercelTokenArg(
|
|
167
|
+
getVercelProjectNameArg(config),
|
|
168
|
+
getVercelTokenArg(config)
|
|
139
169
|
);
|
|
140
170
|
consola2.start(`\u5F00\u59CB link \u4EFB\u52A1: ${target.targetCWD}`);
|
|
141
|
-
const result = spawnSync("vercel", args,
|
|
142
|
-
encoding: "utf-8",
|
|
143
|
-
stdio: "inherit"
|
|
144
|
-
});
|
|
171
|
+
const result = spawnSync("vercel", args, createVercelSpawnOptions());
|
|
145
172
|
if (result.error) {
|
|
146
173
|
consola2.error(`link \u4EFB\u52A1\u5931\u8D25: ${target.targetCWD}`);
|
|
147
174
|
throw result.error;
|
|
@@ -153,26 +180,31 @@ function createLinkTask(config2, target) {
|
|
|
153
180
|
}
|
|
154
181
|
|
|
155
182
|
// src/core/tasks/build.ts
|
|
183
|
+
import fs2 from "fs";
|
|
184
|
+
import { resolve as resolve2 } from "path";
|
|
156
185
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
157
186
|
import { concat as concat2 } from "lodash-es";
|
|
158
187
|
import { consola as consola3 } from "consola";
|
|
159
|
-
function createBuildTask(
|
|
188
|
+
function createBuildTask(config, target) {
|
|
160
189
|
return {
|
|
161
190
|
name: `Build: ${target.targetCWD}`,
|
|
162
191
|
fn: async () => {
|
|
192
|
+
const targetPath = resolve2(target.targetCWD);
|
|
193
|
+
if (!fs2.existsSync(targetPath)) {
|
|
194
|
+
const err = new Error(`\u76EE\u6807\u76EE\u5F55\u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6784\u5EFA: ${target.targetCWD}`);
|
|
195
|
+
consola3.error(err.message);
|
|
196
|
+
throw err;
|
|
197
|
+
}
|
|
163
198
|
const args = concat2(
|
|
164
199
|
["build"],
|
|
165
200
|
["--yes"],
|
|
166
201
|
["--prod"],
|
|
167
202
|
getTargetCWDArg(target),
|
|
168
203
|
getVercelLocalConfigArg(),
|
|
169
|
-
getVercelTokenArg(
|
|
204
|
+
getVercelTokenArg(config)
|
|
170
205
|
);
|
|
171
206
|
consola3.start(`\u5F00\u59CB build \u4EFB\u52A1: ${target.targetCWD}`);
|
|
172
|
-
const result = spawnSync2("vercel", args,
|
|
173
|
-
encoding: "utf-8",
|
|
174
|
-
stdio: "inherit"
|
|
175
|
-
});
|
|
207
|
+
const result = spawnSync2("vercel", args, createVercelSpawnOptions());
|
|
176
208
|
if (result.error) {
|
|
177
209
|
consola3.error(`build \u4EFB\u52A1\u5931\u8D25: ${target.targetCWD}`);
|
|
178
210
|
throw result.error;
|
|
@@ -187,8 +219,8 @@ function createBuildTask(config2, target) {
|
|
|
187
219
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
188
220
|
import { consola as consola4 } from "consola";
|
|
189
221
|
import { isUndefined as isUndefined2, isEmpty } from "lodash-es";
|
|
190
|
-
function createAfterBuildTasks(
|
|
191
|
-
const afterBuildTasks =
|
|
222
|
+
function createAfterBuildTasks(config) {
|
|
223
|
+
const afterBuildTasks = config.afterBuildTasks;
|
|
192
224
|
if (isUndefined2(afterBuildTasks) || isEmpty(afterBuildTasks)) {
|
|
193
225
|
return [
|
|
194
226
|
{
|
|
@@ -243,14 +275,14 @@ function createUserCommandTasks(target) {
|
|
|
243
275
|
}
|
|
244
276
|
|
|
245
277
|
// src/core/tasks/copy-dist.ts
|
|
246
|
-
import { resolve as
|
|
278
|
+
import { resolve as resolve3 } from "path";
|
|
247
279
|
import { rmSync, mkdirSync, cpSync } from "fs";
|
|
248
280
|
import { consola as consola6 } from "consola";
|
|
249
281
|
function createCopyDistTasks(target) {
|
|
250
282
|
const targetCWD = target.targetCWD;
|
|
251
283
|
const outputDirectory = target.outputDirectory;
|
|
252
284
|
function joinPath(dir) {
|
|
253
|
-
return
|
|
285
|
+
return resolve3(process.cwd(), targetCWD, dir);
|
|
254
286
|
}
|
|
255
287
|
const pathVercelOutputStatic = joinPath(VERCEL_OUTPUT_STATIC);
|
|
256
288
|
const pathOutputDirectory = joinPath(outputDirectory);
|
|
@@ -285,25 +317,31 @@ function createCopyDistTasks(target) {
|
|
|
285
317
|
}
|
|
286
318
|
|
|
287
319
|
// src/core/tasks/deploy.ts
|
|
320
|
+
import fs3 from "fs";
|
|
321
|
+
import { resolve as resolve4 } from "path";
|
|
288
322
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
289
323
|
import { concat as concat3 } from "lodash-es";
|
|
290
324
|
import { consola as consola7 } from "consola";
|
|
291
|
-
function createDeployTask(
|
|
325
|
+
function createDeployTask(config, target) {
|
|
292
326
|
return {
|
|
293
327
|
name: `Deploy: ${target.targetCWD}`,
|
|
294
328
|
fn: async () => {
|
|
329
|
+
const targetPath = resolve4(target.targetCWD);
|
|
330
|
+
if (!fs3.existsSync(targetPath)) {
|
|
331
|
+
const err = new Error(`\u76EE\u6807\u76EE\u5F55\u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6784\u5EFA: ${target.targetCWD}`);
|
|
332
|
+
consola7.error(err.message);
|
|
333
|
+
throw err;
|
|
334
|
+
}
|
|
295
335
|
const args = concat3(
|
|
296
336
|
["deploy"],
|
|
297
337
|
["--yes"],
|
|
298
338
|
["--prebuilt"],
|
|
299
339
|
["--prod"],
|
|
300
340
|
getTargetCWDArg(target),
|
|
301
|
-
getVercelTokenArg(
|
|
341
|
+
getVercelTokenArg(config)
|
|
302
342
|
);
|
|
303
343
|
consola7.start(`\u5F00\u59CB\u90E8\u7F72\u4EFB\u52A1: ${target.targetCWD}`);
|
|
304
|
-
const result = spawnSync5("vercel", args,
|
|
305
|
-
encoding: "utf-8"
|
|
306
|
-
});
|
|
344
|
+
const result = spawnSync5("vercel", args, createVercelSpawnOptions("pipe"));
|
|
307
345
|
if (result.error) {
|
|
308
346
|
consola7.error(`\u90E8\u7F72\u5931\u8D25\u4E86: ${target.targetCWD}`);
|
|
309
347
|
consola7.error(result.error);
|
|
@@ -321,16 +359,13 @@ function createDeployTask(config2, target) {
|
|
|
321
359
|
import { spawnSync as spawnSync6 } from "child_process";
|
|
322
360
|
import { concat as concat4 } from "lodash-es";
|
|
323
361
|
import { consola as consola8 } from "consola";
|
|
324
|
-
function createAliasTask(
|
|
362
|
+
function createAliasTask(config, vercelUrl, userUrl) {
|
|
325
363
|
return {
|
|
326
364
|
name: `Alias: ${userUrl}`,
|
|
327
365
|
fn: async () => {
|
|
328
|
-
const args = concat4(["alias", "set", vercelUrl, userUrl], getVercelTokenArg(
|
|
366
|
+
const args = concat4(["alias", "set", vercelUrl, userUrl], getVercelTokenArg(config), getVercelScopeArg(config));
|
|
329
367
|
consola8.start(`\u5F00\u59CB\u522B\u540D\u4EFB\u52A1: ${userUrl}`);
|
|
330
|
-
const result = spawnSync6("vercel", args,
|
|
331
|
-
encoding: "utf-8",
|
|
332
|
-
stdio: "inherit"
|
|
333
|
-
});
|
|
368
|
+
const result = spawnSync6("vercel", args, createVercelSpawnOptions());
|
|
334
369
|
if (result.error) {
|
|
335
370
|
consola8.error(`\u522B\u540D\u4EFB\u52A1\u5931\u8D25: ${userUrl}`);
|
|
336
371
|
throw result.error;
|
|
@@ -344,19 +379,31 @@ function createAliasTask(config2, vercelUrl, userUrl) {
|
|
|
344
379
|
|
|
345
380
|
// src/core/tasks/index.ts
|
|
346
381
|
async function generateVercelNullConfig() {
|
|
347
|
-
|
|
382
|
+
fs4.writeFileSync(VERCEL_NULL_CONFIG_PATH, JSON.stringify(VERCEL_NULL_CONFIG, null, 2));
|
|
348
383
|
consola9.success(`\u751F\u6210 Vercel \u7A7A\u914D\u7F6E\u6587\u4EF6: ${VERCEL_NULL_CONFIG_PATH}`);
|
|
349
384
|
}
|
|
350
|
-
async function executeDeploymentWorkflow(
|
|
385
|
+
async function executeDeploymentWorkflow(config) {
|
|
351
386
|
await generateVercelNullConfig();
|
|
352
|
-
const { deployTargets } =
|
|
387
|
+
const { deployTargets } = config;
|
|
388
|
+
const availableTargets = deployTargets.filter((target) => {
|
|
389
|
+
const targetPath = resolve5(target.targetCWD);
|
|
390
|
+
if (!fs4.existsSync(targetPath)) {
|
|
391
|
+
consola9.warn(`\u76EE\u6807\u76EE\u5F55\u4E0D\u5B58\u5728\uFF0C\u5DF2\u8DF3\u8FC7: ${target.targetCWD}`);
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
394
|
+
return true;
|
|
395
|
+
});
|
|
396
|
+
if (availableTargets.length === 0) {
|
|
397
|
+
consola9.error("\u6CA1\u6709\u53EF\u7528\u7684\u90E8\u7F72\u76EE\u6807\uFF0C\u8BF7\u5148\u6784\u5EFA\u4EA7\u7269");
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
353
400
|
await task("Vercel \u90E8\u7F72\u5DE5\u4F5C\u6D41", async ({ task: task2 }) => {
|
|
354
401
|
await task2("1. Link \u9879\u76EE", async () => {
|
|
355
|
-
const linkTasks =
|
|
402
|
+
const linkTasks = availableTargets.map((target) => createLinkTask(config, target));
|
|
356
403
|
await task2.group((task3) => linkTasks.map((t) => task3(t.name, t.fn)));
|
|
357
404
|
});
|
|
358
405
|
await task2("2. \u6784\u5EFA\u9879\u76EE", async () => {
|
|
359
|
-
const buildTasks =
|
|
406
|
+
const buildTasks = availableTargets.filter(isNeedVercelBuild).map((target) => createBuildTask(config, target));
|
|
360
407
|
if (buildTasks.length === 0) {
|
|
361
408
|
consola9.warn("\u6CA1\u6709\u9700\u8981\u6267\u884C build \u7684\u76EE\u6807");
|
|
362
409
|
return;
|
|
@@ -364,11 +411,11 @@ async function executeDeploymentWorkflow(config2) {
|
|
|
364
411
|
await task2.group((task3) => buildTasks.map((t) => task3(t.name, t.fn)));
|
|
365
412
|
});
|
|
366
413
|
await task2("3. \u6267\u884C AfterBuild \u4EFB\u52A1", async () => {
|
|
367
|
-
const afterBuildTasks = createAfterBuildTasks(
|
|
414
|
+
const afterBuildTasks = createAfterBuildTasks(config);
|
|
368
415
|
await executeSequential("AfterBuild", afterBuildTasks);
|
|
369
416
|
});
|
|
370
417
|
await task2("4. \u6267\u884C\u7528\u6237\u547D\u4EE4\u4E0E\u6587\u4EF6\u590D\u5236", async () => {
|
|
371
|
-
const targetTasks =
|
|
418
|
+
const targetTasks = availableTargets.map((target) => ({
|
|
372
419
|
name: `\u5904\u7406\u76EE\u6807: ${target.targetCWD}`,
|
|
373
420
|
fn: async () => {
|
|
374
421
|
if (!isDeployTargetWithUserCommands(target)) {
|
|
@@ -388,14 +435,14 @@ async function executeDeploymentWorkflow(config2) {
|
|
|
388
435
|
await task2.group((task3) => targetTasks.map((t) => task3(t.name, t.fn)));
|
|
389
436
|
});
|
|
390
437
|
await task2("5. \u90E8\u7F72\u4E0E\u8BBE\u7F6E\u522B\u540D", async () => {
|
|
391
|
-
const deployAliasTasks =
|
|
438
|
+
const deployAliasTasks = availableTargets.map((target) => ({
|
|
392
439
|
name: `\u90E8\u7F72\u4E0E\u522B\u540D: ${target.targetCWD}`,
|
|
393
440
|
fn: async () => {
|
|
394
|
-
const deployTask = createDeployTask(
|
|
441
|
+
const deployTask = createDeployTask(config, target);
|
|
395
442
|
const deployResult = await task2(deployTask.name, deployTask.fn);
|
|
396
443
|
const vercelUrl = deployResult.result;
|
|
397
444
|
if (target.url && target.url.length > 0) {
|
|
398
|
-
const aliasTasks = target.url.map((userUrl) => createAliasTask(
|
|
445
|
+
const aliasTasks = target.url.map((userUrl) => createAliasTask(config, vercelUrl, userUrl));
|
|
399
446
|
await task2.group((task3) => aliasTasks.map((t) => task3(t.name, t.fn)));
|
|
400
447
|
} else {
|
|
401
448
|
consola9.warn(`\u76EE\u6807 ${target.targetCWD} \u6CA1\u6709\u914D\u7F6E\u522B\u540D`);
|
|
@@ -411,14 +458,20 @@ async function executeDeploymentWorkflow(config2) {
|
|
|
411
458
|
// src/commands/deploy.ts
|
|
412
459
|
import { Command } from "commander";
|
|
413
460
|
import { consola as consola10 } from "consola";
|
|
461
|
+
import { config as dotenvxConfig2 } from "@dotenvx/dotenvx";
|
|
414
462
|
function createDeployCommand() {
|
|
415
463
|
const command = new Command("deploy");
|
|
416
|
-
command.description("\u90E8\u7F72\u9879\u76EE\u5230 Vercel").action(async () => {
|
|
464
|
+
command.description("\u90E8\u7F72\u9879\u76EE\u5230 Vercel").option("--env-path <path>", "\u6307\u5B9A dotenv \u6587\u4EF6\u8DEF\u5F84\uFF0C\u7528\u4E8E\u8986\u76D6\u9ED8\u8BA4\u73AF\u5883\u53D8\u91CF").action(async (options) => {
|
|
417
465
|
try {
|
|
466
|
+
if (options?.envPath) {
|
|
467
|
+
process.env.VERCEL_DEPLOY_TOOL_ENV_PATH = options.envPath;
|
|
468
|
+
dotenvxConfig2({ path: options.envPath });
|
|
469
|
+
consola10.info(`\u5DF2\u4ECE --env-path \u52A0\u8F7D\u73AF\u5883\u53D8\u91CF: ${options.envPath}`);
|
|
470
|
+
}
|
|
418
471
|
consola10.start("\u5F00\u59CB\u52A0\u8F7D\u914D\u7F6E...");
|
|
419
|
-
const
|
|
472
|
+
const config = await loadConfig();
|
|
420
473
|
consola10.start("\u5F00\u59CB\u6267\u884C\u90E8\u7F72\u5DE5\u4F5C\u6D41...");
|
|
421
|
-
await executeDeploymentWorkflow(
|
|
474
|
+
await executeDeploymentWorkflow(config);
|
|
422
475
|
consola10.success("\u90E8\u7F72\u5B8C\u6210\uFF01");
|
|
423
476
|
} catch (error) {
|
|
424
477
|
consola10.error("\u90E8\u7F72\u5931\u8D25:");
|
|
@@ -492,7 +545,6 @@ export {
|
|
|
492
545
|
VERCEL_NULL_CONFIG,
|
|
493
546
|
VERCEL_NULL_CONFIG_PATH,
|
|
494
547
|
VERCEL_OUTPUT_STATIC,
|
|
495
|
-
config,
|
|
496
548
|
createDeployCommand,
|
|
497
549
|
createInitCommand,
|
|
498
550
|
defineConfig,
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -50,5 +50,12 @@ program.on("--help", () => {
|
|
|
50
50
|
console.log("");
|
|
51
51
|
});
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
async function main() {
|
|
54
|
+
// 解析命令行参数(异步,确保子命令的 async action 被执行并正确传递异常)
|
|
55
|
+
await program.parseAsync();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
main().catch((error) => {
|
|
59
|
+
console.error(error);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
});
|
package/src/commands/deploy.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { consola } from "consola";
|
|
3
|
+
import { config as dotenvxConfig } from "@dotenvx/dotenvx";
|
|
3
4
|
import { loadConfig } from "../config/loader";
|
|
4
5
|
import { executeDeploymentWorkflow } from "../core/tasks";
|
|
5
6
|
|
|
@@ -17,23 +18,33 @@ import { executeDeploymentWorkflow } from "../core/tasks";
|
|
|
17
18
|
export function createDeployCommand(): Command {
|
|
18
19
|
const command = new Command("deploy");
|
|
19
20
|
|
|
20
|
-
command
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
command
|
|
22
|
+
.description("部署项目到 Vercel")
|
|
23
|
+
.option("--env-path <path>", "指定 dotenv 文件路径,用于覆盖默认环境变量")
|
|
24
|
+
.action(async (options) => {
|
|
25
|
+
try {
|
|
26
|
+
// 允许部署时显式指定 env 文件
|
|
27
|
+
if (options?.envPath) {
|
|
28
|
+
process.env.VERCEL_DEPLOY_TOOL_ENV_PATH = options.envPath;
|
|
29
|
+
dotenvxConfig({ path: options.envPath });
|
|
30
|
+
consola.info(`已从 --env-path 加载环境变量: ${options.envPath}`);
|
|
31
|
+
}
|
|
23
32
|
|
|
24
|
-
|
|
33
|
+
consola.start("开始加载配置...");
|
|
25
34
|
|
|
26
|
-
|
|
35
|
+
const config = await loadConfig();
|
|
27
36
|
|
|
28
|
-
|
|
37
|
+
consola.start("开始执行部署工作流...");
|
|
29
38
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
await executeDeploymentWorkflow(config);
|
|
40
|
+
|
|
41
|
+
consola.success("部署完成!");
|
|
42
|
+
} catch (error) {
|
|
43
|
+
consola.error("部署失败:");
|
|
44
|
+
consola.error(error);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
37
48
|
|
|
38
49
|
return command;
|
|
39
50
|
}
|
package/src/config/loader.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { loadConfig as c12LoadConfig } from "c12";
|
|
2
|
-
import { resolve } from "pathe";
|
|
3
2
|
import { consola } from "consola";
|
|
4
3
|
import { isUndefined } from "lodash-es";
|
|
4
|
+
import { config as dotenvxConfig } from "@dotenvx/dotenvx";
|
|
5
5
|
import { printFormat } from "@ruan-cat/utils";
|
|
6
6
|
import type { VercelDeployConfig } from "./schema";
|
|
7
7
|
|
|
@@ -20,7 +20,12 @@ const DEFAULT_CONFIG: VercelDeployConfig = {
|
|
|
20
20
|
/**
|
|
21
21
|
* 异步加载配置(工厂函数模式)
|
|
22
22
|
* @description
|
|
23
|
-
*
|
|
23
|
+
* 从约定俗成的配置处,获得用户配置文件。不会在模块导入时自动执行,避免 top-level await 引起的执行时警告。
|
|
24
|
+
*
|
|
25
|
+
* 环境变量优先级:
|
|
26
|
+
* 1) 命令行传入的 env-path(通过 VERCEL_DEPLOY_TOOL_ENV_PATH 或 deploy 命令参数注入)
|
|
27
|
+
* 2) Node 进程已有的 process.env
|
|
28
|
+
* 3) c12 默认加载的 .env* 文件
|
|
24
29
|
*
|
|
25
30
|
* @example
|
|
26
31
|
* ```ts
|
|
@@ -28,13 +33,26 @@ const DEFAULT_CONFIG: VercelDeployConfig = {
|
|
|
28
33
|
* ```
|
|
29
34
|
*/
|
|
30
35
|
export async function loadConfig(): Promise<VercelDeployConfig> {
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
consola.start("开始读取配置文件 vercel-deploy-tool.config.* ...");
|
|
37
|
+
|
|
38
|
+
// 兼容 CLI 的 --env-path,允许指定自定义 dotenv 文件
|
|
39
|
+
const envPath = process.env.VERCEL_DEPLOY_TOOL_ENV_PATH;
|
|
40
|
+
if (envPath) {
|
|
41
|
+
dotenvxConfig({ path: envPath });
|
|
42
|
+
consola.info(`已从 VERCEL_DEPLOY_TOOL_ENV_PATH 加载 dotenv: ${envPath}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let config: VercelDeployConfig | undefined;
|
|
46
|
+
|
|
47
|
+
const loaded = await c12LoadConfig<VercelDeployConfig>({
|
|
48
|
+
cwd: process.cwd(),
|
|
33
49
|
name: CONFIG_NAME,
|
|
34
50
|
dotenv: true,
|
|
35
51
|
defaults: DEFAULT_CONFIG,
|
|
36
52
|
});
|
|
37
53
|
|
|
54
|
+
config = loaded.config;
|
|
55
|
+
|
|
38
56
|
// 环境变量覆盖
|
|
39
57
|
const vercelOrgId = process.env.VERCEL_ORG_ID;
|
|
40
58
|
const vercelProjectId = process.env.VERCEL_PROJECT_ID;
|
|
@@ -56,30 +74,22 @@ export async function loadConfig(): Promise<VercelDeployConfig> {
|
|
|
56
74
|
return config;
|
|
57
75
|
}
|
|
58
76
|
|
|
59
|
-
|
|
60
|
-
* 默认导出:已初始化的配置实例(top-level await)
|
|
61
|
-
* @description
|
|
62
|
-
* 这是混合模式的一部分,提供默认的配置实例
|
|
63
|
-
*
|
|
64
|
-
* @example
|
|
65
|
-
* ```ts
|
|
66
|
-
* import { config } from "@ruan-cat/vercel-deploy-tool/config/loader";
|
|
67
|
-
* console.log(config.vercelProjectName);
|
|
68
|
-
* ```
|
|
69
|
-
*/
|
|
70
|
-
export const config = await loadConfig();
|
|
77
|
+
let cachedConfigPromise: Promise<VercelDeployConfig> | null = null;
|
|
71
78
|
|
|
72
79
|
/**
|
|
73
|
-
*
|
|
80
|
+
* 导出配置获取函数(带缓存)
|
|
74
81
|
* @description
|
|
75
|
-
*
|
|
82
|
+
* 首次调用会触发加载,后续复用结果。避免 top-level await。
|
|
76
83
|
*
|
|
77
84
|
* @example
|
|
78
85
|
* ```ts
|
|
79
86
|
* import { getConfig } from "@ruan-cat/vercel-deploy-tool";
|
|
80
|
-
* const config = getConfig();
|
|
87
|
+
* const config = await getConfig();
|
|
81
88
|
* ```
|
|
82
89
|
*/
|
|
83
|
-
export function getConfig(): VercelDeployConfig {
|
|
84
|
-
|
|
90
|
+
export async function getConfig(): Promise<VercelDeployConfig> {
|
|
91
|
+
if (!cachedConfigPromise) {
|
|
92
|
+
cachedConfigPromise = loadConfig();
|
|
93
|
+
}
|
|
94
|
+
return cachedConfigPromise;
|
|
85
95
|
}
|
package/src/core/tasks/alias.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { spawnSync } from "node:child_process";
|
|
|
2
2
|
import { concat } from "lodash-es";
|
|
3
3
|
import { consola } from "consola";
|
|
4
4
|
import type { VercelDeployConfig } from "../../config/schema";
|
|
5
|
-
import { getVercelTokenArg, getVercelScopeArg } from "../vercel";
|
|
5
|
+
import { createVercelSpawnOptions, getVercelTokenArg, getVercelScopeArg } from "../vercel";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* 创建 Alias 任务
|
|
@@ -21,10 +21,7 @@ export function createAliasTask(config: VercelDeployConfig, vercelUrl: string, u
|
|
|
21
21
|
|
|
22
22
|
consola.start(`开始别名任务: ${userUrl}`);
|
|
23
23
|
|
|
24
|
-
const result = spawnSync("vercel", args,
|
|
25
|
-
encoding: "utf-8",
|
|
26
|
-
stdio: "inherit",
|
|
27
|
-
});
|
|
24
|
+
const result = spawnSync("vercel", args, createVercelSpawnOptions());
|
|
28
25
|
|
|
29
26
|
if (result.error) {
|
|
30
27
|
consola.error(`别名任务失败: ${userUrl}`);
|
package/src/core/tasks/build.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
1
3
|
import { spawnSync } from "node:child_process";
|
|
2
4
|
import { concat } from "lodash-es";
|
|
3
5
|
import { consola } from "consola";
|
|
4
6
|
import type { VercelDeployConfig, DeployTarget } from "../../config/schema";
|
|
5
|
-
import { getVercelTokenArg, getVercelLocalConfigArg, getTargetCWDArg } from "../vercel";
|
|
7
|
+
import { createVercelSpawnOptions, getVercelTokenArg, getVercelLocalConfigArg, getTargetCWDArg } from "../vercel";
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* 创建 Build 任务
|
|
@@ -15,6 +17,14 @@ export function createBuildTask(config: VercelDeployConfig, target: DeployTarget
|
|
|
15
17
|
return {
|
|
16
18
|
name: `Build: ${target.targetCWD}`,
|
|
17
19
|
fn: async () => {
|
|
20
|
+
const targetPath = resolve(target.targetCWD);
|
|
21
|
+
|
|
22
|
+
if (!fs.existsSync(targetPath)) {
|
|
23
|
+
const err = new Error(`目标目录不存在,请先构建: ${target.targetCWD}`);
|
|
24
|
+
consola.error(err.message);
|
|
25
|
+
throw err;
|
|
26
|
+
}
|
|
27
|
+
|
|
18
28
|
const args = concat(
|
|
19
29
|
["build"],
|
|
20
30
|
["--yes"],
|
|
@@ -26,10 +36,7 @@ export function createBuildTask(config: VercelDeployConfig, target: DeployTarget
|
|
|
26
36
|
|
|
27
37
|
consola.start(`开始 build 任务: ${target.targetCWD}`);
|
|
28
38
|
|
|
29
|
-
const result = spawnSync("vercel", args,
|
|
30
|
-
encoding: "utf-8",
|
|
31
|
-
stdio: "inherit",
|
|
32
|
-
});
|
|
39
|
+
const result = spawnSync("vercel", args, createVercelSpawnOptions());
|
|
33
40
|
|
|
34
41
|
if (result.error) {
|
|
35
42
|
consola.error(`build 任务失败: ${target.targetCWD}`);
|
package/src/core/tasks/deploy.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
1
3
|
import { spawnSync } from "node:child_process";
|
|
2
4
|
import { concat } from "lodash-es";
|
|
3
5
|
import { consola } from "consola";
|
|
4
6
|
import type { VercelDeployConfig, DeployTarget } from "../../config/schema";
|
|
5
|
-
import { getVercelTokenArg, getTargetCWDArg } from "../vercel";
|
|
7
|
+
import { createVercelSpawnOptions, getVercelTokenArg, getTargetCWDArg } from "../vercel";
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* 创建 Deploy 任务
|
|
@@ -15,6 +17,14 @@ export function createDeployTask(config: VercelDeployConfig, target: DeployTarge
|
|
|
15
17
|
return {
|
|
16
18
|
name: `Deploy: ${target.targetCWD}`,
|
|
17
19
|
fn: async () => {
|
|
20
|
+
const targetPath = resolve(target.targetCWD);
|
|
21
|
+
|
|
22
|
+
if (!fs.existsSync(targetPath)) {
|
|
23
|
+
const err = new Error(`目标目录不存在,请先构建: ${target.targetCWD}`);
|
|
24
|
+
consola.error(err.message);
|
|
25
|
+
throw err;
|
|
26
|
+
}
|
|
27
|
+
|
|
18
28
|
const args = concat(
|
|
19
29
|
["deploy"],
|
|
20
30
|
["--yes"],
|
|
@@ -26,9 +36,7 @@ export function createDeployTask(config: VercelDeployConfig, target: DeployTarge
|
|
|
26
36
|
|
|
27
37
|
consola.start(`开始部署任务: ${target.targetCWD}`);
|
|
28
38
|
|
|
29
|
-
const result = spawnSync("vercel", args,
|
|
30
|
-
encoding: "utf-8",
|
|
31
|
-
});
|
|
39
|
+
const result = spawnSync("vercel", args, createVercelSpawnOptions("pipe"));
|
|
32
40
|
|
|
33
41
|
if (result.error) {
|
|
34
42
|
consola.error(`部署失败了: ${target.targetCWD}`);
|
package/src/core/tasks/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
2
3
|
import { consola } from "consola";
|
|
3
4
|
import { task, executeSequential } from "../executor";
|
|
4
5
|
import type { VercelDeployConfig } from "../../config/schema";
|
|
@@ -40,17 +41,34 @@ export async function executeDeploymentWorkflow(config: VercelDeployConfig) {
|
|
|
40
41
|
|
|
41
42
|
const { deployTargets } = config;
|
|
42
43
|
|
|
44
|
+
// 过滤不存在的目标目录,避免后续 Vercel CLI 抛出 ENOENT
|
|
45
|
+
const availableTargets = deployTargets.filter((target) => {
|
|
46
|
+
const targetPath = resolve(target.targetCWD);
|
|
47
|
+
|
|
48
|
+
if (!fs.existsSync(targetPath)) {
|
|
49
|
+
consola.warn(`目标目录不存在,已跳过: ${target.targetCWD}`);
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return true;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (availableTargets.length === 0) {
|
|
57
|
+
consola.error("没有可用的部署目标,请先构建产物");
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
43
61
|
await task("Vercel 部署工作流", async ({ task }) => {
|
|
44
62
|
// 1. Link 阶段(并行)
|
|
45
63
|
await task("1. Link 项目", async () => {
|
|
46
|
-
const linkTasks =
|
|
64
|
+
const linkTasks = availableTargets.map((target) => createLinkTask(config, target));
|
|
47
65
|
|
|
48
66
|
await task.group((task) => linkTasks.map((t) => task(t.name, t.fn)));
|
|
49
67
|
});
|
|
50
68
|
|
|
51
69
|
// 2. Build 阶段(并行)
|
|
52
70
|
await task("2. 构建项目", async () => {
|
|
53
|
-
const buildTasks =
|
|
71
|
+
const buildTasks = availableTargets.filter(isNeedVercelBuild).map((target) => createBuildTask(config, target));
|
|
54
72
|
|
|
55
73
|
if (buildTasks.length === 0) {
|
|
56
74
|
consola.warn("没有需要执行 build 的目标");
|
|
@@ -69,7 +87,7 @@ export async function executeDeploymentWorkflow(config: VercelDeployConfig) {
|
|
|
69
87
|
|
|
70
88
|
// 4. UserCommands + CopyDist 阶段(并行目标,串行步骤)
|
|
71
89
|
await task("4. 执行用户命令与文件复制", async () => {
|
|
72
|
-
const targetTasks =
|
|
90
|
+
const targetTasks = availableTargets.map((target) => ({
|
|
73
91
|
name: `处理目标: ${target.targetCWD}`,
|
|
74
92
|
fn: async () => {
|
|
75
93
|
// 如果不是 userCommands 类型,跳过
|
|
@@ -98,7 +116,7 @@ export async function executeDeploymentWorkflow(config: VercelDeployConfig) {
|
|
|
98
116
|
|
|
99
117
|
// 5. Deploy + Alias 阶段(并行目标,串行步骤)
|
|
100
118
|
await task("5. 部署与设置别名", async () => {
|
|
101
|
-
const deployAliasTasks =
|
|
119
|
+
const deployAliasTasks = availableTargets.map((target) => ({
|
|
102
120
|
name: `部署与别名: ${target.targetCWD}`,
|
|
103
121
|
fn: async () => {
|
|
104
122
|
// 5.1 部署
|
package/src/core/tasks/link.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
1
3
|
import { spawnSync } from "node:child_process";
|
|
2
4
|
import { concat } from "lodash-es";
|
|
3
5
|
import { consola } from "consola";
|
|
4
6
|
import type { VercelDeployConfig, DeployTarget } from "../../config/schema";
|
|
5
|
-
import { getVercelProjectNameArg, getVercelTokenArg, getTargetCWDArg } from "../vercel";
|
|
7
|
+
import { createVercelSpawnOptions, getVercelProjectNameArg, getVercelTokenArg, getTargetCWDArg } from "../vercel";
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* 创建 Link 任务
|
|
@@ -15,6 +17,14 @@ export function createLinkTask(config: VercelDeployConfig, target: DeployTarget)
|
|
|
15
17
|
return {
|
|
16
18
|
name: `Link: ${target.targetCWD}`,
|
|
17
19
|
fn: async () => {
|
|
20
|
+
const targetPath = resolve(target.targetCWD);
|
|
21
|
+
|
|
22
|
+
if (!fs.existsSync(targetPath)) {
|
|
23
|
+
const err = new Error(`目标目录不存在,请先构建: ${target.targetCWD}`);
|
|
24
|
+
consola.error(err.message);
|
|
25
|
+
throw err;
|
|
26
|
+
}
|
|
27
|
+
|
|
18
28
|
const args = concat(
|
|
19
29
|
["link"],
|
|
20
30
|
["--yes"],
|
|
@@ -25,10 +35,7 @@ export function createLinkTask(config: VercelDeployConfig, target: DeployTarget)
|
|
|
25
35
|
|
|
26
36
|
consola.start(`开始 link 任务: ${target.targetCWD}`);
|
|
27
37
|
|
|
28
|
-
const result = spawnSync("vercel", args,
|
|
29
|
-
encoding: "utf-8",
|
|
30
|
-
stdio: "inherit",
|
|
31
|
-
});
|
|
38
|
+
const result = spawnSync("vercel", args, createVercelSpawnOptions());
|
|
32
39
|
|
|
33
40
|
if (result.error) {
|
|
34
41
|
consola.error(`link 任务失败: ${target.targetCWD}`);
|
package/src/core/vercel.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { VercelDeployConfig, DeployTarget } from "../config/schema";
|
|
2
2
|
import { VERCEL_NULL_CONFIG_PATH } from "../utils/vercel-null-config";
|
|
3
|
+
import type { SpawnSyncOptions } from "node:child_process";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* 获取 Vercel 项目名称参数
|
|
6
7
|
*/
|
|
7
8
|
export function getVercelProjectNameArg(config: VercelDeployConfig): string[] {
|
|
8
|
-
return ["--
|
|
9
|
+
return ["--project", config.vercelProjectName];
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
/**
|
|
@@ -35,3 +36,20 @@ export function getVercelLocalConfigArg(): string[] {
|
|
|
35
36
|
export function getTargetCWDArg(target: DeployTarget): string[] {
|
|
36
37
|
return ["--cwd", target.targetCWD];
|
|
37
38
|
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 统一的 Vercel CLI spawn 配置
|
|
42
|
+
* @param stdoutMode stdout 行为,默认继承终端;需要读取 stdout 时传入 "pipe"
|
|
43
|
+
*/
|
|
44
|
+
export function createVercelSpawnOptions(stdoutMode: "inherit" | "pipe" = "inherit"): SpawnSyncOptions {
|
|
45
|
+
const base: SpawnSyncOptions = {
|
|
46
|
+
encoding: "utf8",
|
|
47
|
+
shell: process.platform === "win32",
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
if (stdoutMode === "pipe") {
|
|
51
|
+
return { ...base, stdio: ["inherit", "pipe", "inherit"] };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return { ...base, stdio: "inherit" };
|
|
55
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -39,7 +39,7 @@ export { defineConfig } from "./config/define-config";
|
|
|
39
39
|
* const config = await loadConfig();
|
|
40
40
|
* ```
|
|
41
41
|
*/
|
|
42
|
-
export { loadConfig, getConfig
|
|
42
|
+
export { loadConfig, getConfig } from "./config/loader";
|
|
43
43
|
|
|
44
44
|
// ==================== 类型定义 ====================
|
|
45
45
|
|