const { resolve } = require('path') const { readFileSync, existsSync, readdirSync } = require('fs') const fs = require('fs') const parser = require('@babel/parser') const traverse = require('@babel/traverse').default const { parse } = require('@vue/compiler-sfc') // 第一阶段路由映射插件 - 收集直接映射关系 function routeMappingPlugin() { return { name: 'route-mapping-phase1', // 防重复生成机制 _lastGenerationTime: 0, _generationInProgress: false, _generationCooldown: 5000, // 5秒冷却时间 // Webpack插件接口 apply(compiler) { const self = this // 在编译开始前就生成映射文件 compiler.hooks.beforeCompile.tapAsync('RouteMappingPlugin', (params, callback) => { if (self._shouldSkipGeneration()) { console.log('⏭️ 跳过路由映射生成(冷却期内)') callback() return } console.log('🔧 第一阶段:开始收集直接路由-API映射关系...') try { self.collectDirectMappings() callback() } catch (error) { console.error('❌ 路由映射插件执行失败:', error) callback(error) } }) // 备用钩子,确保在beforeRun时也执行 compiler.hooks.beforeRun.tapAsync('RouteMappingPlugin', (compilation, callback) => { if (self._shouldSkipGeneration()) { console.log('⏭️ 跳过路由映射生成(冷却期内)') callback() return } console.log('🔧 第一阶段:开始收集直接路由-API映射关系...') try { self.collectDirectMappings() callback() } catch (error) { console.error('❌ 路由映射插件执行失败:', error) callback(error) } }) }, // Vite插件接口 buildStart() { console.log('🔧 第一阶段:开始收集直接路由-API映射关系...') this.collectDirectMappings() }, // 在开发模式下也执行 configureServer(server) { if (this._shouldSkipGeneration()) { console.log('⏭️ 跳过路由映射生成(冷却期内)') return } console.log('🔧 开发模式下收集直接映射关系...') this.collectDirectMappings() }, // 检查是否应该跳过生成 _shouldSkipGeneration() { const now = Date.now() // 如果正在生成中,跳过 if (this._generationInProgress) { return true } // 如果在冷却期内,跳过 if (now - this._lastGenerationTime < this._generationCooldown) { return true } return false }, // 检查是否需要重新生成文件 _shouldRegenerateFile() { const outputPath = resolve(__dirname, '../src/renderer/modules/route-sync/direct-route-mappings.js') // 如果文件不存在,需要生成 if (!existsSync(outputPath)) { return true } // 检查源文件是否比生成文件更新 const sourceFiles = this._getSourceFiles() const outputStats = fs.statSync(outputPath) for (const sourceFile of sourceFiles) { if (existsSync(sourceFile)) { const sourceStats = fs.statSync(sourceFile) if (sourceStats.mtime > outputStats.mtime) { return true } } } return false }, // 获取需要监控的源文件列表 _getSourceFiles() { const sourceFiles = [] // 路由配置文件 sourceFiles.push(resolve(__dirname, '../src/renderer/router/index.js')) // 模块配置文件 sourceFiles.push(resolve(__dirname, '../src/renderer/modules/config.js')) // 页面组件文件 const moduleDirs = this.readModuleNamesFromConfig() moduleDirs.forEach(moduleName => { // 页面组件 const viewsPath = resolve(__dirname, `../src/renderer/modules/${moduleName}/views`) if (existsSync(viewsPath)) { const files = readdirSync(viewsPath).filter(f => f.endsWith('.vue')) files.forEach(file => { sourceFiles.push(resolve(viewsPath, file)) }) } // 弹窗组件 const componentsPath = resolve(__dirname, `../src/renderer/modules/${moduleName}/components`) if (existsSync(componentsPath)) { const files = readdirSync(componentsPath).filter(f => f.endsWith('.vue') && (f.toLowerCase().includes('modal') || f.toLowerCase().includes('dialog')) ) files.forEach(file => { sourceFiles.push(resolve(componentsPath, file)) }) } }) return sourceFiles }, // 收集直接映射关系 collectDirectMappings() { // 设置生成中标志 this._generationInProgress = true try { // 检查文件是否需要重新生成 if (this._shouldRegenerateFile()) { console.log('🔄 检测到需要重新生成路由映射文件') // 1. 分析路由配置 const routes = this.analyzeRoutes() // 2. 收集API信息(第一层) const apiMappings = this.collectApiMappings(routes) // 3. 关联页面与API(第三层) const enhancedApiMappings = this.associatePagesWithApi(routes, apiMappings) // 4. 清理和优化API映射(第四层) const optimizedApiMappings = this.optimizeApiMappings(routes, enhancedApiMappings) // 5. 分析组件关联关系并完善路径信息(第五层) const { componentRelationships, enhancedApiMappings: finalApiMappings } = this.analyzeComponentRelationships(routes, optimizedApiMappings) // 6. 生成最终映射文件 this.generateTestFile(routes, finalApiMappings) console.log('✅ 第一阶段直接映射关系收集完成') } else { console.log('⏭️ 路由映射文件无需重新生成') } } catch (error) { console.error('❌ 直接映射关系收集失败:', error) throw error } finally { // 重置生成中标志并更新时间戳 this._generationInProgress = false this._lastGenerationTime = Date.now() } }, // 分析路由配置 analyzeRoutes() { const routerPath = resolve(__dirname, '../src/renderer/router/index.js') if (!existsSync(routerPath)) { throw new Error('Router file not found') } const routerContent = readFileSync(routerPath, 'utf-8') const ast = parser.parse(routerContent, { sourceType: 'module', plugins: ['jsx'] }) const routes = [] const self = this traverse(ast, { ObjectExpression(path) { const properties = path.node.properties let route = {} properties.forEach(prop => { if (prop.key && prop.key.name === 'path' && prop.value && prop.value.value) { route.path = prop.value.value } if (prop.key && prop.key.name === 'name' && prop.value && prop.value.value) { route.name = prop.value.value } if (prop.key && prop.key.name === 'component' && prop.value) { route.component = self.extractComponentName(prop.value) } }) if (route.path) { route.module = self.extractModuleFromPath(route.path) // 添加description属性 route.description = self.getRouteDescription(route.module, route.path) routes.push(route) } } }) return routes }, // 分析单个服务文件的API映射 analyzeServiceFileForApiMappings(servicePath, serviceName, moduleName) { try { const content = readFileSync(servicePath, 'utf-8') const ast = parser.parse(content, { sourceType: 'module', plugins: ['jsx'] }) const apiMappings = [] const self = this traverse(ast, { // 查找函数定义 FunctionDeclaration(path) { const functionName = path.node.id?.name if (functionName) { // 分析函数内部的API调用 const apiCalls = self.extractApiCallsFromFunction(path) apiCalls.forEach(apiCall => { apiMappings.push({ methodName: functionName, method: apiCall.method, path: apiCall.path, line: apiCall.line }) }) } }, // 查找箭头函数和函数表达式 VariableDeclarator(path) { if (path.node.init && (path.node.init.type === 'ArrowFunctionExpression' || path.node.init.type === 'FunctionExpression')) { const functionName = path.node.id?.name if (functionName) { const apiCalls = self.extractApiCallsFromFunction(path.get('init')) apiCalls.forEach(apiCall => { apiMappings.push({ methodName: functionName, method: apiCall.method, path: apiCall.path, line: apiCall.line }) }) } } }, // 查找对象方法定义 ObjectMethod(path) { const methodName = path.node.key?.name if (methodName) { const apiCalls = self.extractApiCallsFromFunction(path) apiCalls.forEach(apiCall => { apiMappings.push({ methodName: methodName, method: apiCall.method, path: apiCall.path, line: apiCall.line }) }) } }, // 查找对象属性中的函数 ObjectProperty(path) { if (path.node.value && (path.node.value.type === 'ArrowFunctionExpression' || path.node.value.type === 'FunctionExpression')) { const methodName = path.node.key?.name if (methodName) { const apiCalls = self.extractApiCallsFromFunction(path.get('value')) apiCalls.forEach(apiCall => { apiMappings.push({ methodName: methodName, method: apiCall.method, path: apiCall.path, line: apiCall.line }) }) } } } }) return apiMappings } catch (error) { console.warn(`⚠️ 分析服务文件失败: ${servicePath}`, error.message) return [] } }, // 从函数中提取API调用 extractApiCallsFromFunction(functionPath) { const apiCalls = [] const self = this // 使用正确的traverse调用方式 if (functionPath.node) { traverse(functionPath.node, { CallExpression(path) { const callInfo = self.extractApiCall(path) if (callInfo && callInfo.path) { apiCalls.push(callInfo) } } }, functionPath.scope, functionPath) } return apiCalls }, // 第二步(第一层):收集API信息 collectApiMappings(routes) { const apiMappings = [] const moduleDirs = this.readModuleNamesFromConfig() moduleDirs.forEach(moduleName => { const servicesPath = resolve(__dirname, `../src/renderer/modules/${moduleName}/services`) if (existsSync(servicesPath)) { const serviceFiles = readdirSync(servicesPath).filter(f => f.endsWith('.js')) serviceFiles.forEach(serviceFile => { const serviceName = serviceFile.replace('.js', '') const servicePath = resolve(servicesPath, serviceFile) const serviceApiMappings = this.analyzeServiceFileForApiMappings(servicePath, serviceName, moduleName) if (serviceApiMappings.length > 0) { apiMappings.push({ module: moduleName, serviceName: serviceName, servicePath: servicePath, apiMappings: serviceApiMappings }) } }) } else { console.warn(`⚠️ 模块 ${moduleName} 的services目录不存在: ${servicesPath}`) } }) return apiMappings }, // 第三步:关联页面与API associatePagesWithApi(routes, apiMappings) { // 为每个API映射添加调用该API的页面信息 const enhancedApiMappings = apiMappings.map(moduleMapping => { const enhancedApiMappings = moduleMapping.apiMappings.map(apiMapping => { // 查找调用该API方法的组件 const callingComponents = this.findComponentsCallingApiMethod( routes, moduleMapping.module, moduleMapping.serviceName, apiMapping.methodName ) return { ...apiMapping, callingComponents: callingComponents } }) return { ...moduleMapping, apiMappings: enhancedApiMappings } }) return enhancedApiMappings }, // 查找调用特定API方法的组件 findComponentsCallingApiMethod(routes, moduleName, serviceName, methodName) { const callingComponents = [] // 1. 遍历所有路由,查找调用该API方法的页面组件 routes.forEach(route => { if (route.module === moduleName) { const componentAnalysis = this.analyzeComponentForServiceCalls( route.component, route.path, moduleName ) if (componentAnalysis && componentAnalysis.serviceCalls) { // 检查是否有调用该服务方法的调用 const hasServiceCall = componentAnalysis.serviceCalls.some(serviceCall => serviceCall.type === 'service' && serviceCall.service === serviceName && serviceCall.method === methodName ) if (hasServiceCall) { callingComponents.push(route.component) } } } }) // 2. 搜索模块的components目录,查找调用该API方法的组件 const componentsInModule = this.findComponentsInModule(moduleName) componentsInModule.forEach(componentName => { const componentAnalysis = this.analyzeComponentForServiceCalls( componentName, null, // components目录中的组件没有路由路径 moduleName ) if (componentAnalysis && componentAnalysis.serviceCalls) { // 检查是否有调用该服务方法的调用 const hasServiceCall = componentAnalysis.serviceCalls.some(serviceCall => serviceCall.type === 'service' && serviceCall.service === serviceName && serviceCall.method === methodName ) if (hasServiceCall) { callingComponents.push(componentName) } } }) // 去重 return [...new Set(callingComponents)] }, // 查找模块中的所有组件 findComponentsInModule(moduleName) { const components = [] try { // 查找views目录中的组件 const viewsPath = resolve(__dirname, `../src/renderer/modules/${moduleName}/views`) if (existsSync(viewsPath)) { const viewFiles = readdirSync(viewsPath).filter(f => f.endsWith('.vue')) viewFiles.forEach(file => { components.push(file.replace('.vue', '')) }) } // 查找components目录中的组件 const componentsPath = resolve(__dirname, `../src/renderer/modules/${moduleName}/components`) if (existsSync(componentsPath)) { const componentFiles = readdirSync(componentsPath).filter(f => f.endsWith('.vue')) componentFiles.forEach(file => { components.push(file.replace('.vue', '')) }) } } catch (error) { console.warn(`⚠️ 查找模块组件失败: ${moduleName}`, error.message) } return components }, // 第四步:清理和优化API映射 optimizeApiMappings(routes, apiMappings) { const optimizedApiMappings = apiMappings.map(moduleMapping => { // 过滤掉callingComponents为空的API映射 const filteredApiMappings = moduleMapping.apiMappings.filter(apiMapping => { return apiMapping.callingComponents && apiMapping.callingComponents.length > 0 }) // 为每个API映射的callingComponents添加路径信息 const enhancedApiMappings = filteredApiMappings.map(apiMapping => { const enhancedCallingComponents = apiMapping.callingComponents.map(componentName => { // 在routes中查找匹配的组件 const matchingRoute = routes.find(route => route.component === componentName) if (matchingRoute) { return { component: componentName, path: matchingRoute.path } } else { // 如果找不到匹配的路由,只返回组件名 return { component: componentName, path: null } } }) return { ...apiMapping, callingComponents: enhancedCallingComponents } }) return { ...moduleMapping, apiMappings: enhancedApiMappings } }).filter(moduleMapping => moduleMapping.apiMappings.length > 0) // 过滤掉没有API映射的模块 const totalRemoved = apiMappings.reduce((sum, module) => sum + module.apiMappings.length, 0) - optimizedApiMappings.reduce((sum, module) => sum + module.apiMappings.length, 0) return optimizedApiMappings }, // 第五步:分析组件关联关系并完善路径信息 analyzeComponentRelationships(routes, apiMappings) { // 1. 首先收集组件关联关系 const componentRelationships = [] routes.forEach(route => { if (route.component) { const relationships = this.findComponentRelationships(route.component, route.path, route.module) componentRelationships.push(...relationships) } }) // 2. 完善API映射中的路径信息 const enhancedApiMappings = this.enhanceApiMappingsWithPaths(routes, apiMappings, componentRelationships) return { componentRelationships, enhancedApiMappings } }, // 完善API映射中的路径信息 enhanceApiMappingsWithPaths(routes, apiMappings, componentRelationships) { let enhancedCount = 0 // 遍历每个模块的API映射 const enhancedApiMappings = apiMappings.map(moduleMapping => { const enhancedModuleApiMappings = moduleMapping.apiMappings.map(apiMapping => { const enhancedCallingComponents = apiMapping.callingComponents.map(callingComponent => { // 如果path为空,尝试通过组件关系找到根组件路径 if (callingComponent.path === null) { const rootPath = this.findRootComponentPath( callingComponent.component, componentRelationships, routes ) if (rootPath) { enhancedCount++ return { ...callingComponent, path: rootPath } } } return callingComponent }) return { ...apiMapping, callingComponents: enhancedCallingComponents } }) return { ...moduleMapping, apiMappings: enhancedModuleApiMappings } }) return enhancedApiMappings }, // 通过组件关系找到根组件路径 findRootComponentPath(targetComponent, componentRelationships, routes) { // 递归查找组件关系链,直到找到根组件 const findParent = (componentName, visited = new Set()) => { // 避免循环引用 if (visited.has(componentName)) { return null } visited.add(componentName) // 查找该组件的父组件 const parentRelationship = componentRelationships.find(rel => rel.toComponent === componentName ) if (parentRelationship) { const parentComponent = parentRelationship.fromComponent // 检查父组件是否为路由表中的根组件 const route = routes.find(route => route.component === parentComponent) if (route) { return route.path } // 如果父组件不是根组件,继续向上查找 return findParent(parentComponent, visited) } return null } return findParent(targetComponent) }, // 查找单个组件的关联关系 findComponentRelationships(componentName, componentPath, moduleName) { const relationships = [] try { // 查找组件文件 const componentFile = this.findComponentFile(componentName) if (!componentFile || !existsSync(componentFile)) { return relationships } const content = readFileSync(componentFile, 'utf-8') if (componentFile.endsWith('.vue')) { const { descriptor } = parse(content) const scriptContent = descriptor.script?.content || '' const templateContent = descriptor.template?.content || '' // 分析模板中的组件引用 const templateRelationships = this.analyzeTemplateForComponents(templateContent, componentName) relationships.push(...templateRelationships) // 分析脚本中的组件引用 if (scriptContent) { const scriptRelationships = this.analyzeScriptForComponents(scriptContent, componentName) relationships.push(...scriptRelationships) } } } catch (error) { console.warn(`⚠️ 分析组件关联关系失败: ${componentName}`, error.message) } return relationships }, // 分析模板中的组件引用 analyzeTemplateForComponents(templateContent, fromComponent) { const relationships = [] // 查找组件标签(如 ) const componentTagRegex = /<([A-Z][a-zA-Z0-9]*)/g let match while ((match = componentTagRegex.exec(templateContent)) !== null) { const componentTag = match[1] // 跳过HTML标签 if (this.isHtmlTag(componentTag)) { continue } // 确定关联类型 let relationship = 'embed' // 默认是嵌入关系 // 检查是否是弹窗组件 if (this.isModalComponent(componentTag, templateContent, match.index)) { relationship = 'modal' } relationships.push({ fromComponent: fromComponent, toComponent: componentTag, relationship: relationship }) } return relationships }, // 分析脚本中的组件引用 analyzeScriptForComponents(scriptContent, fromComponent) { const relationships = [] try { const ast = parser.parse(scriptContent, { sourceType: 'module', plugins: ['jsx'] }) const self = this traverse(ast, { // 查找import语句 ImportDeclaration(path) { const source = path.node.source.value if (source.startsWith('./') || source.startsWith('../')) { // 相对路径导入,可能是组件 const importedNames = path.node.specifiers.map(spec => { if (spec.type === 'ImportDefaultSpecifier') { return spec.local.name } else if (spec.type === 'ImportSpecifier') { return spec.imported.name } return null }).filter(Boolean) importedNames.forEach(importedName => { if (self.isComponentName(importedName)) { relationships.push({ fromComponent: fromComponent, toComponent: importedName, relationship: 'import' }) } }) } } }) } catch (error) { console.warn(`⚠️ 分析脚本组件引用失败: ${fromComponent}`, error.message) } return relationships }, // 判断是否是HTML标签 isHtmlTag(tagName) { const htmlTags = [ 'div', 'span', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'button', 'input', 'form', 'table', 'tr', 'td', 'th', 'ul', 'ol', 'li', 'a', 'img', 'select', 'option', 'textarea', 'label', 'fieldset', 'legend', 'section', 'article', 'header', 'footer', 'nav', 'aside', 'main' ] return htmlTags.includes(tagName.toLowerCase()) }, // 判断是否是弹窗组件 isModalComponent(componentTag, templateContent, startIndex) { // 查找组件标签周围的上下文 const beforeContext = templateContent.substring(Math.max(0, startIndex - 100), startIndex) const afterContext = templateContent.substring(startIndex, Math.min(templateContent.length, startIndex + 200)) // 检查是否包含弹窗相关的关键词 const modalKeywords = ['modal', 'dialog', 'popup', 'overlay', 'v-if', 'v-show'] const context = (beforeContext + afterContext).toLowerCase() return modalKeywords.some(keyword => context.includes(keyword)) }, // 判断是否是组件名称 isComponentName(name) { // Vue组件通常以大写字母开头 return /^[A-Z]/.test(name) && name.length > 2 }, // 分析组件中的服务调用 analyzeComponentForServiceCalls(componentName, componentPath, moduleName) { try { let filePath = componentPath // 如果是组件名,需要找到对应的文件 if (!filePath || !existsSync(filePath)) { filePath = this.findComponentFile(componentName) } if (!filePath || !existsSync(filePath)) { console.warn(`⚠️ 组件文件未找到: ${componentName}`) return null } const content = readFileSync(filePath, 'utf-8') if (filePath.endsWith('.vue')) { return this.analyzeVueComponentForServiceCalls(content, componentName, moduleName) } else { return this.analyzeJavaScriptComponentForServiceCalls(content, componentName, moduleName) } } catch (error) { console.warn(`⚠️ 分析组件失败: ${componentName}`, error.message) return null } }, // 分析Vue组件中的服务调用 analyzeVueComponentForServiceCalls(content, componentName, moduleName) { const { descriptor } = parse(content) const scriptContent = descriptor.script?.content || '' if (!scriptContent) { return { serviceCalls: [], methods: [] } } const ast = parser.parse(scriptContent, { sourceType: 'module', plugins: ['jsx'] }) return this.extractServiceCallsFromAST(ast, componentName, moduleName) }, // 分析JavaScript组件中的服务调用 analyzeJavaScriptComponentForServiceCalls(content, componentName, moduleName) { const ast = parser.parse(content, { sourceType: 'module', plugins: ['jsx'] }) return this.extractServiceCallsFromAST(ast, componentName, moduleName) }, // 从AST中提取服务调用 extractServiceCallsFromAST(ast, componentName, moduleName) { const serviceCalls = [] const methods = [] const self = this traverse(ast, { // 查找方法定义 FunctionDeclaration(path) { if (path.node.id) { methods.push(path.node.id.name) } }, // 查找箭头函数 ArrowFunctionExpression(path) { if (path.parent && path.parent.type === 'VariableDeclarator' && path.parent.id) { methods.push(path.parent.id.name) } }, // 查找服务调用 CallExpression(path) { const callInfo = self.extractServiceCall(path) if (callInfo) { serviceCalls.push(callInfo) } } }) return { serviceCalls, methods } }, // 提取服务调用信息 extractServiceCall(path) { const node = path.node // 检查是否是服务调用 (service.method()) if (node.callee && node.callee.type === 'MemberExpression') { const object = node.callee.object const property = node.callee.property if (object && property && object.name && property.name) { // 检查是否是服务调用 if (object.name.endsWith('Service') || object.name === 'api' || object.name === 'axios') { return { type: 'service', service: object.name, method: property.name, arguments: node.arguments.map(arg => this.extractArgumentValue(arg)), line: node.loc?.start?.line || 0 } } } } return null }, // 提取API调用信息 extractApiCall(path) { const node = path.node const self = this // 检查是否是直接的API调用 (api.get, api.post等) if (node.callee && node.callee.type === 'MemberExpression') { const object = node.callee.object const property = node.callee.property if (object && property) { // 支持多种API调用方式 const apiObjects = ['api', 'axios', 'http', 'request'] const httpMethods = ['get', 'post', 'put', 'delete', 'patch', 'head', 'options'] // 检查对象名称(可能是Identifier或StringLiteral) let objectName = '' if (object.type === 'Identifier') { objectName = object.name } else if (object.type === 'StringLiteral') { objectName = object.value } // 检查属性名称 let propertyName = '' if (property.type === 'Identifier') { propertyName = property.name } else if (property.type === 'StringLiteral') { propertyName = property.value } if (apiObjects.includes(objectName) && httpMethods.includes(propertyName)) { const method = propertyName const args = node.arguments // 提取API路径 let apiPath = '' if (args.length > 0) { if (args[0].type === 'StringLiteral') { apiPath = args[0].value } else if (args[0].type === 'TemplateLiteral') { // 处理模板字符串 apiPath = self.extractTemplateLiteral(args[0]) } } if (apiPath) { return { method: method.toUpperCase(), path: apiPath, line: node.loc?.start?.line || 0 } } } } } // 检查是否是服务调用 (service.method()) if (node.callee && node.callee.type === 'MemberExpression') { const object = node.callee.object const property = node.callee.property if (object && property) { let objectName = '' let propertyName = '' if (object.type === 'Identifier') { objectName = object.name } else if (object.type === 'StringLiteral') { objectName = object.value } if (property.type === 'Identifier') { propertyName = property.name } else if (property.type === 'StringLiteral') { propertyName = property.value } // 检查是否是服务调用 if (objectName && propertyName && (objectName.endsWith('Service') || objectName === 'api' || objectName === 'axios')) { return { method: propertyName, service: objectName, line: node.loc?.start?.line || 0 } } } } return null }, // 提取参数值 extractArgumentValue(node) { if (node.type === 'StringLiteral') { return node.value } else if (node.type === 'NumericLiteral') { return node.value } else if (node.type === 'Identifier') { return node.name } return null }, // 提取模板字符串内容 extractTemplateLiteral(templateLiteral) { if (templateLiteral.type !== 'TemplateLiteral') { return '' } let result = '' templateLiteral.quasis.forEach((quasi, index) => { result += quasi.value.raw if (index < templateLiteral.expressions.length) { // 对于表达式,我们使用占位符 result += `{${templateLiteral.expressions[index].type}}` } }) return result }, // 从模块配置中读取模块名称 readModuleNamesFromConfig() { try { const configPath = resolve(__dirname, '../src/renderer/modules/config.js') if (!existsSync(configPath)) { return [] } const code = readFileSync(configPath, 'utf-8') const ast = parser.parse(code, { sourceType: 'module' }) const modules = [] traverse(ast, { ExportNamedDeclaration(path) { const decl = path.node.declaration if (decl && decl.type === 'VariableDeclaration') { decl.declarations.forEach(d => { if ( d.id && d.id.name === 'MODULE_CONFIG' && d.init && d.init.type === 'ObjectExpression' ) { d.init.properties.forEach(prop => { if (prop.type === 'ObjectProperty') { if (prop.key.type === 'Identifier') { modules.push(prop.key.name) } else if (prop.key.type === 'StringLiteral') { modules.push(prop.key.value) } } }) } }) } } }) return modules } catch (e) { console.warn('⚠️ 读取 MODULE_CONFIG 失败,使用默认模块集合: ', e.message) return [] } }, // 查找组件文件 findComponentFile(componentName) { // 跳过MainLayout等特殊组件,或者返回正确的路径 if (componentName === 'MainLayout') { // MainLayout在core模块中 const mainLayoutPath = resolve(__dirname, '../src/renderer/modules/core/components/MainLayout.vue') if (existsSync(mainLayoutPath)) { return mainLayoutPath } return null } const moduleDirs = this.readModuleNamesFromConfig() for (const moduleName of moduleDirs) { // 查找views目录 const viewsPath = resolve(__dirname, `../src/renderer/modules/${moduleName}/views`) if (existsSync(viewsPath)) { const files = readdirSync(viewsPath).filter(f => f.endsWith('.vue')) const matchingFile = files.find(f => f.replace('.vue', '') === componentName) if (matchingFile) { return resolve(viewsPath, matchingFile) } } // 查找components目录 const componentsPath = resolve(__dirname, `../src/renderer/modules/${moduleName}/components`) if (existsSync(componentsPath)) { const files = readdirSync(componentsPath).filter(f => f.endsWith('.vue')) const matchingFile = files.find(f => f.replace('.vue', '') === componentName) if (matchingFile) { return resolve(componentsPath, matchingFile) } } } return null }, // 生成最终映射文件 generateTestFile(routes, apiMappings) { const outputPath = resolve(__dirname, '../src/renderer/modules/route-sync/direct-route-mappings.js') const testContent = `// 直接路由映射文件 // 此文件由 route-mapping-plugin 在构建时生成 // 包含路由配置分析结果和API收集结果 // 请勿手动修改 export const directRouteMappings = { // 路由配置分析结果 routes: ${JSON.stringify(routes, null, 2)}, // API收集结果(包含页面关联和路径完善) apiMappings: ${JSON.stringify(apiMappings, null, 2)} } export default directRouteMappings ` fs.writeFileSync(outputPath, testContent, 'utf-8') console.log(`✅ 映射文件已生成: ${outputPath}`) console.log(`📊 统计信息:`) console.log(` - 路由数量: ${routes.length} 个`) console.log(` - 模块数量: ${apiMappings.length} 个`) console.log(` - 总API调用: ${apiMappings.reduce((sum, module) => sum + module.apiMappings.length, 0)} 个`) console.log(` - 总组件关联: ${apiMappings.reduce((sum, module) => sum + module.apiMappings.reduce((apiSum, api) => apiSum + (api.callingComponents ? api.callingComponents.length : 0), 0), 0)} 个`) }, // 提取组件名称 extractComponentName(node) { if (node.type === 'Identifier') { return node.name } else if (node.type === 'StringLiteral') { return node.value } return null }, // 从路径提取模块名 extractModuleFromPath(path) { if (path === '/' || path === '') return 'home' const segments = path.split('/').filter(Boolean) if (segments.length === 0) return 'home' // 特殊处理:user-profile路径对应user-management模块 if (segments[0] === 'user-profile') { return 'user-management' } return segments[0] }, // 获取路由描述 getRouteDescription(module, path) { try { // 读取RouteConfig文件 const routeConfigPath = resolve(__dirname, '../src/renderer/modules/route-sync/RouteConfig.js') if (!existsSync(routeConfigPath)) { console.warn(`⚠️ RouteConfig文件不存在: ${routeConfigPath}`) return `${module}页面` } const routeConfigContent = readFileSync(routeConfigPath, 'utf-8') // 解析RouteConfig文件,提取descriptions对象 const descriptions = this.extractDescriptionsFromRouteConfig(routeConfigContent) // 查找对应的描述 if (descriptions && descriptions[module]) { return descriptions[module] } else { // 特殊处理:如果是user-profile路径,使用user-profile的描述 if (path === '/user-profile' && descriptions && descriptions['user-profile']) { return descriptions['user-profile'] } // 如果找不到对应的key,输出错误日志但不影响运行 console.error(`❌ RouteConfig.descriptions中缺少模块 "${module}" 的配置,路径: ${path}`) return `${module}页面` } } catch (error) { console.warn(`⚠️ 获取路由描述失败: ${error.message}`) return `${module}页面` } }, // 从RouteConfig文件中提取descriptions对象 extractDescriptionsFromRouteConfig(content) { try { const ast = parser.parse(content, { sourceType: 'module', plugins: ['jsx'] }) let descriptions = null traverse(ast, { ObjectExpression(path) { // 查找descriptions属性 const properties = path.node.properties const descriptionsProp = properties.find(prop => prop.key && prop.key.name === 'descriptions' ) if (descriptionsProp && descriptionsProp.value && descriptionsProp.value.type === 'ObjectExpression') { // 解析descriptions对象 descriptions = {} descriptionsProp.value.properties.forEach(prop => { if (prop.key && prop.key.type === 'StringLiteral' && prop.value && prop.value.type === 'StringLiteral') { descriptions[prop.key.value] = prop.value.value } }) } } }) return descriptions } catch (error) { console.warn(`⚠️ 解析RouteConfig失败: ${error.message}`) return null } } } } module.exports = { routeMappingPlugin }