const { readFileSync, existsSync, readdirSync } = require('fs') const { resolve } = require('path') const { parse } = require('@vue/compiler-dom') const parser = require('@babel/parser') const traverse = require('@babel/traverse').default /** * 简化版Trigger分析器 * 从原有trigger-analyzer中提取核心逻辑 */ class TriggerAnalyzerSimple { constructor() { } /** * 为API映射查找触发源 * 使用Babel插件追溯API调用控件,记录控件的类型和名称 * @param {Array} apiMappings - API映射数组 * @param {Array} routes - 路由数组 * @returns {Array} 增强的API映射数组 */ findTriggerSourcesForApiMappings(apiMappings, routes) { const enhancedApiMappings = apiMappings.map(api => { // 查找该API的触发源 const triggerSources = this.findTriggerSourcesForApiMethod( api.serviceName, api.apiMethodName, api.module ) return { ...api, triggerSources: triggerSources } }) return enhancedApiMappings } /** * 查找API方法的触发源 * @param {string} serviceName - 服务名称 * @param {string} apiMethodName - API方法名称 * @param {string} moduleName - 模块名称 * @returns {Array} 触发源数组 */ findTriggerSourcesForApiMethod(serviceName, apiMethodName, moduleName) { const triggerSources = [] // 检查模块中的所有组件 const componentsInModule = this.getModuleComponents(moduleName) componentsInModule.forEach(componentName => { const componentTriggerSources = this.analyzeComponentForApiCalls( componentName, serviceName, apiMethodName, moduleName ) triggerSources.push(...componentTriggerSources) }) // 去重 return this.deduplicateTriggerSources(triggerSources) } /** * 获取模块中的所有组件 * @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) { console.warn(`获取模块组件时出错: ${error.message}`) } return components } /** * 分析组件中的API调用 * @param {string} componentName - 组件名称 * @param {string} serviceName - 服务名称 * @param {string} apiMethodName - API方法名称 * @param {string} moduleName - 模块名称 * @returns {Array} 触发源数组 */ analyzeComponentForApiCalls(componentName, serviceName, apiMethodName, moduleName) { const triggerSources = [] try { // 查找组件文件,优先在指定模块中查找 const componentPath = this.findComponentFile(componentName, moduleName) if (!componentPath) { return triggerSources } // 检查组件的authType,如果是public则跳过 const authType = this.getComponentAuthType(componentName, moduleName) if (authType === 'public') { return triggerSources } const content = readFileSync(componentPath, 'utf-8') // 使用Babel解析Vue组件 const ast = this.parseVueComponent(content) if (!ast) return triggerSources // 查找API调用和函数调用关系 const apiCalls = this.findApiCallsWithContext(ast, serviceName, apiMethodName, componentPath, componentName) // 为每个API调用创建触发源 apiCalls.forEach(call => { triggerSources.push({ component: componentName, module: moduleName, // 添加模块信息 triggerName: call.triggerName || null, // 没有名称则为null triggerType: call.triggerType || 'function' }) }) } catch (error) { // 静默处理错误 } return triggerSources } /** * 查找组件文件 * @param {string} componentName - 组件名称 * @param {string} moduleName - 模块名称(可选,如果提供则优先在该模块中查找) * @returns {string|null} 组件文件路径 */ findComponentFile(componentName, moduleName = null) { // 如果提供了模块名称,只在该模块中查找,不允许跨模块查找 if (moduleName) { // 检查views目录 const viewPath = resolve(__dirname, `../../src/renderer/modules/${moduleName}/views/${componentName}.vue`) if (existsSync(viewPath)) { return viewPath } // 检查components目录 const componentPath = resolve(__dirname, `../../src/renderer/modules/${moduleName}/components/${componentName}.vue`) if (existsSync(componentPath)) { return componentPath } // 如果在指定模块中没有找到,返回null(不允许跨模块查找) return null } // 如果没有提供模块名称,则在所有模块中查找 const modulesPath = resolve(__dirname, '../../src/renderer/modules') if (existsSync(modulesPath)) { const { readdirSync } = require('fs') const moduleDirs = readdirSync(modulesPath, { withFileTypes: true }) .filter(dirent => dirent.isDirectory()) .map(dirent => dirent.name) for (const moduleDir of moduleDirs) { // 检查views目录 const viewPath = resolve(__dirname, `../../src/renderer/modules/${moduleDir}/views/${componentName}.vue`) if (existsSync(viewPath)) { return viewPath } // 检查components目录 const componentPath = resolve(__dirname, `../../src/renderer/modules/${moduleDir}/components/${componentName}.vue`) if (existsSync(componentPath)) { return componentPath } } } return null } /** * 从Vue组件文件中获取authType属性 * @param {string} componentName - 组件名称 * @param {string} moduleName - 模块名称(可选) * @returns {string|null} authType值 */ getComponentAuthType(componentName, moduleName = null) { try { const componentPath = this.findComponentFile(componentName, moduleName) if (componentPath) { const content = readFileSync(componentPath, 'utf-8') const authTypeMatch = content.match(/]*>([\s\S]*?)<\/script>/) if (!scriptMatch) { return null } const scriptContent = scriptMatch[1] // 使用Babel解析JavaScript const ast = parser.parse(scriptContent, { sourceType: 'module', plugins: ['jsx', 'typescript'] }) return ast } catch (error) { return null } } /** * 查找API调用并分析上下文 * @param {Object} ast - AST对象 * @param {string} serviceName - 服务名称 * @param {string} apiMethodName - API方法名称 * @param {string} filePath - 文件路径 * @param {string} componentName - 组件名称 * @returns {Array} API调用数组 */ findApiCallsWithContext(ast, serviceName, apiMethodName, filePath, componentName) { const apiCalls = [] const self = this // 第一遍:建立函数调用关系映射 const functionCallMap = new Map() traverse(ast, { CallExpression(path) { const { node } = path if (node.callee && node.callee.type === 'Identifier') { const functionName = node.callee.name const parentFunction = self.findParentFunction(path) if (parentFunction) { if (!functionCallMap.has(functionName)) { functionCallMap.set(functionName, { callers: [] }) } const funcInfo = functionCallMap.get(functionName) if (!funcInfo.callers) funcInfo.callers = [] funcInfo.callers.push(parentFunction) } } } }) // 第二遍:查找API调用并分析调用链 traverse(ast, { CallExpression(path) { const { node } = path // 查找 serviceName.apiMethodName() 调用 if (node.callee.type === 'MemberExpression' && node.callee.object.name === serviceName && node.callee.property.name === apiMethodName) { // 查找调用上下文 const context = self.findCallContext(path) apiCalls.push({ triggerName: context.triggerName, triggerType: context.triggerType }) } } }) return apiCalls } /** * 查找父级函数 * @param {Object} path - Babel路径对象 * @returns {string|null} 父级函数名 */ findParentFunction(path) { let currentPath = path.parentPath while (currentPath) { const { node } = currentPath if (node.type === 'FunctionDeclaration' && node.id) { return node.id.name } if (node.type === 'VariableDeclarator' && node.id && node.id.type === 'Identifier' && node.init && node.init.type === 'ArrowFunctionExpression') { return node.id.name } if (node.type === 'ObjectMethod' && node.key) { return node.key.name } currentPath = currentPath.parentPath } return null } /** * 查找API调用的上下文 * @param {Object} path - Babel路径对象 * @returns {Object} 上下文信息 */ findCallContext(path) { let currentPath = path let triggerName = null let triggerType = 'function' // 向上遍历AST,查找触发源 while (currentPath) { const { node } = currentPath // 检查是否在Vue 3 Composition API生命周期钩子中 if (node.type === 'CallExpression' && node.callee.type === 'Identifier' && ['onMounted', 'onCreated', 'onBeforeMount', 'onBeforeCreate', 'onUpdated', 'onBeforeUpdate', 'onUnmounted', 'onBeforeUnmount'].includes(node.callee.name)) { triggerName = node.callee.name triggerType = 'lifecycle' break } // 检查是否在Vue 3 Composition API的setup方法中 if (node.type === 'ObjectMethod' && node.key && node.key.name === 'setup') { triggerName = 'setup' triggerType = 'lifecycle' break } // 检查是否在方法定义中 if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression') { if (node.id && node.id.name) { triggerName = node.id.name triggerType = 'function' break } } // 检查是否在对象方法中(非setup) if (node.type === 'ObjectMethod') { if (node.key && node.key.name && node.key.name !== 'setup') { triggerName = node.key.name triggerType = 'method' break } } // 检查是否在箭头函数中 if (node.type === 'ArrowFunctionExpression') { // 查找父级的属性名 const parent = currentPath.parent if (parent && parent.type === 'ObjectProperty' && parent.key) { const methodName = parent.key.name || '' if (methodName === 'setup') { triggerName = 'setup' triggerType = 'lifecycle' } else { triggerName = methodName triggerType = 'method' } break } // 检查是否在const声明的箭头函数中 if (parent && parent.type === 'VariableDeclarator' && parent.id) { const functionName = parent.id.name || '' triggerName = functionName triggerType = 'method' break } } // 检查是否在Vue 2生命周期钩子中 if (node.type === 'CallExpression' && node.callee.type === 'MemberExpression' && node.callee.object.name === 'this' && ['mounted', 'created', 'beforeMount', 'beforeCreate'].includes(node.callee.property.name)) { triggerName = node.callee.property.name triggerType = 'lifecycle' break } currentPath = currentPath.parentPath } return { triggerName, triggerType } } /** * 去重触发源 * @param {Array} triggerSources - 触发源数组 * @returns {Array} 去重后的触发源数组 */ deduplicateTriggerSources(triggerSources) { const seen = new Set() return triggerSources.filter(trigger => { const key = `${trigger.component}-${trigger.triggerName}-${trigger.triggerType}` if (seen.has(key)) { return false } seen.add(key) return true }) } } module.exports = TriggerAnalyzerSimple