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', // Webpack插件接口 apply(compiler) { const self = this // 在编译开始前就生成映射文件 compiler.hooks.beforeCompile.tapAsync('RouteMappingPlugin', (params, callback) => { console.log('🔧 第一阶段:开始收集直接路由-API映射关系...') try { self.collectDirectMappings() callback() } catch (error) { console.error('❌ 路由映射插件执行失败:', error) callback(error) } }) // 备用钩子,确保在beforeRun时也执行 compiler.hooks.beforeRun.tapAsync('RouteMappingPlugin', (compilation, callback) => { console.log('🔧 第一阶段:开始收集直接路由-API映射关系...') try { self.collectDirectMappings() callback() } catch (error) { console.error('❌ 路由映射插件执行失败:', error) callback(error) } }) }, // Vite插件接口 buildStart() { console.log('🔧 第一阶段:开始收集直接路由-API映射关系...') this.collectDirectMappings() }, // 在开发模式下也执行 configureServer(server) { console.log('🔧 开发模式下收集直接映射关系...') this.collectDirectMappings() }, // 收集直接映射关系 collectDirectMappings() { try { // 1. 分析路由配置 const routes = this.analyzeRoutes() // 2. 分析页面组件(第一层) const pageMappings = this.analyzePageComponents(routes) // 3. 分析弹窗组件(第二层) const modalMappings = this.analyzeModalComponents() // 4. 生成映射文件 this.generateMappingFile(pageMappings, modalMappings) console.log('✅ 第一阶段直接映射关系收集完成') } catch (error) { console.error('❌ 直接映射关系收集失败:', error) throw error } }, // 分析路由配置 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) routes.push(route) } } }) return routes }, // 分析页面组件(第一层) analyzePageComponents(routes) { const pageMappings = [] routes.forEach(route => { if (route.component) { const componentAnalysis = this.analyzeComponent(route.component, route.path) if (componentAnalysis && componentAnalysis.apiCalls.length > 0) { pageMappings.push({ route: route.path, routeName: route.name, component: route.component, module: route.module, layer: 'page', apiCalls: componentAnalysis.apiCalls, methods: componentAnalysis.methods }) } } }) return pageMappings }, // 分析弹窗组件(第二层) analyzeModalComponents() { const modalMappings = [] const modalFiles = this.findModalFiles() modalFiles.forEach(modalFile => { const analysis = this.analyzeComponent(modalFile.name, modalFile.path) if (analysis && analysis.apiCalls.length > 0) { modalMappings.push({ component: modalFile.name, path: modalFile.path, module: modalFile.module, layer: 'modal', apiCalls: analysis.apiCalls, methods: analysis.methods }) } }) return modalMappings }, // 分析单个组件 analyzeComponent(componentName, componentPath) { 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.analyzeVueComponent(content, componentName) } else { return this.analyzeJavaScriptComponent(content, componentName) } } catch (error) { console.warn(`⚠️ 分析组件失败: ${componentName}`, error.message) return null } }, // 分析Vue组件 analyzeVueComponent(content, componentName) { const { descriptor } = parse(content) const scriptContent = descriptor.script?.content || '' if (!scriptContent) { return { apiCalls: [], methods: [] } } const ast = parser.parse(scriptContent, { sourceType: 'module', plugins: ['jsx'] }) return this.extractApiCallsFromAST(ast, componentName) }, // 分析JavaScript组件 analyzeJavaScriptComponent(content, componentName) { const ast = parser.parse(content, { sourceType: 'module', plugins: ['jsx'] }) return this.extractApiCallsFromAST(ast, componentName) }, // 从AST中提取API调用 extractApiCallsFromAST(ast, componentName) { const apiCalls = [] 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) } }, // 查找API调用 CallExpression(path) { const callInfo = self.extractApiCall(path) if (callInfo) { apiCalls.push(callInfo) } } }) return { apiCalls, methods } }, // 提取API调用信息 extractApiCall(path) { const node = path.node const self = this // 检查是否是服务调用 (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 => self.extractArgumentValue(arg)), line: node.loc?.start?.line || 0 } } } } // 检查是否是直接的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) { if (object.name === 'api' || object.name === 'axios' || object.name === 'http') { const method = property.name const args = node.arguments if (args.length > 0 && args[0].type === 'StringLiteral') { return { type: 'api', method: method.toUpperCase(), path: args[0].value, arguments: args.slice(1).map(arg => self.extractArgumentValue(arg)), line: node.loc?.start?.line || 0 } } } } } return null }, // 从模块配置中读取模块名称 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 [] } }, // 查找弹窗文件 findModalFiles() { const modalFiles = [] const moduleDirs = this.readModuleNamesFromConfig() moduleDirs.forEach(moduleName => { 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 => { modalFiles.push({ name: file.replace('.vue', ''), path: resolve(componentsPath, file), module: moduleName }) }) } }) return modalFiles }, // 查找组件文件 findComponentFile(componentName) { 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 }, // 生成映射文件 generateMappingFile(pageMappings, modalMappings) { const mappingContent = `// 第一阶段:直接路由-API映射关系 // 此文件由 route-mapping-plugin 在构建时生成 // 收集页面和弹窗组件的直接API调用关系 // 请勿手动修改 export const directRouteMappings = { // 第一层:页面组件的数据操作API pageMappings: ${JSON.stringify(pageMappings, null, 2)}, // 第二层:弹窗组件的数据操作API modalMappings: ${JSON.stringify(modalMappings, null, 2)}, // 分析信息 analysisInfo: { phase: 'phase1', description: '第一阶段:收集直接映射关系', timestamp: new Date().toISOString(), pageCount: ${pageMappings.length}, modalCount: ${modalMappings.length}, totalApiCalls: ${pageMappings.reduce((sum, p) => sum + p.apiCalls.length, 0) + modalMappings.reduce((sum, m) => sum + m.apiCalls.length, 0)} } } // 按模块分组的映射 export const moduleMappings = ${JSON.stringify(this.groupMappingsByModule(pageMappings, modalMappings), null, 2)} // 按API路径分组的映射 export const apiPathMappings = ${JSON.stringify(this.groupMappingsByApiPath(pageMappings, modalMappings), null, 2)} export default directRouteMappings ` const outputPath = resolve(__dirname, '../src/renderer/modules/route-sync/direct-route-mappings.js') fs.writeFileSync(outputPath, mappingContent, 'utf-8') console.log(`✅ 直接映射关系文件已生成: ${outputPath}`) console.log(`📊 统计信息:`) console.log(` - 页面组件: ${pageMappings.length} 个`) console.log(` - 弹窗组件: ${modalMappings.length} 个`) console.log(` - 总API调用: ${pageMappings.reduce((sum, p) => sum + p.apiCalls.length, 0) + modalMappings.reduce((sum, m) => sum + m.apiCalls.length, 0)} 个`) }, // 按模块分组映射 groupMappingsByModule(pageMappings, modalMappings) { const grouped = {} // 分组页面映射 pageMappings.forEach(mapping => { const module = mapping.module || 'unknown' if (!grouped[module]) { grouped[module] = { pages: [], modals: [] } } grouped[module].pages.push(mapping) }) // 分组弹窗映射 modalMappings.forEach(mapping => { const module = mapping.module || 'unknown' if (!grouped[module]) { grouped[module] = { pages: [], modals: [] } } grouped[module].modals.push(mapping) }) return grouped }, // 按API路径分组映射 groupMappingsByApiPath(pageMappings, modalMappings) { const grouped = {} const addToGroup = (mapping, layer) => { mapping.apiCalls.forEach(apiCall => { let key = '' if (apiCall.type === 'api') { key = `${apiCall.method} ${apiCall.path}` } else if (apiCall.type === 'service') { key = `${apiCall.service}.${apiCall.method}` } if (key) { if (!grouped[key]) { grouped[key] = { pages: [], modals: [] } } grouped[key][layer].push({ component: mapping.component, module: mapping.module, route: mapping.route || mapping.path, apiCall: apiCall }) } }) } pageMappings.forEach(mapping => addToGroup(mapping, 'pages')) modalMappings.forEach(mapping => addToGroup(mapping, 'modals')) return grouped }, // 提取组件名称 extractComponentName(node) { if (node.type === 'Identifier') { return node.name } else if (node.type === 'StringLiteral') { return node.value } 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 }, // 从路径提取模块名 extractModuleFromPath(path) { if (path === '/' || path === '') return 'home' const segments = path.split('/').filter(Boolean) if (segments.length === 0) return 'home' return segments[0] } } } module.exports = { routeMappingPlugin }