Files
huachuang/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/scripts/vsh/src/lint/index.ts
2026-07-08 15:08:41 +08:00

134 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { CAC } from 'cac';
import { execSync } from 'node:child_process';
import { availableParallelism, freemem } from 'node:os';
import { execaCommand } from '@vben/node-utils';
interface LintCommandOptions {
/**
* Format lint problem.
*/
format?: boolean;
/**
* Number of threads for oxfmt and oxlint (default: 2).
*/
threads?: number;
}
/**
* CPU 逻辑核心数阈值 4 核。
* - 小于等于该值:视为低配机器,检查命令串行执行,避免瞬时占用飙升。
*/
const CPU_CORE_THRESHOLD = 4;
/**
* 可用内存阈值 8 GB。
* 仅当空余内存大于阈值且当前可用内存大于该值时,
* oxfmt / oxlint 默认线程数才提升到 4否则使用 2。
*/
const FREE_MEMORY_THRESHOLD = 8 * 1024 ** 3;
/**
* 串行执行所有命令:一次只运行一个进程。
* 保证一次能看到所有工具的报错(配置低的机器更友好)。
*/
async function runSerial(commands: string[]) {
const failed: string[] = [];
for (const command of commands) {
try {
await execaCommand(command, { stdio: 'inherit' });
} catch {
failed.push(command);
}
}
if (failed.length > 0) {
throw new Error(
`Lint failed:\n${failed.map((command) => ` - ${command}`).join('\n')}`,
);
}
}
/**
* 并行执行所有命令:同时启动全部进程。
* 任一进程失败时,强制结束其余仍在运行的进程,避免产生遗漏进程。
*/
async function runParallel(commands: string[]) {
const subprocesses = commands.map((command) =>
execaCommand(command, { stdio: 'inherit' }),
);
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
}
}
await Promise.allSettled(subprocesses);
throw error;
}
}
async function runLint({ format, threads }: LintCommandOptions) {
// process.env.FORCE_COLOR = '3';
const cpuCores = availableParallelism();
// CPU 核心数充足且可用内存充足时,默认线程数提升到 4否则维持 2
// 用户通过 --threads 显式指定时优先使用其值。
const defaultThreads =
cpuCores > CPU_CORE_THRESHOLD && freemem() > FREE_MEMORY_THRESHOLD ? 4 : 2;
const threadsArg = ` --threads=${threads || defaultThreads}`;
if (format) {
await execaCommand(`stylelint "**/*.{vue,css,less,scss}" --cache --fix`, {
stdio: 'inherit',
});
await execaCommand(`oxfmt${threadsArg}`, {
stdio: 'inherit',
});
await execaCommand(`oxlint --fix --type-aware${threadsArg}`, {
stdio: 'inherit',
});
await execaCommand(`eslint . --cache --fix`, {
stdio: 'inherit',
});
return;
}
const commands = [
`oxfmt --check${threadsArg}`,
`oxlint --type-aware${threadsArg}`,
`eslint . --cache`,
`stylelint "**/*.{vue,css,less,scss}" --cache`,
];
// 低配机器CPU 核心数较少)串行执行,避免多进程并发导致瞬时占用飙升;
// 高配机器并行执行以缩短整体耗时。
await (cpuCores <= CPU_CORE_THRESHOLD
? runSerial(commands)
: runParallel(commands));
}
function defineLintCommand(cac: CAC) {
cac
.command('lint')
.usage('Batch execute project lint check.')
.option('--format', 'Format lint problem.')
.option('--threads <count>', 'Number of threads for oxfmt and oxlint.')
.action(runLint);
}
export { defineLintCommand };