|
|
|
|
import { resolve } from 'path'
|
|
|
|
|
import fs from 'fs'
|
|
|
|
|
import * as parser from '@babel/parser'
|
|
|
|
|
import traverse from '@babel/traverse'
|
|
|
|
|
import { parse } from '@vue/compiler-sfc'
|
|
|
|
|
|
|
|
|
|
// 路由映射插件 - 基于AST解析获取实际存在的路由关系,不进行规则推断
|
|
|
|
|
export function routeMappingPlugin() {
|
|
|
|
|
return {
|
|
|
|
|
name: 'route-mapping',
|
|
|
|
|
|
|
|
|
|
// 在构建开始时执行
|
|
|
|
|
buildStart() {
|
|
|
|
|
console.log('🔧 开始分析AST和Vite编译时关系...')
|
|
|
|
|
this.analyzeASTAndViteRelations()
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 在开发模式下也执行
|
|
|
|
|
configureServer(server) {
|
|
|
|
|
console.log('🔧 开发模式下分析AST和Vite编译时关系...')
|
|
|
|
|
this.analyzeASTAndViteRelations()
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 利用AST解析获取实际存在的路由关系
|
|
|
|
|
analyzeASTAndViteRelations() {
|
|
|
|
|
try {
|
|
|
|
|
// 1. 分析路由配置的AST
|
|
|
|
|
const routerAST = this.analyzeRouterAST()
|
|
|
|
|
|
|
|
|
|
// 2. 分析组件文件的AST
|
|
|
|
|
const componentASTs = this.analyzeComponentASTs()
|
|
|
|
|
|
|
|
|
|
// 3. 分析服务文件的AST
|
|
|
|
|
const serviceASTs = this.analyzeServiceASTs()
|
|
|
|
|
|
|
|
|
|
// 4. 建立真实的调用关系(只基于AST分析)
|
|
|
|
|
const callRelations = this.buildCallRelations(routerAST, componentASTs, serviceASTs)
|
|
|
|
|
|
|
|
|
|
// 5. 生成基于实际关系的路由映射
|
|
|
|
|
const routeMappings = this.generateRealRouteMappings(callRelations)
|
|
|
|
|
|
|
|
|
|
// 6. 生成映射文件
|
|
|
|
|
this.generateRouteMappingFile(routeMappings)
|
|
|
|
|
|
|
|
|
|
console.log('✅ 基于AST的实际路由关系分析完成')
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('❌ AST路由关系分析失败:', error)
|
|
|
|
|
// AST分析失败时直接记录错误,不进行回退分析
|
|
|
|
|
console.error('❌ 路由映射生成失败,请检查代码结构和依赖')
|
|
|
|
|
throw error
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 分析路由AST
|
|
|
|
|
analyzeRouterAST() {
|
|
|
|
|
const routerPath = resolve(__dirname, '../src/renderer/router/index.js')
|
|
|
|
|
if (!fs.existsSync(routerPath)) {
|
|
|
|
|
throw new Error('Router file not found')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const routerContent = fs.readFileSync(routerPath, 'utf-8')
|
|
|
|
|
const ast = parser.parse(routerContent, {
|
|
|
|
|
sourceType: 'module',
|
|
|
|
|
plugins: ['jsx']
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const routes = []
|
|
|
|
|
|
|
|
|
|
traverse(ast, {
|
|
|
|
|
// 查找路由配置对象
|
|
|
|
|
ObjectExpression(path) {
|
|
|
|
|
const properties = path.node.properties
|
|
|
|
|
let route = {}
|
|
|
|
|
|
|
|
|
|
properties.forEach(prop => {
|
|
|
|
|
if (prop.key && prop.key.name === 'path' && prop.value && prop.value.value) {
|
|
|
|
|
route.path = prop.value.value
|
|
|
|
|
}
|
|
|
|
|
if (prop.key && prop.key.name === 'name' && prop.value && prop.value.value) {
|
|
|
|
|
route.name = prop.value.value
|
|
|
|
|
}
|
|
|
|
|
if (prop.key && prop.key.name === 'component' && prop.value) {
|
|
|
|
|
route.component = this.extractComponentName(prop.value)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (route.path) {
|
|
|
|
|
route.module = this.extractModuleFromPath(route.path)
|
|
|
|
|
routes.push(route)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return { routes, ast }
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 分析组件AST
|
|
|
|
|
analyzeComponentASTs() {
|
|
|
|
|
const componentASTs = new Map()
|
|
|
|
|
const componentFiles = this.findComponentFiles()
|
|
|
|
|
|
|
|
|
|
componentFiles.forEach(componentFile => {
|
|
|
|
|
try {
|
|
|
|
|
const content = fs.readFileSync(componentFile.path, 'utf-8')
|
|
|
|
|
|
|
|
|
|
if (componentFile.path.endsWith('.vue')) {
|
|
|
|
|
// 解析Vue文件
|
|
|
|
|
const { descriptor } = parse(content)
|
|
|
|
|
const scriptContent = descriptor.script?.content || ''
|
|
|
|
|
|
|
|
|
|
if (scriptContent) {
|
|
|
|
|
const scriptAST = parser.parse(scriptContent, {
|
|
|
|
|
sourceType: 'module',
|
|
|
|
|
plugins: ['jsx']
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const analysis = this.analyzeVueComponentAST(scriptAST, componentFile.name)
|
|
|
|
|
componentASTs.set(componentFile.name, {
|
|
|
|
|
...analysis,
|
|
|
|
|
file: componentFile,
|
|
|
|
|
template: descriptor.template?.content || ''
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// 解析JS文件
|
|
|
|
|
const ast = parser.parse(content, {
|
|
|
|
|
sourceType: 'module',
|
|
|
|
|
plugins: ['jsx']
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const analysis = this.analyzeJavaScriptComponentAST(ast, componentFile.name)
|
|
|
|
|
componentASTs.set(componentFile.name, {
|
|
|
|
|
...analysis,
|
|
|
|
|
file: componentFile
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.warn(`⚠️ 解析组件文件失败: ${componentFile.path}`, error.message)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return componentASTs
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 分析服务AST
|
|
|
|
|
analyzeServiceASTs() {
|
|
|
|
|
const serviceASTs = new Map()
|
|
|
|
|
const serviceFiles = this.findServiceFiles()
|
|
|
|
|
|
|
|
|
|
serviceFiles.forEach(serviceFile => {
|
|
|
|
|
try {
|
|
|
|
|
const content = fs.readFileSync(serviceFile.path, 'utf-8')
|
|
|
|
|
const ast = parser.parse(content, {
|
|
|
|
|
sourceType: 'module',
|
|
|
|
|
plugins: ['jsx']
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const analysis = this.analyzeServiceAST(ast, serviceFile.name)
|
|
|
|
|
serviceASTs.set(serviceFile.name, {
|
|
|
|
|
...analysis,
|
|
|
|
|
file: serviceFile
|
|
|
|
|
})
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.warn(`⚠️ 解析服务文件失败: ${serviceFile.path}`, error.message)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return serviceASTs
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 分析Vue组件AST
|
|
|
|
|
analyzeVueComponentAST(ast, componentName) {
|
|
|
|
|
const analysis = {
|
|
|
|
|
imports: [],
|
|
|
|
|
exports: [],
|
|
|
|
|
methods: [],
|
|
|
|
|
serviceCalls: [],
|
|
|
|
|
events: []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
traverse(ast, {
|
|
|
|
|
// 查找import语句
|
|
|
|
|
ImportDeclaration(path) {
|
|
|
|
|
const importSpecifiers = path.node.specifiers
|
|
|
|
|
importSpecifiers.forEach(specifier => {
|
|
|
|
|
if (specifier.type === 'ImportSpecifier') {
|
|
|
|
|
analysis.imports.push({
|
|
|
|
|
name: specifier.imported.name,
|
|
|
|
|
local: specifier.local.name,
|
|
|
|
|
source: path.node.source.value
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 查找方法定义
|
|
|
|
|
FunctionDeclaration(path) {
|
|
|
|
|
if (path.node.id) {
|
|
|
|
|
analysis.methods.push(path.node.id.name)
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 查找箭头函数
|
|
|
|
|
ArrowFunctionExpression(path) {
|
|
|
|
|
if (path.parent && path.parent.type === 'VariableDeclarator' && path.parent.id) {
|
|
|
|
|
analysis.methods.push(path.parent.id.name)
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 查找服务调用
|
|
|
|
|
CallExpression(path) {
|
|
|
|
|
if (path.node.callee && path.node.callee.type === 'MemberExpression') {
|
|
|
|
|
const object = path.node.callee.object
|
|
|
|
|
const property = path.node.callee.property
|
|
|
|
|
|
|
|
|
|
if (object && property && object.name && property.name) {
|
|
|
|
|
// 检查是否是服务调用
|
|
|
|
|
const serviceName = object.name
|
|
|
|
|
const methodName = property.name
|
|
|
|
|
|
|
|
|
|
if (serviceName.endsWith('Service')) {
|
|
|
|
|
analysis.serviceCalls.push({
|
|
|
|
|
service: serviceName,
|
|
|
|
|
method: methodName,
|
|
|
|
|
arguments: path.node.arguments.map(arg => this.extractArgumentValue(arg))
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return analysis
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 分析JavaScript组件AST
|
|
|
|
|
analyzeJavaScriptComponentAST(ast, componentName) {
|
|
|
|
|
return this.analyzeVueComponentAST(ast, componentName)
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 分析服务AST
|
|
|
|
|
analyzeServiceAST(ast, serviceName) {
|
|
|
|
|
const analysis = {
|
|
|
|
|
exports: [],
|
|
|
|
|
methods: [],
|
|
|
|
|
apiCalls: [],
|
|
|
|
|
imports: []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
traverse(ast, {
|
|
|
|
|
// 查找export语句
|
|
|
|
|
ExportNamedDeclaration(path) {
|
|
|
|
|
if (path.node.declaration && path.node.declaration.type === 'VariableDeclarator') {
|
|
|
|
|
analysis.exports.push(path.node.declaration.id.name)
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 查找方法定义
|
|
|
|
|
FunctionDeclaration(path) {
|
|
|
|
|
if (path.node.id) {
|
|
|
|
|
analysis.methods.push(path.node.id.name)
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 查找API调用
|
|
|
|
|
CallExpression(path) {
|
|
|
|
|
if (path.node.callee && path.node.callee.type === 'MemberExpression') {
|
|
|
|
|
const object = path.node.callee.object
|
|
|
|
|
const property = path.node.callee.property
|
|
|
|
|
|
|
|
|
|
if (object && property) {
|
|
|
|
|
// 检查是否是API调用
|
|
|
|
|
if (object.name === 'api' || object.name === 'axios' || object.name === 'http') {
|
|
|
|
|
const method = property.name
|
|
|
|
|
const args = path.node.arguments
|
|
|
|
|
|
|
|
|
|
if (args.length > 0 && args[0].type === 'StringLiteral') {
|
|
|
|
|
analysis.apiCalls.push({
|
|
|
|
|
method: method.toUpperCase(),
|
|
|
|
|
path: args[0].value,
|
|
|
|
|
arguments: args.slice(1).map(arg => this.extractArgumentValue(arg))
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return analysis
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 建立调用关系
|
|
|
|
|
buildCallRelations(routerAST, componentASTs, serviceASTs) {
|
|
|
|
|
const relations = {
|
|
|
|
|
routes: routerAST.routes,
|
|
|
|
|
components: new Map(),
|
|
|
|
|
services: new Map(),
|
|
|
|
|
callGraph: new Map(),
|
|
|
|
|
imports: new Map()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 建立组件关系
|
|
|
|
|
componentASTs.forEach((analysis, componentName) => {
|
|
|
|
|
relations.components.set(componentName, {
|
|
|
|
|
...analysis,
|
|
|
|
|
serviceDependencies: analysis.serviceCalls.map(call => call.service)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 建立服务关系
|
|
|
|
|
serviceASTs.forEach((analysis, serviceName) => {
|
|
|
|
|
relations.services.set(serviceName, {
|
|
|
|
|
...analysis,
|
|
|
|
|
apiEndpoints: analysis.apiCalls
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 建立调用图
|
|
|
|
|
componentASTs.forEach((componentAnalysis, componentName) => {
|
|
|
|
|
const calls = []
|
|
|
|
|
|
|
|
|
|
componentAnalysis.serviceCalls.forEach(call => {
|
|
|
|
|
calls.push({
|
|
|
|
|
type: 'service',
|
|
|
|
|
target: call.service,
|
|
|
|
|
method: call.method,
|
|
|
|
|
component: componentName
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
relations.callGraph.set(componentName, calls)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return relations
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 生成基于实际关系的路由映射(不进行规则推断)
|
|
|
|
|
generateRealRouteMappings(callRelations) {
|
|
|
|
|
const mappings = {
|
|
|
|
|
mainRoutes: [],
|
|
|
|
|
moduleApiMappings: {},
|
|
|
|
|
subRouteMappings: {},
|
|
|
|
|
callRelations: callRelations
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 生成主路由(基于实际路由配置)
|
|
|
|
|
callRelations.routes.forEach(route => {
|
|
|
|
|
mappings.mainRoutes.push({
|
|
|
|
|
path: route.path,
|
|
|
|
|
name: route.name,
|
|
|
|
|
module: route.module,
|
|
|
|
|
description: this.generateDescription(route.name, route.module),
|
|
|
|
|
type: this.determineRouteType(route.path, route.module),
|
|
|
|
|
component: route.component
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 生成API映射(基于实际API调用)
|
|
|
|
|
callRelations.services.forEach((serviceAnalysis, serviceName) => {
|
|
|
|
|
const moduleName = this.extractModuleFromService(serviceName)
|
|
|
|
|
if (moduleName && serviceAnalysis.apiCalls.length > 0) {
|
|
|
|
|
const apiMapping = this.buildApiMappingFromRealCalls(serviceAnalysis.apiCalls, moduleName)
|
|
|
|
|
mappings.moduleApiMappings[moduleName] = apiMapping
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 生成子路由映射(基于实际调用关系)
|
|
|
|
|
mappings.subRouteMappings = this.generateSubRouteMappingsFromRelations(callRelations)
|
|
|
|
|
|
|
|
|
|
return mappings
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 从实际API调用构建API映射(不进行操作推断)
|
|
|
|
|
buildApiMappingFromRealCalls(apiCalls, moduleName) {
|
|
|
|
|
const operations = {}
|
|
|
|
|
const paths = []
|
|
|
|
|
|
|
|
|
|
apiCalls.forEach(call => {
|
|
|
|
|
// 使用实际的API路径作为操作名,不进行推断
|
|
|
|
|
const operationKey = `${call.method}_${call.path.replace(/[^a-zA-Z0-9]/g, '_')}`
|
|
|
|
|
operations[operationKey] = {
|
|
|
|
|
path: call.path,
|
|
|
|
|
method: call.method
|
|
|
|
|
}
|
|
|
|
|
paths.push(call.path)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const basePath = this.extractBasePath(paths)
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
basePath: basePath,
|
|
|
|
|
operations: operations
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 从API调用获取实际操作(不进行推断)
|
|
|
|
|
inferOperationFromApiCall(path, method) {
|
|
|
|
|
// 只返回实际存在的路径,不进行规则推断
|
|
|
|
|
return path
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 从关系生成子路由映射(只基于实际存在的调用关系)
|
|
|
|
|
generateSubRouteMappingsFromRelations(callRelations) {
|
|
|
|
|
const subRouteMappings = {}
|
|
|
|
|
|
|
|
|
|
callRelations.callGraph.forEach((calls, componentName) => {
|
|
|
|
|
calls.forEach(call => {
|
|
|
|
|
if (call.type === 'service') {
|
|
|
|
|
// 只记录实际存在的服务调用,不生成预估的子路由
|
|
|
|
|
const serviceName = call.target
|
|
|
|
|
const methodName = call.method
|
|
|
|
|
|
|
|
|
|
// 检查服务是否真实存在
|
|
|
|
|
if (callRelations.services.has(serviceName)) {
|
|
|
|
|
const serviceAnalysis = callRelations.services.get(serviceName)
|
|
|
|
|
|
|
|
|
|
// 只记录实际存在的API调用
|
|
|
|
|
serviceAnalysis.apiCalls.forEach(apiCall => {
|
|
|
|
|
const key = `${componentName}_${serviceName}_${methodName}`
|
|
|
|
|
subRouteMappings[key] = {
|
|
|
|
|
component: componentName,
|
|
|
|
|
service: serviceName,
|
|
|
|
|
method: methodName,
|
|
|
|
|
apiPath: apiCall.path,
|
|
|
|
|
apiMethod: apiCall.method
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return subRouteMappings
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 查找组件文件
|
|
|
|
|
findComponentFiles() {
|
|
|
|
|
const componentFiles = []
|
|
|
|
|
const moduleDirs = ['user-management', 'role-management', 'system-settings']
|
|
|
|
|
|
|
|
|
|
moduleDirs.forEach(moduleName => {
|
|
|
|
|
const viewsPath = resolve(__dirname, `../src/renderer/modules/${moduleName}/views`)
|
|
|
|
|
const componentsPath = resolve(__dirname, `../src/renderer/modules/${moduleName}/components`)
|
|
|
|
|
|
|
|
|
|
if (fs.existsSync(viewsPath)) {
|
|
|
|
|
const files = fs.readdirSync(viewsPath).filter(f => f.endsWith('.vue'))
|
|
|
|
|
files.forEach(file => {
|
|
|
|
|
componentFiles.push({
|
|
|
|
|
name: file.replace('.vue', ''),
|
|
|
|
|
path: resolve(viewsPath, file),
|
|
|
|
|
module: moduleName,
|
|
|
|
|
type: 'view'
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fs.existsSync(componentsPath)) {
|
|
|
|
|
const files = fs.readdirSync(componentsPath).filter(f => f.endsWith('.vue'))
|
|
|
|
|
files.forEach(file => {
|
|
|
|
|
componentFiles.push({
|
|
|
|
|
name: file.replace('.vue', ''),
|
|
|
|
|
path: resolve(componentsPath, file),
|
|
|
|
|
module: moduleName,
|
|
|
|
|
type: 'component'
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return componentFiles
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 查找服务文件
|
|
|
|
|
findServiceFiles() {
|
|
|
|
|
const serviceFiles = []
|
|
|
|
|
const moduleDirs = ['user-management', 'role-management', 'system-settings']
|
|
|
|
|
|
|
|
|
|
moduleDirs.forEach(moduleName => {
|
|
|
|
|
const servicesPath = resolve(__dirname, `../src/renderer/modules/${moduleName}/services`)
|
|
|
|
|
if (fs.existsSync(servicesPath)) {
|
|
|
|
|
const files = fs.readdirSync(servicesPath).filter(f => f.endsWith('.js'))
|
|
|
|
|
files.forEach(file => {
|
|
|
|
|
serviceFiles.push({
|
|
|
|
|
name: file.replace('.js', ''),
|
|
|
|
|
path: resolve(servicesPath, file),
|
|
|
|
|
module: moduleName
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return serviceFiles
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 提取组件名称
|
|
|
|
|
extractComponentName(node) {
|
|
|
|
|
if (node.type === 'Identifier') {
|
|
|
|
|
return node.name
|
|
|
|
|
} else if (node.type === 'StringLiteral') {
|
|
|
|
|
return node.value
|
|
|
|
|
}
|
|
|
|
|
return null
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 提取参数值
|
|
|
|
|
extractArgumentValue(node) {
|
|
|
|
|
if (node.type === 'StringLiteral') {
|
|
|
|
|
return node.value
|
|
|
|
|
} else if (node.type === 'NumericLiteral') {
|
|
|
|
|
return node.value
|
|
|
|
|
} else if (node.type === 'Identifier') {
|
|
|
|
|
return node.name
|
|
|
|
|
}
|
|
|
|
|
return null
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 从服务方法获取实际操作(不进行推断)
|
|
|
|
|
inferOperationFromServiceMethod(methodName) {
|
|
|
|
|
// 只返回实际存在的方法名,不进行硬编码映射
|
|
|
|
|
return methodName
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 从服务名提取模块名(基于实际文件路径)
|
|
|
|
|
extractModuleFromService(serviceName) {
|
|
|
|
|
// 通过实际文件路径来确定模块,而不是硬编码映射
|
|
|
|
|
const serviceFiles = this.findServiceFiles()
|
|
|
|
|
const serviceFile = serviceFiles.find(file => file.name === serviceName)
|
|
|
|
|
|
|
|
|
|
if (serviceFile) {
|
|
|
|
|
return serviceFile.module
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 提取basePath
|
|
|
|
|
extractBasePath(paths) {
|
|
|
|
|
if (paths.length === 0) return ''
|
|
|
|
|
|
|
|
|
|
// 找到所有路径的共同前缀
|
|
|
|
|
const firstPath = paths[0]
|
|
|
|
|
let commonPrefix = ''
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < firstPath.length; i++) {
|
|
|
|
|
const char = firstPath[i]
|
|
|
|
|
if (paths.every(path => path[i] === char)) {
|
|
|
|
|
commonPrefix += char
|
|
|
|
|
} else {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果找到了完整的前缀,返回它
|
|
|
|
|
if (commonPrefix && !commonPrefix.endsWith('/')) {
|
|
|
|
|
// 找到最后一个斜杠的位置
|
|
|
|
|
const lastSlashIndex = commonPrefix.lastIndexOf('/')
|
|
|
|
|
if (lastSlashIndex > 0) {
|
|
|
|
|
return commonPrefix.substring(0, lastSlashIndex + 1)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return commonPrefix
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 从路径提取模块名(基于实际路由配置)
|
|
|
|
|
extractModuleFromPath(path) {
|
|
|
|
|
if (path === '/' || path === '') return 'home'
|
|
|
|
|
|
|
|
|
|
const segments = path.split('/').filter(Boolean)
|
|
|
|
|
if (segments.length === 0) return 'home'
|
|
|
|
|
|
|
|
|
|
// 获取第一个段作为模块名,不进行硬编码映射
|
|
|
|
|
return segments[0]
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 生成路由描述(基于实际名称)
|
|
|
|
|
generateDescription(name, module) {
|
|
|
|
|
// 只使用实际的路由名称,不进行硬编码描述
|
|
|
|
|
return `${name}页面`
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 确定路由类型(基于实际路径)
|
|
|
|
|
determineRouteType(path, module) {
|
|
|
|
|
if (path === '/' || path === '') return 'home'
|
|
|
|
|
|
|
|
|
|
// 基于实际路径结构确定类型,不进行硬编码映射
|
|
|
|
|
if (path.includes('/:') || path.includes('/{')) {
|
|
|
|
|
return 'detail'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 'list'
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 生成路由映射文件
|
|
|
|
|
generateRouteMappingFile(routeMappings) {
|
|
|
|
|
const mappingContent = `// 自动生成的路由映射文件
|
|
|
|
|
// 此文件由 route-mapping-plugin 在构建时生成
|
|
|
|
|
// 基于AST解析获取实际存在的路由关系,不进行规则推断
|
|
|
|
|
// 请勿手动修改
|
|
|
|
|
|
|
|
|
|
export const mainRoutes = ${JSON.stringify(routeMappings.mainRoutes, null, 2)}
|
|
|
|
|
|
|
|
|
|
// 模块到API映射配置(从AST解析提取)
|
|
|
|
|
export const moduleApiMappings = ${JSON.stringify(routeMappings.moduleApiMappings, null, 2)}
|
|
|
|
|
|
|
|
|
|
// 子路由到主路由的映射(从调用关系提取)
|
|
|
|
|
export const subRouteMappings = ${JSON.stringify(routeMappings.subRouteMappings, null, 2)}
|
|
|
|
|
|
|
|
|
|
// 分析方法和结果
|
|
|
|
|
export const analysisInfo = ${JSON.stringify({
|
|
|
|
|
method: routeMappings.method || 'ast-analysis',
|
|
|
|
|
timestamp: new Date().toISOString(),
|
|
|
|
|
hasCallRelations: !!routeMappings.callRelations,
|
|
|
|
|
componentCount: routeMappings.callRelations?.components?.size || 0,
|
|
|
|
|
serviceCount: routeMappings.callRelations?.services?.size || 0
|
|
|
|
|
}, null, 2)}
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
mainRoutes,
|
|
|
|
|
moduleApiMappings,
|
|
|
|
|
subRouteMappings,
|
|
|
|
|
analysisInfo
|
|
|
|
|
}
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
const outputPath = resolve(__dirname, '../src/renderer/modules/route-sync/generated-route-mapping.js')
|
|
|
|
|
fs.writeFileSync(outputPath, mappingContent, 'utf-8')
|
|
|
|
|
|
|
|
|
|
console.log(`✅ 基于AST实际路由关系的映射文件已生成: ${outputPath}`)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|