@ranger1/dx 0.1.56 → 0.1.58

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.
@@ -11,17 +11,25 @@ const TARGET_CONFIGS = {
11
11
  front: {
12
12
  configFile: 'vercel.front.json',
13
13
  projectIdEnvVar: 'VERCEL_PROJECT_ID_FRONT',
14
+ deployCwd: 'apps/front',
15
+ deployMode: 'prebuilt',
16
+ prebuiltCwd: '.',
14
17
  },
15
18
  admin: {
16
19
  configFile: 'vercel.admin.json',
17
20
  projectIdEnvVar: 'VERCEL_PROJECT_ID_ADMIN',
21
+ deployCwd: 'apps/admin-front',
22
+ deployMode: 'prebuilt',
18
23
  },
19
24
  'telegram-bot': {
20
25
  configFile: 'vercel.telegram-bot.json',
21
26
  projectIdEnvVar: 'VERCEL_PROJECT_ID_TELEGRAM_BOT',
27
+ deployMode: 'prebuilt',
22
28
  },
23
29
  }
24
30
 
31
+ const ALLOWED_DEPLOY_MODES = ['prebuilt']
32
+
25
33
  const APP_ENV_MAP = {
26
34
  development: 'dev',
27
35
  staging: 'staging',
@@ -51,6 +59,14 @@ function isMissingFilesError(err) {
51
59
  )
52
60
  }
53
61
 
62
+ function isNextPrebuiltMissingPathError(err) {
63
+ const text = collectErrorText(err)
64
+ return (
65
+ text.includes('ENOENT') &&
66
+ (text.includes('next-server.js') || text.includes('/node_modules/.pnpm/'))
67
+ )
68
+ }
69
+
54
70
  function listMissingVarKeys(targetConfigs, token, orgId) {
55
71
  const missing = []
56
72
 
@@ -85,13 +101,9 @@ function listMissingConfigs(targetConfigs, projectRoot) {
85
101
  return missing
86
102
  }
87
103
 
88
- function appendTargetArgs(baseArgs, { cwd, orgId }) {
104
+ function appendTargetArgs(baseArgs, { orgId }) {
89
105
  const args = [...baseArgs]
90
106
 
91
- if (cwd) {
92
- args.push('--cwd', cwd)
93
- }
94
-
95
107
  if (orgId) {
96
108
  args.push('--scope', orgId)
97
109
  }
@@ -99,14 +111,33 @@ function appendTargetArgs(baseArgs, { cwd, orgId }) {
99
111
  return args
100
112
  }
101
113
 
114
+ export function resolveTargetRunCwd(projectRoot, targetConfig) {
115
+ if (!targetConfig?.deployCwd) return projectRoot
116
+ return join(projectRoot, targetConfig.deployCwd)
117
+ }
118
+
119
+ export function resolveTargetDeployMode(targetConfig) {
120
+ if (!targetConfig?.deployMode) return 'prebuilt'
121
+ return targetConfig.deployMode
122
+ }
123
+
124
+ export function isSupportedDeployMode(mode) {
125
+ return ALLOWED_DEPLOY_MODES.includes(mode)
126
+ }
127
+
128
+ export function resolveTargetPrebuiltCwd(projectRoot, targetConfig, runCwd) {
129
+ if (!targetConfig?.prebuiltCwd) return runCwd
130
+ return join(projectRoot, targetConfig.prebuiltCwd)
131
+ }
132
+
102
133
  function maskIdentifier(value) {
103
134
  const raw = String(value || '').trim()
104
135
  if (raw.length <= 10) return raw || '-'
105
136
  return `${raw.slice(0, 6)}...${raw.slice(-4)}`
106
137
  }
107
138
 
108
- function readLinkedProjectContext(projectRoot) {
109
- const path = join(projectRoot, VERCEL_PROJECT_LINK_PATH)
139
+ function readLinkedProjectContext(contextRoot) {
140
+ const path = join(contextRoot, VERCEL_PROJECT_LINK_PATH)
110
141
  if (!existsSync(path)) {
111
142
  return { exists: false, path, orgId: null, projectId: null, parseError: null }
112
143
  }
@@ -132,8 +163,8 @@ function readLinkedProjectContext(projectRoot) {
132
163
  }
133
164
  }
134
165
 
135
- function clearLinkedProjectContext(projectRoot) {
136
- const path = join(projectRoot, VERCEL_PROJECT_LINK_PATH)
166
+ function clearLinkedProjectContext(contextRoot) {
167
+ const path = join(contextRoot, VERCEL_PROJECT_LINK_PATH)
137
168
  rmSync(path, { force: true })
138
169
  }
139
170
 
@@ -191,7 +222,7 @@ export async function deployPrebuiltWithFallback(options) {
191
222
  run = runVercel,
192
223
  cleanupArchiveParts = () => {
193
224
  try {
194
- execSync('rm -f .vercel/source.tgz.part*', { stdio: 'ignore' })
225
+ execSync('rm -f .vercel/source.tgz.part*', { stdio: 'ignore', cwd: cwd || process.cwd() })
195
226
  } catch {
196
227
  // ignore
197
228
  }
@@ -298,7 +329,32 @@ export async function deployToVercel(target, options = {}) {
298
329
  const projectId = process.env[targetConfig.projectIdEnvVar]
299
330
  const configFile = targetConfig.configFile
300
331
  const configPath = join(projectRoot, configFile)
301
- const linkedContext = readLinkedProjectContext(projectRoot)
332
+ const runCwd = resolveTargetRunCwd(projectRoot, targetConfig)
333
+ const deployMode = resolveTargetDeployMode(targetConfig)
334
+ const prebuiltCwd = resolveTargetPrebuiltCwd(projectRoot, targetConfig, runCwd)
335
+
336
+ if (!existsSync(runCwd)) {
337
+ logger.error(`部署目录不存在: target=${t} deployCwd=${targetConfig.deployCwd || '.'} resolved=${runCwd}`)
338
+ process.exitCode = 1
339
+ return
340
+ }
341
+
342
+ if (!isSupportedDeployMode(deployMode)) {
343
+ logger.error(`不支持的部署模式: target=${t} mode=${deployMode}`)
344
+ logger.info(`可用部署模式: ${ALLOWED_DEPLOY_MODES.join(', ')}`)
345
+ process.exitCode = 1
346
+ return
347
+ }
348
+
349
+ if (!existsSync(prebuiltCwd)) {
350
+ logger.error(
351
+ `预构建部署目录不存在: target=${t} prebuiltCwd=${targetConfig.prebuiltCwd || targetConfig.deployCwd || '.'} resolved=${prebuiltCwd}`,
352
+ )
353
+ process.exitCode = 1
354
+ return
355
+ }
356
+
357
+ const linkedContext = readLinkedProjectContext(prebuiltCwd)
302
358
 
303
359
  const linkedMismatch =
304
360
  linkedContext.exists &&
@@ -332,7 +388,7 @@ export async function deployToVercel(target, options = {}) {
332
388
  }
333
389
 
334
390
  logger.info(
335
- `[deploy-context] env=${environment} target=${t} strict=${strictContext ? 1 : 0} org=${maskIdentifier(orgId)} project=${maskIdentifier(projectId)} linked=${linkedContext.exists ? 'yes' : 'no'} token=env`,
391
+ `[deploy-context] env=${environment} target=${t} mode=${deployMode} runCwd=${runCwd} prebuiltCwd=${prebuiltCwd} strict=${strictContext ? 1 : 0} org=${maskIdentifier(orgId)} project=${maskIdentifier(projectId)} linked=${linkedContext.exists ? 'yes' : 'no'} token=env`,
336
392
  )
337
393
 
338
394
  const envVars = {
@@ -363,7 +419,7 @@ export async function deployToVercel(target, options = {}) {
363
419
 
364
420
  try {
365
421
  if (strictContext && process.env.DX_VERCEL_KEEP_LINK !== '1') {
366
- clearLinkedProjectContext(projectRoot)
422
+ clearLinkedProjectContext(prebuiltCwd)
367
423
  }
368
424
 
369
425
  // 第一步:本地构建
@@ -371,7 +427,6 @@ export async function deployToVercel(target, options = {}) {
371
427
  const buildArgs = appendTargetArgs(
372
428
  ['build', '--local-config', configPath, '--yes'],
373
429
  {
374
- cwd: projectRoot,
375
430
  orgId,
376
431
  },
377
432
  )
@@ -381,15 +436,14 @@ export async function deployToVercel(target, options = {}) {
381
436
  buildArgs.push('--prod')
382
437
  }
383
438
 
384
- await run(buildArgs, { env: envVars, cwd: projectRoot })
439
+ await run(buildArgs, { env: envVars, cwd: runCwd })
385
440
  logger.success(`${t} 本地构建成功`)
386
441
 
387
- // 第二步:上传预构建产物
388
442
  logger.step(`部署 ${t} 到 Vercel (${environment})`)
443
+
389
444
  const baseDeployArgs = appendTargetArgs(
390
445
  ['deploy', '--prebuilt', '--local-config', configPath, '--yes'],
391
446
  {
392
- cwd: projectRoot,
393
447
  orgId,
394
448
  },
395
449
  )
@@ -402,7 +456,7 @@ export async function deployToVercel(target, options = {}) {
402
456
  const deployResult = await deployPrebuiltWithFallback({
403
457
  baseArgs: baseDeployArgs,
404
458
  env: envVars,
405
- cwd: projectRoot,
459
+ cwd: prebuiltCwd,
406
460
  run,
407
461
  })
408
462
 
@@ -423,6 +477,11 @@ export async function deployToVercel(target, options = {}) {
423
477
  logger.success(`${t} 部署成功`)
424
478
  }
425
479
  } catch (error) {
480
+ if (deployMode === 'prebuilt' && isNextPrebuiltMissingPathError(error)) {
481
+ logger.error(
482
+ '高优先级提示:检测到 Next.js 预构建产物缺失(next-server.js/node_modules/.pnpm)。请检查 front prebuiltCwd 与构建产物路径是否一致。',
483
+ )
484
+ }
426
485
  const message = error?.message || String(error)
427
486
  logger.error(`${t} 构建或部署失败: ${message}`)
428
487
  process.exitCode = 1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ranger1/dx",
3
- "version": "0.1.56",
3
+ "version": "0.1.58",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {