|
|
|
@ -1,5 +1,7 @@
@@ -1,5 +1,7 @@
|
|
|
|
|
const { readFileSync, existsSync, readdirSync } = require('fs') |
|
|
|
|
const { resolve } = require('path') |
|
|
|
|
const parser = require('@babel/parser') |
|
|
|
|
const traverse = require('@babel/traverse').default |
|
|
|
|
const AstAnalyzer = require('./ast-analyzer') |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -12,7 +14,7 @@ class TriggerAnalyzer {
@@ -12,7 +14,7 @@ class TriggerAnalyzer {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 查找API方法的触发器源 |
|
|
|
|
* 查找API方法的触发器源 - 第一步:第一层触发源分析 |
|
|
|
|
* @param {Array} apiMappings - API映射数组 |
|
|
|
|
* @returns {Array} 增强的API映射数组 |
|
|
|
|
*/ |
|
|
|
@ -26,30 +28,72 @@ class TriggerAnalyzer {
@@ -26,30 +28,72 @@ class TriggerAnalyzer {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
moduleMapping.apiMappings.forEach(apiMapping => { |
|
|
|
|
const triggerSources = this.findTriggerSourcesForApiMethod( |
|
|
|
|
apiMapping.serviceName, |
|
|
|
|
// 第一步:查找第一层触发源
|
|
|
|
|
const triggerSources = this.findFirstLevelTriggerSources( |
|
|
|
|
moduleMapping.serviceName, // 使用第一层的serviceName
|
|
|
|
|
apiMapping.apiMethodName, |
|
|
|
|
moduleMapping.module |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
if (triggerSources.length > 0) { |
|
|
|
|
enhancedModuleMapping.apiMappings.push({ |
|
|
|
|
...apiMapping, |
|
|
|
|
triggerSources: triggerSources |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
// 为所有API映射添加triggerSources字段(即使为空)
|
|
|
|
|
enhancedModuleMapping.apiMappings.push({ |
|
|
|
|
...apiMapping, |
|
|
|
|
triggerSources: triggerSources || [] |
|
|
|
|
}) |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
if (enhancedModuleMapping.apiMappings.length > 0) { |
|
|
|
|
enhancedApiMappings.push(enhancedModuleMapping) |
|
|
|
|
} |
|
|
|
|
// 返回所有模块映射(包括没有触发源的)
|
|
|
|
|
enhancedApiMappings.push(enhancedModuleMapping) |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
return enhancedApiMappings |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 查找API方法的触发器源 |
|
|
|
|
* 查找第一层触发源 - 使用Babel分析真正的调用关系 |
|
|
|
|
* @param {string} serviceName - 服务名称 |
|
|
|
|
* @param {string} apiMethodName - API方法名称 |
|
|
|
|
* @param {string} moduleName - 模块名称 |
|
|
|
|
* @returns {Array} 触发器源数组 |
|
|
|
|
*/ |
|
|
|
|
findFirstLevelTriggerSources(serviceName, apiMethodName, moduleName) { |
|
|
|
|
const triggerSources = [] |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
// 1. 遍历模块中的所有Vue组件文件
|
|
|
|
|
const modulePath = resolve(__dirname, `../../src/renderer/modules/${moduleName}`) |
|
|
|
|
if (existsSync(modulePath)) { |
|
|
|
|
const files = readdirSync(modulePath, { recursive: true }) |
|
|
|
|
|
|
|
|
|
files.forEach(file => { |
|
|
|
|
if (typeof file === 'string' && file.endsWith('.vue')) { |
|
|
|
|
const filePath = resolve(modulePath, file) |
|
|
|
|
const componentName = this.extractComponentNameFromPath(file) |
|
|
|
|
|
|
|
|
|
// 2. 使用Babel分析Vue组件中的API调用
|
|
|
|
|
const componentTriggerSources = this.analyzeComponentForApiCalls( |
|
|
|
|
filePath,
|
|
|
|
|
serviceName,
|
|
|
|
|
apiMethodName,
|
|
|
|
|
componentName |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
triggerSources.push(...componentTriggerSources) |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} catch (error) { |
|
|
|
|
console.warn(`分析模块 ${moduleName} 时出错:`, error.message) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 去重
|
|
|
|
|
const uniqueTriggerSources = this.deduplicateTriggerSources(triggerSources) |
|
|
|
|
|
|
|
|
|
return uniqueTriggerSources |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 查找API方法的触发器源(保留原方法用于后续扩展) |
|
|
|
|
* @param {string} serviceName - 服务名称 |
|
|
|
|
* @param {string} apiMethodName - API方法名称 |
|
|
|
|
* @param {string} moduleName - 模块名称 |
|
|
|
@ -105,17 +149,23 @@ class TriggerAnalyzer {
@@ -105,17 +149,23 @@ class TriggerAnalyzer {
|
|
|
|
|
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] |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
// 根据模块名称映射到对应的组件名称
|
|
|
|
|
const moduleToComponentMap = { |
|
|
|
|
'role-management': 'RoleManagement', |
|
|
|
|
'user-management': 'UserManagement', |
|
|
|
|
'system-settings': 'Settings', |
|
|
|
|
'user-profile': 'UserProfile' |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const componentName = moduleToComponentMap[moduleName] |
|
|
|
|
if (componentName) { |
|
|
|
|
// 查找包含该组件的路由
|
|
|
|
|
const routeMatches = routeContent.match(new RegExp(`component:\\s*${componentName}`, 'g')) |
|
|
|
|
if (routeMatches) { |
|
|
|
|
routeComponents.push({ |
|
|
|
|
component: componentName |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} catch (error) { |
|
|
|
@ -402,6 +452,358 @@ class TriggerAnalyzer {
@@ -402,6 +452,358 @@ class TriggerAnalyzer {
|
|
|
|
|
return true |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 从文件路径提取组件名称 |
|
|
|
|
* @param {string} filePath - 文件路径 |
|
|
|
|
* @returns {string} 组件名称 |
|
|
|
|
*/ |
|
|
|
|
extractComponentNameFromPath(filePath) { |
|
|
|
|
const fileName = filePath.split('/').pop().split('\\').pop() |
|
|
|
|
return fileName.replace('.vue', '') |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 分析Vue组件中的API调用 |
|
|
|
|
* @param {string} filePath - 组件文件路径 |
|
|
|
|
* @param {string} serviceName - 服务名称 |
|
|
|
|
* @param {string} apiMethodName - API方法名称 |
|
|
|
|
* @param {string} componentName - 组件名称 |
|
|
|
|
* @returns {Array} 触发源数组 |
|
|
|
|
*/ |
|
|
|
|
analyzeComponentForApiCalls(filePath, serviceName, apiMethodName, componentName) { |
|
|
|
|
const triggerSources = [] |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
const content = readFileSync(filePath, 'utf-8') |
|
|
|
|
|
|
|
|
|
// 使用Babel解析Vue组件
|
|
|
|
|
const ast = this.parseVueComponent(content) |
|
|
|
|
if (!ast) return triggerSources |
|
|
|
|
|
|
|
|
|
// 查找API调用和函数调用关系
|
|
|
|
|
const apiCalls = this.findApiCallsWithContext(ast, serviceName, apiMethodName) |
|
|
|
|
|
|
|
|
|
// 为每个API调用创建触发源
|
|
|
|
|
apiCalls.forEach(call => { |
|
|
|
|
triggerSources.push({ |
|
|
|
|
component: componentName, |
|
|
|
|
triggerName: call.triggerName || '', |
|
|
|
|
triggerType: call.triggerType || 'function' |
|
|
|
|
}) |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
} catch (error) { |
|
|
|
|
console.warn(`分析组件 ${filePath} 时出错:`, error.message) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return triggerSources |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 解析Vue组件内容 |
|
|
|
|
* @param {string} content - 组件内容 |
|
|
|
|
* @returns {Object|null} AST对象 |
|
|
|
|
*/ |
|
|
|
|
parseVueComponent(content) { |
|
|
|
|
try { |
|
|
|
|
// 提取<script>部分
|
|
|
|
|
const scriptMatch = content.match(/<script[^>]*>([\s\S]*?)<\/script>/) |
|
|
|
|
if (!scriptMatch) return null |
|
|
|
|
|
|
|
|
|
const scriptContent = scriptMatch[1] |
|
|
|
|
|
|
|
|
|
// 使用Babel解析JavaScript代码
|
|
|
|
|
const ast = parser.parse(scriptContent, { |
|
|
|
|
sourceType: 'module', |
|
|
|
|
plugins: ['jsx', 'decorators-legacy', 'classProperties', 'objectRestSpread'] |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
return ast |
|
|
|
|
} catch (error) { |
|
|
|
|
console.warn('解析Vue组件脚本时出错:', error.message) |
|
|
|
|
return null |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 在AST中查找API调用和函数调用关系 |
|
|
|
|
* @param {Object} ast - AST对象 |
|
|
|
|
* @param {string} serviceName - 服务名称 |
|
|
|
|
* @param {string} apiMethodName - API方法名称 |
|
|
|
|
* @returns {Array} API调用信息数组 |
|
|
|
|
*/ |
|
|
|
|
findApiCallsWithContext(ast, serviceName, apiMethodName) { |
|
|
|
|
const apiCalls = [] |
|
|
|
|
const self = this |
|
|
|
|
const functionCallMap = new Map() // 存储函数调用关系
|
|
|
|
|
|
|
|
|
|
// 第一遍:收集所有函数定义和调用关系
|
|
|
|
|
traverse(ast, { |
|
|
|
|
// 收集函数定义
|
|
|
|
|
FunctionDeclaration(path) { |
|
|
|
|
if (path.node.id && path.node.id.name) { |
|
|
|
|
functionCallMap.set(path.node.id.name, { |
|
|
|
|
type: 'function', |
|
|
|
|
path: path |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
// 收集箭头函数和const函数
|
|
|
|
|
VariableDeclarator(path) { |
|
|
|
|
if (path.node.id && path.node.id.type === 'Identifier' &&
|
|
|
|
|
path.node.init && path.node.init.type === 'ArrowFunctionExpression') { |
|
|
|
|
functionCallMap.set(path.node.id.name, { |
|
|
|
|
type: 'arrowFunction', |
|
|
|
|
path: path |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
// 收集函数调用关系
|
|
|
|
|
CallExpression(path) { |
|
|
|
|
const { node } = path |
|
|
|
|
|
|
|
|
|
// 记录函数调用
|
|
|
|
|
if (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.findCallContextWithChain(path, functionCallMap) |
|
|
|
|
|
|
|
|
|
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路径对象 |
|
|
|
|
* @param {Map} functionCallMap - 函数调用关系映射 |
|
|
|
|
* @returns {Object} 上下文信息 |
|
|
|
|
*/ |
|
|
|
|
findCallContextWithChain(path, functionCallMap) { |
|
|
|
|
let currentPath = path |
|
|
|
|
let triggerName = '' |
|
|
|
|
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' // setup是Vue 3的生命周期钩子
|
|
|
|
|
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 } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 查找API调用的上下文 |
|
|
|
|
* @param {Object} path - Babel路径对象 |
|
|
|
|
* @returns {Object} 上下文信息 |
|
|
|
|
*/ |
|
|
|
|
findCallContext(path) { |
|
|
|
|
let currentPath = path |
|
|
|
|
let triggerName = '' |
|
|
|
|
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' // setup是Vue 3的生命周期钩子
|
|
|
|
|
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 |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 检查是否在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 } |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
module.exports = TriggerAnalyzer |
|
|
|
|