UNPKG

module-migration-tool

Version:

分析项目文件依赖并迁移到新项目的工具

265 lines (225 loc) 7.51 kB
/** * 迁移核心模块 */ const path = require("path") const fs = require("fs") const { analyzeFile } = require("./analyzers") const { resolveDependencyPath } = require("./resolvers") const { generateReport } = require("./reporters") const { getAllFiles, copyFile, getRelativePath } = require("./utils/fileUtils") const { detectProjectConfig, mergeConfig } = require("./utils/configUtils") const chalk = require("chalk") /** * 迁移工具类 */ class Migrator { /** * 创建迁移工具实例 * @param {Object} options - 选项 */ constructor(options = {}) { // 源路径 this.sourcePath = path.resolve(options.sourcePath || "./src") // 入口点 this.entryPoint = path.resolve(options.entryPoint || "./src/index.js") // 目标路径 this.targetPath = path.resolve(options.targetPath || "./dist") // 检测项目配置 const detectedConfig = detectProjectConfig(path.dirname(this.sourcePath)) // 合并配置 this.config = mergeConfig({ ...options, ...detectedConfig, }) // 存储已处理的文件路径 this.processedFiles = new Set() // 存储依赖关系 this.dependencyMap = new Map() } /** * 执行迁移流程 */ async migrate() { console.log(chalk.blue("=== 文件迁移工具 ===")) console.log(`源路径: ${chalk.yellow(this.sourcePath)}`) console.log(`入口点: ${chalk.yellow(this.entryPoint)}`) console.log(`目标路径: ${chalk.yellow(this.targetPath)}`) // 确保目标目录存在 if (!fs.existsSync(this.targetPath)) { fs.mkdirSync(this.targetPath, { recursive: true }) } const startTime = Date.now() try { // 检查入口点是否存在 if (!fs.existsSync(this.entryPoint)) { throw new Error(`入口点不存在: ${this.entryPoint}`) } // 分析入口点 if (fs.statSync(this.entryPoint).isDirectory()) { // 目录入口 await this.processDirectoryEntry() } else { // 文件入口 await this.processFileEntry() } // 生成报告 if (this.config.report.generateReport) { generateReport(this.dependencyMap, this.config.report.outputPath, { projectRoot: this.sourcePath, }) } const duration = ((Date.now() - startTime) / 1000).toFixed(2) console.log(chalk.green("\n迁移完成!")) console.log(`共迁移 ${chalk.cyan(this.processedFiles.size)} 个文件`) console.log(`用时: ${chalk.cyan(duration)} 秒`) } catch (error) { console.error(chalk.red("迁移过程中出错:"), error.message) process.exit(1) } } /** * 处理目录入口 */ async processDirectoryEntry() { console.log(chalk.yellow("入口点是目录,查找所有文件...")) // 获取目录下的所有文件 const files = getAllFiles(this.entryPoint, { extensions: this.config.extensions, ignorePaths: this.config.ignorePaths, }) console.log(`共找到 ${chalk.cyan(files.length)} 个文件`) // 分析所有文件依赖 let allDependencies = new Set() for (const file of files) { if (this.config.extensions.includes(path.extname(file))) { console.log(`处理入口文件: ${chalk.cyan(file)}`) const deps = await this.analyzeDependencies(file) deps.forEach((d) => allDependencies.add(d)) } else { // 非代码文件直接添加 allDependencies.add(path.resolve(file)) } } // 复制入口目录结构 const entryDirRelative = path.relative( path.resolve(this.sourcePath), path.resolve(this.entryPoint) ) const entryDirTarget = path.join(this.targetPath, entryDirRelative) // 确保入口目录结构被复制 if (!fs.existsSync(entryDirTarget)) { fs.mkdirSync(entryDirTarget, { recursive: true }) } // 复制所有依赖文件 await this.copyFilesToTarget(Array.from(allDependencies)) } /** * 处理文件入口 */ async processFileEntry() { console.log(`分析入口文件: ${chalk.cyan(this.entryPoint)}`) // 分析依赖 const dependencies = await this.analyzeDependencies(this.entryPoint) // 复制文件 await this.copyFilesToTarget(dependencies) } /** * 递归分析文件依赖 * @param {string} filePath - 文件路径 * @returns {Array<string>} 依赖文件路径数组 */ async analyzeDependencies(filePath) { const queue = [path.resolve(filePath)] const allDependencies = new Set() while (queue.length > 0) { const currentFile = queue.shift() // 跳过已处理文件 if (this.processedFiles.has(currentFile)) { continue } // 标记文件为已处理 this.processedFiles.add(currentFile) allDependencies.add(currentFile) // 查找文件依赖 const importPaths = await this.findDependencies(currentFile) // 将依赖信息存入映射表 this.dependencyMap.set( currentFile, importPaths.map((p) => p.source) ) // 将解析到的依赖添加到队列 for (const importInfo of importPaths) { if ( importInfo.resolved && !this.config.ignorePaths.some((p) => importInfo.resolved.includes(p)) ) { queue.push(importInfo.resolved) } } } return Array.from(allDependencies) } /** * 查找文件中的依赖 * @param {string} filePath - 文件路径 * @returns {Array<Object>} 依赖信息数组,包含源路径和解析后路径 */ async findDependencies(filePath) { try { // 分析文件中的import语句 const dependencies = analyzeFile(filePath) // 解析依赖路径 return dependencies.map((dep) => { const resolved = resolveDependencyPath( dep, filePath, this.config, this.sourcePath ) return { source: dep, resolved, } }) } catch (error) { console.error( chalk.red(`分析文件 ${filePath} 依赖时出错:`), error.message ) return [] } } /** * 复制文件到目标目录 * @param {Array<string>} filePaths - 文件路径数组 */ async copyFilesToTarget(filePaths) { console.log(chalk.yellow("开始复制文件...")) let successCount = 0 let failCount = 0 for (const filePath of filePaths) { try { // 获取相对于源目录的路径 const relativePath = getRelativePath(this.sourcePath, filePath) const targetFilePath = path.join(this.targetPath, relativePath) // 复制文件 const success = copyFile(filePath, targetFilePath) if (success) { successCount++ console.log(`已复制: ${chalk.green(relativePath)}`) } else { failCount++ } } catch (error) { failCount++ console.error(chalk.red(`复制文件 ${filePath} 时出错:`), error.message) } } console.log( `成功复制 ${chalk.green(successCount)} 个文件,失败 ${chalk.red( failCount )} 个文件` ) } } module.exports = Migrator