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
281 lines
8.8 KiB
23 hours ago
|
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
|