const { readFileSync, existsSync, readdirSync } = require('fs') const { resolve } = require('path') const AstAnalyzer = require('./ast-analyzer') /** * 触发器分析模块 * 负责分析组件中的按钮和事件触发器 */ class TriggerAnalyzer { constructor() { this.astAnalyzer = new AstAnalyzer() } /** * 查找API方法的触发器源 * @param {Array} apiMappings - API映射数组 * @returns {Array} 增强的API映射数组 */ findTriggerSourcesForApiMappings(apiMappings) { const enhancedApiMappings = [] apiMappings.forEach(moduleMapping => { const enhancedModuleMapping = { ...moduleMapping, apiMappings: [] } moduleMapping.apiMappings.forEach(apiMapping => { const triggerSources = this.findTriggerSourcesForApiMethod( apiMapping.serviceName, apiMapping.apiMethodName, moduleMapping.module ) if (triggerSources.length > 0) { enhancedModuleMapping.apiMappings.push({ ...apiMapping, triggerSources: triggerSources }) } }) if (enhancedModuleMapping.apiMappings.length > 0) { enhancedApiMappings.push(enhancedModuleMapping) } }) return enhancedApiMappings } /** * 查找API方法的触发器源 * @param {string} serviceName - 服务名称 * @param {string} apiMethodName - API方法名称 * @param {string} moduleName - 模块名称 * @returns {Array} 触发器源数组 */ findTriggerSourcesForApiMethod(serviceName, apiMethodName, moduleName) { const triggerSources = [] // 1. 检查路由组件 const routeComponents = this.getRouteComponents(moduleName) routeComponents.forEach(route => { const triggerAnalysis = this.analyzeComponentWithAST( route.component, serviceName, apiMethodName ) if (triggerAnalysis.triggerSources.length > 0) { triggerSources.push(...triggerAnalysis.triggerSources) } }) // 2. 检查模块中的所有组件 const componentsInModule = this.getModuleComponents(moduleName) componentsInModule.forEach(componentName => { const triggerAnalysis = this.analyzeComponentWithAST( componentName, serviceName, apiMethodName ) if (triggerAnalysis.triggerSources.length > 0) { triggerSources.push(...triggerAnalysis.triggerSources) } }) // 去重 const uniqueTriggerSources = this.deduplicateTriggerSources(triggerSources) return uniqueTriggerSources } /** * 获取路由组件 * @param {string} moduleName - 模块名称 * @returns {Array} 路由组件数组 */ getRouteComponents(moduleName) { const routeComponents = [] try { const routeConfigPath = resolve(__dirname, '../../src/renderer/router/index.js') if (existsSync(routeConfigPath)) { const routeContent = readFileSync(routeConfigPath, 'utf-8') // 简单的路由解析,查找模块相关的路由 const routeMatches = routeContent.match(new RegExp(`component:\\s*['"]${moduleName}[^'"]*['"]`, 'g')) if (routeMatches) { routeMatches.forEach(match => { const componentMatch = match.match(/component:\s*['"]([^'"]+)['"]/) if (componentMatch) { routeComponents.push({ component: componentMatch[1] }) } }) } } } catch (error) { // 静默处理错误 } return routeComponents } /** * 获取模块中的所有组件 * @param {string} moduleName - 模块名称 * @returns {Array} 组件名称数组 */ getModuleComponents(moduleName) { const components = [] try { const modulePath = resolve(__dirname, '../../src/renderer/modules', moduleName) if (existsSync(modulePath)) { // 查找views目录 const viewsPath = resolve(modulePath, 'views') if (existsSync(viewsPath)) { const viewFiles = readdirSync(viewsPath).filter(file => file.endsWith('.vue')) viewFiles.forEach(file => { components.push(file.replace('.vue', '')) }) } // 查找components目录 const componentsPath = resolve(modulePath, 'components') if (existsSync(componentsPath)) { const componentFiles = readdirSync(componentsPath).filter(file => file.endsWith('.vue')) componentFiles.forEach(file => { components.push(file.replace('.vue', '')) }) } } } catch (error) { // 静默处理错误 } return components } /** * 使用AST分析组件 * @param {string} componentName - 组件名称 * @param {string} serviceName - 服务名称 * @param {string} apiMethodName - API方法名称 * @returns {Object} 触发器分析结果 */ analyzeComponentWithAST(componentName, serviceName, apiMethodName) { const triggerSources = [] try { const filePath = this.findComponentFile(componentName) if (!filePath) { return { triggerSources: [] } } const content = readFileSync(filePath, 'utf-8') // 1. 检查组件的authType属性,如果是public则跳过 const authTypeMatch = content.match(/authType\s*[=:]\s*["']([^"']+)["']/) if (authTypeMatch && authTypeMatch[1] === 'public') { return { triggerSources: [] } } // 2. 检查组件是否包含目标API调用 if (!content.includes(`${serviceName}.${apiMethodName}`)) { return { triggerSources: [] } } // 3. 使用Babel AST分析找到调用该API的方法 const callingMethods = this.astAnalyzer.findMethodsCallingServiceWithBabel(content, serviceName, apiMethodName) if (callingMethods.length > 0) { // 4. 分析每个调用方法的触发源 const methodTriggers = this.analyzeMethodTriggerSources(content, componentName, callingMethods, filePath) triggerSources.push(...methodTriggers) } else { // 如果没有找到按钮触发器,记录页面级触发器 triggerSources.push({ component: componentName, triggerName: null, triggerType: 'page' }) } } catch (error) { // 静默处理错误 } return { triggerSources } } /** * 查找组件文件 * @param {string} componentName - 组件名称 * @returns {string|null} 组件文件路径 */ findComponentFile(componentName) { const possiblePaths = [ resolve(__dirname, '../../src/renderer/modules', componentName, 'views', `${componentName}.vue`), resolve(__dirname, '../../src/renderer/modules', componentName, 'components', `${componentName}.vue`), resolve(__dirname, '../../src/renderer/components', `${componentName}.vue`), resolve(__dirname, '../../src/renderer/views', `${componentName}.vue`) ] for (const path of possiblePaths) { if (existsSync(path)) { return path } } return null } /** * 分析方法的触发源 * @param {string} content - 组件内容 * @param {string} componentName - 组件名称 * @param {Array} methodNames - 方法名数组 * @param {string} filePath - 文件路径 * @returns {Array} 触发器源数组 */ analyzeMethodTriggerSources(content, componentName, methodNames, filePath = null) { const triggerSources = [] // 提取模板部分 const templateMatch = content.match(/