From 9bfebc17cbf53e935dee83285e1fcaaad4b993a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=AF=9B88?= Date: Wed, 26 Aug 2026 17:06:34 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=20lint=20?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除 node:child_process 的 execSync 导入 - 添加 colors 工具导入用于错误信息着色 - 创建统一的 LintError 类处理 lint 失败错误 - 串行和并行执行模式统一使用 LintError 抛出错误 - 并行执行改为等待全部完成后再汇总失败命令 - 移除并行执行中的进程强制终止逻辑 - 在主入口捕获 LintError 并显示友好的错误信息 - 导出 LintError 类供外部使用 --- scripts/vsh/src/index.ts | 8 ++++- scripts/vsh/src/lint/index.ts | 56 +++++++++++++++++++---------------- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/scripts/vsh/src/index.ts b/scripts/vsh/src/index.ts index ed9eb6c9b..00f711feb 100644 --- a/scripts/vsh/src/index.ts +++ b/scripts/vsh/src/index.ts @@ -6,7 +6,7 @@ import { version } from '../package.json'; import { defineCheckCircularCommand } from './check-circular'; import { defineCheckDepCommand } from './check-dep'; import { defineCodeWorkspaceCommand } from './code-workspace'; -import { defineLintCommand } from './lint'; +import { defineLintCommand, LintError } from './lint'; import { definePubLintCommand } from './publint'; // 命令描述 @@ -57,6 +57,12 @@ async function main(): Promise { await vsh.runMatchedCommand(); } catch (error) { + // lint 检查失败是预期内的结果(格式/规范不达标), + // 直接展示可操作的错误信息,而非笼统的 "unexpected error" + if (error instanceof LintError) { + consola.error(error.message); + process.exit(1); + } consola.error( colors.red('An unexpected error occurred:'), '\n', diff --git a/scripts/vsh/src/lint/index.ts b/scripts/vsh/src/lint/index.ts index e19c8f6fb..6ec9ecf63 100644 --- a/scripts/vsh/src/lint/index.ts +++ b/scripts/vsh/src/lint/index.ts @@ -1,9 +1,8 @@ import type { CAC } from 'cac'; -import { execSync } from 'node:child_process'; import { availableParallelism, freemem } from 'node:os'; -import { execa } from '@vben/node-utils'; +import { colors, execa } from '@vben/node-utils'; interface LintCommandOptions { /** @@ -48,6 +47,21 @@ function formatCommand([file, args]: Command) { return [file, ...args].join(' '); } +/** + * 统一的 lint 失败错误。 + * 相比 execa 的原始错误,会列出所有失败命令并给出修复提示。 + */ +class LintError extends Error { + constructor(failed: Command[]) { + super( + `Lint failed:\n${failed + .map((command) => ` - ${formatCommand(command)}`) + .join('\n')}\n\n运行 ${colors.cyan('vsh lint --format')} 可自动修复。`, + ); + this.name = 'LintError'; + } +} + /** * 串行执行所有命令:一次只运行一个进程。 * 保证一次能看到所有工具的报错(配置低的机器更友好)。 @@ -64,39 +78,29 @@ async function runSerial(commands: Command[]) { } if (failed.length > 0) { - throw new Error( - `Lint failed:\n${failed - .map((command) => ` - ${formatCommand(command)}`) - .join('\n')}`, - ); + throw new LintError(failed); } } /** * 并行执行所有命令:同时启动全部进程。 - * 任一进程失败时,强制结束其余仍在运行的进程,避免产生遗漏进程。 + * 等待全部结束后汇总失败命令,与串行模式保持一致的错误输出。 */ async function runParallel(commands: Command[]) { const subprocesses = commands.map((command) => runCommand(command)); - try { - await Promise.all(subprocesses); - } catch (error) { - for (const subprocess of subprocesses) { - try { - if (process.platform === 'win32' && subprocess.pid) { - execSync(`taskkill /F /T /PID ${subprocess.pid}`, { - stdio: 'ignore', - }); - } else { - subprocess.kill('SIGKILL'); - } - } catch { - // process may have already exited - } + const results = await Promise.allSettled(subprocesses); + + // 汇总所有失败的命令,与串行模式保持一致的错误输出 + const failed: Command[] = []; + results.forEach((result, index) => { + if (result.status === 'rejected') { + failed.push(commands[index]); } - await Promise.allSettled(subprocesses); - throw error; + }); + + if (failed.length > 0) { + throw new LintError(failed); } } @@ -143,4 +147,4 @@ function defineLintCommand(cac: CAC) { .action(runLint); } -export { defineLintCommand }; +export { defineLintCommand, LintError };