import { resolve } from 'path' import fs from 'fs' import * as parser from '@babel/parser' import traverse from '@babel/traverse' import { parse } from '@vue/compiler-sfc' // 路由映射插件 - 真正利用AST解析和Vite编译时关系 export function routeMappingPlugin() { return { name: 'route-mapping', // 在构建开始时执行 buildStart() { console.log('🔧 开始分析AST和Vite编译时关系...') this.analyzeASTAndViteRelations() }, // 在开发模式下也执行 configureServer(server) { console.log('🔧 开发模式下分析AST和Vite编译时关系...') this.analyzeASTAndViteRelations() }, // 利用AST和Vite编译时API分析关系 analyzeASTAndViteRelations() { try { // 1. 分析路由配置的AST const routerAST = this.analyzeRouterAST() // 2. 分析组件文件的AST const componentASTs = this.analyzeComponentASTs() // 3. 分析服务文件的AST const serviceASTs = this.analyzeServiceASTs() // 4. 建立真实的调用关系 const callRelations = this.buildCallRelations(routerAST, componentASTs, serviceASTs) // 5. 生成真实的路由映射 const routeMappings = this.generateRealRouteMappings(callRelations) // 6. 生成映射文件 this.generateRouteMappingFile(routeMappings) console.log('✅ AST和Vite编译时关系分析完成') } catch (error) { console.error('❌ AST和Vite编译时关系分析失败:', error) // 回退到简单分析 this.fallbackToSimpleAnalysis() } }, // 分析路由AST analyzeRouterAST() { const routerPath = resolve(__dirname, '../src/renderer/router/index.js') if (!fs.existsSync(routerPath)) { throw new Error('Router file not found') } const routerContent = fs.readFileSync(routerPath, 'utf-8') const ast = parser.parse(routerContent, { sourceType: 'module', plugins: ['jsx'] }) const routes = [] 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 = this.extractComponentName(prop.value) } }) if (route.path) { route.module = this.extractModuleFromPath(route.path) routes.push(route) } } }) return { routes, ast } }, // 分析组件AST analyzeComponentASTs() { const componentASTs = new Map() const componentFiles = this.findComponentFiles() componentFiles.forEach(componentFile => { try { const content = fs.readFileSync(componentFile.path, 'utf-8') if (componentFile.path.endsWith('.vue')) { // 解析Vue文件 const { descriptor } = parse(content) const scriptContent = descriptor.script?.content || '' if (scriptContent) { const scriptAST = parser.parse(scriptContent, { sourceType: 'module', plugins: ['jsx'] }) const analysis = this.analyzeVueComponentAST(scriptAST, componentFile.name) componentASTs.set(componentFile.name, { ...analysis, file: componentFile, template: descriptor.template?.content || '' }) } } else { // 解析JS文件 const ast = parser.parse(content, { sourceType: 'module', plugins: ['jsx'] }) const analysis = this.analyzeJavaScriptComponentAST(ast, componentFile.name) componentASTs.set(componentFile.name, { ...analysis, file: componentFile }) } } catch (error) { console.warn(`⚠️ 解析组件文件失败: ${componentFile.path}`, error.message) } }) return componentASTs }, // 分析服务AST analyzeServiceASTs() { const serviceASTs = new Map() const serviceFiles = this.findServiceFiles() serviceFiles.forEach(serviceFile => { try { const content = fs.readFileSync(serviceFile.path, 'utf-8') const ast = parser.parse(content, { sourceType: 'module', plugins: ['jsx'] }) const analysis = this.analyzeServiceAST(ast, serviceFile.name) serviceASTs.set(serviceFile.name, { ...analysis, file: serviceFile }) } catch (error) { console.warn(`⚠️ 解析服务文件失败: ${serviceFile.path}`, error.message) } }) return serviceASTs }, // 分析Vue组件AST analyzeVueComponentAST(ast, componentName) { const analysis = { imports: [], exports: [], methods: [], serviceCalls: [], events: [] } traverse(ast, { // 查找import语句 ImportDeclaration(path) { const importSpecifiers = path.node.specifiers importSpecifiers.forEach(specifier => { if (specifier.type === 'ImportSpecifier') { analysis.imports.push({ name: specifier.imported.name, local: specifier.local.name, source: path.node.source.value }) } }) }, // 查找方法定义 FunctionDeclaration(path) { if (path.node.id) { analysis.methods.push(path.node.id.name) } }, // 查找箭头函数 ArrowFunctionExpression(path) { if (path.parent && path.parent.type === 'VariableDeclarator' && path.parent.id) { analysis.methods.push(path.parent.id.name) } }, // 查找服务调用 CallExpression(path) { if (path.node.callee && path.node.callee.type === 'MemberExpression') { const object = path.node.callee.object const property = path.node.callee.property if (object && property && object.name && property.name) { // 检查是否是服务调用 const serviceName = object.name const methodName = property.name if (serviceName.endsWith('Service')) { analysis.serviceCalls.push({ service: serviceName, method: methodName, arguments: path.node.arguments.map(arg => this.extractArgumentValue(arg)) }) } } } } }) return analysis }, // 分析JavaScript组件AST analyzeJavaScriptComponentAST(ast, componentName) { return this.analyzeVueComponentAST(ast, componentName) }, // 分析服务AST analyzeServiceAST(ast, serviceName) { const analysis = { exports: [], methods: [], apiCalls: [], imports: [] } traverse(ast, { // 查找export语句 ExportNamedDeclaration(path) { if (path.node.declaration && path.node.declaration.type === 'VariableDeclarator') { analysis.exports.push(path.node.declaration.id.name) } }, // 查找方法定义 FunctionDeclaration(path) { if (path.node.id) { analysis.methods.push(path.node.id.name) } }, // 查找API调用 CallExpression(path) { if (path.node.callee && path.node.callee.type === 'MemberExpression') { const object = path.node.callee.object const property = path.node.callee.property if (object && property) { // 检查是否是API调用 if (object.name === 'api' || object.name === 'axios' || object.name === 'http') { const method = property.name const args = path.node.arguments if (args.length > 0 && args[0].type === 'StringLiteral') { analysis.apiCalls.push({ method: method.toUpperCase(), path: args[0].value, arguments: args.slice(1).map(arg => this.extractArgumentValue(arg)) }) } } } } } }) return analysis }, // 建立调用关系 buildCallRelations(routerAST, componentASTs, serviceASTs) { const relations = { routes: routerAST.routes, components: new Map(), services: new Map(), callGraph: new Map(), imports: new Map() } // 建立组件关系 componentASTs.forEach((analysis, componentName) => { relations.components.set(componentName, { ...analysis, serviceDependencies: analysis.serviceCalls.map(call => call.service) }) }) // 建立服务关系 serviceASTs.forEach((analysis, serviceName) => { relations.services.set(serviceName, { ...analysis, apiEndpoints: analysis.apiCalls }) }) // 建立调用图 componentASTs.forEach((componentAnalysis, componentName) => { const calls = [] componentAnalysis.serviceCalls.forEach(call => { calls.push({ type: 'service', target: call.service, method: call.method, component: componentName }) }) relations.callGraph.set(componentName, calls) }) return relations }, // 生成真实的路由映射 generateRealRouteMappings(callRelations) { const mappings = { mainRoutes: [], moduleApiMappings: {}, subRouteMappings: {}, callRelations: callRelations } // 生成主路由 callRelations.routes.forEach(route => { mappings.mainRoutes.push({ path: route.path, name: route.name, module: route.module, description: this.generateDescription(route.name, route.module), type: this.determineRouteType(route.path, route.module), component: route.component }) }) // 生成API映射 callRelations.services.forEach((serviceAnalysis, serviceName) => { const moduleName = this.extractModuleFromService(serviceName) if (moduleName && serviceAnalysis.apiCalls.length > 0) { const apiMapping = this.buildApiMappingFromRealCalls(serviceAnalysis.apiCalls, moduleName) mappings.moduleApiMappings[moduleName] = apiMapping } }) // 生成子路由映射 mappings.subRouteMappings = this.generateSubRouteMappingsFromRelations(callRelations) return mappings }, // 从真实调用构建API映射 buildApiMappingFromRealCalls(apiCalls, moduleName) { const operations = {} const paths = [] apiCalls.forEach(call => { const operation = this.inferOperationFromApiCall(call.path, call.method) if (operation) { operations[operation] = { path: call.path, method: call.method } paths.push(call.path) } }) const basePath = this.extractBasePath(paths) return { basePath: basePath, operations: operations } }, // 从API调用推断操作 inferOperationFromApiCall(path, method) { const pathLower = path.toLowerCase() // 根据路径模式推断操作 if (path.includes('/:id') || path.includes('/{id}')) { if (method === 'GET') return 'detail' if (method === 'PUT') return 'update' if (method === 'DELETE') return 'delete' } if (path.includes('/search')) return 'search' if (path.includes('/filter')) return 'filter' if (path.includes('/roles')) return 'getRoles' if (path.includes('/permissions')) return 'getPermissions' if (path.includes('/assign')) return 'assignRoles' if (path.includes('/remove')) return 'removeRoles' // 根据HTTP方法推断 if (method === 'GET' && !path.includes('/')) return 'list' if (method === 'POST' && !path.includes('/')) return 'create' if (method === 'PUT' && !path.includes('/')) return 'update' if (method === 'DELETE' && !path.includes('/')) return 'delete' // 默认操作名 return `${method.toLowerCase()}_${path.replace(/[^a-zA-Z0-9]/g, '_')}` }, // 从关系生成子路由映射 generateSubRouteMappingsFromRelations(callRelations) { const subRouteMappings = {} callRelations.callGraph.forEach((calls, componentName) => { calls.forEach(call => { if (call.type === 'service') { const operation = this.inferOperationFromServiceMethod(call.method) if (operation && operation !== 'list') { // 找到对应的主路由 const mainRoute = callRelations.routes.find(route => route.component === componentName || route.name.toLowerCase().includes(componentName.toLowerCase()) ) if (mainRoute) { const subPath = `${mainRoute.path}/${operation}` subRouteMappings[subPath] = { mainRoute: mainRoute.path, operation: operation } } } } }) }) return subRouteMappings }, // 回退到简单分析 fallbackToSimpleAnalysis() { console.log('⚠️ 使用简单分析作为回退...') const mappings = { mainRoutes: [], moduleApiMappings: {}, subRouteMappings: {}, method: 'simple-analysis' } // 简单的路由提取 const routerPath = resolve(__dirname, '../src/renderer/router/index.js') if (fs.existsSync(routerPath)) { const content = fs.readFileSync(routerPath, 'utf-8') mappings.mainRoutes = this.simpleRouteExtraction(content) } this.generateRouteMappingFile(mappings) console.log('✅ 简单分析完成') }, // 简单的路由提取 simpleRouteExtraction(content) { const routes = [] const routeRegex = /path:\s*['"`]([^'"`]+)['"`]/g const nameRegex = /name:\s*['"`]([^'"`]+)['"`]/g let match while ((match = routeRegex.exec(content)) !== null) { const path = match[1] const nameMatch = nameRegex.exec(content) const name = nameMatch ? nameMatch[1] : path.split('/').pop() routes.push({ path, name, module: this.extractModuleFromPath(path), description: this.generateDescription(name, this.extractModuleFromPath(path)), type: this.determineRouteType(path, this.extractModuleFromPath(path)) }) } return routes }, // 查找组件文件 findComponentFiles() { const componentFiles = [] const moduleDirs = ['user-management', 'role-management', 'system-settings'] moduleDirs.forEach(moduleName => { const viewsPath = resolve(__dirname, `../src/renderer/modules/${moduleName}/views`) const componentsPath = resolve(__dirname, `../src/renderer/modules/${moduleName}/components`) if (fs.existsSync(viewsPath)) { const files = fs.readdirSync(viewsPath).filter(f => f.endsWith('.vue')) files.forEach(file => { componentFiles.push({ name: file.replace('.vue', ''), path: resolve(viewsPath, file), module: moduleName, type: 'view' }) }) } if (fs.existsSync(componentsPath)) { const files = fs.readdirSync(componentsPath).filter(f => f.endsWith('.vue')) files.forEach(file => { componentFiles.push({ name: file.replace('.vue', ''), path: resolve(componentsPath, file), module: moduleName, type: 'component' }) }) } }) return componentFiles }, // 查找服务文件 findServiceFiles() { const serviceFiles = [] const moduleDirs = ['user-management', 'role-management', 'system-settings'] moduleDirs.forEach(moduleName => { const servicesPath = resolve(__dirname, `../src/renderer/modules/${moduleName}/services`) if (fs.existsSync(servicesPath)) { const files = fs.readdirSync(servicesPath).filter(f => f.endsWith('.js')) files.forEach(file => { serviceFiles.push({ name: file.replace('.js', ''), path: resolve(servicesPath, file), module: moduleName }) }) } }) return serviceFiles }, // 提取组件名称 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 }, // 从服务方法推断操作 inferOperationFromServiceMethod(methodName) { const methodMap = { 'getUsers': 'list', 'getUser': 'detail', 'createUser': 'create', 'updateUser': 'update', 'deleteUser': 'delete', 'getRoles': 'getRoles', 'getRole': 'detail', 'createRole': 'create', 'updateRole': 'update', 'deleteRole': 'delete', 'getPermissions': 'getPermissions' } return methodMap[methodName] || methodName }, // 从服务名提取模块名 extractModuleFromService(serviceName) { const serviceMap = { 'userService': 'user-management', 'roleService': 'role-management', 'settingsService': 'system-settings' } return serviceMap[serviceName] || null }, // 提取basePath extractBasePath(paths) { if (paths.length === 0) return '' // 找到所有路径的共同前缀 const firstPath = paths[0] let commonPrefix = '' for (let i = 0; i < firstPath.length; i++) { const char = firstPath[i] if (paths.every(path => path[i] === char)) { commonPrefix += char } else { break } } // 如果找到了完整的前缀,返回它 if (commonPrefix && !commonPrefix.endsWith('/')) { // 找到最后一个斜杠的位置 const lastSlashIndex = commonPrefix.lastIndexOf('/') if (lastSlashIndex > 0) { return commonPrefix.substring(0, lastSlashIndex + 1) } } return commonPrefix }, // 从路径提取模块名 extractModuleFromPath(path) { if (path === '/' || path === '') return 'home' const segments = path.split('/').filter(Boolean) if (segments.length === 0) return 'home' // 获取第一个段作为模块名 const module = segments[0] // 映射模块名 const moduleMap = { 'user-management': 'user-management', 'role-management': 'role-management', 'settings': 'system-settings', 'user-profile': 'user-management', 'route-sync-test': 'route-sync' } return moduleMap[module] || module }, // 生成路由描述 generateDescription(name, module) { const descriptions = { 'home': '首页', 'user-management': '用户管理', 'role-management': '角色管理', 'system-settings': '系统设置', 'route-sync': '路由同步测试' } return descriptions[module] || `${name}页面` }, // 确定路由类型 determineRouteType(path, module) { if (path === '/' || path === '') return 'home' // 根据模块确定类型 const typeMap = { 'user-management': 'list', 'role-management': 'list', 'system-settings': 'form', 'route-sync': 'test' } return typeMap[module] || 'list' }, // 生成路由映射文件 generateRouteMappingFile(routeMappings) { const mappingContent = `// 自动生成的路由映射文件 // 此文件由 route-mapping-plugin 在构建时生成 // 基于AST解析和Vite编译时关系分析 // 请勿手动修改 export const mainRoutes = ${JSON.stringify(routeMappings.mainRoutes, null, 2)} // 模块到API映射配置(从AST解析提取) export const moduleApiMappings = ${JSON.stringify(routeMappings.moduleApiMappings, null, 2)} // 子路由到主路由的映射(从调用关系提取) export const subRouteMappings = ${JSON.stringify(routeMappings.subRouteMappings, null, 2)} // 分析方法和结果 export const analysisInfo = ${JSON.stringify({ method: routeMappings.method || 'ast-analysis', timestamp: new Date().toISOString(), hasCallRelations: !!routeMappings.callRelations, componentCount: routeMappings.callRelations?.components?.size || 0, serviceCount: routeMappings.callRelations?.services?.size || 0 }, null, 2)} export default { mainRoutes, moduleApiMappings, subRouteMappings, analysisInfo } ` const outputPath = resolve(__dirname, '../src/renderer/modules/route-sync/generated-route-mapping.js') fs.writeFileSync(outputPath, mappingContent, 'utf-8') console.log(`✅ 基于AST解析的路由映射文件已生成: ${outputPath}`) } } }