170 lines
6.0 KiB
JavaScript
170 lines
6.0 KiB
JavaScript
#!/usr/bin/env node
|
||
'use strict';
|
||
|
||
const { execFileSync } = require('child_process');
|
||
const os = require('os');
|
||
|
||
const TARGETS = {
|
||
addon: {
|
||
label: 'native addon.node',
|
||
version: '25.8.2',
|
||
nextCheck: 'node scripts/load_native_addon.js --json',
|
||
},
|
||
'isolated-vm': {
|
||
label: 'xbs isolated-vm',
|
||
version: '26.3.1',
|
||
nextCheck: 'node scripts/check_xbs_isolated_vm.js --strict --json',
|
||
},
|
||
xbs: {
|
||
label: 'xbs isolated-vm',
|
||
version: '26.3.1',
|
||
nextCheck: 'node scripts/check_xbs_isolated_vm.js --strict --json',
|
||
},
|
||
};
|
||
|
||
function parseArgs(argv) {
|
||
const args = { target: 'addon', requiredVersion: '', json: false, markdown: false };
|
||
for (let i = 2; i < argv.length; i++) {
|
||
const a = argv[i];
|
||
if (a === '--target') args.target = argv[++i] || '';
|
||
else if (a === '--required-version') args.requiredVersion = (argv[++i] || '').replace(/^v/i, '');
|
||
else if (a === '--json') args.json = true;
|
||
else if (a === '--markdown') args.markdown = true;
|
||
else if (a === '--help' || a === '-h') args.help = true;
|
||
else throw new Error('未知参数:' + a);
|
||
}
|
||
if (!args.json && !args.markdown) args.markdown = true;
|
||
return args;
|
||
}
|
||
|
||
function usage() {
|
||
return [
|
||
'用法:',
|
||
' node scripts/check_node_runtime_compat.js --target addon --markdown',
|
||
' node scripts/check_node_runtime_compat.js --target isolated-vm --json',
|
||
' node scripts/check_node_runtime_compat.js --required-version 25.8.2 --markdown',
|
||
'',
|
||
'说明:只检测当前 Node / ABI / nvm 可用性并输出中文恢复建议,不会安装或切换 Node。',
|
||
].join('\n');
|
||
}
|
||
|
||
function tryExec(command, args, options = {}) {
|
||
try {
|
||
return {
|
||
ok: true,
|
||
stdout: execFileSync(command, args, {
|
||
encoding: 'utf8',
|
||
stdio: ['ignore', 'pipe', 'pipe'],
|
||
timeout: options.timeout || 5000,
|
||
}).trim(),
|
||
error: '',
|
||
};
|
||
} catch (error) {
|
||
return { ok: false, stdout: '', error: String(error && error.message ? error.message : error) };
|
||
}
|
||
}
|
||
|
||
function detectNvm() {
|
||
if (process.platform === 'win32') {
|
||
const where = tryExec('where.exe', ['nvm']);
|
||
const version = tryExec('nvm', ['version']);
|
||
const root = tryExec('nvm', ['root']);
|
||
return {
|
||
available: version.ok,
|
||
kind: 'nvm-windows',
|
||
path: where.stdout.split(/\r?\n/).filter(Boolean)[0] || '',
|
||
version: version.stdout,
|
||
root: root.stdout,
|
||
error: version.ok ? '' : (where.error || version.error),
|
||
};
|
||
}
|
||
|
||
const shell = tryExec('bash', ['-lc', 'if command -v nvm >/dev/null 2>&1; then nvm --version; elif [ -s "$NVM_DIR/nvm.sh" ]; then . "$NVM_DIR/nvm.sh" && nvm --version; elif [ -s "$HOME/.nvm/nvm.sh" ]; then . "$HOME/.nvm/nvm.sh" && nvm --version; else exit 127; fi']);
|
||
return {
|
||
available: shell.ok,
|
||
kind: 'nvm-sh',
|
||
path: shell.ok ? '$NVM_DIR/nvm.sh 或 $HOME/.nvm/nvm.sh' : '',
|
||
version: shell.stdout,
|
||
root: '',
|
||
error: shell.error,
|
||
};
|
||
}
|
||
|
||
function buildResult(args) {
|
||
const target = TARGETS[args.target] || null;
|
||
const requiredVersion = args.requiredVersion || (target && target.version) || args.target.replace(/^v/i, '');
|
||
const currentVersion = process.version.replace(/^v/i, '');
|
||
const nvm = detectNvm();
|
||
const label = target ? target.label : '自定义 Node native 组件';
|
||
const nextCheck = target ? target.nextCheck : '';
|
||
return {
|
||
tool: 'check_node_runtime_compat',
|
||
target: args.target,
|
||
label,
|
||
requiredVersion,
|
||
currentVersion,
|
||
currentVersionWithV: process.version,
|
||
versionMatched: currentVersion === requiredVersion,
|
||
nodeAbi: process.versions && process.versions.modules ? process.versions.modules : '',
|
||
platform: process.platform,
|
||
arch: process.arch,
|
||
os: os.platform() + '-' + os.arch(),
|
||
nvm,
|
||
needsUserConfirmation: currentVersion !== requiredVersion,
|
||
installCommands: requiredVersion ? [
|
||
`nvm install ${requiredVersion}`,
|
||
`nvm use ${requiredVersion}`,
|
||
'node -v',
|
||
'node -p "process.versions.modules"',
|
||
nextCheck,
|
||
].filter(Boolean) : [],
|
||
};
|
||
}
|
||
|
||
function renderMarkdown(result) {
|
||
const lines = [];
|
||
lines.push('# Node 版本兼容检查');
|
||
lines.push('');
|
||
lines.push('- 目标组件:' + result.label);
|
||
lines.push('- 需要 Node:v' + result.requiredVersion);
|
||
lines.push('- 当前 Node:' + result.currentVersionWithV);
|
||
lines.push('- 当前 ABI:' + result.nodeAbi);
|
||
lines.push('- 平台:' + result.platform + '-' + result.arch);
|
||
lines.push('- 版本是否匹配:' + (result.versionMatched ? '是' : '否'));
|
||
lines.push('- nvm 是否可用:' + (result.nvm.available ? '是' : '否'));
|
||
if (result.nvm.version) lines.push('- nvm 版本:' + result.nvm.version);
|
||
if (result.nvm.path) lines.push('- nvm 路径:' + result.nvm.path);
|
||
if (result.nvm.root) lines.push('- nvm root:' + result.nvm.root);
|
||
if (!result.versionMatched) {
|
||
lines.push('');
|
||
lines.push('## 处理建议');
|
||
lines.push('');
|
||
lines.push('- 不要直接降级;先让用户确认是否通过 nvm 安装 / 切换兼容 Node。');
|
||
lines.push('- 用户同意后再执行以下命令:');
|
||
lines.push('');
|
||
lines.push('```bash');
|
||
for (const cmd of result.installCommands) lines.push(cmd);
|
||
lines.push('```');
|
||
if (!result.nvm.available) {
|
||
lines.push('');
|
||
lines.push('- 当前未检测到 nvm。先让用户选择自动安装 nvm、手动安装 nvm、提供已安装 nvm 路径,或拒绝安装后再进入降级 / 改选流程。');
|
||
}
|
||
}
|
||
return lines.join('\n') + '\n';
|
||
}
|
||
|
||
try {
|
||
const args = parseArgs(process.argv);
|
||
if (args.help) {
|
||
console.log(usage());
|
||
process.exit(0);
|
||
}
|
||
const result = buildResult(args);
|
||
if (args.json) console.log(JSON.stringify(result, null, 2));
|
||
if (args.markdown) process.stdout.write(renderMarkdown(result));
|
||
} catch (error) {
|
||
console.error(error && error.message ? error.message : String(error));
|
||
console.error(usage());
|
||
process.exit(1);
|
||
}
|