364 lines
12 KiB
JavaScript
364 lines
12 KiB
JavaScript
#!/usr/bin/env node
|
||
|
||
const { Command } = require('commander');
|
||
const chalk = require('chalk');
|
||
const inquirer = require('inquirer');
|
||
const path = require('path');
|
||
const fs = require('fs-extra');
|
||
const scanner = require('./scanner');
|
||
const docGenerator = require('./doc-generator');
|
||
const sourceExporter = require('./source-exporter');
|
||
|
||
// 软著关键词检测
|
||
const COPYRIGHT_KEYWORDS = [
|
||
'软著', '著作权', '软件著作权', '版权', 'copyright', 'patent', '专利'
|
||
];
|
||
|
||
/**
|
||
* 检测用户输入是否包含软著相关关键词
|
||
* @param {string} input 用户输入
|
||
* @returns {boolean} 是否包含软著关键词
|
||
*/
|
||
function containsCopyrightKeyword(input) {
|
||
return COPYRIGHT_KEYWORDS.some(keyword =>
|
||
input.toLowerCase().includes(keyword.toLowerCase())
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 智能关键词处理和软著材料生成
|
||
* @param {string} input 用户输入
|
||
* @param {string} projectPath 项目路径
|
||
* @param {string} outputDir 输出路径
|
||
* @returns {Promise<boolean>} 是否执行了软著生成
|
||
*/
|
||
async function handleCopyrightKeywords(input, projectPath, outputDir) {
|
||
if (!containsCopyrightKeyword(input)) {
|
||
return false;
|
||
}
|
||
|
||
console.log(chalk.blue.bold('🎯 检测到软著相关关键词,启动SoftCopyright功能'));
|
||
console.log(chalk.cyan(`关键词: "${input}"`));
|
||
console.log(chalk.gray('='.repeat(50)));
|
||
|
||
try {
|
||
// 扫描项目
|
||
console.log(chalk.yellow('📊 分析项目源码...'));
|
||
const projectInfo = await scanner.scanProject(projectPath || process.cwd());
|
||
|
||
console.log(chalk.green(`✅ 项目分析完成: ${projectInfo.name}`));
|
||
console.log(chalk.white(` - 文件数: ${projectInfo.files.length}`));
|
||
console.log(chalk.white(` - 代码行数: ${projectInfo.totalLines}`));
|
||
console.log(chalk.white(` - 主要语言: ${projectInfo.languages.join(', ')}`));
|
||
|
||
// 显示项目信息并确认
|
||
console.log(chalk.cyan('\n📋 项目信息确认:'));
|
||
const { confirmed } = await inquirer.prompt([
|
||
{
|
||
type: 'confirm',
|
||
name: 'confirmed',
|
||
message: '是否为该项目生成软著申请材料?',
|
||
default: true
|
||
}
|
||
]);
|
||
|
||
if (!confirmed) {
|
||
console.log(chalk.yellow('❌ 用户取消操作'));
|
||
return false;
|
||
}
|
||
|
||
// 询问生成类型
|
||
const { generateType } = await inquirer.prompt([
|
||
{
|
||
type: 'list',
|
||
name: 'generateType',
|
||
message: '请选择要生成的材料类型:',
|
||
choices: [
|
||
{ name: '📋 生成全部材料 (HTML自动转PDF)', value: 'auto-pdf' },
|
||
{ name: '📖 仅生成软件说明书 (HTML)', value: 'manual' },
|
||
{ name: '💻 仅生成源代码文档 (PDF)', value: 'source' },
|
||
{ name: '📊 仅查看项目分析', value: 'scan' }
|
||
]
|
||
}
|
||
]);
|
||
|
||
// 根据选择执行相应操作
|
||
if (generateType === 'scan') {
|
||
console.log(chalk.green('✅ 项目分析完成'));
|
||
return true;
|
||
}
|
||
|
||
// 生成材料
|
||
if (generateType === 'auto-pdf') {
|
||
const { generateAutoPrintPDF } = require('./auto-print-pdf');
|
||
console.log(chalk.yellow('📄 生成HTML并自动转换为PDF...'));
|
||
await generateAutoPrintPDF(projectInfo, outputDir);
|
||
} else if (generateType === 'manual') {
|
||
console.log(chalk.yellow('📝 生成软件说明书(HTML格式)...'));
|
||
await docGenerator.generateManual(projectInfo, outputDir);
|
||
} else if (generateType === 'source') {
|
||
console.log(chalk.yellow('💻 生成源代码文档...'));
|
||
await sourceExporter.exportSourceCode(projectInfo, outputDir);
|
||
}
|
||
|
||
console.log(chalk.green.bold('\n🎉 软著材料生成完成!'));
|
||
|
||
return true;
|
||
|
||
} catch (error) {
|
||
console.error(chalk.red('❌ 软著材料生成失败:'), error.message);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
const program = new Command();
|
||
|
||
// 版本信息
|
||
program
|
||
.name('softcopyright')
|
||
.description('智能软件著作权申请材料生成工具')
|
||
.version('1.0.0');
|
||
|
||
// 主命令 - 生成软著材料
|
||
program
|
||
.command('generate')
|
||
.description('生成软件著作权申请材料')
|
||
.option('-p, --path <path>', '项目路径', process.cwd())
|
||
.option('-t, --type <type>', '生成类型 (manual|source|all)', 'all')
|
||
.option('-o, --output <path>', '输出路径', process.cwd())
|
||
.action(async (options) => {
|
||
try {
|
||
console.log(chalk.blue.bold('🚀 SoftCopyright - 软件著作权申请材料生成工具'));
|
||
console.log(chalk.gray('='.repeat(60)));
|
||
|
||
// 确认项目路径
|
||
const projectPath = path.resolve(options.path);
|
||
if (!await fs.pathExists(projectPath)) {
|
||
console.error(chalk.red(`❌ 项目路径不存在: ${projectPath}`));
|
||
process.exit(1);
|
||
}
|
||
|
||
console.log(chalk.cyan(`📁 项目路径: ${projectPath}`));
|
||
|
||
// 询问生成类型
|
||
if (options.type === 'all') {
|
||
const { type } = await inquirer.prompt([
|
||
{
|
||
type: 'list',
|
||
name: 'type',
|
||
message: '请选择要生成的材料类型:',
|
||
choices: [
|
||
{ name: '📋 生成全部材料 (推荐)', value: 'all' },
|
||
{ name: '📖 仅生成软件说明书', value: 'manual' },
|
||
{ name: '💻 仅生成源代码文档', value: 'source' }
|
||
]
|
||
}
|
||
]);
|
||
options.type = type;
|
||
}
|
||
|
||
console.log(chalk.yellow(`🔍 开始分析项目...`));
|
||
|
||
// 扫描项目
|
||
const projectInfo = await scanner.scanProject(projectPath);
|
||
console.log(chalk.green(`✅ 项目分析完成,发现 ${projectInfo.files.length} 个源代码文件`));
|
||
|
||
const outputDir = path.resolve(options.output);
|
||
await fs.ensureDir(outputDir);
|
||
|
||
// 生成材料
|
||
if (options.type === 'manual' || options.type === 'all') {
|
||
console.log(chalk.yellow('📝 生成软件说明书...'));
|
||
const manualPath = await docGenerator.generateManual(projectInfo, outputDir);
|
||
console.log(chalk.green(`✅ 软件说明书已生成: ${manualPath}`));
|
||
}
|
||
|
||
if (options.type === 'source' || options.type === 'all') {
|
||
console.log(chalk.yellow('💻 生成源代码文档...'));
|
||
const sourcePath = await sourceExporter.exportSourceCode(projectInfo, outputDir);
|
||
console.log(chalk.green(`✅ 源代码文档已生成: ${sourcePath}`));
|
||
}
|
||
|
||
console.log(chalk.blue.bold('\n🎉 生成完成!'));
|
||
console.log(chalk.gray(`输出目录: ${outputDir}`));
|
||
|
||
} catch (error) {
|
||
console.error(chalk.red('❌ 生成失败:'), error.message);
|
||
process.exit(1);
|
||
}
|
||
});
|
||
|
||
// 扫描命令
|
||
program
|
||
.command('scan')
|
||
.description('扫描项目源码')
|
||
.argument('<path>', '项目路径')
|
||
.action(async (projectPath) => {
|
||
try {
|
||
const resolvedPath = path.resolve(projectPath);
|
||
console.log(chalk.blue(`🔍 扫描项目: ${resolvedPath}`));
|
||
|
||
const projectInfo = await scanner.scanProject(resolvedPath);
|
||
|
||
console.log(chalk.green('\n📊 扫描结果:'));
|
||
console.log(chalk.white(`项目名称: ${projectInfo.name}`));
|
||
console.log(chalk.white(`源代码文件: ${projectInfo.files.length} 个`));
|
||
console.log(chalk.white(`总代码行数: ${projectInfo.totalLines} 行`));
|
||
console.log(chalk.white(`主要语言: ${projectInfo.languages.join(', ')}`));
|
||
|
||
// 显示文件统计
|
||
console.log(chalk.cyan('\n📁 文件类型统计:'));
|
||
Object.entries(projectInfo.fileStats).forEach(([ext, count]) => {
|
||
console.log(chalk.white(` ${ext}: ${count} 个文件`));
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error(chalk.red('❌ 扫描失败:'), error.message);
|
||
process.exit(1);
|
||
}
|
||
});
|
||
|
||
// 交互式命令
|
||
program
|
||
.command('interactive')
|
||
.description('交互式生成软著材料')
|
||
.action(async () => {
|
||
try {
|
||
console.log(chalk.blue.bold('🎯 欢迎使用 SoftCopyright 交互式向导'));
|
||
console.log(chalk.gray('='.repeat(60)));
|
||
|
||
// 询问项目路径
|
||
const { projectPath } = await inquirer.prompt([
|
||
{
|
||
type: 'input',
|
||
name: 'projectPath',
|
||
message: '请输入项目路径:',
|
||
default: process.cwd(),
|
||
validate: async (input) => {
|
||
const resolvedPath = path.resolve(input);
|
||
return await fs.pathExists(resolvedPath) || '路径不存在,请重新输入';
|
||
}
|
||
}
|
||
]);
|
||
|
||
console.log(chalk.yellow(`🔍 分析项目: ${projectPath}`));
|
||
const projectInfo = await scanner.scanProject(projectPath);
|
||
|
||
// 显示项目信息
|
||
console.log(chalk.cyan('\n📊 项目信息:'));
|
||
console.log(chalk.white(` 检测到的项目名称: ${projectInfo.name}`));
|
||
console.log(chalk.white(` 源代码文件数: ${projectInfo.files.length}`));
|
||
console.log(chalk.white(` 主要编程语言: ${projectInfo.languages.join(', ')}`));
|
||
|
||
// 确认项目信息
|
||
const { confirmed } = await inquirer.prompt([
|
||
{
|
||
type: 'confirm',
|
||
name: 'confirmed',
|
||
message: '项目信息是否正确?',
|
||
default: true
|
||
}
|
||
]);
|
||
|
||
if (!confirmed) {
|
||
console.log(chalk.yellow('❌ 操作已取消'));
|
||
return;
|
||
}
|
||
|
||
// 选择生成内容
|
||
const { generateType } = await inquirer.prompt([
|
||
{
|
||
type: 'checkbox',
|
||
name: 'generateType',
|
||
message: '请选择要生成的内容:',
|
||
choices: [
|
||
{ name: '📋 软件说明书 (约2000-3000字)', value: 'manual', checked: true },
|
||
{ name: '💻 源代码文档 (60页,每页50行)', value: 'source', checked: true }
|
||
]
|
||
}
|
||
]);
|
||
|
||
if (generateType.length === 0) {
|
||
console.log(chalk.yellow('⚠️ 请至少选择一种生成内容'));
|
||
return;
|
||
}
|
||
|
||
// 输出路径
|
||
const { outputPath } = await inquirer.prompt([
|
||
{
|
||
type: 'input',
|
||
name: 'outputPath',
|
||
message: '输出目录路径:',
|
||
default: path.join(projectPath, 'softcopyright-output')
|
||
}
|
||
]);
|
||
|
||
await fs.ensureDir(outputPath);
|
||
|
||
// 开始生成
|
||
console.log(chalk.blue('\n🚀 开始生成...'));
|
||
|
||
if (generateType.includes('manual')) {
|
||
console.log(chalk.yellow('📝 生成软件说明书...'));
|
||
const manualPath = await docGenerator.generateManual(projectInfo, outputPath);
|
||
console.log(chalk.green(`✅ 软件说明书: ${manualPath}`));
|
||
}
|
||
|
||
if (generateType.includes('source')) {
|
||
console.log(chalk.yellow('💻 生成源代码文档...'));
|
||
const sourcePath = await sourceExporter.exportSourceCode(projectInfo, outputPath);
|
||
console.log(chalk.green(`✅ 源代码文档: ${sourcePath}`));
|
||
}
|
||
|
||
console.log(chalk.blue.bold('\n🎉 生成完成!'));
|
||
console.log(chalk.gray(`文件已保存到: ${outputPath}`));
|
||
|
||
} catch (error) {
|
||
console.error(chalk.red('❌ 操作失败:'), error.message);
|
||
process.exit(1);
|
||
}
|
||
});
|
||
|
||
// 错误处理
|
||
program.on('command:*', () => {
|
||
console.error(chalk.red('❌ 未知命令,使用 --help 查看可用命令'));
|
||
process.exit(1);
|
||
});
|
||
|
||
// 检查是否包含软著关键词(在命令解析之前)
|
||
const userArgs = process.argv.slice(2);
|
||
const userArgsStr = userArgs.join(' ');
|
||
const projectPath = process.cwd(); // 默认使用当前目录作为项目路径
|
||
const outputDir = process.cwd(); // 默认使用当前目录作为输出路径
|
||
|
||
// 检查是否包含命令选项(以--或-开头的参数)
|
||
const hasCommandOptions = userArgs.some(arg => arg.startsWith('--') || arg.startsWith('-'));
|
||
|
||
// 只有当用户输入纯关键词(不含命令选项和明确命令)时才触发自动功能
|
||
if (containsCopyrightKeyword(userArgsStr) &&
|
||
!hasCommandOptions &&
|
||
!userArgs.includes('generate') &&
|
||
!userArgs.includes('scan') &&
|
||
!userArgs.includes('interactive') &&
|
||
!userArgs.includes('help') &&
|
||
userArgs.length > 0 &&
|
||
userArgs.length <= 3) { // 限制参数数量,避免复杂的命令被误判
|
||
|
||
console.log(chalk.blue.bold('🎯 检测到软著相关关键词,启动SoftCopyright功能'));
|
||
console.log(chalk.cyan(`关键词: "${userArgsStr}"`));
|
||
|
||
handleCopyrightKeywords(userArgsStr, projectPath, outputDir).then(() => {
|
||
process.exit(0);
|
||
}).catch(error => {
|
||
console.error(chalk.red('❌ 软著生成失败:'), error.message);
|
||
process.exit(1);
|
||
});
|
||
}
|
||
|
||
// 解析命令行参数
|
||
program.parse();
|
||
|
||
// 如果没有提供命令,显示帮助
|
||
if (!process.argv.slice(2).length) {
|
||
program.outputHelp();
|
||
} |