You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

281 lines
8.8 KiB

const { readFileSync, existsSync, readdirSync } = require('fs')
const { resolve } = require('path')
const parser = require('@babel/parser')
const traverse = require('@babel/traverse').default
/**
* 组件关系分析器
* 分析组件之间的层级关系从子组件追溯到顶级组件
*/
class ComponentRelationshipAnalyzer {
constructor() {
this.componentRelationships = []
this.processedComponents = new Set()
}
/**
* 分析组件关系
* @param {Array} routes - 路由数组
* @param {Array} apiMappings - API映射数组
* @returns {Array} 组件关系列表
*/
analyzeComponentRelationships(routes, apiMappings) {
this.componentRelationships = []
this.processedComponents.clear()
// 获取所有顶级组件(routes中的组件)
const topLevelComponents = routes.map(route => ({
component: route.component,
module: this.getModuleFromRoute(route),
path: route.path
}))
// 获取所有触发源组件
const triggerSourceComponents = this.extractTriggerSourceComponents(apiMappings)
// 为每个触发源组件分析其与顶级组件的关系
triggerSourceComponents.forEach(triggerComponent => {
this.analyzeComponentToTopLevel(triggerComponent, topLevelComponents)
})
return this.componentRelationships
}
/**
* 从路由中获取模块信息
* @param {Object} route - 路由对象
* @returns {string} 模块名称
*/
getModuleFromRoute(route) {
// 根据路径推断模块
if (route.path === '/') return 'core'
if (route.path.includes('user-management')) return 'user-management'
if (route.path.includes('role-management')) return 'role-management'
if (route.path.includes('settings')) return 'system-settings'
if (route.path.includes('user-profile')) return 'user-management'
return 'core'
}
/**
* 提取所有触发源组件
* @param {Array} apiMappings - API映射数组
* @returns {Array} 触发源组件数组
*/
extractTriggerSourceComponents(apiMappings) {
const components = []
apiMappings.forEach(apiMapping => {
if (apiMapping.triggerSources) {
apiMapping.triggerSources.forEach(triggerSource => {
components.push({
component: triggerSource.component,
module: triggerSource.module,
triggerName: triggerSource.triggerName,
triggerType: triggerSource.triggerType,
apiMethodName: apiMapping.apiMethodName,
apiModule: apiMapping.module
})
})
}
})
return components
}
/**
* 分析组件到顶级组件的关系
* @param {Object} triggerComponent - 触发源组件
* @param {Array} topLevelComponents - 顶级组件数组
*/
analyzeComponentToTopLevel(triggerComponent, topLevelComponents) {
const componentKey = `${triggerComponent.module}:${triggerComponent.component}`
// 避免重复处理
if (this.processedComponents.has(componentKey)) {
return
}
this.processedComponents.add(componentKey)
// 如果触发源组件本身就是顶级组件,直接建立关系
const isTopLevel = topLevelComponents.find(top =>
top.component === triggerComponent.component && top.module === triggerComponent.module
)
if (isTopLevel) {
this.componentRelationships.push({
startComponent: triggerComponent.component,
startModule: triggerComponent.module,
endComponent: triggerComponent.component,
endModule: triggerComponent.module,
relationshipType: 'self',
path: isTopLevel.path,
triggerName: triggerComponent.triggerName,
triggerType: triggerComponent.triggerType,
apiMethodName: triggerComponent.apiMethodName
})
return
}
// 查找父组件关系
const parentComponents = this.findParentComponents(triggerComponent)
parentComponents.forEach(parent => {
// 递归查找父组件的父组件,直到找到顶级组件
this.findTopLevelAncestors(parent, triggerComponent, topLevelComponents)
})
}
/**
* 查找组件的父组件
* @param {Object} component - 组件信息
* @returns {Array} 父组件数组
*/
findParentComponents(component) {
const parents = []
try {
// 查找所有模块中的组件,看哪些引用了当前组件
const modulesPath = resolve(__dirname, '../../src/renderer/modules')
if (existsSync(modulesPath)) {
const moduleDirs = readdirSync(modulesPath, { withFileTypes: true })
.filter(dirent => dirent.isDirectory())
.map(dirent => dirent.name)
moduleDirs.forEach(moduleName => {
const parentComponents = this.findComponentsInModule(moduleName, component)
parents.push(...parentComponents)
})
}
} catch (error) {
// 静默处理错误
}
return parents
}
/**
* 在指定模块中查找引用目标组件的组件
* @param {string} moduleName - 模块名称
* @param {Object} targetComponent - 目标组件
* @returns {Array} 父组件数组
*/
findComponentsInModule(moduleName, targetComponent) {
const parents = []
try {
const modulePath = resolve(__dirname, '../../src/renderer/modules', moduleName)
if (!existsSync(modulePath)) return parents
// 查找views目录
const viewsPath = resolve(modulePath, 'views')
if (existsSync(viewsPath)) {
const viewFiles = readdirSync(viewsPath).filter(file => file.endsWith('.vue'))
viewFiles.forEach(file => {
const componentName = file.replace('.vue', '')
if (this.componentReferencesTarget(viewsPath, file, targetComponent)) {
parents.push({
component: componentName,
module: moduleName,
type: 'view'
})
}
})
}
// 查找components目录
const componentsPath = resolve(modulePath, 'components')
if (existsSync(componentsPath)) {
const componentFiles = readdirSync(componentsPath).filter(file => file.endsWith('.vue'))
componentFiles.forEach(file => {
const componentName = file.replace('.vue', '')
if (this.componentReferencesTarget(componentsPath, file, targetComponent)) {
parents.push({
component: componentName,
module: moduleName,
type: 'component'
})
}
})
}
} catch (error) {
// 静默处理错误
}
return parents
}
/**
* 检查组件是否引用了目标组件
* @param {string} dirPath - 目录路径
* @param {string} fileName - 文件名
* @param {Object} targetComponent - 目标组件
* @returns {boolean} 是否引用
*/
componentReferencesTarget(dirPath, fileName, targetComponent) {
try {
const filePath = resolve(dirPath, fileName)
const content = readFileSync(filePath, 'utf-8')
// 检查import语句
const importPattern = new RegExp(`import.*${targetComponent.component}.*from`, 'g')
if (importPattern.test(content)) {
return true
}
// 检查components注册
const componentPattern = new RegExp(`${targetComponent.component}\\s*:`, 'g')
if (componentPattern.test(content)) {
return true
}
// 检查模板中的使用
const templatePattern = new RegExp(`<${targetComponent.component}[\\s>]`, 'g')
if (templatePattern.test(content)) {
return true
}
return false
} catch (error) {
return false
}
}
/**
* 查找顶级祖先组件
* @param {Object} parentComponent - 父组件
* @param {Object} originalTrigger - 原始触发组件
* @param {Array} topLevelComponents - 顶级组件数组
*/
findTopLevelAncestors(parentComponent, originalTrigger, topLevelComponents) {
// 检查父组件是否是顶级组件
const isTopLevel = topLevelComponents.find(top =>
top.component === parentComponent.component && top.module === parentComponent.module
)
if (isTopLevel) {
// 找到顶级组件,建立关系
this.componentRelationships.push({
startComponent: originalTrigger.component,
startModule: originalTrigger.module,
endComponent: parentComponent.component,
endModule: parentComponent.module,
relationshipType: 'ancestor',
path: isTopLevel.path,
triggerName: originalTrigger.triggerName,
triggerType: originalTrigger.triggerType,
apiMethodName: originalTrigger.apiMethodName
})
return
}
// 继续向上查找
const grandParents = this.findParentComponents(parentComponent)
grandParents.forEach(grandParent => {
this.findTopLevelAncestors(grandParent, originalTrigger, topLevelComponents)
})
}
}
module.exports = ComponentRelationshipAnalyzer