|
|
|
const { resolve } = require('path')
|
|
|
|
const { existsSync, statSync } = require('fs')
|
|
|
|
const ApiCollector = require('./modules/api-collector')
|
|
|
|
const TriggerAnalyzerSimple = require('./modules/trigger-analyzer-simple')
|
|
|
|
const CallChainTracer = require('./modules/call-chain-tracer')
|
|
|
|
const FileGenerator = require('./modules/file-generator')
|
|
|
|
const RouteAnalyzer = require('./modules/route-analyzer')
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 模块化路由映射插件
|
|
|
|
* 收集直接映射关系
|
|
|
|
*/
|
|
|
|
function routeMappingPlugin() {
|
|
|
|
// 检查是否在静默模式下运行
|
|
|
|
const isSilentMode = process.env.ROUTE_MAPPING_SILENT === 'true' || process.argv.includes('--silent')
|
|
|
|
|
|
|
|
// 日志函数
|
|
|
|
const log = (message) => {
|
|
|
|
if (!isSilentMode) {
|
|
|
|
console.log(message)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
name: 'route-mapping-phase1',
|
|
|
|
|
|
|
|
// 防重复生成机制
|
|
|
|
_lastGenerationTime: 0,
|
|
|
|
_generationInProgress: false,
|
|
|
|
_generationCooldown: 10000, // 10秒冷却时间
|
|
|
|
_maxGenerationsPerMinute: 3, // 每分钟最多生成3次
|
|
|
|
_generationCount: 0,
|
|
|
|
_generationWindowStart: 0,
|
|
|
|
|
|
|
|
// 初始化模块
|
|
|
|
_apiCollector: new ApiCollector(),
|
|
|
|
_triggerAnalyzer: new TriggerAnalyzerSimple(),
|
|
|
|
_callChainTracer: new CallChainTracer(),
|
|
|
|
_fileGenerator: new FileGenerator(),
|
|
|
|
_routeAnalyzer: new RouteAnalyzer(),
|
|
|
|
|
|
|
|
// Webpack插件接口
|
|
|
|
apply(compiler) {
|
|
|
|
const self = this
|
|
|
|
|
|
|
|
// 在编译开始前就生成映射文件
|
|
|
|
compiler.hooks.beforeCompile.tapAsync('RouteMappingPlugin', (params, callback) => {
|
|
|
|
if (self._shouldSkipGeneration()) {
|
|
|
|
callback()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
self.collectDirectMappings()
|
|
|
|
callback()
|
|
|
|
} catch (error) {
|
|
|
|
callback(error)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
// 备用钩子,确保在beforeRun时也执行
|
|
|
|
compiler.hooks.beforeRun.tapAsync('RouteMappingPlugin', (compilation, callback) => {
|
|
|
|
if (self._shouldSkipGeneration()) {
|
|
|
|
callback()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
self.collectDirectMappings()
|
|
|
|
callback()
|
|
|
|
} catch (error) {
|
|
|
|
callback(error)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
},
|
|
|
|
|
|
|
|
// Vite插件接口
|
|
|
|
buildStart() {
|
|
|
|
this.collectDirectMappings()
|
|
|
|
},
|
|
|
|
|
|
|
|
// 在开发模式下也执行
|
|
|
|
configureServer(server) {
|
|
|
|
if (this._shouldSkipGeneration()) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
this.collectDirectMappings()
|
|
|
|
},
|
|
|
|
|
|
|
|
// 检查是否应该跳过生成
|
|
|
|
_shouldSkipGeneration() {
|
|
|
|
const now = Date.now()
|
|
|
|
|
|
|
|
// 如果正在生成中,跳过
|
|
|
|
if (this._generationInProgress) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// 如果在冷却期内,跳过
|
|
|
|
if (now - this._lastGenerationTime < this._generationCooldown) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// 检查每分钟生成次数限制
|
|
|
|
if (now - this._generationWindowStart > 60000) {
|
|
|
|
// 重置计数窗口
|
|
|
|
this._generationCount = 0
|
|
|
|
this._generationWindowStart = now
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this._generationCount >= this._maxGenerationsPerMinute) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
},
|
|
|
|
|
|
|
|
// 检查是否需要重新生成文件 - 优化版本:每次都生成
|
|
|
|
_shouldRegenerateFile() {
|
|
|
|
// 为了优化调试,每次都重新生成文件
|
|
|
|
return true
|
|
|
|
},
|
|
|
|
|
|
|
|
// 收集直接映射关系 - 分步执行版本
|
|
|
|
collectDirectMappings() {
|
|
|
|
try {
|
|
|
|
// 设置生成中标志
|
|
|
|
this._generationInProgress = true
|
|
|
|
|
|
|
|
// 检查文件是否需要重新生成
|
|
|
|
if (!this._shouldRegenerateFile()) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// ========== 第一步:收集route和api列表 ==========
|
|
|
|
const routes = this._routeAnalyzer.analyzeRoutes()
|
|
|
|
const moduleDirs = this._routeAnalyzer.getModuleDirs()
|
|
|
|
const apiMappings = this._apiCollector.collectApiMappings(moduleDirs)
|
|
|
|
|
|
|
|
// 生成包含路由和API信息的文件(不包含triggerSources)
|
|
|
|
this._fileGenerator.generateRouteApiMappingFile(routes, apiMappings)
|
|
|
|
|
|
|
|
// ========== 第二步:Trigger追溯 ==========
|
|
|
|
const enhancedApiMappings = this._triggerAnalyzer.findTriggerSourcesForApiMappings(apiMappings, routes)
|
|
|
|
|
|
|
|
// ========== 第二步半:调用链追溯 ==========
|
|
|
|
const tracedApiMappings = this._callChainTracer.traceMethodTriggersToVisualComponents(enhancedApiMappings)
|
|
|
|
|
|
|
|
// 生成包含triggerSources的文件
|
|
|
|
this._fileGenerator.generateRouteApiMappingFile(routes, tracedApiMappings)
|
|
|
|
|
|
|
|
// ========== 第三步:删除triggerSources为空的apiMapping ==========
|
|
|
|
const DataCleaner = require('./modules/data-cleaner.js')
|
|
|
|
const dataCleaner = new DataCleaner()
|
|
|
|
const cleanedMappings = dataCleaner.cleanData()
|
|
|
|
|
|
|
|
// 重置生成中标志并更新时间戳
|
|
|
|
this._generationInProgress = false
|
|
|
|
this._lastGenerationTime = Date.now()
|
|
|
|
this._generationCount++
|
|
|
|
|
|
|
|
if (this._generationCount === 1) {
|
|
|
|
this._generationWindowStart = this._lastGenerationTime
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
this._generationInProgress = false
|
|
|
|
console.error('❌ 生成direct-route-mappings.js时出错:', error)
|
|
|
|
throw error
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = routeMappingPlugin
|