@ry-krystal/kicad-converter
Version:
专业的KiCad符号文件与JSON互转工具
287 lines (274 loc) • 9.35 kB
JavaScript
/**
* KiCad转换器命令行工具主入口
* 提供统一的CLI界面和智能转换选择
*/
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
import { join, extname, basename, dirname } from 'path';
import { fileURLToPath } from 'url';
import { performance } from 'perf_hooks';
// 导入核心转换器
import { convertKiCadToJson, convertJsonToKicad } from '../core/index.js';
// 导入增强版转换器
import { enhancedKicadToJson, enhancedJsonToKicad } from '../enhanced/index.js';
// 导入工具函数
import { parseCliArgs, createLogger } from './utils/index.js';
import { batchCommand } from './commands/batch.js';
import { validateCommand } from './commands/validate.js';
/**
* 显示帮助信息
*/
function showHelp() {
console.log(`
KiCad转换器 - 专业的KiCad符号文件与JSON互转工具
使用方法:
kicad-converter [选项] [输入文件]
选项:
-i, --input <文件> 输入文件路径
-o, --output <文件> 输出文件路径
-m, --mode <模式> 转换模式: auto|to-json|to-kicad (默认: auto)
-e, --engine <引擎> 转换引擎: core|enhanced|auto (默认: auto)
转换选项:
-b, --batch 批量处理模式
-r, --recursive 递归处理子目录
--validate 启用数据验证
--optimize 启用输出优化
--stats 显示详细统计信息
输出选项:
-v, --verbose 详细输出模式
-q, --quiet 静默模式
-h, --help 显示此帮助信息
--version 显示版本信息
示例:
# 自动检测并转换单个文件
kicad-converter input.kicad_sym
# KiCad转JSON (指定输出文件)
kicad-converter -i input.kicad_sym -o output.json
# JSON转KiCad (指定模式)
kicad-converter -m to-kicad input.json
# 批量转换目录
kicad-converter --batch ./symbols/
# 使用增强版引擎并显示统计
kicad-converter -e enhanced --stats input.kicad_sym
# 验证文件
kicad-converter --validate input.kicad_sym
# 递归批量处理子目录
kicad-converter --batch --recursive ./project/symbols/
开发模式 (项目内使用):
npm run convert # 通用转换命令
npm run kicad-to-json # KiCad转JSON
npm run json-to-kicad # JSON转KiCad
npm run batch # 批量处理
npm run validate # 验证文件
npm run stats # 显示统计
`);
}
/**
* 显示版本信息
*/
function showVersion() {
const currentDir = dirname(fileURLToPath(import.meta.url));
const packagePath = join(currentDir, '../../package.json');
try {
const pkg = JSON.parse(readFileSync(packagePath, 'utf8'));
console.log(`KiCad转换器 v${pkg.version}`);
console.log(`Node.js ${process.version}`);
}
catch {
console.log('KiCad转换器');
}
}
/**
* 智能选择转换引擎
*/
function selectEngine(options) {
if (options.engine === 'core')
return 'core';
if (options.engine === 'enhanced')
return 'enhanced';
// 自动选择:复杂操作使用增强版,简单操作使用核心版
if (options.batch || options.optimize || options.stats) {
return 'enhanced';
}
return 'core'; // 默认使用核心版,更稳定
}
/**
* 自动检测文件类型和转换模式
*/
function detectMode(inputFile, mode) {
if (mode === 'to-json')
return 'to-json';
if (mode === 'to-kicad')
return 'to-kicad';
const ext = extname(inputFile).toLowerCase();
if (ext === '.kicad_sym')
return 'to-json';
if (ext === '.json')
return 'to-kicad';
// 默认转换为JSON
return 'to-json';
}
/**
* 生成输出文件路径
*/
function generateOutputPath(inputFile, mode, outputFile) {
if (outputFile)
return outputFile;
const dir = dirname(inputFile);
const name = basename(inputFile, extname(inputFile));
if (mode === 'to-json') {
return join(dir, `${name}.json`);
}
else {
return join(dir, `${name}.kicad_sym`);
}
}
/**
* 单文件转换
*/
async function convertSingleFile(options) {
if (!options.input) {
console.error('错误: 缺少输入文件');
return false;
}
if (!existsSync(options.input)) {
console.error(`错误: 输入文件不存在: ${options.input}`);
return false;
}
const logger = createLogger(options.verbose, options.quiet);
const startTime = performance.now();
try {
// 检测转换模式
const mode = detectMode(options.input, options.mode);
const engine = selectEngine(options);
const outputPath = generateOutputPath(options.input, mode, options.output);
logger.info(`使用${engine === 'enhanced' ? '增强版' : '核心版'}引擎`);
logger.info(`转换模式: ${mode === 'to-json' ? 'KiCad→JSON' : 'JSON→KiCad'}`);
logger.info(`输入文件: ${options.input}`);
logger.info(`输出文件: ${outputPath}`);
// 读取输入文件
const content = readFileSync(options.input, 'utf8');
// 执行转换
let result;
if (mode === 'to-json') {
if (engine === 'enhanced') {
result = await enhancedKicadToJson(content, {
validateOutput: options.validate,
optimizeOutput: options.optimize,
verbose: options.verbose,
quiet: options.quiet
});
}
else {
result = await convertKiCadToJson(content, {
validateOutput: options.validate,
optimizeOutput: options.optimize,
verbose: options.verbose,
quiet: options.quiet
});
}
}
else {
if (engine === 'enhanced') {
result = await enhancedJsonToKicad(content, {
validateOutput: options.validate,
verbose: options.verbose,
quiet: options.quiet
});
}
else {
result = await convertJsonToKicad(content, {
validateOutput: options.validate,
verbose: options.verbose,
quiet: options.quiet
});
}
}
// 检查转换结果
if (!result.success) {
console.error('转换失败:');
result.errors.forEach(err => console.error(` - ${err}`));
return false;
}
// 确保输出目录存在
const outputDir = dirname(outputPath);
if (!existsSync(outputDir)) {
mkdirSync(outputDir, { recursive: true });
}
// 写入输出文件
writeFileSync(outputPath, result.data);
const endTime = performance.now();
const duration = ((endTime - startTime) / 1000).toFixed(2);
logger.success(`转换完成! 耗时: ${duration}秒`);
// 显示统计信息
if (options.stats && result.statistics) {
const stats = result.statistics;
console.log('\\n📊 统计信息:');
console.log(` 处理时间: ${stats.processingTime.toFixed(2)}ms`);
console.log(` 输入大小: ${stats.inputSize} 字节`);
console.log(` 输出大小: ${stats.outputSize} 字节`);
if (stats.symbolCount) {
console.log(` 符号数量: ${stats.symbolCount}`);
console.log(` 引脚数量: ${stats.pinCount}`);
console.log(` 属性数量: ${stats.propertyCount}`);
}
}
// 显示警告
if (result.warnings.length > 0) {
console.log('\\n⚠️ 警告:');
result.warnings.forEach(warn => console.log(` - ${warn}`));
}
return true;
}
catch (error) {
console.error(`转换过程中发生错误: ${error instanceof Error ? error.message : String(error)}`);
return false;
}
}
/**
* 主函数
*/
async function main() {
const options = parseCliArgs();
// 处理特殊命令
if (options.help) {
showHelp();
process.exit(0);
}
if (options.version) {
showVersion();
process.exit(0);
}
// 路由到相应的命令处理器
let success = false;
try {
if (options.batch) {
success = await batchCommand(options);
}
else if (options.validate && !options.input) {
success = await validateCommand(options);
}
else {
success = await convertSingleFile(options);
}
}
catch (error) {
console.error(`执行失败: ${error instanceof Error ? error.message : String(error)}`);
success = false;
}
process.exit(success ? 0 : 1);
}
// 处理未捕获的异常
process.on('uncaughtException', (error) => {
console.error('未捕获的异常:', error);
process.exit(1);
});
process.on('unhandledRejection', (reason, _promise) => {
console.error('未处理的Promise拒绝:', reason);
process.exit(1);
});
// 运行主函数
if (import.meta.url === `file://${process.argv[1]}`) {
main().catch(console.error);
}
export { main };