vite-plugin-ai-perf-analyzer 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -13,6 +13,8 @@ interface PerfAnalyzerOptions {
13
13
  apiKey?: string;
14
14
  apiUrl?: string;
15
15
  model?: string;
16
+ temperature?: number;
17
+ maxTokens?: number;
16
18
  enabled?: boolean;
17
19
  threshold?: {
18
20
  bundleSize?: number;
package/dist/index.d.ts CHANGED
@@ -13,6 +13,8 @@ interface PerfAnalyzerOptions {
13
13
  apiKey?: string;
14
14
  apiUrl?: string;
15
15
  model?: string;
16
+ temperature?: number;
17
+ maxTokens?: number;
16
18
  enabled?: boolean;
17
19
  threshold?: {
18
20
  bundleSize?: number;
package/dist/index.js CHANGED
@@ -612,8 +612,8 @@ var PerfAnalyzer = class {
612
612
  openAIApiKey: options.apiKey,
613
613
  configuration: { baseURL: options.apiUrl },
614
614
  modelName: options.model,
615
- temperature: 0.2,
616
- maxTokens: 4e3
615
+ temperature: options.temperature ?? 0.2,
616
+ maxTokens: options.maxTokens ?? 4e3
617
617
  });
618
618
  }
619
619
  }
@@ -1558,6 +1558,8 @@ function vitePluginAIPerfAnalyzer(options = {}) {
1558
1558
  apiKey = process.env.OPENAI_API_KEY || "",
1559
1559
  apiUrl = process.env.OPENAI_API_URL || "https://api.openai.com/v1",
1560
1560
  model = process.env.OPENAI_MODEL || "gpt-4",
1561
+ temperature = 0.2,
1562
+ maxTokens = 4e3,
1561
1563
  enabled = true,
1562
1564
  threshold = {
1563
1565
  bundleSize: 500,
@@ -1572,7 +1574,14 @@ function vitePluginAIPerfAnalyzer(options = {}) {
1572
1574
  json: false
1573
1575
  }
1574
1576
  } = options;
1575
- const analyzer = new PerfAnalyzer({ apiKey, apiUrl, model, threshold });
1577
+ const analyzer = new PerfAnalyzer({
1578
+ apiKey,
1579
+ apiUrl,
1580
+ model,
1581
+ threshold,
1582
+ temperature,
1583
+ maxTokens
1584
+ });
1576
1585
  const reporter = new PerfReporter();
1577
1586
  let analysisResult = null;
1578
1587
  return {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/analyzer.ts","../src/dependency-analyzer.ts","../src/history-analyzer.ts","../src/optimization-examples.ts","../src/reporter.ts"],"sourcesContent":["/**\r\n * AI 性能分析插件\r\n *\r\n * 功能:\r\n * - 分析构建产物大小\r\n * - 检测性能问题\r\n * - 提供优化建议\r\n */\r\n\r\nimport type { Plugin } from \"vite\";\r\nimport pc from \"picocolors\";\r\nimport { PerfAnalyzer } from \"./analyzer\";\r\nimport { PerfReporter } from \"./reporter\";\r\nimport type { AnalysisResult } from \"./types\";\r\n\r\nexport interface PerfAnalyzerOptions {\r\n // AI 配置\r\n apiKey?: string;\r\n apiUrl?: string;\r\n model?: string;\r\n // 分析配置\r\n enabled?: boolean;\r\n threshold?: {\r\n bundleSize?: number; // 单个文件大小阈值 (KB)\r\n totalSize?: number; // 总大小阈值 (MB)\r\n chunkCount?: number; // chunk 数量阈值\r\n };\r\n // 输出配置\r\n output?: {\r\n console?: boolean;\r\n html?: boolean;\r\n json?: boolean;\r\n };\r\n}\r\n\r\nexport function vitePluginAIPerfAnalyzer(\r\n options: PerfAnalyzerOptions = {},\r\n): Plugin {\r\n const {\r\n apiKey = process.env.OPENAI_API_KEY || \"\",\r\n apiUrl = process.env.OPENAI_API_URL || \"https://api.openai.com/v1\",\r\n model = process.env.OPENAI_MODEL || \"gpt-4\",\r\n enabled = true,\r\n threshold = {\r\n bundleSize: 500, // 500KB\r\n totalSize: 5, // 5MB\r\n chunkCount: 20,\r\n },\r\n output = {\r\n console: true,\r\n html: true,\r\n json: false,\r\n },\r\n } = options;\r\n\r\n const analyzer = new PerfAnalyzer({ apiKey, apiUrl, model, threshold });\r\n const reporter = new PerfReporter();\r\n\r\n let analysisResult: AnalysisResult | null = null;\r\n\r\n return {\r\n name: \"vite-plugin-ai-perf-analyzer\",\r\n enforce: \"post\",\r\n\r\n configResolved(config) {\r\n if (!enabled) return;\r\n\r\n console.log(pc.cyan(\"\\n⚡ AI 性能分析插件已启动...\"));\r\n console.log(`📊 分析阈值:`);\r\n console.log(` 单文件: ${pc.yellow(threshold.bundleSize + \"KB\")}`);\r\n console.log(` 总大小: ${pc.yellow(threshold.totalSize + \"MB\")}`);\r\n console.log(\r\n ` Chunk数: ${pc.yellow((threshold.chunkCount ?? 10).toString())}`,\r\n );\r\n console.log(`🔑 API Key: ${apiKey ? \"已配置\" : \"未配置\"}\\n`);\r\n },\r\n\r\n async closeBundle() {\r\n if (!enabled) return;\r\n\r\n console.log(\"\\n⚡ 正在分析构建产物...\\n\");\r\n\r\n try {\r\n // 分析构建产物\r\n analysisResult = await analyzer.analyze();\r\n\r\n // 生成报告\r\n await reporter.generate(analysisResult, output);\r\n\r\n // 控制台输出\r\n if (output.console) {\r\n reporter.printConsole(analysisResult);\r\n }\r\n } catch (error: any) {\r\n console.error(\"❌ 性能分析失败:\", error.message);\r\n }\r\n },\r\n };\r\n}\r\n\r\n// 默认导出\r\nexport default vitePluginAIPerfAnalyzer;\r\n","/**\r\n * 性能分析器\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport { gzipSync } from \"zlib\";\r\nimport { ChatOpenAI } from \"@langchain/openai\";\r\nimport { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\r\nimport type {\r\n AnalysisResult,\r\n BundleInfo,\r\n PerformanceIssue,\r\n AnalyzerOptions,\r\n} from \"./types\";\r\nimport { DependencyAnalyzer } from \"./dependency-analyzer\";\r\nimport { HistoryAnalyzer } from \"./history-analyzer\";\r\nimport { OptimizationExamples } from \"./optimization-examples\";\r\n\r\nexport class PerfAnalyzer {\r\n private llm: ChatOpenAI | null = null;\r\n private options: AnalyzerOptions;\r\n private depAnalyzer: DependencyAnalyzer;\r\n private historyAnalyzer: HistoryAnalyzer;\r\n private examplesGenerator: OptimizationExamples;\r\n\r\n constructor(options: AnalyzerOptions) {\r\n this.options = options;\r\n this.depAnalyzer = new DependencyAnalyzer();\r\n this.historyAnalyzer = new HistoryAnalyzer();\r\n this.examplesGenerator = new OptimizationExamples();\r\n\r\n if (options.apiKey) {\r\n this.llm = new ChatOpenAI({\r\n openAIApiKey: options.apiKey,\r\n configuration: { baseURL: options.apiUrl },\r\n modelName: options.model,\r\n temperature: 0.2,\r\n maxTokens: 4000,\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * 分析构建产物\r\n */\r\n async analyze(): Promise<AnalysisResult> {\r\n const distDir = path.resolve(process.cwd(), \"dist\");\r\n\r\n if (!fs.existsSync(distDir)) {\r\n throw new Error(\"构建目录不存在,请先执行构建\");\r\n }\r\n\r\n // 收集文件信息\r\n const bundles = this.collectBundles(distDir);\r\n\r\n // 计算统计信息\r\n const summary = this.calculateSummary(bundles);\r\n\r\n // 依赖分析\r\n console.log(\"📦 正在分析依赖...\");\r\n const dependencies = this.depAnalyzer.analyzeDependencies(bundles);\r\n\r\n // 历史对比\r\n console.log(\"📊 正在对比历史记录...\");\r\n const comparison = this.historyAnalyzer.compare(\r\n bundles,\r\n summary.totalSize,\r\n summary.fileCount\r\n );\r\n\r\n // 检测性能问题\r\n const issues = this.detectIssues(bundles, summary, dependencies);\r\n\r\n // 生成基础建议\r\n const suggestions = this.generateSuggestions(issues);\r\n\r\n // 生成优化示例\r\n console.log(\"💡 正在生成优化示例...\");\r\n const optimizationExamples = this.examplesGenerator.generate(\r\n issues,\r\n dependencies.duplicates\r\n );\r\n\r\n // AI 分析(如果配置了 API Key)\r\n let aiAnalysis: string | undefined;\r\n if (this.llm) {\r\n console.log(\"🤖 正在使用 AI 分析性能...\\n\");\r\n aiAnalysis = await this.performAIAnalysis(\r\n bundles,\r\n summary,\r\n issues,\r\n dependencies,\r\n comparison\r\n );\r\n }\r\n\r\n return {\r\n timestamp: new Date().toLocaleString(\"zh-CN\"),\r\n summary,\r\n dependencies,\r\n comparison,\r\n issues,\r\n suggestions,\r\n optimizationExamples,\r\n aiAnalysis,\r\n };\r\n }\r\n\r\n /**\r\n * 收集构建产物信息\r\n */\r\n private collectBundles(dir: string, baseDir: string = dir): BundleInfo[] {\r\n const bundles: BundleInfo[] = [];\r\n const files = fs.readdirSync(dir);\r\n\r\n for (const file of files) {\r\n const filePath = path.join(dir, file);\r\n const stat = fs.statSync(filePath);\r\n\r\n if (stat.isDirectory()) {\r\n bundles.push(...this.collectBundles(filePath, baseDir));\r\n } else {\r\n const content = fs.readFileSync(filePath);\r\n const gzipSize = gzipSync(content).length;\r\n const relativePath = path.relative(baseDir, filePath);\r\n\r\n bundles.push({\r\n name: file,\r\n size: stat.size,\r\n gzipSize,\r\n type: this.getFileType(file),\r\n path: relativePath,\r\n });\r\n }\r\n }\r\n\r\n return bundles;\r\n }\r\n\r\n /**\r\n * 获取文件类型\r\n */\r\n private getFileType(filename: string): string {\r\n const ext = path.extname(filename).toLowerCase();\r\n const typeMap: Record<string, string> = {\r\n \".js\": \"javascript\",\r\n \".mjs\": \"javascript\",\r\n \".css\": \"stylesheet\",\r\n \".html\": \"html\",\r\n \".png\": \"image\",\r\n \".jpg\": \"image\",\r\n \".jpeg\": \"image\",\r\n \".gif\": \"image\",\r\n \".svg\": \"image\",\r\n \".webp\": \"image\",\r\n \".woff\": \"font\",\r\n \".woff2\": \"font\",\r\n \".ttf\": \"font\",\r\n \".eot\": \"font\",\r\n \".json\": \"data\",\r\n \".map\": \"sourcemap\",\r\n };\r\n return typeMap[ext] || \"other\";\r\n }\r\n\r\n /**\r\n * 计算统计信息\r\n */\r\n private calculateSummary(bundles: BundleInfo[]) {\r\n const totalSize = bundles.reduce((sum, b) => sum + b.size, 0);\r\n const totalGzipSize = bundles.reduce(\r\n (sum, b) => sum + (b.gzipSize || 0),\r\n 0\r\n );\r\n\r\n // 按大小排序,取前 10\r\n const largestFiles = [...bundles]\r\n .sort((a, b) => b.size - a.size)\r\n .slice(0, 10);\r\n\r\n // 按类型分组\r\n const byType: Record<string, { count: number; size: number }> = {};\r\n bundles.forEach((bundle) => {\r\n if (!byType[bundle.type]) {\r\n byType[bundle.type] = { count: 0, size: 0 };\r\n }\r\n byType[bundle.type].count++;\r\n byType[bundle.type].size += bundle.size;\r\n });\r\n\r\n return {\r\n totalSize,\r\n totalGzipSize,\r\n fileCount: bundles.length,\r\n largestFiles,\r\n byType,\r\n };\r\n }\r\n\r\n /**\r\n * 检测性能问题\r\n */\r\n private detectIssues(\r\n bundles: BundleInfo[],\r\n summary: any,\r\n dependencies?: {\r\n total: number;\r\n duplicates: DependencyInfo[];\r\n largest: DependencyInfo[];\r\n }\r\n ): PerformanceIssue[] {\r\n const issues: PerformanceIssue[] = [];\r\n const { threshold } = this.options;\r\n\r\n // 检查总大小\r\n const totalSizeMB = summary.totalSize / 1024 / 1024;\r\n if (threshold.totalSize && totalSizeMB > threshold.totalSize) {\r\n issues.push({\r\n type: \"size\",\r\n severity: \"high\",\r\n title: \"构建产物总大小过大\",\r\n description: `总大小 ${totalSizeMB.toFixed(2)}MB 超过阈值 ${\r\n threshold.totalSize\r\n }MB`,\r\n suggestion: \"考虑代码分割、tree-shaking、压缩等优化手段\",\r\n });\r\n }\r\n\r\n // 检查单个文件大小\r\n const largeBundles = bundles.filter(\r\n (b) => b.size / 1024 > (threshold.bundleSize || 500)\r\n );\r\n if (largeBundles.length > 0) {\r\n issues.push({\r\n type: \"size\",\r\n severity: \"medium\",\r\n title: \"存在过大的单个文件\",\r\n description: `发现 ${largeBundles.length} 个文件超过 ${threshold.bundleSize}KB`,\r\n files: largeBundles.map(\r\n (b) => `${b.name} (${(b.size / 1024).toFixed(2)}KB)`\r\n ),\r\n suggestion: \"考虑拆分大文件,使用动态导入或代码分割\",\r\n });\r\n }\r\n\r\n // 检查 chunk 数量\r\n const jsFiles = bundles.filter((b) => b.type === \"javascript\");\r\n if (threshold.chunkCount && jsFiles.length > threshold.chunkCount) {\r\n issues.push({\r\n type: \"count\",\r\n severity: \"low\",\r\n title: \"JavaScript 文件数量过多\",\r\n description: `共有 ${jsFiles.length} 个 JS 文件,超过阈值 ${threshold.chunkCount}`,\r\n suggestion: \"过多的文件会增加 HTTP 请求数,考虑合并小文件\",\r\n });\r\n }\r\n\r\n // 检查未压缩的图片\r\n const images = bundles.filter((b) => b.type === \"image\");\r\n const largeImages = images.filter((img) => img.size > 100 * 1024); // 100KB\r\n if (largeImages.length > 0) {\r\n issues.push({\r\n type: \"optimization\",\r\n severity: \"medium\",\r\n title: \"存在未优化的图片\",\r\n description: `发现 ${largeImages.length} 个大于 100KB 的图片`,\r\n files: largeImages.map(\r\n (img) => `${img.name} (${(img.size / 1024).toFixed(2)}KB)`\r\n ),\r\n suggestion: \"使用图片压缩工具,或转换为 WebP 格式\",\r\n });\r\n }\r\n\r\n // 检查 sourcemap\r\n const sourcemaps = bundles.filter((b) => b.type === \"sourcemap\");\r\n if (sourcemaps.length > 0) {\r\n const totalMapSize = sourcemaps.reduce((sum, m) => sum + m.size, 0);\r\n issues.push({\r\n type: \"optimization\",\r\n severity: \"low\",\r\n title: \"生产环境包含 sourcemap\",\r\n description: `Sourcemap 文件占用 ${(totalMapSize / 1024 / 1024).toFixed(\r\n 2\r\n )}MB`,\r\n suggestion: \"生产环境建议禁用 sourcemap 或使用外部 sourcemap\",\r\n });\r\n }\r\n\r\n // 检查重复依赖\r\n if (dependencies && dependencies.duplicates.length > 0) {\r\n const topDuplicates = dependencies.duplicates.slice(0, 3);\r\n issues.push({\r\n type: \"dependency\",\r\n severity: \"medium\",\r\n title: \"检测到重复打包的依赖\",\r\n description: `发现 ${dependencies.duplicates.length} 个依赖被多次打包`,\r\n files: topDuplicates.map(\r\n (d) =>\r\n `${d.name} (被 ${d.usedBy.length} 个文件使用, ${(\r\n d.size / 1024\r\n ).toFixed(2)}KB)`\r\n ),\r\n suggestion: \"将重复依赖提取到公共 chunk 中\",\r\n });\r\n }\r\n\r\n return issues;\r\n }\r\n\r\n /**\r\n * 生成基础建议\r\n */\r\n private generateSuggestions(issues: PerformanceIssue[]): string[] {\r\n const suggestions: string[] = [];\r\n\r\n if (issues.some((i) => i.type === \"size\")) {\r\n suggestions.push(\"启用 gzip/brotli 压缩\");\r\n suggestions.push(\"配置 Vite 的 build.rollupOptions 进行代码分割\");\r\n suggestions.push(\"使用 vite-plugin-compression 插件\");\r\n }\r\n\r\n if (issues.some((i) => i.type === \"optimization\")) {\r\n suggestions.push(\"使用 vite-plugin-imagemin 优化图片\");\r\n suggestions.push(\"配置 CSS 压缩和 tree-shaking\");\r\n }\r\n\r\n if (issues.length === 0) {\r\n suggestions.push(\"构建产物已经很优秀,继续保持!\");\r\n }\r\n\r\n return suggestions;\r\n }\r\n\r\n /**\r\n * AI 性能分析\r\n */\r\n private async performAIAnalysis(\r\n bundles: BundleInfo[],\r\n summary: any,\r\n issues: PerformanceIssue[],\r\n dependencies?: {\r\n total: number;\r\n duplicates: DependencyInfo[];\r\n largest: DependencyInfo[];\r\n },\r\n comparison?: any\r\n ): Promise<string> {\r\n const systemPrompt = new SystemMessage(\r\n \"你是一个专业的前端性能优化专家,精通 Vite、Webpack 等构建工具。请分析构建产物并提供专业的优化建议。\"\r\n );\r\n\r\n const bundlesSummary = summary.largestFiles\r\n .slice(0, 5)\r\n .map((b: BundleInfo) => `- ${b.name}: ${(b.size / 1024).toFixed(2)}KB`)\r\n .join(\"\\n\");\r\n\r\n const typesSummary = Object.entries(summary.byType)\r\n .map(\r\n ([type, info]: [string, any]) =>\r\n `- ${type}: ${info.count} 个文件, ${(info.size / 1024).toFixed(2)}KB`\r\n )\r\n .join(\"\\n\");\r\n\r\n const issuesSummary = issues\r\n .map(\r\n (issue) => `- [${issue.severity}] ${issue.title}: ${issue.description}`\r\n )\r\n .join(\"\\n\");\r\n\r\n // 依赖分析摘要\r\n let dependencySummary = \"\";\r\n if (dependencies) {\r\n dependencySummary = `\r\n## 依赖分析\r\n- 总依赖数: ${dependencies.total}\r\n- 重复依赖: ${dependencies.duplicates.length} 个\r\n- 最大依赖: ${dependencies.largest\r\n .slice(0, 3)\r\n .map((d) => `${d.name} (${(d.size / 1024).toFixed(2)}KB)`)\r\n .join(\", \")}`;\r\n }\r\n\r\n // 历史对比摘要\r\n let comparisonSummary = \"\";\r\n if (comparison) {\r\n const sizeChange =\r\n comparison.totalSize.trend === \"increased\" ? \"增加\" : \"减少\";\r\n comparisonSummary = `\r\n## 历史对比\r\n- 总大小${sizeChange}: ${Math.abs(comparison.totalSize.diffPercent).toFixed(2)}%\r\n- 文件数量变化: ${comparison.fileCount.diff > 0 ? \"+\" : \"\"}${\r\n comparison.fileCount.diff\r\n }\r\n- 新增文件: ${comparison.newFiles.length} 个\r\n- 删除文件: ${comparison.removedFiles.length} 个`;\r\n }\r\n\r\n const userPrompt = new HumanMessage(`\r\n请分析以下构建产物信息:\r\n\r\n## 总体统计\r\n- 总大小: ${(summary.totalSize / 1024 / 1024).toFixed(2)}MB\r\n- Gzip 后: ${(summary.totalGzipSize / 1024 / 1024).toFixed(2)}MB\r\n- 文件数量: ${summary.fileCount}\r\n\r\n## 最大的文件\r\n${bundlesSummary}\r\n\r\n## 按类型分组\r\n${typesSummary}\r\n${dependencySummary}\r\n${comparisonSummary}\r\n\r\n## 检测到的问题\r\n${issuesSummary || \"无明显问题\"}\r\n\r\n请提供:\r\n1. 性能评估(3-5 句话)\r\n2. 具体优化建议(3-5 条,每条简洁明了)\r\n3. 优先级排序\r\n\r\n请用简洁专业的语言回答,不要过于冗长。\r\n`);\r\n\r\n const response = await this.llm!.invoke([systemPrompt, userPrompt]);\r\n return response.content.toString();\r\n }\r\n}\r\n","/**\r\n * 依赖分析器\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport type { BundleInfo, DependencyInfo } from \"./types\";\r\n\r\nexport class DependencyAnalyzer {\r\n /**\r\n * 分析依赖\r\n */\r\n analyzeDependencies(bundles: BundleInfo[]): {\r\n total: number;\r\n duplicates: DependencyInfo[];\r\n largest: DependencyInfo[];\r\n } {\r\n const jsFiles = bundles.filter((b) => b.type === \"javascript\");\r\n const dependencies = this.extractDependencies(jsFiles);\r\n\r\n // 检测重复依赖\r\n const duplicates = this.findDuplicates(dependencies);\r\n\r\n // 找出最大的依赖\r\n const largest = [...dependencies]\r\n .sort((a, b) => b.size - a.size)\r\n .slice(0, 10);\r\n\r\n return {\r\n total: dependencies.length,\r\n duplicates,\r\n largest,\r\n };\r\n }\r\n\r\n /**\r\n * 从 JS 文件中提取依赖信息\r\n */\r\n private extractDependencies(jsFiles: BundleInfo[]): DependencyInfo[] {\r\n const depMap = new Map<string, DependencyInfo>();\r\n\r\n jsFiles.forEach((file) => {\r\n // 尝试解析文件内容,提取依赖\r\n const imports = this.parseImports(file);\r\n\r\n imports.forEach((depName) => {\r\n if (!depMap.has(depName)) {\r\n depMap.set(depName, {\r\n name: depName,\r\n size: 0,\r\n usedBy: [],\r\n isDuplicate: false,\r\n });\r\n }\r\n\r\n const dep = depMap.get(depName)!;\r\n dep.usedBy.push(file.name);\r\n // 估算依赖大小(简化处理)\r\n dep.size += file.size / imports.length;\r\n });\r\n });\r\n\r\n return Array.from(depMap.values());\r\n }\r\n\r\n /**\r\n * 解析文件中的 import 语句\r\n */\r\n private parseImports(file: BundleInfo): string[] {\r\n const imports: string[] = [];\r\n\r\n try {\r\n const distDir = path.resolve(process.cwd(), \"dist\");\r\n const filePath = path.join(distDir, file.path);\r\n\r\n if (!fs.existsSync(filePath)) {\r\n return imports;\r\n }\r\n\r\n const content = fs.readFileSync(filePath, \"utf-8\");\r\n\r\n // 匹配常见的依赖模式\r\n const patterns = [\r\n // node_modules 依赖\r\n /from\\s+[\"']([^\"']+)[\"']/g,\r\n /require\\([\"']([^\"']+)[\"']\\)/g,\r\n // Vite 特殊标记\r\n /__vite_ssr_import__\\(\"([^\"]+)\"\\)/g,\r\n ];\r\n\r\n patterns.forEach((pattern) => {\r\n let match;\r\n while ((match = pattern.exec(content)) !== null) {\r\n const depName = match[1];\r\n // 只保留 node_modules 依赖\r\n if (!depName.startsWith(\".\") && !depName.startsWith(\"/\")) {\r\n // 提取包名(去掉子路径)\r\n const pkgName = depName\r\n .split(\"/\")\r\n .slice(0, depName.startsWith(\"@\") ? 2 : 1)\r\n .join(\"/\");\r\n if (!imports.includes(pkgName)) {\r\n imports.push(pkgName);\r\n }\r\n }\r\n }\r\n });\r\n } catch (error) {\r\n // 忽略解析错误\r\n }\r\n\r\n return imports;\r\n }\r\n\r\n /**\r\n * 查找重复打包的依赖\r\n */\r\n private findDuplicates(dependencies: DependencyInfo[]): DependencyInfo[] {\r\n return dependencies\r\n .filter((dep) => dep.usedBy.length > 1)\r\n .map((dep) => ({\r\n ...dep,\r\n isDuplicate: true,\r\n }))\r\n .sort((a, b) => b.usedBy.length - a.usedBy.length)\r\n .slice(0, 10);\r\n }\r\n}\r\n","/**\r\n * 历史对比分析器\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport type {\r\n BundleInfo,\r\n HistoryComparison,\r\n HistoryRecord,\r\n ComparisonResult,\r\n} from \"./types\";\r\n\r\nexport class HistoryAnalyzer {\r\n private historyFile: string;\r\n\r\n constructor() {\r\n const reportsDir = path.resolve(process.cwd(), \"ai-reports\");\r\n this.historyFile = path.join(reportsDir, \".perf-history.json\");\r\n }\r\n\r\n /**\r\n * 对比当前构建与历史记录\r\n */\r\n compare(\r\n currentBundles: BundleInfo[],\r\n currentTotalSize: number,\r\n currentFileCount: number\r\n ): HistoryComparison | undefined {\r\n const history = this.loadHistory();\r\n\r\n if (history.length === 0) {\r\n // 没有历史记录,保存当前记录\r\n this.saveHistory(currentBundles, currentTotalSize, currentFileCount);\r\n return undefined;\r\n }\r\n\r\n // 获取上一次记录\r\n const previous = history[history.length - 1];\r\n\r\n // 对比总大小\r\n const totalSize = this.compareValue(currentTotalSize, previous.totalSize);\r\n\r\n // 对比文件数量\r\n const fileCount = this.compareValue(currentFileCount, previous.fileCount);\r\n\r\n // 对比最大文件变化\r\n const largestFileChanges = this.compareLargestFiles(\r\n currentBundles,\r\n previous.files\r\n );\r\n\r\n // 检测新增和删除的文件\r\n const currentFileNames = new Set(currentBundles.map((b) => b.name));\r\n const previousFileNames = new Set(previous.files.map((f) => f.name));\r\n\r\n const newFiles = currentBundles\r\n .filter((b) => !previousFileNames.has(b.name))\r\n .map((b) => b.name);\r\n\r\n const removedFiles = previous.files\r\n .filter((f) => !currentFileNames.has(f.name))\r\n .map((f) => f.name);\r\n\r\n // 保存当前记录\r\n this.saveHistory(currentBundles, currentTotalSize, currentFileCount);\r\n\r\n return {\r\n totalSize,\r\n fileCount,\r\n largestFileChanges,\r\n newFiles,\r\n removedFiles,\r\n };\r\n }\r\n\r\n /**\r\n * 对比数值\r\n */\r\n private compareValue(current: number, previous: number): ComparisonResult {\r\n const diff = current - previous;\r\n const diffPercent = previous === 0 ? 0 : (diff / previous) * 100;\r\n\r\n let trend: \"increased\" | \"decreased\" | \"unchanged\";\r\n if (Math.abs(diffPercent) < 1) {\r\n trend = \"unchanged\";\r\n } else if (diff > 0) {\r\n trend = \"increased\";\r\n } else {\r\n trend = \"decreased\";\r\n }\r\n\r\n return {\r\n current,\r\n previous,\r\n diff,\r\n diffPercent,\r\n trend,\r\n };\r\n }\r\n\r\n /**\r\n * 对比最大文件的变化\r\n */\r\n private compareLargestFiles(\r\n currentBundles: BundleInfo[],\r\n previousFiles: Array<{ name: string; size: number }>\r\n ): Array<{\r\n name: string;\r\n current: number;\r\n previous: number;\r\n diff: number;\r\n }> {\r\n const changes: Array<{\r\n name: string;\r\n current: number;\r\n previous: number;\r\n diff: number;\r\n }> = [];\r\n\r\n // 创建文件映射\r\n const previousMap = new Map(previousFiles.map((f) => [f.name, f.size]));\r\n\r\n currentBundles.forEach((bundle) => {\r\n const previousSize = previousMap.get(bundle.name);\r\n if (previousSize !== undefined) {\r\n const diff = bundle.size - previousSize;\r\n // 只记录变化超过 10KB 的文件\r\n if (Math.abs(diff) > 10 * 1024) {\r\n changes.push({\r\n name: bundle.name,\r\n current: bundle.size,\r\n previous: previousSize,\r\n diff,\r\n });\r\n }\r\n }\r\n });\r\n\r\n // 按变化幅度排序\r\n return changes\r\n .sort((a, b) => Math.abs(b.diff) - Math.abs(a.diff))\r\n .slice(0, 10);\r\n }\r\n\r\n /**\r\n * 加载历史记录\r\n */\r\n private loadHistory(): HistoryRecord[] {\r\n if (!fs.existsSync(this.historyFile)) {\r\n return [];\r\n }\r\n\r\n try {\r\n const content = fs.readFileSync(this.historyFile, \"utf-8\");\r\n const history = JSON.parse(content);\r\n // 只保留最近 10 次记录\r\n return history.slice(-10);\r\n } catch (error) {\r\n console.warn(\"⚠️ 无法读取历史记录\");\r\n return [];\r\n }\r\n }\r\n\r\n /**\r\n * 保存历史记录\r\n */\r\n private saveHistory(\r\n bundles: BundleInfo[],\r\n totalSize: number,\r\n fileCount: number\r\n ): void {\r\n const history = this.loadHistory();\r\n\r\n const record: HistoryRecord = {\r\n timestamp: new Date().toISOString(),\r\n totalSize,\r\n fileCount,\r\n files: bundles.map((b) => ({\r\n name: b.name,\r\n size: b.size,\r\n })),\r\n };\r\n\r\n history.push(record);\r\n\r\n // 确保目录存在\r\n const dir = path.dirname(this.historyFile);\r\n if (!fs.existsSync(dir)) {\r\n fs.mkdirSync(dir, { recursive: true });\r\n }\r\n\r\n // 只保留最近 10 次记录\r\n const recentHistory = history.slice(-10);\r\n\r\n fs.writeFileSync(\r\n this.historyFile,\r\n JSON.stringify(recentHistory, null, 2),\r\n \"utf-8\"\r\n );\r\n }\r\n\r\n /**\r\n * 清除历史记录\r\n */\r\n clearHistory(): void {\r\n if (fs.existsSync(this.historyFile)) {\r\n fs.unlinkSync(this.historyFile);\r\n }\r\n }\r\n}\r\n","/**\r\n * 优化示例生成器\r\n */\r\n\r\nimport type {\r\n OptimizationExample,\r\n PerformanceIssue,\r\n DependencyInfo,\r\n} from \"./types\";\r\n\r\nexport class OptimizationExamples {\r\n /**\r\n * 根据问题生成优化示例\r\n */\r\n generate(\r\n issues: PerformanceIssue[],\r\n duplicateDeps?: DependencyInfo[]\r\n ): OptimizationExample[] {\r\n const examples: OptimizationExample[] = [];\r\n\r\n // 代码分割示例\r\n if (issues.some((i) => i.type === \"size\")) {\r\n examples.push(this.getCodeSplittingExample());\r\n }\r\n\r\n // 重复依赖优化\r\n if (duplicateDeps && duplicateDeps.length > 0) {\r\n examples.push(this.getDependencyOptimizationExample(duplicateDeps));\r\n }\r\n\r\n // 压缩优化\r\n if (issues.some((i) => i.type === \"optimization\")) {\r\n examples.push(this.getCompressionExample());\r\n }\r\n\r\n // 图片优化\r\n if (issues.some((i) => i.description.includes(\"图片\"))) {\r\n examples.push(this.getImageOptimizationExample());\r\n }\r\n\r\n // 动态导入\r\n if (issues.some((i) => i.type === \"size\" && i.severity === \"high\")) {\r\n examples.push(this.getDynamicImportExample());\r\n }\r\n\r\n // Tree-shaking 优化\r\n examples.push(this.getTreeShakingExample());\r\n\r\n return examples;\r\n }\r\n\r\n /**\r\n * 代码分割示例\r\n */\r\n private getCodeSplittingExample(): OptimizationExample {\r\n return {\r\n title: \"配置代码分割\",\r\n description: \"将大文件拆分成多个小文件,提高加载性能\",\r\n priority: \"high\",\r\n impact: \"可减少 30-50% 的初始加载大小\",\r\n difficulty: \"简单\",\r\n code: {\r\n language: \"typescript\",\r\n content: `// vite.config.ts\r\nexport default defineConfig({\r\n build: {\r\n rollupOptions: {\r\n output: {\r\n manualChunks: {\r\n // 提取 Vue 核心库\r\n 'vue-vendor': ['vue', 'vue-router', 'pinia'],\r\n \r\n // 提取 UI 组件库\r\n 'ui-vendor': ['element-plus', '@element-plus/icons-vue'],\r\n \r\n // 提取工具库\r\n 'utils': ['lodash-es', 'dayjs', 'axios'],\r\n \r\n // 提取图表库(如果使用)\r\n 'charts': ['echarts', 'chart.js'],\r\n },\r\n \r\n // 自动分割大于 500KB 的文件\r\n chunkFileNames: (chunkInfo) => {\r\n const facadeModuleId = chunkInfo.facadeModuleId \r\n ? chunkInfo.facadeModuleId.split('/').pop() \r\n : 'chunk';\r\n return \\`js/\\${facadeModuleId}-[hash].js\\`;\r\n },\r\n },\r\n },\r\n \r\n // 设置 chunk 大小警告阈值\r\n chunkSizeWarningLimit: 500,\r\n },\r\n});`,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * 重复依赖优化示例\r\n */\r\n private getDependencyOptimizationExample(\r\n duplicates: DependencyInfo[]\r\n ): OptimizationExample {\r\n const topDuplicates = duplicates.slice(0, 3).map((d) => d.name);\r\n\r\n return {\r\n title: \"优化重复依赖\",\r\n description: `检测到 ${duplicates.length} 个重复打包的依赖,建议提取到公共 chunk`,\r\n priority: \"high\",\r\n impact: `可减少 ${(\r\n duplicates.reduce((sum, d) => sum + d.size, 0) /\r\n 1024 /\r\n 1024\r\n ).toFixed(2)}MB`,\r\n difficulty: \"简单\",\r\n code: {\r\n language: \"typescript\",\r\n content: `// vite.config.ts\r\nexport default defineConfig({\r\n build: {\r\n rollupOptions: {\r\n output: {\r\n manualChunks: {\r\n // 提取重复依赖到公共 chunk\r\n 'common-vendor': [\r\n ${topDuplicates.map((name) => `'${name}'`).join(\",\\n \")}\r\n ],\r\n },\r\n },\r\n },\r\n },\r\n \r\n // 或者使用自动分割策略\r\n optimizeDeps: {\r\n include: [\r\n ${topDuplicates.map((name) => `'${name}'`).join(\",\\n \")}\r\n ],\r\n },\r\n});`,\r\n },\r\n relatedFiles: duplicates.flatMap((d) => d.usedBy).slice(0, 5),\r\n };\r\n }\r\n\r\n /**\r\n * 压缩优化示例\r\n */\r\n private getCompressionExample(): OptimizationExample {\r\n return {\r\n title: \"启用 Gzip/Brotli 压缩\",\r\n description: \"使用压缩插件减少传输大小\",\r\n priority: \"medium\",\r\n impact: \"可减少 60-70% 的传输大小\",\r\n difficulty: \"简单\",\r\n code: {\r\n language: \"bash\",\r\n content: `# 1. 安装压缩插件\r\nnpm install vite-plugin-compression -D\r\n\r\n# 2. 配置插件\r\n# vite.config.ts\r\nimport viteCompression from 'vite-plugin-compression';\r\n\r\nexport default defineConfig({\r\n plugins: [\r\n // Gzip 压缩\r\n viteCompression({\r\n algorithm: 'gzip',\r\n ext: '.gz',\r\n threshold: 10240, // 大于 10KB 才压缩\r\n deleteOriginFile: false,\r\n }),\r\n \r\n // Brotli 压缩(可选,压缩率更高)\r\n viteCompression({\r\n algorithm: 'brotliCompress',\r\n ext: '.br',\r\n threshold: 10240,\r\n deleteOriginFile: false,\r\n }),\r\n ],\r\n});\r\n\r\n# 3. Nginx 配置(服务器端)\r\n# nginx.conf\r\ngzip on;\r\ngzip_types text/plain text/css application/json application/javascript;\r\ngzip_min_length 1024;`,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * 图片优化示例\r\n */\r\n private getImageOptimizationExample(): OptimizationExample {\r\n return {\r\n title: \"优化图片资源\",\r\n description: \"压缩图片并转换为现代格式\",\r\n priority: \"medium\",\r\n impact: \"可减少 50-80% 的图片大小\",\r\n difficulty: \"简单\",\r\n code: {\r\n language: \"bash\",\r\n content: `# 1. 安装图片优化插件\r\nnpm install vite-plugin-imagemin -D\r\n\r\n# 2. 配置插件\r\n# vite.config.ts\r\nimport viteImagemin from 'vite-plugin-imagemin';\r\n\r\nexport default defineConfig({\r\n plugins: [\r\n viteImagemin({\r\n // GIF 优化\r\n gifsicle: {\r\n optimizationLevel: 7,\r\n interlaced: false,\r\n },\r\n \r\n // PNG 优化\r\n optipng: {\r\n optimizationLevel: 7,\r\n },\r\n \r\n // JPEG 优化\r\n mozjpeg: {\r\n quality: 80,\r\n },\r\n \r\n // SVG 优化\r\n svgo: {\r\n plugins: [\r\n { name: 'removeViewBox', active: false },\r\n { name: 'removeEmptyAttrs', active: true },\r\n ],\r\n },\r\n \r\n // WebP 转换\r\n webp: {\r\n quality: 80,\r\n },\r\n }),\r\n ],\r\n});\r\n\r\n# 3. 使用 WebP 格式(HTML)\r\n<picture>\r\n <source srcset=\"image.webp\" type=\"image/webp\">\r\n <img src=\"image.jpg\" alt=\"description\">\r\n</picture>`,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * 动态导入示例\r\n */\r\n private getDynamicImportExample(): OptimizationExample {\r\n return {\r\n title: \"使用动态导入(懒加载)\",\r\n description: \"按需加载组件和路由,减少初始加载大小\",\r\n priority: \"high\",\r\n impact: \"可减少 40-60% 的初始加载大小\",\r\n difficulty: \"中等\",\r\n code: {\r\n language: \"typescript\",\r\n content: `// 1. 路由懒加载\r\n// router/index.ts\r\nconst routes = [\r\n {\r\n path: '/dashboard',\r\n name: 'Dashboard',\r\n // 使用动态导入\r\n component: () => import('@/views/Dashboard.vue'),\r\n },\r\n {\r\n path: '/settings',\r\n name: 'Settings',\r\n component: () => import('@/views/Settings.vue'),\r\n },\r\n];\r\n\r\n// 2. 组件懒加载\r\n// App.vue\r\n<script setup>\r\nimport { defineAsyncComponent } from 'vue';\r\n\r\n// 异步组件\r\nconst HeavyComponent = defineAsyncComponent(() =>\r\n import('./components/HeavyComponent.vue')\r\n);\r\n\r\n// 带加载状态的异步组件\r\nconst AsyncComponent = defineAsyncComponent({\r\n loader: () => import('./components/AsyncComponent.vue'),\r\n loadingComponent: LoadingSpinner,\r\n errorComponent: ErrorComponent,\r\n delay: 200,\r\n timeout: 3000,\r\n});\r\n</script>\r\n\r\n// 3. 条件加载\r\n<script setup>\r\nconst loadCharts = async () => {\r\n if (needCharts) {\r\n const { default: ECharts } = await import('echarts');\r\n // 使用 ECharts\r\n }\r\n};\r\n</script>`,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * Tree-shaking 优化示例\r\n */\r\n private getTreeShakingExample(): OptimizationExample {\r\n return {\r\n title: \"优化 Tree-shaking\",\r\n description: \"确保未使用的代码被正确移除\",\r\n priority: \"medium\",\r\n impact: \"可减少 10-30% 的代码大小\",\r\n difficulty: \"中等\",\r\n code: {\r\n language: \"typescript\",\r\n content: `// 1. 使用 ES6 模块导入(支持 tree-shaking)\r\n// ❌ 不推荐\r\nimport _ from 'lodash';\r\nconst result = _.debounce(fn, 300);\r\n\r\n// ✅ 推荐\r\nimport { debounce } from 'lodash-es';\r\nconst result = debounce(fn, 300);\r\n\r\n// 2. 配置 package.json\r\n{\r\n \"sideEffects\": false, // 标记为无副作用\r\n // 或指定有副作用的文件\r\n \"sideEffects\": [\"*.css\", \"*.scss\"]\r\n}\r\n\r\n// 3. Vite 配置\r\n// vite.config.ts\r\nexport default defineConfig({\r\n build: {\r\n // 启用 tree-shaking\r\n minify: 'terser',\r\n terserOptions: {\r\n compress: {\r\n drop_console: true, // 移除 console\r\n drop_debugger: true, // 移除 debugger\r\n pure_funcs: ['console.log'], // 移除特定函数\r\n },\r\n },\r\n },\r\n});\r\n\r\n// 4. 避免副作用导入\r\n// ❌ 不推荐(会导入整个模块)\r\nimport 'some-library';\r\n\r\n// ✅ 推荐(明确导入需要的部分)\r\nimport { specificFunction } from 'some-library';`,\r\n },\r\n };\r\n }\r\n}\r\n","/**\r\n * 性能分析报告生成器\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport pc from \"picocolors\";\r\nimport type { AnalysisResult, PerformanceIssue } from \"./types\";\r\n\r\nexport interface ReporterOptions {\r\n console?: boolean;\r\n html?: boolean;\r\n json?: boolean;\r\n}\r\n\r\nexport class PerfReporter {\r\n /**\r\n * 生成报告\r\n */\r\n async generate(\r\n result: AnalysisResult,\r\n options: ReporterOptions = {},\r\n ): Promise<void> {\r\n if (options.html) {\r\n await this.generateHTMLReport(result);\r\n }\r\n\r\n if (options.json) {\r\n await this.generateJSONReport(result);\r\n }\r\n }\r\n\r\n /**\r\n * 控制台输出\r\n */\r\n printConsole(result: AnalysisResult): void {\r\n console.log(\"\\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\");\r\n console.log(\"⚡ 性能分析报告\");\r\n console.log(\"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n\");\r\n\r\n // 总体统计\r\n console.log(\"📊 总体统计:\");\r\n console.log(\r\n ` 总大小: ${(result.summary.totalSize / 1024 / 1024).toFixed(2)}MB`,\r\n );\r\n console.log(\r\n ` Gzip后: ${(result.summary.totalGzipSize / 1024 / 1024).toFixed(2)}MB`,\r\n );\r\n console.log(` 文件数: ${result.summary.fileCount}\\n`);\r\n\r\n // 历史对比\r\n if (result.comparison) {\r\n console.log(\"📈 历史对比:\");\r\n const { totalSize, fileCount, newFiles, removedFiles } =\r\n result.comparison;\r\n const sizeIcon = totalSize.trend === \"increased\" ? \"📈\" : \"📉\";\r\n console.log(\r\n ` ${sizeIcon} 总大小: ${totalSize.trend === \"increased\" ? \"+\" : \"\"}${(\r\n totalSize.diff /\r\n 1024 /\r\n 1024\r\n ).toFixed(2)}MB (${\r\n totalSize.diffPercent > 0 ? \"+\" : \"\"\r\n }${totalSize.diffPercent.toFixed(2)}%)`,\r\n );\r\n console.log(\r\n ` 📊 文件数: ${fileCount.diff > 0 ? \"+\" : \"\"}${fileCount.diff}`,\r\n );\r\n if (newFiles.length > 0) {\r\n console.log(` ✨ 新增: ${newFiles.slice(0, 3).join(\", \")}`);\r\n }\r\n if (removedFiles.length > 0) {\r\n console.log(` 🗑️ 删除: ${removedFiles.slice(0, 3).join(\", \")}`);\r\n }\r\n console.log();\r\n }\r\n\r\n // 依赖分析\r\n if (result.dependencies) {\r\n console.log(\"📦 依赖分析:\");\r\n console.log(` 总依赖数: ${result.dependencies.total}`);\r\n if (result.dependencies.duplicates.length > 0) {\r\n console.log(\r\n ` ⚠️ 重复依赖: ${result.dependencies.duplicates.length} 个`,\r\n );\r\n result.dependencies.duplicates.slice(0, 3).forEach((dep) => {\r\n console.log(\r\n ` - ${dep.name}: 被 ${dep.usedBy.length} 个文件使用`,\r\n );\r\n });\r\n }\r\n console.log();\r\n }\r\n\r\n // 最大文件\r\n console.log(\"📦 最大的文件:\");\r\n result.summary.largestFiles.slice(0, 5).forEach((file, index) => {\r\n console.log(\r\n ` ${index + 1}. ${file.name}: ${(file.size / 1024).toFixed(2)}KB`,\r\n );\r\n });\r\n console.log();\r\n\r\n // 按类型统计\r\n console.log(\"📋 按类型统计:\");\r\n Object.entries(result.summary.byType).forEach(([type, info]) => {\r\n console.log(\r\n ` ${type}: ${info.count} 个, ${(info.size / 1024).toFixed(2)}KB`,\r\n );\r\n });\r\n console.log();\r\n\r\n // 性能问题\r\n if (result.issues.length > 0) {\r\n console.log(\"⚠️ 性能问题:\");\r\n result.issues.forEach((issue, index) => {\r\n const icon = this.getSeverityIcon(issue.severity);\r\n console.log(` ${index + 1}. ${icon} ${issue.title}`);\r\n console.log(` ${issue.description}`);\r\n if (issue.suggestion) {\r\n console.log(` 💡 ${issue.suggestion}`);\r\n }\r\n });\r\n console.log();\r\n } else {\r\n console.log(\"✅ 未发现明显的性能问题\\n\");\r\n }\r\n\r\n // 优化建议\r\n if (result.suggestions.length > 0) {\r\n console.log(\"💡 优化建议:\");\r\n result.suggestions.forEach((suggestion, index) => {\r\n console.log(` ${index + 1}. ${suggestion}`);\r\n });\r\n console.log();\r\n }\r\n\r\n // 优化示例\r\n if (result.optimizationExamples && result.optimizationExamples.length > 0) {\r\n console.log(\"🔧 优化示例:\");\r\n result.optimizationExamples.slice(0, 3).forEach((example, index) => {\r\n console.log(` ${index + 1}. ${example.title} (${example.priority})`);\r\n console.log(` ${example.description}`);\r\n });\r\n console.log();\r\n }\r\n\r\n // AI 分析\r\n if (result.aiAnalysis) {\r\n console.log(\"🤖 AI 分析:\");\r\n console.log(\r\n result.aiAnalysis\r\n .split(\"\\n\")\r\n .map((line) => ` ${line}`)\r\n .join(\"\\n\"),\r\n );\r\n console.log();\r\n }\r\n\r\n console.log(\"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n\");\r\n }\r\n\r\n /**\r\n * 生成 HTML 报告\r\n */\r\n private async generateHTMLReport(result: AnalysisResult): Promise<void> {\r\n const html = `\r\n<!DOCTYPE html>\r\n<html lang=\"zh-CN\">\r\n<head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <title>性能分析报告</title>\r\n <style>\r\n * { margin: 0; padding: 0; box-sizing: border-box; }\r\n body {\r\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\r\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\r\n padding: 20px;\r\n min-height: 100vh;\r\n }\r\n .container {\r\n max-width: 1200px;\r\n margin: 0 auto;\r\n background: white;\r\n border-radius: 12px;\r\n box-shadow: 0 8px 32px rgba(0,0,0,0.2);\r\n overflow: hidden;\r\n }\r\n .header {\r\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\r\n color: white;\r\n padding: 30px;\r\n text-align: center;\r\n }\r\n .header h1 {\r\n font-size: 32px;\r\n margin-bottom: 10px;\r\n }\r\n .header .time {\r\n font-size: 14px;\r\n opacity: 0.9;\r\n }\r\n .content {\r\n padding: 30px;\r\n }\r\n .section {\r\n margin-bottom: 30px;\r\n }\r\n .section-title {\r\n font-size: 20px;\r\n font-weight: bold;\r\n color: #333;\r\n margin-bottom: 15px;\r\n padding-bottom: 10px;\r\n border-bottom: 2px solid #667eea;\r\n }\r\n .stats-grid {\r\n display: grid;\r\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\r\n gap: 20px;\r\n margin-bottom: 20px;\r\n }\r\n .stat-card {\r\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\r\n color: white;\r\n padding: 20px;\r\n border-radius: 12px;\r\n text-align: center;\r\n }\r\n .stat-value {\r\n font-size: 32px;\r\n font-weight: bold;\r\n margin: 10px 0;\r\n }\r\n .stat-label {\r\n font-size: 14px;\r\n opacity: 0.9;\r\n }\r\n .file-list {\r\n background: #f7fafc;\r\n border-radius: 8px;\r\n padding: 15px;\r\n }\r\n .file-item {\r\n display: flex;\r\n justify-content: space-between;\r\n padding: 10px;\r\n border-bottom: 1px solid #e2e8f0;\r\n }\r\n .file-item:last-child {\r\n border-bottom: none;\r\n }\r\n .file-name {\r\n font-family: monospace;\r\n color: #333;\r\n }\r\n .file-size {\r\n color: #667eea;\r\n font-weight: bold;\r\n }\r\n .issue {\r\n background: #fff5f5;\r\n border-left: 4px solid #f56565;\r\n padding: 15px;\r\n border-radius: 8px;\r\n margin-bottom: 15px;\r\n }\r\n .issue.medium {\r\n background: #fffaf0;\r\n border-left-color: #ed8936;\r\n }\r\n .issue.low {\r\n background: #f0f9ff;\r\n border-left-color: #4299e1;\r\n }\r\n .issue-title {\r\n font-weight: bold;\r\n color: #333;\r\n margin-bottom: 8px;\r\n }\r\n .issue-desc {\r\n color: #666;\r\n margin-bottom: 8px;\r\n }\r\n .issue-suggestion {\r\n background: #e6fffa;\r\n padding: 10px;\r\n border-radius: 6px;\r\n margin-top: 10px;\r\n color: #234e52;\r\n }\r\n .suggestion-list {\r\n background: #f0fdf4;\r\n border-radius: 8px;\r\n padding: 15px;\r\n }\r\n .suggestion-item {\r\n padding: 10px;\r\n border-bottom: 1px solid #d1fae5;\r\n color: #065f46;\r\n }\r\n .suggestion-item:last-child {\r\n border-bottom: none;\r\n }\r\n .ai-analysis {\r\n background: #f0f9ff;\r\n border-left: 4px solid #3b82f6;\r\n padding: 20px;\r\n border-radius: 8px;\r\n line-height: 1.8;\r\n color: #1e40af;\r\n white-space: pre-wrap;\r\n }\r\n .footer {\r\n background: #f7fafc;\r\n padding: 20px;\r\n text-align: center;\r\n color: #718096;\r\n font-size: 14px;\r\n }\r\n .trend-up {\r\n background: linear-gradient(135deg, #f56565 0%, #ed8936 100%) !important;\r\n }\r\n .trend-down {\r\n background: linear-gradient(135deg, #48bb78 0%, #38a169 100%) !important;\r\n }\r\n .optimization-example {\r\n background: #f7fafc;\r\n border-radius: 12px;\r\n padding: 20px;\r\n margin-bottom: 20px;\r\n border: 1px solid #e2e8f0;\r\n }\r\n .example-header {\r\n margin-bottom: 15px;\r\n }\r\n .example-title {\r\n font-size: 18px;\r\n font-weight: bold;\r\n color: #333;\r\n margin-bottom: 8px;\r\n display: flex;\r\n align-items: center;\r\n gap: 10px;\r\n }\r\n .priority-badge {\r\n font-size: 12px;\r\n padding: 4px 12px;\r\n border-radius: 12px;\r\n font-weight: normal;\r\n }\r\n .priority-high {\r\n background: #fed7d7;\r\n color: #c53030;\r\n }\r\n .priority-medium {\r\n background: #feebc8;\r\n color: #c05621;\r\n }\r\n .priority-low {\r\n background: #bee3f8;\r\n color: #2c5282;\r\n }\r\n .example-meta {\r\n display: flex;\r\n gap: 15px;\r\n font-size: 14px;\r\n color: #666;\r\n }\r\n .example-desc {\r\n color: #666;\r\n margin-bottom: 15px;\r\n line-height: 1.6;\r\n }\r\n .code-block {\r\n background: #1e293b;\r\n border-radius: 8px;\r\n overflow: hidden;\r\n margin-top: 10px;\r\n }\r\n .code-header {\r\n background: #334155;\r\n color: #94a3b8;\r\n padding: 8px 15px;\r\n font-size: 12px;\r\n font-family: monospace;\r\n }\r\n .code-block pre {\r\n margin: 0;\r\n padding: 15px;\r\n overflow-x: auto;\r\n }\r\n .code-block code {\r\n font-family: 'Consolas', 'Monaco', 'Courier New', monospace;\r\n font-size: 13px;\r\n line-height: 1.6;\r\n color: #e2e8f0;\r\n }\r\n .related-files {\r\n margin-top: 15px;\r\n padding: 10px;\r\n background: #e6fffa;\r\n border-radius: 6px;\r\n font-size: 14px;\r\n color: #234e52;\r\n }\r\n </style>\r\n</head>\r\n<body>\r\n <div class=\"container\">\r\n <div class=\"header\">\r\n <h1>⚡ 性能分析报告</h1>\r\n <div class=\"time\">生成时间: ${result.timestamp}</div>\r\n </div>\r\n\r\n <div class=\"content\">\r\n <!-- 总体统计 -->\r\n <div class=\"section\">\r\n <div class=\"section-title\">📊 总体统计</div>\r\n <div class=\"stats-grid\">\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">总大小</div>\r\n <div class=\"stat-value\">${(\r\n result.summary.totalSize /\r\n 1024 /\r\n 1024\r\n ).toFixed(2)}MB</div>\r\n </div>\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">Gzip 后</div>\r\n <div class=\"stat-value\">${(\r\n result.summary.totalGzipSize /\r\n 1024 /\r\n 1024\r\n ).toFixed(2)}MB</div>\r\n </div>\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">文件数量</div>\r\n <div class=\"stat-value\">${result.summary.fileCount}</div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 最大文件 -->\r\n <div class=\"section\">\r\n <div class=\"section-title\">📦 最大的文件</div>\r\n <div class=\"file-list\">\r\n ${result.summary.largestFiles\r\n .slice(0, 10)\r\n .map(\r\n (file) => `\r\n <div class=\"file-item\">\r\n <span class=\"file-name\">${file.name}</span>\r\n <span class=\"file-size\">${(file.size / 1024).toFixed(2)}KB</span>\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n </div>\r\n\r\n <!-- 按类型统计 -->\r\n <div class=\"section\">\r\n <div class=\"section-title\">📋 按类型统计</div>\r\n <div class=\"file-list\">\r\n ${Object.entries(result.summary.byType)\r\n .map(\r\n ([type, info]) => `\r\n <div class=\"file-item\">\r\n <span class=\"file-name\">${type}</span>\r\n <span class=\"file-size\">${info.count} 个, ${(\r\n info.size / 1024\r\n ).toFixed(2)}KB</span>\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n </div>\r\n\r\n <!-- 历史对比 -->\r\n ${\r\n result.comparison\r\n ? `\r\n <div class=\"section\">\r\n <div class=\"section-title\">📈 历史对比</div>\r\n <div class=\"stats-grid\">\r\n <div class=\"stat-card ${\r\n result.comparison.totalSize.trend === \"increased\"\r\n ? \"trend-up\"\r\n : \"trend-down\"\r\n }\">\r\n <div class=\"stat-label\">总大小变化</div>\r\n <div class=\"stat-value\">${\r\n result.comparison.totalSize.diffPercent > 0 ? \"+\" : \"\"\r\n }${result.comparison.totalSize.diffPercent.toFixed(2)}%</div>\r\n <div class=\"stat-label\">${\r\n result.comparison.totalSize.trend === \"increased\" ? \"+\" : \"\"\r\n }${(result.comparison.totalSize.diff / 1024 / 1024).toFixed(\r\n 2,\r\n )}MB</div>\r\n </div>\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">文件数变化</div>\r\n <div class=\"stat-value\">${\r\n result.comparison.fileCount.diff > 0 ? \"+\" : \"\"\r\n }${result.comparison.fileCount.diff}</div>\r\n </div>\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">新增文件</div>\r\n <div class=\"stat-value\">${result.comparison.newFiles.length}</div>\r\n </div>\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">删除文件</div>\r\n <div class=\"stat-value\">${\r\n result.comparison.removedFiles.length\r\n }</div>\r\n </div>\r\n </div>\r\n ${\r\n result.comparison.largestFileChanges.length > 0\r\n ? `\r\n <div class=\"file-list\" style=\"margin-top: 15px;\">\r\n <div style=\"font-weight: bold; padding: 10px; color: #333;\">文件大小变化 Top 5:</div>\r\n ${result.comparison.largestFileChanges\r\n .slice(0, 5)\r\n .map(\r\n (change) => `\r\n <div class=\"file-item\">\r\n <span class=\"file-name\">${change.name}</span>\r\n <span class=\"file-size ${\r\n change.diff > 0 ? \"trend-up\" : \"trend-down\"\r\n }\">\r\n ${change.diff > 0 ? \"+\" : \"\"}${(change.diff / 1024).toFixed(\r\n 2,\r\n )}KB\r\n </span>\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n\r\n <!-- 依赖分析 -->\r\n ${\r\n result.dependencies\r\n ? `\r\n <div class=\"section\">\r\n <div class=\"section-title\">📦 依赖分析</div>\r\n <div class=\"stats-grid\">\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">总依赖数</div>\r\n <div class=\"stat-value\">${result.dependencies.total}</div>\r\n </div>\r\n <div class=\"stat-card ${\r\n result.dependencies.duplicates.length > 0 ? \"trend-up\" : \"\"\r\n }\">\r\n <div class=\"stat-label\">重复依赖</div>\r\n <div class=\"stat-value\">${\r\n result.dependencies.duplicates.length\r\n }</div>\r\n </div>\r\n </div>\r\n ${\r\n result.dependencies.duplicates.length > 0\r\n ? `\r\n <div class=\"file-list\" style=\"margin-top: 15px;\">\r\n <div style=\"font-weight: bold; padding: 10px; color: #333;\">重复打包的依赖:</div>\r\n ${result.dependencies.duplicates\r\n .slice(0, 10)\r\n .map(\r\n (dep) => `\r\n <div class=\"file-item\">\r\n <span class=\"file-name\">${dep.name}</span>\r\n <span class=\"file-size\">\r\n ${(dep.size / 1024).toFixed(2)}KB · 被 ${\r\n dep.usedBy.length\r\n } 个文件使用\r\n </span>\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n ${\r\n result.dependencies.largest.length > 0\r\n ? `\r\n <div class=\"file-list\" style=\"margin-top: 15px;\">\r\n <div style=\"font-weight: bold; padding: 10px; color: #333;\">最大的依赖:</div>\r\n ${result.dependencies.largest\r\n .slice(0, 10)\r\n .map(\r\n (dep) => `\r\n <div class=\"file-item\">\r\n <span class=\"file-name\">${dep.name}</span>\r\n <span class=\"file-size\">${(dep.size / 1024).toFixed(\r\n 2,\r\n )}KB</span>\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n\r\n <!-- 性能问题 -->\r\n ${\r\n result.issues.length > 0\r\n ? `\r\n <div class=\"section\">\r\n <div class=\"section-title\">⚠️ 性能问题</div>\r\n ${result.issues\r\n .map(\r\n (issue) => `\r\n <div class=\"issue ${issue.severity}\">\r\n <div class=\"issue-title\">${this.getSeverityIcon(\r\n issue.severity,\r\n )} ${issue.title}</div>\r\n <div class=\"issue-desc\">${issue.description}</div>\r\n ${\r\n issue.files\r\n ? `<div class=\"issue-desc\">相关文件: ${issue.files.join(\r\n \", \",\r\n )}</div>`\r\n : \"\"\r\n }\r\n ${\r\n issue.suggestion\r\n ? `<div class=\"issue-suggestion\">💡 ${issue.suggestion}</div>`\r\n : \"\"\r\n }\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n `\r\n : '<div class=\"section\"><div class=\"section-title\">✅ 未发现明显的性能问题</div></div>'\r\n }\r\n\r\n <!-- 优化建议 -->\r\n ${\r\n result.suggestions.length > 0\r\n ? `\r\n <div class=\"section\">\r\n <div class=\"section-title\">💡 优化建议</div>\r\n <div class=\"suggestion-list\">\r\n ${result.suggestions\r\n .map(\r\n (suggestion, index) => `\r\n <div class=\"suggestion-item\">${index + 1}. ${suggestion}</div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n\r\n <!-- 优化示例 -->\r\n ${\r\n result.optimizationExamples && result.optimizationExamples.length > 0\r\n ? `\r\n <div class=\"section\">\r\n <div class=\"section-title\">🔧 优化示例</div>\r\n ${result.optimizationExamples\r\n .map(\r\n (example) => `\r\n <div class=\"optimization-example\">\r\n <div class=\"example-header\">\r\n <div class=\"example-title\">\r\n ${example.title}\r\n <span class=\"priority-badge priority-${example.priority}\">${\r\n example.priority === \"high\"\r\n ? \"高优先级\"\r\n : example.priority === \"medium\"\r\n ? \"中优先级\"\r\n : \"低优先级\"\r\n }</span>\r\n </div>\r\n <div class=\"example-meta\">\r\n <span>💪 ${example.difficulty}</span>\r\n <span>📈 ${example.impact}</span>\r\n </div>\r\n </div>\r\n <div class=\"example-desc\">${example.description}</div>\r\n <div class=\"code-block\">\r\n <div class=\"code-header\">${example.code.language}</div>\r\n <pre><code>${this.escapeHtml(example.code.content)}</code></pre>\r\n </div>\r\n ${\r\n example.relatedFiles && example.relatedFiles.length > 0\r\n ? `\r\n <div class=\"related-files\">\r\n <strong>相关文件:</strong> ${example.relatedFiles.join(\", \")}\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n\r\n <!-- AI 分析 -->\r\n ${\r\n result.aiAnalysis\r\n ? `\r\n <div class=\"section\">\r\n <div class=\"section-title\">🤖 AI 分析</div>\r\n <div class=\"ai-analysis\">${this.escapeHtml(result.aiAnalysis)}</div>\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n </div>\r\n\r\n <div class=\"footer\">\r\n <p>AI Performance Analyzer v1.0.0</p>\r\n <p>Powered by Vite & OpenAI</p>\r\n </div>\r\n </div>\r\n</body>\r\n</html>\r\n `.trim();\r\n\r\n // 创建报告目录\r\n const reportsDir = path.resolve(process.cwd(), \"ai-reports\");\r\n if (!fs.existsSync(reportsDir)) {\r\n fs.mkdirSync(reportsDir, { recursive: true });\r\n }\r\n\r\n const reportPath = path.resolve(reportsDir, \"performance-report.html\");\r\n fs.writeFileSync(reportPath, html, \"utf-8\");\r\n console.log(pc.green(`\\n📄 性能报告已生成: ${pc.cyan(reportPath)}\\n`));\r\n }\r\n\r\n /**\r\n * 生成 JSON 报告\r\n */\r\n private async generateJSONReport(result: AnalysisResult): Promise<void> {\r\n const reportsDir = path.resolve(process.cwd(), \"ai-reports\");\r\n if (!fs.existsSync(reportsDir)) {\r\n fs.mkdirSync(reportsDir, { recursive: true });\r\n }\r\n\r\n const reportPath = path.resolve(reportsDir, \"performance-report.json\");\r\n fs.writeFileSync(reportPath, JSON.stringify(result, null, 2), \"utf-8\");\r\n console.log(pc.green(`📄 JSON 报告已生成: ${pc.cyan(reportPath)}`));\r\n }\r\n\r\n /**\r\n * 获取严重程度图标\r\n */\r\n private getSeverityIcon(severity: string): string {\r\n const icons: Record<string, string> = {\r\n high: \"🔴\",\r\n medium: \"🟡\",\r\n low: \"🔵\",\r\n };\r\n return icons[severity] || \"⚪\";\r\n }\r\n\r\n /**\r\n * 转义 HTML\r\n */\r\n private escapeHtml(text: string): string {\r\n const map: Record<string, string> = {\r\n \"&\": \"&amp;\",\r\n \"<\": \"&lt;\",\r\n \">\": \"&gt;\",\r\n '\"': \"&quot;\",\r\n \"'\": \"&#039;\",\r\n };\r\n return text.replace(/[&<>\"']/g, (m) => map[m]);\r\n }\r\n}\r\n"],"mappings":"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","names":["import_picocolors","import_fs","import_path","path","fs","import_fs","import_path","path","fs","path","fs","import_fs","import_path","path","fs","pc","pc"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/analyzer.ts","../src/dependency-analyzer.ts","../src/history-analyzer.ts","../src/optimization-examples.ts","../src/reporter.ts"],"sourcesContent":["/**\r\n * AI 性能分析插件\r\n *\r\n * 功能:\r\n * - 分析构建产物大小\r\n * - 检测性能问题\r\n * - 提供优化建议\r\n */\r\n\r\nimport type { Plugin } from \"vite\";\r\nimport pc from \"picocolors\";\r\nimport { PerfAnalyzer } from \"./analyzer\";\r\nimport { PerfReporter } from \"./reporter\";\r\nimport type { AnalysisResult } from \"./types\";\r\n\r\nexport interface PerfAnalyzerOptions {\r\n // AI 配置\r\n apiKey?: string;\r\n apiUrl?: string;\r\n model?: string;\r\n temperature?: number;\r\n maxTokens?: number;\r\n // 分析配置\r\n enabled?: boolean;\r\n threshold?: {\r\n bundleSize?: number; // 单个文件大小阈值 (KB)\r\n totalSize?: number; // 总大小阈值 (MB)\r\n chunkCount?: number; // chunk 数量阈值\r\n };\r\n // 输出配置\r\n output?: {\r\n console?: boolean;\r\n html?: boolean;\r\n json?: boolean;\r\n };\r\n}\r\n\r\nexport function vitePluginAIPerfAnalyzer(\r\n options: PerfAnalyzerOptions = {},\r\n): Plugin {\r\n const {\r\n apiKey = process.env.OPENAI_API_KEY || \"\",\r\n apiUrl = process.env.OPENAI_API_URL || \"https://api.openai.com/v1\",\r\n model = process.env.OPENAI_MODEL || \"gpt-4\",\r\n temperature = 0.2,\r\n maxTokens = 4000,\r\n enabled = true,\r\n threshold = {\r\n bundleSize: 500, // 500KB\r\n totalSize: 5, // 5MB\r\n chunkCount: 20,\r\n },\r\n output = {\r\n console: true,\r\n html: true,\r\n json: false,\r\n },\r\n } = options;\r\n\r\n const analyzer = new PerfAnalyzer({\r\n apiKey,\r\n apiUrl,\r\n model,\r\n threshold,\r\n temperature,\r\n maxTokens,\r\n });\r\n const reporter = new PerfReporter();\r\n\r\n let analysisResult: AnalysisResult | null = null;\r\n\r\n return {\r\n name: \"vite-plugin-ai-perf-analyzer\",\r\n enforce: \"post\",\r\n\r\n configResolved(config) {\r\n if (!enabled) return;\r\n\r\n console.log(pc.cyan(\"\\n⚡ AI 性能分析插件已启动...\"));\r\n console.log(`📊 分析阈值:`);\r\n console.log(` 单文件: ${pc.yellow(threshold.bundleSize + \"KB\")}`);\r\n console.log(` 总大小: ${pc.yellow(threshold.totalSize + \"MB\")}`);\r\n console.log(\r\n ` Chunk数: ${pc.yellow((threshold.chunkCount ?? 10).toString())}`,\r\n );\r\n console.log(`🔑 API Key: ${apiKey ? \"已配置\" : \"未配置\"}\\n`);\r\n },\r\n\r\n async closeBundle() {\r\n if (!enabled) return;\r\n\r\n console.log(\"\\n⚡ 正在分析构建产物...\\n\");\r\n\r\n try {\r\n // 分析构建产物\r\n analysisResult = await analyzer.analyze();\r\n\r\n // 生成报告\r\n await reporter.generate(analysisResult, output);\r\n\r\n // 控制台输出\r\n if (output.console) {\r\n reporter.printConsole(analysisResult);\r\n }\r\n } catch (error: any) {\r\n console.error(\"❌ 性能分析失败:\", error.message);\r\n }\r\n },\r\n };\r\n}\r\n\r\n// 默认导出\r\nexport default vitePluginAIPerfAnalyzer;\r\n","/**\r\n * 性能分析器\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport { gzipSync } from \"zlib\";\r\nimport { ChatOpenAI } from \"@langchain/openai\";\r\nimport { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\r\nimport type {\r\n AnalysisResult,\r\n BundleInfo,\r\n PerformanceIssue,\r\n AnalyzerOptions,\r\n} from \"./types\";\r\nimport { DependencyAnalyzer } from \"./dependency-analyzer\";\r\nimport { HistoryAnalyzer } from \"./history-analyzer\";\r\nimport { OptimizationExamples } from \"./optimization-examples\";\r\n\r\nexport class PerfAnalyzer {\r\n private llm: ChatOpenAI | null = null;\r\n private options: AnalyzerOptions;\r\n private depAnalyzer: DependencyAnalyzer;\r\n private historyAnalyzer: HistoryAnalyzer;\r\n private examplesGenerator: OptimizationExamples;\r\n\r\n constructor(options: AnalyzerOptions) {\r\n this.options = options;\r\n this.depAnalyzer = new DependencyAnalyzer();\r\n this.historyAnalyzer = new HistoryAnalyzer();\r\n this.examplesGenerator = new OptimizationExamples();\r\n\r\n if (options.apiKey) {\r\n this.llm = new ChatOpenAI({\r\n openAIApiKey: options.apiKey,\r\n configuration: { baseURL: options.apiUrl },\r\n modelName: options.model,\r\n temperature: options.temperature ?? 0.2,\r\n maxTokens: options.maxTokens ?? 4000,\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * 分析构建产物\r\n */\r\n async analyze(): Promise<AnalysisResult> {\r\n const distDir = path.resolve(process.cwd(), \"dist\");\r\n\r\n if (!fs.existsSync(distDir)) {\r\n throw new Error(\"构建目录不存在,请先执行构建\");\r\n }\r\n\r\n // 收集文件信息\r\n const bundles = this.collectBundles(distDir);\r\n\r\n // 计算统计信息\r\n const summary = this.calculateSummary(bundles);\r\n\r\n // 依赖分析\r\n console.log(\"📦 正在分析依赖...\");\r\n const dependencies = this.depAnalyzer.analyzeDependencies(bundles);\r\n\r\n // 历史对比\r\n console.log(\"📊 正在对比历史记录...\");\r\n const comparison = this.historyAnalyzer.compare(\r\n bundles,\r\n summary.totalSize,\r\n summary.fileCount,\r\n );\r\n\r\n // 检测性能问题\r\n const issues = this.detectIssues(bundles, summary, dependencies);\r\n\r\n // 生成基础建议\r\n const suggestions = this.generateSuggestions(issues);\r\n\r\n // 生成优化示例\r\n console.log(\"💡 正在生成优化示例...\");\r\n const optimizationExamples = this.examplesGenerator.generate(\r\n issues,\r\n dependencies.duplicates,\r\n );\r\n\r\n // AI 分析(如果配置了 API Key)\r\n let aiAnalysis: string | undefined;\r\n if (this.llm) {\r\n console.log(\"🤖 正在使用 AI 分析性能...\\n\");\r\n aiAnalysis = await this.performAIAnalysis(\r\n bundles,\r\n summary,\r\n issues,\r\n dependencies,\r\n comparison,\r\n );\r\n }\r\n\r\n return {\r\n timestamp: new Date().toLocaleString(\"zh-CN\"),\r\n summary,\r\n dependencies,\r\n comparison,\r\n issues,\r\n suggestions,\r\n optimizationExamples,\r\n aiAnalysis,\r\n };\r\n }\r\n\r\n /**\r\n * 收集构建产物信息\r\n */\r\n private collectBundles(dir: string, baseDir: string = dir): BundleInfo[] {\r\n const bundles: BundleInfo[] = [];\r\n const files = fs.readdirSync(dir);\r\n\r\n for (const file of files) {\r\n const filePath = path.join(dir, file);\r\n const stat = fs.statSync(filePath);\r\n\r\n if (stat.isDirectory()) {\r\n bundles.push(...this.collectBundles(filePath, baseDir));\r\n } else {\r\n const content = fs.readFileSync(filePath);\r\n const gzipSize = gzipSync(content).length;\r\n const relativePath = path.relative(baseDir, filePath);\r\n\r\n bundles.push({\r\n name: file,\r\n size: stat.size,\r\n gzipSize,\r\n type: this.getFileType(file),\r\n path: relativePath,\r\n });\r\n }\r\n }\r\n\r\n return bundles;\r\n }\r\n\r\n /**\r\n * 获取文件类型\r\n */\r\n private getFileType(filename: string): string {\r\n const ext = path.extname(filename).toLowerCase();\r\n const typeMap: Record<string, string> = {\r\n \".js\": \"javascript\",\r\n \".mjs\": \"javascript\",\r\n \".css\": \"stylesheet\",\r\n \".html\": \"html\",\r\n \".png\": \"image\",\r\n \".jpg\": \"image\",\r\n \".jpeg\": \"image\",\r\n \".gif\": \"image\",\r\n \".svg\": \"image\",\r\n \".webp\": \"image\",\r\n \".woff\": \"font\",\r\n \".woff2\": \"font\",\r\n \".ttf\": \"font\",\r\n \".eot\": \"font\",\r\n \".json\": \"data\",\r\n \".map\": \"sourcemap\",\r\n };\r\n return typeMap[ext] || \"other\";\r\n }\r\n\r\n /**\r\n * 计算统计信息\r\n */\r\n private calculateSummary(bundles: BundleInfo[]) {\r\n const totalSize = bundles.reduce((sum, b) => sum + b.size, 0);\r\n const totalGzipSize = bundles.reduce(\r\n (sum, b) => sum + (b.gzipSize || 0),\r\n 0,\r\n );\r\n\r\n // 按大小排序,取前 10\r\n const largestFiles = [...bundles]\r\n .sort((a, b) => b.size - a.size)\r\n .slice(0, 10);\r\n\r\n // 按类型分组\r\n const byType: Record<string, { count: number; size: number }> = {};\r\n bundles.forEach((bundle) => {\r\n if (!byType[bundle.type]) {\r\n byType[bundle.type] = { count: 0, size: 0 };\r\n }\r\n byType[bundle.type].count++;\r\n byType[bundle.type].size += bundle.size;\r\n });\r\n\r\n return {\r\n totalSize,\r\n totalGzipSize,\r\n fileCount: bundles.length,\r\n largestFiles,\r\n byType,\r\n };\r\n }\r\n\r\n /**\r\n * 检测性能问题\r\n */\r\n private detectIssues(\r\n bundles: BundleInfo[],\r\n summary: any,\r\n dependencies?: {\r\n total: number;\r\n duplicates: DependencyInfo[];\r\n largest: DependencyInfo[];\r\n },\r\n ): PerformanceIssue[] {\r\n const issues: PerformanceIssue[] = [];\r\n const { threshold } = this.options;\r\n\r\n // 检查总大小\r\n const totalSizeMB = summary.totalSize / 1024 / 1024;\r\n if (threshold.totalSize && totalSizeMB > threshold.totalSize) {\r\n issues.push({\r\n type: \"size\",\r\n severity: \"high\",\r\n title: \"构建产物总大小过大\",\r\n description: `总大小 ${totalSizeMB.toFixed(2)}MB 超过阈值 ${\r\n threshold.totalSize\r\n }MB`,\r\n suggestion: \"考虑代码分割、tree-shaking、压缩等优化手段\",\r\n });\r\n }\r\n\r\n // 检查单个文件大小\r\n const largeBundles = bundles.filter(\r\n (b) => b.size / 1024 > (threshold.bundleSize || 500),\r\n );\r\n if (largeBundles.length > 0) {\r\n issues.push({\r\n type: \"size\",\r\n severity: \"medium\",\r\n title: \"存在过大的单个文件\",\r\n description: `发现 ${largeBundles.length} 个文件超过 ${threshold.bundleSize}KB`,\r\n files: largeBundles.map(\r\n (b) => `${b.name} (${(b.size / 1024).toFixed(2)}KB)`,\r\n ),\r\n suggestion: \"考虑拆分大文件,使用动态导入或代码分割\",\r\n });\r\n }\r\n\r\n // 检查 chunk 数量\r\n const jsFiles = bundles.filter((b) => b.type === \"javascript\");\r\n if (threshold.chunkCount && jsFiles.length > threshold.chunkCount) {\r\n issues.push({\r\n type: \"count\",\r\n severity: \"low\",\r\n title: \"JavaScript 文件数量过多\",\r\n description: `共有 ${jsFiles.length} 个 JS 文件,超过阈值 ${threshold.chunkCount}`,\r\n suggestion: \"过多的文件会增加 HTTP 请求数,考虑合并小文件\",\r\n });\r\n }\r\n\r\n // 检查未压缩的图片\r\n const images = bundles.filter((b) => b.type === \"image\");\r\n const largeImages = images.filter((img) => img.size > 100 * 1024); // 100KB\r\n if (largeImages.length > 0) {\r\n issues.push({\r\n type: \"optimization\",\r\n severity: \"medium\",\r\n title: \"存在未优化的图片\",\r\n description: `发现 ${largeImages.length} 个大于 100KB 的图片`,\r\n files: largeImages.map(\r\n (img) => `${img.name} (${(img.size / 1024).toFixed(2)}KB)`,\r\n ),\r\n suggestion: \"使用图片压缩工具,或转换为 WebP 格式\",\r\n });\r\n }\r\n\r\n // 检查 sourcemap\r\n const sourcemaps = bundles.filter((b) => b.type === \"sourcemap\");\r\n if (sourcemaps.length > 0) {\r\n const totalMapSize = sourcemaps.reduce((sum, m) => sum + m.size, 0);\r\n issues.push({\r\n type: \"optimization\",\r\n severity: \"low\",\r\n title: \"生产环境包含 sourcemap\",\r\n description: `Sourcemap 文件占用 ${(totalMapSize / 1024 / 1024).toFixed(\r\n 2,\r\n )}MB`,\r\n suggestion: \"生产环境建议禁用 sourcemap 或使用外部 sourcemap\",\r\n });\r\n }\r\n\r\n // 检查重复依赖\r\n if (dependencies && dependencies.duplicates.length > 0) {\r\n const topDuplicates = dependencies.duplicates.slice(0, 3);\r\n issues.push({\r\n type: \"dependency\",\r\n severity: \"medium\",\r\n title: \"检测到重复打包的依赖\",\r\n description: `发现 ${dependencies.duplicates.length} 个依赖被多次打包`,\r\n files: topDuplicates.map(\r\n (d) =>\r\n `${d.name} (被 ${d.usedBy.length} 个文件使用, ${(\r\n d.size / 1024\r\n ).toFixed(2)}KB)`,\r\n ),\r\n suggestion: \"将重复依赖提取到公共 chunk 中\",\r\n });\r\n }\r\n\r\n return issues;\r\n }\r\n\r\n /**\r\n * 生成基础建议\r\n */\r\n private generateSuggestions(issues: PerformanceIssue[]): string[] {\r\n const suggestions: string[] = [];\r\n\r\n if (issues.some((i) => i.type === \"size\")) {\r\n suggestions.push(\"启用 gzip/brotli 压缩\");\r\n suggestions.push(\"配置 Vite 的 build.rollupOptions 进行代码分割\");\r\n suggestions.push(\"使用 vite-plugin-compression 插件\");\r\n }\r\n\r\n if (issues.some((i) => i.type === \"optimization\")) {\r\n suggestions.push(\"使用 vite-plugin-imagemin 优化图片\");\r\n suggestions.push(\"配置 CSS 压缩和 tree-shaking\");\r\n }\r\n\r\n if (issues.length === 0) {\r\n suggestions.push(\"构建产物已经很优秀,继续保持!\");\r\n }\r\n\r\n return suggestions;\r\n }\r\n\r\n /**\r\n * AI 性能分析\r\n */\r\n private async performAIAnalysis(\r\n bundles: BundleInfo[],\r\n summary: any,\r\n issues: PerformanceIssue[],\r\n dependencies?: {\r\n total: number;\r\n duplicates: DependencyInfo[];\r\n largest: DependencyInfo[];\r\n },\r\n comparison?: any,\r\n ): Promise<string> {\r\n const systemPrompt = new SystemMessage(\r\n \"你是一个专业的前端性能优化专家,精通 Vite、Webpack 等构建工具。请分析构建产物并提供专业的优化建议。\",\r\n );\r\n\r\n const bundlesSummary = summary.largestFiles\r\n .slice(0, 5)\r\n .map((b: BundleInfo) => `- ${b.name}: ${(b.size / 1024).toFixed(2)}KB`)\r\n .join(\"\\n\");\r\n\r\n const typesSummary = Object.entries(summary.byType)\r\n .map(\r\n ([type, info]: [string, any]) =>\r\n `- ${type}: ${info.count} 个文件, ${(info.size / 1024).toFixed(2)}KB`,\r\n )\r\n .join(\"\\n\");\r\n\r\n const issuesSummary = issues\r\n .map(\r\n (issue) => `- [${issue.severity}] ${issue.title}: ${issue.description}`,\r\n )\r\n .join(\"\\n\");\r\n\r\n // 依赖分析摘要\r\n let dependencySummary = \"\";\r\n if (dependencies) {\r\n dependencySummary = `\r\n## 依赖分析\r\n- 总依赖数: ${dependencies.total}\r\n- 重复依赖: ${dependencies.duplicates.length} 个\r\n- 最大依赖: ${dependencies.largest\r\n .slice(0, 3)\r\n .map((d) => `${d.name} (${(d.size / 1024).toFixed(2)}KB)`)\r\n .join(\", \")}`;\r\n }\r\n\r\n // 历史对比摘要\r\n let comparisonSummary = \"\";\r\n if (comparison) {\r\n const sizeChange =\r\n comparison.totalSize.trend === \"increased\" ? \"增加\" : \"减少\";\r\n comparisonSummary = `\r\n## 历史对比\r\n- 总大小${sizeChange}: ${Math.abs(comparison.totalSize.diffPercent).toFixed(2)}%\r\n- 文件数量变化: ${comparison.fileCount.diff > 0 ? \"+\" : \"\"}${\r\n comparison.fileCount.diff\r\n }\r\n- 新增文件: ${comparison.newFiles.length} 个\r\n- 删除文件: ${comparison.removedFiles.length} 个`;\r\n }\r\n\r\n const userPrompt = new HumanMessage(`\r\n请分析以下构建产物信息:\r\n\r\n## 总体统计\r\n- 总大小: ${(summary.totalSize / 1024 / 1024).toFixed(2)}MB\r\n- Gzip 后: ${(summary.totalGzipSize / 1024 / 1024).toFixed(2)}MB\r\n- 文件数量: ${summary.fileCount}\r\n\r\n## 最大的文件\r\n${bundlesSummary}\r\n\r\n## 按类型分组\r\n${typesSummary}\r\n${dependencySummary}\r\n${comparisonSummary}\r\n\r\n## 检测到的问题\r\n${issuesSummary || \"无明显问题\"}\r\n\r\n请提供:\r\n1. 性能评估(3-5 句话)\r\n2. 具体优化建议(3-5 条,每条简洁明了)\r\n3. 优先级排序\r\n\r\n请用简洁专业的语言回答,不要过于冗长。\r\n`);\r\n\r\n const response = await this.llm!.invoke([systemPrompt, userPrompt]);\r\n return response.content.toString();\r\n }\r\n}\r\n","/**\r\n * 依赖分析器\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport type { BundleInfo, DependencyInfo } from \"./types\";\r\n\r\nexport class DependencyAnalyzer {\r\n /**\r\n * 分析依赖\r\n */\r\n analyzeDependencies(bundles: BundleInfo[]): {\r\n total: number;\r\n duplicates: DependencyInfo[];\r\n largest: DependencyInfo[];\r\n } {\r\n const jsFiles = bundles.filter((b) => b.type === \"javascript\");\r\n const dependencies = this.extractDependencies(jsFiles);\r\n\r\n // 检测重复依赖\r\n const duplicates = this.findDuplicates(dependencies);\r\n\r\n // 找出最大的依赖\r\n const largest = [...dependencies]\r\n .sort((a, b) => b.size - a.size)\r\n .slice(0, 10);\r\n\r\n return {\r\n total: dependencies.length,\r\n duplicates,\r\n largest,\r\n };\r\n }\r\n\r\n /**\r\n * 从 JS 文件中提取依赖信息\r\n */\r\n private extractDependencies(jsFiles: BundleInfo[]): DependencyInfo[] {\r\n const depMap = new Map<string, DependencyInfo>();\r\n\r\n jsFiles.forEach((file) => {\r\n // 尝试解析文件内容,提取依赖\r\n const imports = this.parseImports(file);\r\n\r\n imports.forEach((depName) => {\r\n if (!depMap.has(depName)) {\r\n depMap.set(depName, {\r\n name: depName,\r\n size: 0,\r\n usedBy: [],\r\n isDuplicate: false,\r\n });\r\n }\r\n\r\n const dep = depMap.get(depName)!;\r\n dep.usedBy.push(file.name);\r\n // 估算依赖大小(简化处理)\r\n dep.size += file.size / imports.length;\r\n });\r\n });\r\n\r\n return Array.from(depMap.values());\r\n }\r\n\r\n /**\r\n * 解析文件中的 import 语句\r\n */\r\n private parseImports(file: BundleInfo): string[] {\r\n const imports: string[] = [];\r\n\r\n try {\r\n const distDir = path.resolve(process.cwd(), \"dist\");\r\n const filePath = path.join(distDir, file.path);\r\n\r\n if (!fs.existsSync(filePath)) {\r\n return imports;\r\n }\r\n\r\n const content = fs.readFileSync(filePath, \"utf-8\");\r\n\r\n // 匹配常见的依赖模式\r\n const patterns = [\r\n // node_modules 依赖\r\n /from\\s+[\"']([^\"']+)[\"']/g,\r\n /require\\([\"']([^\"']+)[\"']\\)/g,\r\n // Vite 特殊标记\r\n /__vite_ssr_import__\\(\"([^\"]+)\"\\)/g,\r\n ];\r\n\r\n patterns.forEach((pattern) => {\r\n let match;\r\n while ((match = pattern.exec(content)) !== null) {\r\n const depName = match[1];\r\n // 只保留 node_modules 依赖\r\n if (!depName.startsWith(\".\") && !depName.startsWith(\"/\")) {\r\n // 提取包名(去掉子路径)\r\n const pkgName = depName\r\n .split(\"/\")\r\n .slice(0, depName.startsWith(\"@\") ? 2 : 1)\r\n .join(\"/\");\r\n if (!imports.includes(pkgName)) {\r\n imports.push(pkgName);\r\n }\r\n }\r\n }\r\n });\r\n } catch (error) {\r\n // 忽略解析错误\r\n }\r\n\r\n return imports;\r\n }\r\n\r\n /**\r\n * 查找重复打包的依赖\r\n */\r\n private findDuplicates(dependencies: DependencyInfo[]): DependencyInfo[] {\r\n return dependencies\r\n .filter((dep) => dep.usedBy.length > 1)\r\n .map((dep) => ({\r\n ...dep,\r\n isDuplicate: true,\r\n }))\r\n .sort((a, b) => b.usedBy.length - a.usedBy.length)\r\n .slice(0, 10);\r\n }\r\n}\r\n","/**\r\n * 历史对比分析器\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport type {\r\n BundleInfo,\r\n HistoryComparison,\r\n HistoryRecord,\r\n ComparisonResult,\r\n} from \"./types\";\r\n\r\nexport class HistoryAnalyzer {\r\n private historyFile: string;\r\n\r\n constructor() {\r\n const reportsDir = path.resolve(process.cwd(), \"ai-reports\");\r\n this.historyFile = path.join(reportsDir, \".perf-history.json\");\r\n }\r\n\r\n /**\r\n * 对比当前构建与历史记录\r\n */\r\n compare(\r\n currentBundles: BundleInfo[],\r\n currentTotalSize: number,\r\n currentFileCount: number\r\n ): HistoryComparison | undefined {\r\n const history = this.loadHistory();\r\n\r\n if (history.length === 0) {\r\n // 没有历史记录,保存当前记录\r\n this.saveHistory(currentBundles, currentTotalSize, currentFileCount);\r\n return undefined;\r\n }\r\n\r\n // 获取上一次记录\r\n const previous = history[history.length - 1];\r\n\r\n // 对比总大小\r\n const totalSize = this.compareValue(currentTotalSize, previous.totalSize);\r\n\r\n // 对比文件数量\r\n const fileCount = this.compareValue(currentFileCount, previous.fileCount);\r\n\r\n // 对比最大文件变化\r\n const largestFileChanges = this.compareLargestFiles(\r\n currentBundles,\r\n previous.files\r\n );\r\n\r\n // 检测新增和删除的文件\r\n const currentFileNames = new Set(currentBundles.map((b) => b.name));\r\n const previousFileNames = new Set(previous.files.map((f) => f.name));\r\n\r\n const newFiles = currentBundles\r\n .filter((b) => !previousFileNames.has(b.name))\r\n .map((b) => b.name);\r\n\r\n const removedFiles = previous.files\r\n .filter((f) => !currentFileNames.has(f.name))\r\n .map((f) => f.name);\r\n\r\n // 保存当前记录\r\n this.saveHistory(currentBundles, currentTotalSize, currentFileCount);\r\n\r\n return {\r\n totalSize,\r\n fileCount,\r\n largestFileChanges,\r\n newFiles,\r\n removedFiles,\r\n };\r\n }\r\n\r\n /**\r\n * 对比数值\r\n */\r\n private compareValue(current: number, previous: number): ComparisonResult {\r\n const diff = current - previous;\r\n const diffPercent = previous === 0 ? 0 : (diff / previous) * 100;\r\n\r\n let trend: \"increased\" | \"decreased\" | \"unchanged\";\r\n if (Math.abs(diffPercent) < 1) {\r\n trend = \"unchanged\";\r\n } else if (diff > 0) {\r\n trend = \"increased\";\r\n } else {\r\n trend = \"decreased\";\r\n }\r\n\r\n return {\r\n current,\r\n previous,\r\n diff,\r\n diffPercent,\r\n trend,\r\n };\r\n }\r\n\r\n /**\r\n * 对比最大文件的变化\r\n */\r\n private compareLargestFiles(\r\n currentBundles: BundleInfo[],\r\n previousFiles: Array<{ name: string; size: number }>\r\n ): Array<{\r\n name: string;\r\n current: number;\r\n previous: number;\r\n diff: number;\r\n }> {\r\n const changes: Array<{\r\n name: string;\r\n current: number;\r\n previous: number;\r\n diff: number;\r\n }> = [];\r\n\r\n // 创建文件映射\r\n const previousMap = new Map(previousFiles.map((f) => [f.name, f.size]));\r\n\r\n currentBundles.forEach((bundle) => {\r\n const previousSize = previousMap.get(bundle.name);\r\n if (previousSize !== undefined) {\r\n const diff = bundle.size - previousSize;\r\n // 只记录变化超过 10KB 的文件\r\n if (Math.abs(diff) > 10 * 1024) {\r\n changes.push({\r\n name: bundle.name,\r\n current: bundle.size,\r\n previous: previousSize,\r\n diff,\r\n });\r\n }\r\n }\r\n });\r\n\r\n // 按变化幅度排序\r\n return changes\r\n .sort((a, b) => Math.abs(b.diff) - Math.abs(a.diff))\r\n .slice(0, 10);\r\n }\r\n\r\n /**\r\n * 加载历史记录\r\n */\r\n private loadHistory(): HistoryRecord[] {\r\n if (!fs.existsSync(this.historyFile)) {\r\n return [];\r\n }\r\n\r\n try {\r\n const content = fs.readFileSync(this.historyFile, \"utf-8\");\r\n const history = JSON.parse(content);\r\n // 只保留最近 10 次记录\r\n return history.slice(-10);\r\n } catch (error) {\r\n console.warn(\"⚠️ 无法读取历史记录\");\r\n return [];\r\n }\r\n }\r\n\r\n /**\r\n * 保存历史记录\r\n */\r\n private saveHistory(\r\n bundles: BundleInfo[],\r\n totalSize: number,\r\n fileCount: number\r\n ): void {\r\n const history = this.loadHistory();\r\n\r\n const record: HistoryRecord = {\r\n timestamp: new Date().toISOString(),\r\n totalSize,\r\n fileCount,\r\n files: bundles.map((b) => ({\r\n name: b.name,\r\n size: b.size,\r\n })),\r\n };\r\n\r\n history.push(record);\r\n\r\n // 确保目录存在\r\n const dir = path.dirname(this.historyFile);\r\n if (!fs.existsSync(dir)) {\r\n fs.mkdirSync(dir, { recursive: true });\r\n }\r\n\r\n // 只保留最近 10 次记录\r\n const recentHistory = history.slice(-10);\r\n\r\n fs.writeFileSync(\r\n this.historyFile,\r\n JSON.stringify(recentHistory, null, 2),\r\n \"utf-8\"\r\n );\r\n }\r\n\r\n /**\r\n * 清除历史记录\r\n */\r\n clearHistory(): void {\r\n if (fs.existsSync(this.historyFile)) {\r\n fs.unlinkSync(this.historyFile);\r\n }\r\n }\r\n}\r\n","/**\r\n * 优化示例生成器\r\n */\r\n\r\nimport type {\r\n OptimizationExample,\r\n PerformanceIssue,\r\n DependencyInfo,\r\n} from \"./types\";\r\n\r\nexport class OptimizationExamples {\r\n /**\r\n * 根据问题生成优化示例\r\n */\r\n generate(\r\n issues: PerformanceIssue[],\r\n duplicateDeps?: DependencyInfo[]\r\n ): OptimizationExample[] {\r\n const examples: OptimizationExample[] = [];\r\n\r\n // 代码分割示例\r\n if (issues.some((i) => i.type === \"size\")) {\r\n examples.push(this.getCodeSplittingExample());\r\n }\r\n\r\n // 重复依赖优化\r\n if (duplicateDeps && duplicateDeps.length > 0) {\r\n examples.push(this.getDependencyOptimizationExample(duplicateDeps));\r\n }\r\n\r\n // 压缩优化\r\n if (issues.some((i) => i.type === \"optimization\")) {\r\n examples.push(this.getCompressionExample());\r\n }\r\n\r\n // 图片优化\r\n if (issues.some((i) => i.description.includes(\"图片\"))) {\r\n examples.push(this.getImageOptimizationExample());\r\n }\r\n\r\n // 动态导入\r\n if (issues.some((i) => i.type === \"size\" && i.severity === \"high\")) {\r\n examples.push(this.getDynamicImportExample());\r\n }\r\n\r\n // Tree-shaking 优化\r\n examples.push(this.getTreeShakingExample());\r\n\r\n return examples;\r\n }\r\n\r\n /**\r\n * 代码分割示例\r\n */\r\n private getCodeSplittingExample(): OptimizationExample {\r\n return {\r\n title: \"配置代码分割\",\r\n description: \"将大文件拆分成多个小文件,提高加载性能\",\r\n priority: \"high\",\r\n impact: \"可减少 30-50% 的初始加载大小\",\r\n difficulty: \"简单\",\r\n code: {\r\n language: \"typescript\",\r\n content: `// vite.config.ts\r\nexport default defineConfig({\r\n build: {\r\n rollupOptions: {\r\n output: {\r\n manualChunks: {\r\n // 提取 Vue 核心库\r\n 'vue-vendor': ['vue', 'vue-router', 'pinia'],\r\n \r\n // 提取 UI 组件库\r\n 'ui-vendor': ['element-plus', '@element-plus/icons-vue'],\r\n \r\n // 提取工具库\r\n 'utils': ['lodash-es', 'dayjs', 'axios'],\r\n \r\n // 提取图表库(如果使用)\r\n 'charts': ['echarts', 'chart.js'],\r\n },\r\n \r\n // 自动分割大于 500KB 的文件\r\n chunkFileNames: (chunkInfo) => {\r\n const facadeModuleId = chunkInfo.facadeModuleId \r\n ? chunkInfo.facadeModuleId.split('/').pop() \r\n : 'chunk';\r\n return \\`js/\\${facadeModuleId}-[hash].js\\`;\r\n },\r\n },\r\n },\r\n \r\n // 设置 chunk 大小警告阈值\r\n chunkSizeWarningLimit: 500,\r\n },\r\n});`,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * 重复依赖优化示例\r\n */\r\n private getDependencyOptimizationExample(\r\n duplicates: DependencyInfo[]\r\n ): OptimizationExample {\r\n const topDuplicates = duplicates.slice(0, 3).map((d) => d.name);\r\n\r\n return {\r\n title: \"优化重复依赖\",\r\n description: `检测到 ${duplicates.length} 个重复打包的依赖,建议提取到公共 chunk`,\r\n priority: \"high\",\r\n impact: `可减少 ${(\r\n duplicates.reduce((sum, d) => sum + d.size, 0) /\r\n 1024 /\r\n 1024\r\n ).toFixed(2)}MB`,\r\n difficulty: \"简单\",\r\n code: {\r\n language: \"typescript\",\r\n content: `// vite.config.ts\r\nexport default defineConfig({\r\n build: {\r\n rollupOptions: {\r\n output: {\r\n manualChunks: {\r\n // 提取重复依赖到公共 chunk\r\n 'common-vendor': [\r\n ${topDuplicates.map((name) => `'${name}'`).join(\",\\n \")}\r\n ],\r\n },\r\n },\r\n },\r\n },\r\n \r\n // 或者使用自动分割策略\r\n optimizeDeps: {\r\n include: [\r\n ${topDuplicates.map((name) => `'${name}'`).join(\",\\n \")}\r\n ],\r\n },\r\n});`,\r\n },\r\n relatedFiles: duplicates.flatMap((d) => d.usedBy).slice(0, 5),\r\n };\r\n }\r\n\r\n /**\r\n * 压缩优化示例\r\n */\r\n private getCompressionExample(): OptimizationExample {\r\n return {\r\n title: \"启用 Gzip/Brotli 压缩\",\r\n description: \"使用压缩插件减少传输大小\",\r\n priority: \"medium\",\r\n impact: \"可减少 60-70% 的传输大小\",\r\n difficulty: \"简单\",\r\n code: {\r\n language: \"bash\",\r\n content: `# 1. 安装压缩插件\r\nnpm install vite-plugin-compression -D\r\n\r\n# 2. 配置插件\r\n# vite.config.ts\r\nimport viteCompression from 'vite-plugin-compression';\r\n\r\nexport default defineConfig({\r\n plugins: [\r\n // Gzip 压缩\r\n viteCompression({\r\n algorithm: 'gzip',\r\n ext: '.gz',\r\n threshold: 10240, // 大于 10KB 才压缩\r\n deleteOriginFile: false,\r\n }),\r\n \r\n // Brotli 压缩(可选,压缩率更高)\r\n viteCompression({\r\n algorithm: 'brotliCompress',\r\n ext: '.br',\r\n threshold: 10240,\r\n deleteOriginFile: false,\r\n }),\r\n ],\r\n});\r\n\r\n# 3. Nginx 配置(服务器端)\r\n# nginx.conf\r\ngzip on;\r\ngzip_types text/plain text/css application/json application/javascript;\r\ngzip_min_length 1024;`,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * 图片优化示例\r\n */\r\n private getImageOptimizationExample(): OptimizationExample {\r\n return {\r\n title: \"优化图片资源\",\r\n description: \"压缩图片并转换为现代格式\",\r\n priority: \"medium\",\r\n impact: \"可减少 50-80% 的图片大小\",\r\n difficulty: \"简单\",\r\n code: {\r\n language: \"bash\",\r\n content: `# 1. 安装图片优化插件\r\nnpm install vite-plugin-imagemin -D\r\n\r\n# 2. 配置插件\r\n# vite.config.ts\r\nimport viteImagemin from 'vite-plugin-imagemin';\r\n\r\nexport default defineConfig({\r\n plugins: [\r\n viteImagemin({\r\n // GIF 优化\r\n gifsicle: {\r\n optimizationLevel: 7,\r\n interlaced: false,\r\n },\r\n \r\n // PNG 优化\r\n optipng: {\r\n optimizationLevel: 7,\r\n },\r\n \r\n // JPEG 优化\r\n mozjpeg: {\r\n quality: 80,\r\n },\r\n \r\n // SVG 优化\r\n svgo: {\r\n plugins: [\r\n { name: 'removeViewBox', active: false },\r\n { name: 'removeEmptyAttrs', active: true },\r\n ],\r\n },\r\n \r\n // WebP 转换\r\n webp: {\r\n quality: 80,\r\n },\r\n }),\r\n ],\r\n});\r\n\r\n# 3. 使用 WebP 格式(HTML)\r\n<picture>\r\n <source srcset=\"image.webp\" type=\"image/webp\">\r\n <img src=\"image.jpg\" alt=\"description\">\r\n</picture>`,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * 动态导入示例\r\n */\r\n private getDynamicImportExample(): OptimizationExample {\r\n return {\r\n title: \"使用动态导入(懒加载)\",\r\n description: \"按需加载组件和路由,减少初始加载大小\",\r\n priority: \"high\",\r\n impact: \"可减少 40-60% 的初始加载大小\",\r\n difficulty: \"中等\",\r\n code: {\r\n language: \"typescript\",\r\n content: `// 1. 路由懒加载\r\n// router/index.ts\r\nconst routes = [\r\n {\r\n path: '/dashboard',\r\n name: 'Dashboard',\r\n // 使用动态导入\r\n component: () => import('@/views/Dashboard.vue'),\r\n },\r\n {\r\n path: '/settings',\r\n name: 'Settings',\r\n component: () => import('@/views/Settings.vue'),\r\n },\r\n];\r\n\r\n// 2. 组件懒加载\r\n// App.vue\r\n<script setup>\r\nimport { defineAsyncComponent } from 'vue';\r\n\r\n// 异步组件\r\nconst HeavyComponent = defineAsyncComponent(() =>\r\n import('./components/HeavyComponent.vue')\r\n);\r\n\r\n// 带加载状态的异步组件\r\nconst AsyncComponent = defineAsyncComponent({\r\n loader: () => import('./components/AsyncComponent.vue'),\r\n loadingComponent: LoadingSpinner,\r\n errorComponent: ErrorComponent,\r\n delay: 200,\r\n timeout: 3000,\r\n});\r\n</script>\r\n\r\n// 3. 条件加载\r\n<script setup>\r\nconst loadCharts = async () => {\r\n if (needCharts) {\r\n const { default: ECharts } = await import('echarts');\r\n // 使用 ECharts\r\n }\r\n};\r\n</script>`,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * Tree-shaking 优化示例\r\n */\r\n private getTreeShakingExample(): OptimizationExample {\r\n return {\r\n title: \"优化 Tree-shaking\",\r\n description: \"确保未使用的代码被正确移除\",\r\n priority: \"medium\",\r\n impact: \"可减少 10-30% 的代码大小\",\r\n difficulty: \"中等\",\r\n code: {\r\n language: \"typescript\",\r\n content: `// 1. 使用 ES6 模块导入(支持 tree-shaking)\r\n// ❌ 不推荐\r\nimport _ from 'lodash';\r\nconst result = _.debounce(fn, 300);\r\n\r\n// ✅ 推荐\r\nimport { debounce } from 'lodash-es';\r\nconst result = debounce(fn, 300);\r\n\r\n// 2. 配置 package.json\r\n{\r\n \"sideEffects\": false, // 标记为无副作用\r\n // 或指定有副作用的文件\r\n \"sideEffects\": [\"*.css\", \"*.scss\"]\r\n}\r\n\r\n// 3. Vite 配置\r\n// vite.config.ts\r\nexport default defineConfig({\r\n build: {\r\n // 启用 tree-shaking\r\n minify: 'terser',\r\n terserOptions: {\r\n compress: {\r\n drop_console: true, // 移除 console\r\n drop_debugger: true, // 移除 debugger\r\n pure_funcs: ['console.log'], // 移除特定函数\r\n },\r\n },\r\n },\r\n});\r\n\r\n// 4. 避免副作用导入\r\n// ❌ 不推荐(会导入整个模块)\r\nimport 'some-library';\r\n\r\n// ✅ 推荐(明确导入需要的部分)\r\nimport { specificFunction } from 'some-library';`,\r\n },\r\n };\r\n }\r\n}\r\n","/**\r\n * 性能分析报告生成器\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport pc from \"picocolors\";\r\nimport type { AnalysisResult, PerformanceIssue } from \"./types\";\r\n\r\nexport interface ReporterOptions {\r\n console?: boolean;\r\n html?: boolean;\r\n json?: boolean;\r\n}\r\n\r\nexport class PerfReporter {\r\n /**\r\n * 生成报告\r\n */\r\n async generate(\r\n result: AnalysisResult,\r\n options: ReporterOptions = {},\r\n ): Promise<void> {\r\n if (options.html) {\r\n await this.generateHTMLReport(result);\r\n }\r\n\r\n if (options.json) {\r\n await this.generateJSONReport(result);\r\n }\r\n }\r\n\r\n /**\r\n * 控制台输出\r\n */\r\n printConsole(result: AnalysisResult): void {\r\n console.log(\"\\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\");\r\n console.log(\"⚡ 性能分析报告\");\r\n console.log(\"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n\");\r\n\r\n // 总体统计\r\n console.log(\"📊 总体统计:\");\r\n console.log(\r\n ` 总大小: ${(result.summary.totalSize / 1024 / 1024).toFixed(2)}MB`,\r\n );\r\n console.log(\r\n ` Gzip后: ${(result.summary.totalGzipSize / 1024 / 1024).toFixed(2)}MB`,\r\n );\r\n console.log(` 文件数: ${result.summary.fileCount}\\n`);\r\n\r\n // 历史对比\r\n if (result.comparison) {\r\n console.log(\"📈 历史对比:\");\r\n const { totalSize, fileCount, newFiles, removedFiles } =\r\n result.comparison;\r\n const sizeIcon = totalSize.trend === \"increased\" ? \"📈\" : \"📉\";\r\n console.log(\r\n ` ${sizeIcon} 总大小: ${totalSize.trend === \"increased\" ? \"+\" : \"\"}${(\r\n totalSize.diff /\r\n 1024 /\r\n 1024\r\n ).toFixed(2)}MB (${\r\n totalSize.diffPercent > 0 ? \"+\" : \"\"\r\n }${totalSize.diffPercent.toFixed(2)}%)`,\r\n );\r\n console.log(\r\n ` 📊 文件数: ${fileCount.diff > 0 ? \"+\" : \"\"}${fileCount.diff}`,\r\n );\r\n if (newFiles.length > 0) {\r\n console.log(` ✨ 新增: ${newFiles.slice(0, 3).join(\", \")}`);\r\n }\r\n if (removedFiles.length > 0) {\r\n console.log(` 🗑️ 删除: ${removedFiles.slice(0, 3).join(\", \")}`);\r\n }\r\n console.log();\r\n }\r\n\r\n // 依赖分析\r\n if (result.dependencies) {\r\n console.log(\"📦 依赖分析:\");\r\n console.log(` 总依赖数: ${result.dependencies.total}`);\r\n if (result.dependencies.duplicates.length > 0) {\r\n console.log(\r\n ` ⚠️ 重复依赖: ${result.dependencies.duplicates.length} 个`,\r\n );\r\n result.dependencies.duplicates.slice(0, 3).forEach((dep) => {\r\n console.log(\r\n ` - ${dep.name}: 被 ${dep.usedBy.length} 个文件使用`,\r\n );\r\n });\r\n }\r\n console.log();\r\n }\r\n\r\n // 最大文件\r\n console.log(\"📦 最大的文件:\");\r\n result.summary.largestFiles.slice(0, 5).forEach((file, index) => {\r\n console.log(\r\n ` ${index + 1}. ${file.name}: ${(file.size / 1024).toFixed(2)}KB`,\r\n );\r\n });\r\n console.log();\r\n\r\n // 按类型统计\r\n console.log(\"📋 按类型统计:\");\r\n Object.entries(result.summary.byType).forEach(([type, info]) => {\r\n console.log(\r\n ` ${type}: ${info.count} 个, ${(info.size / 1024).toFixed(2)}KB`,\r\n );\r\n });\r\n console.log();\r\n\r\n // 性能问题\r\n if (result.issues.length > 0) {\r\n console.log(\"⚠️ 性能问题:\");\r\n result.issues.forEach((issue, index) => {\r\n const icon = this.getSeverityIcon(issue.severity);\r\n console.log(` ${index + 1}. ${icon} ${issue.title}`);\r\n console.log(` ${issue.description}`);\r\n if (issue.suggestion) {\r\n console.log(` 💡 ${issue.suggestion}`);\r\n }\r\n });\r\n console.log();\r\n } else {\r\n console.log(\"✅ 未发现明显的性能问题\\n\");\r\n }\r\n\r\n // 优化建议\r\n if (result.suggestions.length > 0) {\r\n console.log(\"💡 优化建议:\");\r\n result.suggestions.forEach((suggestion, index) => {\r\n console.log(` ${index + 1}. ${suggestion}`);\r\n });\r\n console.log();\r\n }\r\n\r\n // 优化示例\r\n if (result.optimizationExamples && result.optimizationExamples.length > 0) {\r\n console.log(\"🔧 优化示例:\");\r\n result.optimizationExamples.slice(0, 3).forEach((example, index) => {\r\n console.log(` ${index + 1}. ${example.title} (${example.priority})`);\r\n console.log(` ${example.description}`);\r\n });\r\n console.log();\r\n }\r\n\r\n // AI 分析\r\n if (result.aiAnalysis) {\r\n console.log(\"🤖 AI 分析:\");\r\n console.log(\r\n result.aiAnalysis\r\n .split(\"\\n\")\r\n .map((line) => ` ${line}`)\r\n .join(\"\\n\"),\r\n );\r\n console.log();\r\n }\r\n\r\n console.log(\"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n\");\r\n }\r\n\r\n /**\r\n * 生成 HTML 报告\r\n */\r\n private async generateHTMLReport(result: AnalysisResult): Promise<void> {\r\n const html = `\r\n<!DOCTYPE html>\r\n<html lang=\"zh-CN\">\r\n<head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <title>性能分析报告</title>\r\n <style>\r\n * { margin: 0; padding: 0; box-sizing: border-box; }\r\n body {\r\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\r\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\r\n padding: 20px;\r\n min-height: 100vh;\r\n }\r\n .container {\r\n max-width: 1200px;\r\n margin: 0 auto;\r\n background: white;\r\n border-radius: 12px;\r\n box-shadow: 0 8px 32px rgba(0,0,0,0.2);\r\n overflow: hidden;\r\n }\r\n .header {\r\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\r\n color: white;\r\n padding: 30px;\r\n text-align: center;\r\n }\r\n .header h1 {\r\n font-size: 32px;\r\n margin-bottom: 10px;\r\n }\r\n .header .time {\r\n font-size: 14px;\r\n opacity: 0.9;\r\n }\r\n .content {\r\n padding: 30px;\r\n }\r\n .section {\r\n margin-bottom: 30px;\r\n }\r\n .section-title {\r\n font-size: 20px;\r\n font-weight: bold;\r\n color: #333;\r\n margin-bottom: 15px;\r\n padding-bottom: 10px;\r\n border-bottom: 2px solid #667eea;\r\n }\r\n .stats-grid {\r\n display: grid;\r\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\r\n gap: 20px;\r\n margin-bottom: 20px;\r\n }\r\n .stat-card {\r\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\r\n color: white;\r\n padding: 20px;\r\n border-radius: 12px;\r\n text-align: center;\r\n }\r\n .stat-value {\r\n font-size: 32px;\r\n font-weight: bold;\r\n margin: 10px 0;\r\n }\r\n .stat-label {\r\n font-size: 14px;\r\n opacity: 0.9;\r\n }\r\n .file-list {\r\n background: #f7fafc;\r\n border-radius: 8px;\r\n padding: 15px;\r\n }\r\n .file-item {\r\n display: flex;\r\n justify-content: space-between;\r\n padding: 10px;\r\n border-bottom: 1px solid #e2e8f0;\r\n }\r\n .file-item:last-child {\r\n border-bottom: none;\r\n }\r\n .file-name {\r\n font-family: monospace;\r\n color: #333;\r\n }\r\n .file-size {\r\n color: #667eea;\r\n font-weight: bold;\r\n }\r\n .issue {\r\n background: #fff5f5;\r\n border-left: 4px solid #f56565;\r\n padding: 15px;\r\n border-radius: 8px;\r\n margin-bottom: 15px;\r\n }\r\n .issue.medium {\r\n background: #fffaf0;\r\n border-left-color: #ed8936;\r\n }\r\n .issue.low {\r\n background: #f0f9ff;\r\n border-left-color: #4299e1;\r\n }\r\n .issue-title {\r\n font-weight: bold;\r\n color: #333;\r\n margin-bottom: 8px;\r\n }\r\n .issue-desc {\r\n color: #666;\r\n margin-bottom: 8px;\r\n }\r\n .issue-suggestion {\r\n background: #e6fffa;\r\n padding: 10px;\r\n border-radius: 6px;\r\n margin-top: 10px;\r\n color: #234e52;\r\n }\r\n .suggestion-list {\r\n background: #f0fdf4;\r\n border-radius: 8px;\r\n padding: 15px;\r\n }\r\n .suggestion-item {\r\n padding: 10px;\r\n border-bottom: 1px solid #d1fae5;\r\n color: #065f46;\r\n }\r\n .suggestion-item:last-child {\r\n border-bottom: none;\r\n }\r\n .ai-analysis {\r\n background: #f0f9ff;\r\n border-left: 4px solid #3b82f6;\r\n padding: 20px;\r\n border-radius: 8px;\r\n line-height: 1.8;\r\n color: #1e40af;\r\n white-space: pre-wrap;\r\n }\r\n .footer {\r\n background: #f7fafc;\r\n padding: 20px;\r\n text-align: center;\r\n color: #718096;\r\n font-size: 14px;\r\n }\r\n .trend-up {\r\n background: linear-gradient(135deg, #f56565 0%, #ed8936 100%) !important;\r\n }\r\n .trend-down {\r\n background: linear-gradient(135deg, #48bb78 0%, #38a169 100%) !important;\r\n }\r\n .optimization-example {\r\n background: #f7fafc;\r\n border-radius: 12px;\r\n padding: 20px;\r\n margin-bottom: 20px;\r\n border: 1px solid #e2e8f0;\r\n }\r\n .example-header {\r\n margin-bottom: 15px;\r\n }\r\n .example-title {\r\n font-size: 18px;\r\n font-weight: bold;\r\n color: #333;\r\n margin-bottom: 8px;\r\n display: flex;\r\n align-items: center;\r\n gap: 10px;\r\n }\r\n .priority-badge {\r\n font-size: 12px;\r\n padding: 4px 12px;\r\n border-radius: 12px;\r\n font-weight: normal;\r\n }\r\n .priority-high {\r\n background: #fed7d7;\r\n color: #c53030;\r\n }\r\n .priority-medium {\r\n background: #feebc8;\r\n color: #c05621;\r\n }\r\n .priority-low {\r\n background: #bee3f8;\r\n color: #2c5282;\r\n }\r\n .example-meta {\r\n display: flex;\r\n gap: 15px;\r\n font-size: 14px;\r\n color: #666;\r\n }\r\n .example-desc {\r\n color: #666;\r\n margin-bottom: 15px;\r\n line-height: 1.6;\r\n }\r\n .code-block {\r\n background: #1e293b;\r\n border-radius: 8px;\r\n overflow: hidden;\r\n margin-top: 10px;\r\n }\r\n .code-header {\r\n background: #334155;\r\n color: #94a3b8;\r\n padding: 8px 15px;\r\n font-size: 12px;\r\n font-family: monospace;\r\n }\r\n .code-block pre {\r\n margin: 0;\r\n padding: 15px;\r\n overflow-x: auto;\r\n }\r\n .code-block code {\r\n font-family: 'Consolas', 'Monaco', 'Courier New', monospace;\r\n font-size: 13px;\r\n line-height: 1.6;\r\n color: #e2e8f0;\r\n }\r\n .related-files {\r\n margin-top: 15px;\r\n padding: 10px;\r\n background: #e6fffa;\r\n border-radius: 6px;\r\n font-size: 14px;\r\n color: #234e52;\r\n }\r\n </style>\r\n</head>\r\n<body>\r\n <div class=\"container\">\r\n <div class=\"header\">\r\n <h1>⚡ 性能分析报告</h1>\r\n <div class=\"time\">生成时间: ${result.timestamp}</div>\r\n </div>\r\n\r\n <div class=\"content\">\r\n <!-- 总体统计 -->\r\n <div class=\"section\">\r\n <div class=\"section-title\">📊 总体统计</div>\r\n <div class=\"stats-grid\">\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">总大小</div>\r\n <div class=\"stat-value\">${(\r\n result.summary.totalSize /\r\n 1024 /\r\n 1024\r\n ).toFixed(2)}MB</div>\r\n </div>\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">Gzip 后</div>\r\n <div class=\"stat-value\">${(\r\n result.summary.totalGzipSize /\r\n 1024 /\r\n 1024\r\n ).toFixed(2)}MB</div>\r\n </div>\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">文件数量</div>\r\n <div class=\"stat-value\">${result.summary.fileCount}</div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 最大文件 -->\r\n <div class=\"section\">\r\n <div class=\"section-title\">📦 最大的文件</div>\r\n <div class=\"file-list\">\r\n ${result.summary.largestFiles\r\n .slice(0, 10)\r\n .map(\r\n (file) => `\r\n <div class=\"file-item\">\r\n <span class=\"file-name\">${file.name}</span>\r\n <span class=\"file-size\">${(file.size / 1024).toFixed(2)}KB</span>\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n </div>\r\n\r\n <!-- 按类型统计 -->\r\n <div class=\"section\">\r\n <div class=\"section-title\">📋 按类型统计</div>\r\n <div class=\"file-list\">\r\n ${Object.entries(result.summary.byType)\r\n .map(\r\n ([type, info]) => `\r\n <div class=\"file-item\">\r\n <span class=\"file-name\">${type}</span>\r\n <span class=\"file-size\">${info.count} 个, ${(\r\n info.size / 1024\r\n ).toFixed(2)}KB</span>\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n </div>\r\n\r\n <!-- 历史对比 -->\r\n ${\r\n result.comparison\r\n ? `\r\n <div class=\"section\">\r\n <div class=\"section-title\">📈 历史对比</div>\r\n <div class=\"stats-grid\">\r\n <div class=\"stat-card ${\r\n result.comparison.totalSize.trend === \"increased\"\r\n ? \"trend-up\"\r\n : \"trend-down\"\r\n }\">\r\n <div class=\"stat-label\">总大小变化</div>\r\n <div class=\"stat-value\">${\r\n result.comparison.totalSize.diffPercent > 0 ? \"+\" : \"\"\r\n }${result.comparison.totalSize.diffPercent.toFixed(2)}%</div>\r\n <div class=\"stat-label\">${\r\n result.comparison.totalSize.trend === \"increased\" ? \"+\" : \"\"\r\n }${(result.comparison.totalSize.diff / 1024 / 1024).toFixed(\r\n 2,\r\n )}MB</div>\r\n </div>\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">文件数变化</div>\r\n <div class=\"stat-value\">${\r\n result.comparison.fileCount.diff > 0 ? \"+\" : \"\"\r\n }${result.comparison.fileCount.diff}</div>\r\n </div>\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">新增文件</div>\r\n <div class=\"stat-value\">${result.comparison.newFiles.length}</div>\r\n </div>\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">删除文件</div>\r\n <div class=\"stat-value\">${\r\n result.comparison.removedFiles.length\r\n }</div>\r\n </div>\r\n </div>\r\n ${\r\n result.comparison.largestFileChanges.length > 0\r\n ? `\r\n <div class=\"file-list\" style=\"margin-top: 15px;\">\r\n <div style=\"font-weight: bold; padding: 10px; color: #333;\">文件大小变化 Top 5:</div>\r\n ${result.comparison.largestFileChanges\r\n .slice(0, 5)\r\n .map(\r\n (change) => `\r\n <div class=\"file-item\">\r\n <span class=\"file-name\">${change.name}</span>\r\n <span class=\"file-size ${\r\n change.diff > 0 ? \"trend-up\" : \"trend-down\"\r\n }\">\r\n ${change.diff > 0 ? \"+\" : \"\"}${(change.diff / 1024).toFixed(\r\n 2,\r\n )}KB\r\n </span>\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n\r\n <!-- 依赖分析 -->\r\n ${\r\n result.dependencies\r\n ? `\r\n <div class=\"section\">\r\n <div class=\"section-title\">📦 依赖分析</div>\r\n <div class=\"stats-grid\">\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">总依赖数</div>\r\n <div class=\"stat-value\">${result.dependencies.total}</div>\r\n </div>\r\n <div class=\"stat-card ${\r\n result.dependencies.duplicates.length > 0 ? \"trend-up\" : \"\"\r\n }\">\r\n <div class=\"stat-label\">重复依赖</div>\r\n <div class=\"stat-value\">${\r\n result.dependencies.duplicates.length\r\n }</div>\r\n </div>\r\n </div>\r\n ${\r\n result.dependencies.duplicates.length > 0\r\n ? `\r\n <div class=\"file-list\" style=\"margin-top: 15px;\">\r\n <div style=\"font-weight: bold; padding: 10px; color: #333;\">重复打包的依赖:</div>\r\n ${result.dependencies.duplicates\r\n .slice(0, 10)\r\n .map(\r\n (dep) => `\r\n <div class=\"file-item\">\r\n <span class=\"file-name\">${dep.name}</span>\r\n <span class=\"file-size\">\r\n ${(dep.size / 1024).toFixed(2)}KB · 被 ${\r\n dep.usedBy.length\r\n } 个文件使用\r\n </span>\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n ${\r\n result.dependencies.largest.length > 0\r\n ? `\r\n <div class=\"file-list\" style=\"margin-top: 15px;\">\r\n <div style=\"font-weight: bold; padding: 10px; color: #333;\">最大的依赖:</div>\r\n ${result.dependencies.largest\r\n .slice(0, 10)\r\n .map(\r\n (dep) => `\r\n <div class=\"file-item\">\r\n <span class=\"file-name\">${dep.name}</span>\r\n <span class=\"file-size\">${(dep.size / 1024).toFixed(\r\n 2,\r\n )}KB</span>\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n\r\n <!-- 性能问题 -->\r\n ${\r\n result.issues.length > 0\r\n ? `\r\n <div class=\"section\">\r\n <div class=\"section-title\">⚠️ 性能问题</div>\r\n ${result.issues\r\n .map(\r\n (issue) => `\r\n <div class=\"issue ${issue.severity}\">\r\n <div class=\"issue-title\">${this.getSeverityIcon(\r\n issue.severity,\r\n )} ${issue.title}</div>\r\n <div class=\"issue-desc\">${issue.description}</div>\r\n ${\r\n issue.files\r\n ? `<div class=\"issue-desc\">相关文件: ${issue.files.join(\r\n \", \",\r\n )}</div>`\r\n : \"\"\r\n }\r\n ${\r\n issue.suggestion\r\n ? `<div class=\"issue-suggestion\">💡 ${issue.suggestion}</div>`\r\n : \"\"\r\n }\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n `\r\n : '<div class=\"section\"><div class=\"section-title\">✅ 未发现明显的性能问题</div></div>'\r\n }\r\n\r\n <!-- 优化建议 -->\r\n ${\r\n result.suggestions.length > 0\r\n ? `\r\n <div class=\"section\">\r\n <div class=\"section-title\">💡 优化建议</div>\r\n <div class=\"suggestion-list\">\r\n ${result.suggestions\r\n .map(\r\n (suggestion, index) => `\r\n <div class=\"suggestion-item\">${index + 1}. ${suggestion}</div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n\r\n <!-- 优化示例 -->\r\n ${\r\n result.optimizationExamples && result.optimizationExamples.length > 0\r\n ? `\r\n <div class=\"section\">\r\n <div class=\"section-title\">🔧 优化示例</div>\r\n ${result.optimizationExamples\r\n .map(\r\n (example) => `\r\n <div class=\"optimization-example\">\r\n <div class=\"example-header\">\r\n <div class=\"example-title\">\r\n ${example.title}\r\n <span class=\"priority-badge priority-${example.priority}\">${\r\n example.priority === \"high\"\r\n ? \"高优先级\"\r\n : example.priority === \"medium\"\r\n ? \"中优先级\"\r\n : \"低优先级\"\r\n }</span>\r\n </div>\r\n <div class=\"example-meta\">\r\n <span>💪 ${example.difficulty}</span>\r\n <span>📈 ${example.impact}</span>\r\n </div>\r\n </div>\r\n <div class=\"example-desc\">${example.description}</div>\r\n <div class=\"code-block\">\r\n <div class=\"code-header\">${example.code.language}</div>\r\n <pre><code>${this.escapeHtml(example.code.content)}</code></pre>\r\n </div>\r\n ${\r\n example.relatedFiles && example.relatedFiles.length > 0\r\n ? `\r\n <div class=\"related-files\">\r\n <strong>相关文件:</strong> ${example.relatedFiles.join(\", \")}\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n\r\n <!-- AI 分析 -->\r\n ${\r\n result.aiAnalysis\r\n ? `\r\n <div class=\"section\">\r\n <div class=\"section-title\">🤖 AI 分析</div>\r\n <div class=\"ai-analysis\">${this.escapeHtml(result.aiAnalysis)}</div>\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n </div>\r\n\r\n <div class=\"footer\">\r\n <p>AI Performance Analyzer v1.0.0</p>\r\n <p>Powered by Vite & OpenAI</p>\r\n </div>\r\n </div>\r\n</body>\r\n</html>\r\n `.trim();\r\n\r\n // 创建报告目录\r\n const reportsDir = path.resolve(process.cwd(), \"ai-reports\");\r\n if (!fs.existsSync(reportsDir)) {\r\n fs.mkdirSync(reportsDir, { recursive: true });\r\n }\r\n\r\n const reportPath = path.resolve(reportsDir, \"performance-report.html\");\r\n fs.writeFileSync(reportPath, html, \"utf-8\");\r\n console.log(pc.green(`\\n📄 性能报告已生成: ${pc.cyan(reportPath)}\\n`));\r\n }\r\n\r\n /**\r\n * 生成 JSON 报告\r\n */\r\n private async generateJSONReport(result: AnalysisResult): Promise<void> {\r\n const reportsDir = path.resolve(process.cwd(), \"ai-reports\");\r\n if (!fs.existsSync(reportsDir)) {\r\n fs.mkdirSync(reportsDir, { recursive: true });\r\n }\r\n\r\n const reportPath = path.resolve(reportsDir, \"performance-report.json\");\r\n fs.writeFileSync(reportPath, JSON.stringify(result, null, 2), \"utf-8\");\r\n console.log(pc.green(`📄 JSON 报告已生成: ${pc.cyan(reportPath)}`));\r\n }\r\n\r\n /**\r\n * 获取严重程度图标\r\n */\r\n private getSeverityIcon(severity: string): string {\r\n const icons: Record<string, string> = {\r\n high: \"🔴\",\r\n medium: \"🟡\",\r\n low: \"🔵\",\r\n };\r\n return icons[severity] || \"⚪\";\r\n }\r\n\r\n /**\r\n * 转义 HTML\r\n */\r\n private escapeHtml(text: string): string {\r\n const map: Record<string, string> = {\r\n \"&\": \"&amp;\",\r\n \"<\": \"&lt;\",\r\n \">\": \"&gt;\",\r\n '\"': \"&quot;\",\r\n \"'\": \"&#039;\",\r\n };\r\n return text.replace(/[&<>\"']/g, (m) => map[m]);\r\n }\r\n}\r\n"],"mappings":"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","names":["import_picocolors","import_fs","import_path","path","fs","import_fs","import_path","path","fs","path","fs","import_fs","import_path","path","fs","pc","pc"]}
package/dist/index.mjs CHANGED
@@ -577,8 +577,8 @@ var PerfAnalyzer = class {
577
577
  openAIApiKey: options.apiKey,
578
578
  configuration: { baseURL: options.apiUrl },
579
579
  modelName: options.model,
580
- temperature: 0.2,
581
- maxTokens: 4e3
580
+ temperature: options.temperature ?? 0.2,
581
+ maxTokens: options.maxTokens ?? 4e3
582
582
  });
583
583
  }
584
584
  }
@@ -1523,6 +1523,8 @@ function vitePluginAIPerfAnalyzer(options = {}) {
1523
1523
  apiKey = process.env.OPENAI_API_KEY || "",
1524
1524
  apiUrl = process.env.OPENAI_API_URL || "https://api.openai.com/v1",
1525
1525
  model = process.env.OPENAI_MODEL || "gpt-4",
1526
+ temperature = 0.2,
1527
+ maxTokens = 4e3,
1526
1528
  enabled = true,
1527
1529
  threshold = {
1528
1530
  bundleSize: 500,
@@ -1537,7 +1539,14 @@ function vitePluginAIPerfAnalyzer(options = {}) {
1537
1539
  json: false
1538
1540
  }
1539
1541
  } = options;
1540
- const analyzer = new PerfAnalyzer({ apiKey, apiUrl, model, threshold });
1542
+ const analyzer = new PerfAnalyzer({
1543
+ apiKey,
1544
+ apiUrl,
1545
+ model,
1546
+ threshold,
1547
+ temperature,
1548
+ maxTokens
1549
+ });
1541
1550
  const reporter = new PerfReporter();
1542
1551
  let analysisResult = null;
1543
1552
  return {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/analyzer.ts","../src/dependency-analyzer.ts","../src/history-analyzer.ts","../src/optimization-examples.ts","../src/reporter.ts"],"sourcesContent":["/**\r\n * AI 性能分析插件\r\n *\r\n * 功能:\r\n * - 分析构建产物大小\r\n * - 检测性能问题\r\n * - 提供优化建议\r\n */\r\n\r\nimport type { Plugin } from \"vite\";\r\nimport pc from \"picocolors\";\r\nimport { PerfAnalyzer } from \"./analyzer\";\r\nimport { PerfReporter } from \"./reporter\";\r\nimport type { AnalysisResult } from \"./types\";\r\n\r\nexport interface PerfAnalyzerOptions {\r\n // AI 配置\r\n apiKey?: string;\r\n apiUrl?: string;\r\n model?: string;\r\n // 分析配置\r\n enabled?: boolean;\r\n threshold?: {\r\n bundleSize?: number; // 单个文件大小阈值 (KB)\r\n totalSize?: number; // 总大小阈值 (MB)\r\n chunkCount?: number; // chunk 数量阈值\r\n };\r\n // 输出配置\r\n output?: {\r\n console?: boolean;\r\n html?: boolean;\r\n json?: boolean;\r\n };\r\n}\r\n\r\nexport function vitePluginAIPerfAnalyzer(\r\n options: PerfAnalyzerOptions = {},\r\n): Plugin {\r\n const {\r\n apiKey = process.env.OPENAI_API_KEY || \"\",\r\n apiUrl = process.env.OPENAI_API_URL || \"https://api.openai.com/v1\",\r\n model = process.env.OPENAI_MODEL || \"gpt-4\",\r\n enabled = true,\r\n threshold = {\r\n bundleSize: 500, // 500KB\r\n totalSize: 5, // 5MB\r\n chunkCount: 20,\r\n },\r\n output = {\r\n console: true,\r\n html: true,\r\n json: false,\r\n },\r\n } = options;\r\n\r\n const analyzer = new PerfAnalyzer({ apiKey, apiUrl, model, threshold });\r\n const reporter = new PerfReporter();\r\n\r\n let analysisResult: AnalysisResult | null = null;\r\n\r\n return {\r\n name: \"vite-plugin-ai-perf-analyzer\",\r\n enforce: \"post\",\r\n\r\n configResolved(config) {\r\n if (!enabled) return;\r\n\r\n console.log(pc.cyan(\"\\n⚡ AI 性能分析插件已启动...\"));\r\n console.log(`📊 分析阈值:`);\r\n console.log(` 单文件: ${pc.yellow(threshold.bundleSize + \"KB\")}`);\r\n console.log(` 总大小: ${pc.yellow(threshold.totalSize + \"MB\")}`);\r\n console.log(\r\n ` Chunk数: ${pc.yellow((threshold.chunkCount ?? 10).toString())}`,\r\n );\r\n console.log(`🔑 API Key: ${apiKey ? \"已配置\" : \"未配置\"}\\n`);\r\n },\r\n\r\n async closeBundle() {\r\n if (!enabled) return;\r\n\r\n console.log(\"\\n⚡ 正在分析构建产物...\\n\");\r\n\r\n try {\r\n // 分析构建产物\r\n analysisResult = await analyzer.analyze();\r\n\r\n // 生成报告\r\n await reporter.generate(analysisResult, output);\r\n\r\n // 控制台输出\r\n if (output.console) {\r\n reporter.printConsole(analysisResult);\r\n }\r\n } catch (error: any) {\r\n console.error(\"❌ 性能分析失败:\", error.message);\r\n }\r\n },\r\n };\r\n}\r\n\r\n// 默认导出\r\nexport default vitePluginAIPerfAnalyzer;\r\n","/**\r\n * 性能分析器\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport { gzipSync } from \"zlib\";\r\nimport { ChatOpenAI } from \"@langchain/openai\";\r\nimport { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\r\nimport type {\r\n AnalysisResult,\r\n BundleInfo,\r\n PerformanceIssue,\r\n AnalyzerOptions,\r\n} from \"./types\";\r\nimport { DependencyAnalyzer } from \"./dependency-analyzer\";\r\nimport { HistoryAnalyzer } from \"./history-analyzer\";\r\nimport { OptimizationExamples } from \"./optimization-examples\";\r\n\r\nexport class PerfAnalyzer {\r\n private llm: ChatOpenAI | null = null;\r\n private options: AnalyzerOptions;\r\n private depAnalyzer: DependencyAnalyzer;\r\n private historyAnalyzer: HistoryAnalyzer;\r\n private examplesGenerator: OptimizationExamples;\r\n\r\n constructor(options: AnalyzerOptions) {\r\n this.options = options;\r\n this.depAnalyzer = new DependencyAnalyzer();\r\n this.historyAnalyzer = new HistoryAnalyzer();\r\n this.examplesGenerator = new OptimizationExamples();\r\n\r\n if (options.apiKey) {\r\n this.llm = new ChatOpenAI({\r\n openAIApiKey: options.apiKey,\r\n configuration: { baseURL: options.apiUrl },\r\n modelName: options.model,\r\n temperature: 0.2,\r\n maxTokens: 4000,\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * 分析构建产物\r\n */\r\n async analyze(): Promise<AnalysisResult> {\r\n const distDir = path.resolve(process.cwd(), \"dist\");\r\n\r\n if (!fs.existsSync(distDir)) {\r\n throw new Error(\"构建目录不存在,请先执行构建\");\r\n }\r\n\r\n // 收集文件信息\r\n const bundles = this.collectBundles(distDir);\r\n\r\n // 计算统计信息\r\n const summary = this.calculateSummary(bundles);\r\n\r\n // 依赖分析\r\n console.log(\"📦 正在分析依赖...\");\r\n const dependencies = this.depAnalyzer.analyzeDependencies(bundles);\r\n\r\n // 历史对比\r\n console.log(\"📊 正在对比历史记录...\");\r\n const comparison = this.historyAnalyzer.compare(\r\n bundles,\r\n summary.totalSize,\r\n summary.fileCount\r\n );\r\n\r\n // 检测性能问题\r\n const issues = this.detectIssues(bundles, summary, dependencies);\r\n\r\n // 生成基础建议\r\n const suggestions = this.generateSuggestions(issues);\r\n\r\n // 生成优化示例\r\n console.log(\"💡 正在生成优化示例...\");\r\n const optimizationExamples = this.examplesGenerator.generate(\r\n issues,\r\n dependencies.duplicates\r\n );\r\n\r\n // AI 分析(如果配置了 API Key)\r\n let aiAnalysis: string | undefined;\r\n if (this.llm) {\r\n console.log(\"🤖 正在使用 AI 分析性能...\\n\");\r\n aiAnalysis = await this.performAIAnalysis(\r\n bundles,\r\n summary,\r\n issues,\r\n dependencies,\r\n comparison\r\n );\r\n }\r\n\r\n return {\r\n timestamp: new Date().toLocaleString(\"zh-CN\"),\r\n summary,\r\n dependencies,\r\n comparison,\r\n issues,\r\n suggestions,\r\n optimizationExamples,\r\n aiAnalysis,\r\n };\r\n }\r\n\r\n /**\r\n * 收集构建产物信息\r\n */\r\n private collectBundles(dir: string, baseDir: string = dir): BundleInfo[] {\r\n const bundles: BundleInfo[] = [];\r\n const files = fs.readdirSync(dir);\r\n\r\n for (const file of files) {\r\n const filePath = path.join(dir, file);\r\n const stat = fs.statSync(filePath);\r\n\r\n if (stat.isDirectory()) {\r\n bundles.push(...this.collectBundles(filePath, baseDir));\r\n } else {\r\n const content = fs.readFileSync(filePath);\r\n const gzipSize = gzipSync(content).length;\r\n const relativePath = path.relative(baseDir, filePath);\r\n\r\n bundles.push({\r\n name: file,\r\n size: stat.size,\r\n gzipSize,\r\n type: this.getFileType(file),\r\n path: relativePath,\r\n });\r\n }\r\n }\r\n\r\n return bundles;\r\n }\r\n\r\n /**\r\n * 获取文件类型\r\n */\r\n private getFileType(filename: string): string {\r\n const ext = path.extname(filename).toLowerCase();\r\n const typeMap: Record<string, string> = {\r\n \".js\": \"javascript\",\r\n \".mjs\": \"javascript\",\r\n \".css\": \"stylesheet\",\r\n \".html\": \"html\",\r\n \".png\": \"image\",\r\n \".jpg\": \"image\",\r\n \".jpeg\": \"image\",\r\n \".gif\": \"image\",\r\n \".svg\": \"image\",\r\n \".webp\": \"image\",\r\n \".woff\": \"font\",\r\n \".woff2\": \"font\",\r\n \".ttf\": \"font\",\r\n \".eot\": \"font\",\r\n \".json\": \"data\",\r\n \".map\": \"sourcemap\",\r\n };\r\n return typeMap[ext] || \"other\";\r\n }\r\n\r\n /**\r\n * 计算统计信息\r\n */\r\n private calculateSummary(bundles: BundleInfo[]) {\r\n const totalSize = bundles.reduce((sum, b) => sum + b.size, 0);\r\n const totalGzipSize = bundles.reduce(\r\n (sum, b) => sum + (b.gzipSize || 0),\r\n 0\r\n );\r\n\r\n // 按大小排序,取前 10\r\n const largestFiles = [...bundles]\r\n .sort((a, b) => b.size - a.size)\r\n .slice(0, 10);\r\n\r\n // 按类型分组\r\n const byType: Record<string, { count: number; size: number }> = {};\r\n bundles.forEach((bundle) => {\r\n if (!byType[bundle.type]) {\r\n byType[bundle.type] = { count: 0, size: 0 };\r\n }\r\n byType[bundle.type].count++;\r\n byType[bundle.type].size += bundle.size;\r\n });\r\n\r\n return {\r\n totalSize,\r\n totalGzipSize,\r\n fileCount: bundles.length,\r\n largestFiles,\r\n byType,\r\n };\r\n }\r\n\r\n /**\r\n * 检测性能问题\r\n */\r\n private detectIssues(\r\n bundles: BundleInfo[],\r\n summary: any,\r\n dependencies?: {\r\n total: number;\r\n duplicates: DependencyInfo[];\r\n largest: DependencyInfo[];\r\n }\r\n ): PerformanceIssue[] {\r\n const issues: PerformanceIssue[] = [];\r\n const { threshold } = this.options;\r\n\r\n // 检查总大小\r\n const totalSizeMB = summary.totalSize / 1024 / 1024;\r\n if (threshold.totalSize && totalSizeMB > threshold.totalSize) {\r\n issues.push({\r\n type: \"size\",\r\n severity: \"high\",\r\n title: \"构建产物总大小过大\",\r\n description: `总大小 ${totalSizeMB.toFixed(2)}MB 超过阈值 ${\r\n threshold.totalSize\r\n }MB`,\r\n suggestion: \"考虑代码分割、tree-shaking、压缩等优化手段\",\r\n });\r\n }\r\n\r\n // 检查单个文件大小\r\n const largeBundles = bundles.filter(\r\n (b) => b.size / 1024 > (threshold.bundleSize || 500)\r\n );\r\n if (largeBundles.length > 0) {\r\n issues.push({\r\n type: \"size\",\r\n severity: \"medium\",\r\n title: \"存在过大的单个文件\",\r\n description: `发现 ${largeBundles.length} 个文件超过 ${threshold.bundleSize}KB`,\r\n files: largeBundles.map(\r\n (b) => `${b.name} (${(b.size / 1024).toFixed(2)}KB)`\r\n ),\r\n suggestion: \"考虑拆分大文件,使用动态导入或代码分割\",\r\n });\r\n }\r\n\r\n // 检查 chunk 数量\r\n const jsFiles = bundles.filter((b) => b.type === \"javascript\");\r\n if (threshold.chunkCount && jsFiles.length > threshold.chunkCount) {\r\n issues.push({\r\n type: \"count\",\r\n severity: \"low\",\r\n title: \"JavaScript 文件数量过多\",\r\n description: `共有 ${jsFiles.length} 个 JS 文件,超过阈值 ${threshold.chunkCount}`,\r\n suggestion: \"过多的文件会增加 HTTP 请求数,考虑合并小文件\",\r\n });\r\n }\r\n\r\n // 检查未压缩的图片\r\n const images = bundles.filter((b) => b.type === \"image\");\r\n const largeImages = images.filter((img) => img.size > 100 * 1024); // 100KB\r\n if (largeImages.length > 0) {\r\n issues.push({\r\n type: \"optimization\",\r\n severity: \"medium\",\r\n title: \"存在未优化的图片\",\r\n description: `发现 ${largeImages.length} 个大于 100KB 的图片`,\r\n files: largeImages.map(\r\n (img) => `${img.name} (${(img.size / 1024).toFixed(2)}KB)`\r\n ),\r\n suggestion: \"使用图片压缩工具,或转换为 WebP 格式\",\r\n });\r\n }\r\n\r\n // 检查 sourcemap\r\n const sourcemaps = bundles.filter((b) => b.type === \"sourcemap\");\r\n if (sourcemaps.length > 0) {\r\n const totalMapSize = sourcemaps.reduce((sum, m) => sum + m.size, 0);\r\n issues.push({\r\n type: \"optimization\",\r\n severity: \"low\",\r\n title: \"生产环境包含 sourcemap\",\r\n description: `Sourcemap 文件占用 ${(totalMapSize / 1024 / 1024).toFixed(\r\n 2\r\n )}MB`,\r\n suggestion: \"生产环境建议禁用 sourcemap 或使用外部 sourcemap\",\r\n });\r\n }\r\n\r\n // 检查重复依赖\r\n if (dependencies && dependencies.duplicates.length > 0) {\r\n const topDuplicates = dependencies.duplicates.slice(0, 3);\r\n issues.push({\r\n type: \"dependency\",\r\n severity: \"medium\",\r\n title: \"检测到重复打包的依赖\",\r\n description: `发现 ${dependencies.duplicates.length} 个依赖被多次打包`,\r\n files: topDuplicates.map(\r\n (d) =>\r\n `${d.name} (被 ${d.usedBy.length} 个文件使用, ${(\r\n d.size / 1024\r\n ).toFixed(2)}KB)`\r\n ),\r\n suggestion: \"将重复依赖提取到公共 chunk 中\",\r\n });\r\n }\r\n\r\n return issues;\r\n }\r\n\r\n /**\r\n * 生成基础建议\r\n */\r\n private generateSuggestions(issues: PerformanceIssue[]): string[] {\r\n const suggestions: string[] = [];\r\n\r\n if (issues.some((i) => i.type === \"size\")) {\r\n suggestions.push(\"启用 gzip/brotli 压缩\");\r\n suggestions.push(\"配置 Vite 的 build.rollupOptions 进行代码分割\");\r\n suggestions.push(\"使用 vite-plugin-compression 插件\");\r\n }\r\n\r\n if (issues.some((i) => i.type === \"optimization\")) {\r\n suggestions.push(\"使用 vite-plugin-imagemin 优化图片\");\r\n suggestions.push(\"配置 CSS 压缩和 tree-shaking\");\r\n }\r\n\r\n if (issues.length === 0) {\r\n suggestions.push(\"构建产物已经很优秀,继续保持!\");\r\n }\r\n\r\n return suggestions;\r\n }\r\n\r\n /**\r\n * AI 性能分析\r\n */\r\n private async performAIAnalysis(\r\n bundles: BundleInfo[],\r\n summary: any,\r\n issues: PerformanceIssue[],\r\n dependencies?: {\r\n total: number;\r\n duplicates: DependencyInfo[];\r\n largest: DependencyInfo[];\r\n },\r\n comparison?: any\r\n ): Promise<string> {\r\n const systemPrompt = new SystemMessage(\r\n \"你是一个专业的前端性能优化专家,精通 Vite、Webpack 等构建工具。请分析构建产物并提供专业的优化建议。\"\r\n );\r\n\r\n const bundlesSummary = summary.largestFiles\r\n .slice(0, 5)\r\n .map((b: BundleInfo) => `- ${b.name}: ${(b.size / 1024).toFixed(2)}KB`)\r\n .join(\"\\n\");\r\n\r\n const typesSummary = Object.entries(summary.byType)\r\n .map(\r\n ([type, info]: [string, any]) =>\r\n `- ${type}: ${info.count} 个文件, ${(info.size / 1024).toFixed(2)}KB`\r\n )\r\n .join(\"\\n\");\r\n\r\n const issuesSummary = issues\r\n .map(\r\n (issue) => `- [${issue.severity}] ${issue.title}: ${issue.description}`\r\n )\r\n .join(\"\\n\");\r\n\r\n // 依赖分析摘要\r\n let dependencySummary = \"\";\r\n if (dependencies) {\r\n dependencySummary = `\r\n## 依赖分析\r\n- 总依赖数: ${dependencies.total}\r\n- 重复依赖: ${dependencies.duplicates.length} 个\r\n- 最大依赖: ${dependencies.largest\r\n .slice(0, 3)\r\n .map((d) => `${d.name} (${(d.size / 1024).toFixed(2)}KB)`)\r\n .join(\", \")}`;\r\n }\r\n\r\n // 历史对比摘要\r\n let comparisonSummary = \"\";\r\n if (comparison) {\r\n const sizeChange =\r\n comparison.totalSize.trend === \"increased\" ? \"增加\" : \"减少\";\r\n comparisonSummary = `\r\n## 历史对比\r\n- 总大小${sizeChange}: ${Math.abs(comparison.totalSize.diffPercent).toFixed(2)}%\r\n- 文件数量变化: ${comparison.fileCount.diff > 0 ? \"+\" : \"\"}${\r\n comparison.fileCount.diff\r\n }\r\n- 新增文件: ${comparison.newFiles.length} 个\r\n- 删除文件: ${comparison.removedFiles.length} 个`;\r\n }\r\n\r\n const userPrompt = new HumanMessage(`\r\n请分析以下构建产物信息:\r\n\r\n## 总体统计\r\n- 总大小: ${(summary.totalSize / 1024 / 1024).toFixed(2)}MB\r\n- Gzip 后: ${(summary.totalGzipSize / 1024 / 1024).toFixed(2)}MB\r\n- 文件数量: ${summary.fileCount}\r\n\r\n## 最大的文件\r\n${bundlesSummary}\r\n\r\n## 按类型分组\r\n${typesSummary}\r\n${dependencySummary}\r\n${comparisonSummary}\r\n\r\n## 检测到的问题\r\n${issuesSummary || \"无明显问题\"}\r\n\r\n请提供:\r\n1. 性能评估(3-5 句话)\r\n2. 具体优化建议(3-5 条,每条简洁明了)\r\n3. 优先级排序\r\n\r\n请用简洁专业的语言回答,不要过于冗长。\r\n`);\r\n\r\n const response = await this.llm!.invoke([systemPrompt, userPrompt]);\r\n return response.content.toString();\r\n }\r\n}\r\n","/**\r\n * 依赖分析器\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport type { BundleInfo, DependencyInfo } from \"./types\";\r\n\r\nexport class DependencyAnalyzer {\r\n /**\r\n * 分析依赖\r\n */\r\n analyzeDependencies(bundles: BundleInfo[]): {\r\n total: number;\r\n duplicates: DependencyInfo[];\r\n largest: DependencyInfo[];\r\n } {\r\n const jsFiles = bundles.filter((b) => b.type === \"javascript\");\r\n const dependencies = this.extractDependencies(jsFiles);\r\n\r\n // 检测重复依赖\r\n const duplicates = this.findDuplicates(dependencies);\r\n\r\n // 找出最大的依赖\r\n const largest = [...dependencies]\r\n .sort((a, b) => b.size - a.size)\r\n .slice(0, 10);\r\n\r\n return {\r\n total: dependencies.length,\r\n duplicates,\r\n largest,\r\n };\r\n }\r\n\r\n /**\r\n * 从 JS 文件中提取依赖信息\r\n */\r\n private extractDependencies(jsFiles: BundleInfo[]): DependencyInfo[] {\r\n const depMap = new Map<string, DependencyInfo>();\r\n\r\n jsFiles.forEach((file) => {\r\n // 尝试解析文件内容,提取依赖\r\n const imports = this.parseImports(file);\r\n\r\n imports.forEach((depName) => {\r\n if (!depMap.has(depName)) {\r\n depMap.set(depName, {\r\n name: depName,\r\n size: 0,\r\n usedBy: [],\r\n isDuplicate: false,\r\n });\r\n }\r\n\r\n const dep = depMap.get(depName)!;\r\n dep.usedBy.push(file.name);\r\n // 估算依赖大小(简化处理)\r\n dep.size += file.size / imports.length;\r\n });\r\n });\r\n\r\n return Array.from(depMap.values());\r\n }\r\n\r\n /**\r\n * 解析文件中的 import 语句\r\n */\r\n private parseImports(file: BundleInfo): string[] {\r\n const imports: string[] = [];\r\n\r\n try {\r\n const distDir = path.resolve(process.cwd(), \"dist\");\r\n const filePath = path.join(distDir, file.path);\r\n\r\n if (!fs.existsSync(filePath)) {\r\n return imports;\r\n }\r\n\r\n const content = fs.readFileSync(filePath, \"utf-8\");\r\n\r\n // 匹配常见的依赖模式\r\n const patterns = [\r\n // node_modules 依赖\r\n /from\\s+[\"']([^\"']+)[\"']/g,\r\n /require\\([\"']([^\"']+)[\"']\\)/g,\r\n // Vite 特殊标记\r\n /__vite_ssr_import__\\(\"([^\"]+)\"\\)/g,\r\n ];\r\n\r\n patterns.forEach((pattern) => {\r\n let match;\r\n while ((match = pattern.exec(content)) !== null) {\r\n const depName = match[1];\r\n // 只保留 node_modules 依赖\r\n if (!depName.startsWith(\".\") && !depName.startsWith(\"/\")) {\r\n // 提取包名(去掉子路径)\r\n const pkgName = depName\r\n .split(\"/\")\r\n .slice(0, depName.startsWith(\"@\") ? 2 : 1)\r\n .join(\"/\");\r\n if (!imports.includes(pkgName)) {\r\n imports.push(pkgName);\r\n }\r\n }\r\n }\r\n });\r\n } catch (error) {\r\n // 忽略解析错误\r\n }\r\n\r\n return imports;\r\n }\r\n\r\n /**\r\n * 查找重复打包的依赖\r\n */\r\n private findDuplicates(dependencies: DependencyInfo[]): DependencyInfo[] {\r\n return dependencies\r\n .filter((dep) => dep.usedBy.length > 1)\r\n .map((dep) => ({\r\n ...dep,\r\n isDuplicate: true,\r\n }))\r\n .sort((a, b) => b.usedBy.length - a.usedBy.length)\r\n .slice(0, 10);\r\n }\r\n}\r\n","/**\r\n * 历史对比分析器\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport type {\r\n BundleInfo,\r\n HistoryComparison,\r\n HistoryRecord,\r\n ComparisonResult,\r\n} from \"./types\";\r\n\r\nexport class HistoryAnalyzer {\r\n private historyFile: string;\r\n\r\n constructor() {\r\n const reportsDir = path.resolve(process.cwd(), \"ai-reports\");\r\n this.historyFile = path.join(reportsDir, \".perf-history.json\");\r\n }\r\n\r\n /**\r\n * 对比当前构建与历史记录\r\n */\r\n compare(\r\n currentBundles: BundleInfo[],\r\n currentTotalSize: number,\r\n currentFileCount: number\r\n ): HistoryComparison | undefined {\r\n const history = this.loadHistory();\r\n\r\n if (history.length === 0) {\r\n // 没有历史记录,保存当前记录\r\n this.saveHistory(currentBundles, currentTotalSize, currentFileCount);\r\n return undefined;\r\n }\r\n\r\n // 获取上一次记录\r\n const previous = history[history.length - 1];\r\n\r\n // 对比总大小\r\n const totalSize = this.compareValue(currentTotalSize, previous.totalSize);\r\n\r\n // 对比文件数量\r\n const fileCount = this.compareValue(currentFileCount, previous.fileCount);\r\n\r\n // 对比最大文件变化\r\n const largestFileChanges = this.compareLargestFiles(\r\n currentBundles,\r\n previous.files\r\n );\r\n\r\n // 检测新增和删除的文件\r\n const currentFileNames = new Set(currentBundles.map((b) => b.name));\r\n const previousFileNames = new Set(previous.files.map((f) => f.name));\r\n\r\n const newFiles = currentBundles\r\n .filter((b) => !previousFileNames.has(b.name))\r\n .map((b) => b.name);\r\n\r\n const removedFiles = previous.files\r\n .filter((f) => !currentFileNames.has(f.name))\r\n .map((f) => f.name);\r\n\r\n // 保存当前记录\r\n this.saveHistory(currentBundles, currentTotalSize, currentFileCount);\r\n\r\n return {\r\n totalSize,\r\n fileCount,\r\n largestFileChanges,\r\n newFiles,\r\n removedFiles,\r\n };\r\n }\r\n\r\n /**\r\n * 对比数值\r\n */\r\n private compareValue(current: number, previous: number): ComparisonResult {\r\n const diff = current - previous;\r\n const diffPercent = previous === 0 ? 0 : (diff / previous) * 100;\r\n\r\n let trend: \"increased\" | \"decreased\" | \"unchanged\";\r\n if (Math.abs(diffPercent) < 1) {\r\n trend = \"unchanged\";\r\n } else if (diff > 0) {\r\n trend = \"increased\";\r\n } else {\r\n trend = \"decreased\";\r\n }\r\n\r\n return {\r\n current,\r\n previous,\r\n diff,\r\n diffPercent,\r\n trend,\r\n };\r\n }\r\n\r\n /**\r\n * 对比最大文件的变化\r\n */\r\n private compareLargestFiles(\r\n currentBundles: BundleInfo[],\r\n previousFiles: Array<{ name: string; size: number }>\r\n ): Array<{\r\n name: string;\r\n current: number;\r\n previous: number;\r\n diff: number;\r\n }> {\r\n const changes: Array<{\r\n name: string;\r\n current: number;\r\n previous: number;\r\n diff: number;\r\n }> = [];\r\n\r\n // 创建文件映射\r\n const previousMap = new Map(previousFiles.map((f) => [f.name, f.size]));\r\n\r\n currentBundles.forEach((bundle) => {\r\n const previousSize = previousMap.get(bundle.name);\r\n if (previousSize !== undefined) {\r\n const diff = bundle.size - previousSize;\r\n // 只记录变化超过 10KB 的文件\r\n if (Math.abs(diff) > 10 * 1024) {\r\n changes.push({\r\n name: bundle.name,\r\n current: bundle.size,\r\n previous: previousSize,\r\n diff,\r\n });\r\n }\r\n }\r\n });\r\n\r\n // 按变化幅度排序\r\n return changes\r\n .sort((a, b) => Math.abs(b.diff) - Math.abs(a.diff))\r\n .slice(0, 10);\r\n }\r\n\r\n /**\r\n * 加载历史记录\r\n */\r\n private loadHistory(): HistoryRecord[] {\r\n if (!fs.existsSync(this.historyFile)) {\r\n return [];\r\n }\r\n\r\n try {\r\n const content = fs.readFileSync(this.historyFile, \"utf-8\");\r\n const history = JSON.parse(content);\r\n // 只保留最近 10 次记录\r\n return history.slice(-10);\r\n } catch (error) {\r\n console.warn(\"⚠️ 无法读取历史记录\");\r\n return [];\r\n }\r\n }\r\n\r\n /**\r\n * 保存历史记录\r\n */\r\n private saveHistory(\r\n bundles: BundleInfo[],\r\n totalSize: number,\r\n fileCount: number\r\n ): void {\r\n const history = this.loadHistory();\r\n\r\n const record: HistoryRecord = {\r\n timestamp: new Date().toISOString(),\r\n totalSize,\r\n fileCount,\r\n files: bundles.map((b) => ({\r\n name: b.name,\r\n size: b.size,\r\n })),\r\n };\r\n\r\n history.push(record);\r\n\r\n // 确保目录存在\r\n const dir = path.dirname(this.historyFile);\r\n if (!fs.existsSync(dir)) {\r\n fs.mkdirSync(dir, { recursive: true });\r\n }\r\n\r\n // 只保留最近 10 次记录\r\n const recentHistory = history.slice(-10);\r\n\r\n fs.writeFileSync(\r\n this.historyFile,\r\n JSON.stringify(recentHistory, null, 2),\r\n \"utf-8\"\r\n );\r\n }\r\n\r\n /**\r\n * 清除历史记录\r\n */\r\n clearHistory(): void {\r\n if (fs.existsSync(this.historyFile)) {\r\n fs.unlinkSync(this.historyFile);\r\n }\r\n }\r\n}\r\n","/**\r\n * 优化示例生成器\r\n */\r\n\r\nimport type {\r\n OptimizationExample,\r\n PerformanceIssue,\r\n DependencyInfo,\r\n} from \"./types\";\r\n\r\nexport class OptimizationExamples {\r\n /**\r\n * 根据问题生成优化示例\r\n */\r\n generate(\r\n issues: PerformanceIssue[],\r\n duplicateDeps?: DependencyInfo[]\r\n ): OptimizationExample[] {\r\n const examples: OptimizationExample[] = [];\r\n\r\n // 代码分割示例\r\n if (issues.some((i) => i.type === \"size\")) {\r\n examples.push(this.getCodeSplittingExample());\r\n }\r\n\r\n // 重复依赖优化\r\n if (duplicateDeps && duplicateDeps.length > 0) {\r\n examples.push(this.getDependencyOptimizationExample(duplicateDeps));\r\n }\r\n\r\n // 压缩优化\r\n if (issues.some((i) => i.type === \"optimization\")) {\r\n examples.push(this.getCompressionExample());\r\n }\r\n\r\n // 图片优化\r\n if (issues.some((i) => i.description.includes(\"图片\"))) {\r\n examples.push(this.getImageOptimizationExample());\r\n }\r\n\r\n // 动态导入\r\n if (issues.some((i) => i.type === \"size\" && i.severity === \"high\")) {\r\n examples.push(this.getDynamicImportExample());\r\n }\r\n\r\n // Tree-shaking 优化\r\n examples.push(this.getTreeShakingExample());\r\n\r\n return examples;\r\n }\r\n\r\n /**\r\n * 代码分割示例\r\n */\r\n private getCodeSplittingExample(): OptimizationExample {\r\n return {\r\n title: \"配置代码分割\",\r\n description: \"将大文件拆分成多个小文件,提高加载性能\",\r\n priority: \"high\",\r\n impact: \"可减少 30-50% 的初始加载大小\",\r\n difficulty: \"简单\",\r\n code: {\r\n language: \"typescript\",\r\n content: `// vite.config.ts\r\nexport default defineConfig({\r\n build: {\r\n rollupOptions: {\r\n output: {\r\n manualChunks: {\r\n // 提取 Vue 核心库\r\n 'vue-vendor': ['vue', 'vue-router', 'pinia'],\r\n \r\n // 提取 UI 组件库\r\n 'ui-vendor': ['element-plus', '@element-plus/icons-vue'],\r\n \r\n // 提取工具库\r\n 'utils': ['lodash-es', 'dayjs', 'axios'],\r\n \r\n // 提取图表库(如果使用)\r\n 'charts': ['echarts', 'chart.js'],\r\n },\r\n \r\n // 自动分割大于 500KB 的文件\r\n chunkFileNames: (chunkInfo) => {\r\n const facadeModuleId = chunkInfo.facadeModuleId \r\n ? chunkInfo.facadeModuleId.split('/').pop() \r\n : 'chunk';\r\n return \\`js/\\${facadeModuleId}-[hash].js\\`;\r\n },\r\n },\r\n },\r\n \r\n // 设置 chunk 大小警告阈值\r\n chunkSizeWarningLimit: 500,\r\n },\r\n});`,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * 重复依赖优化示例\r\n */\r\n private getDependencyOptimizationExample(\r\n duplicates: DependencyInfo[]\r\n ): OptimizationExample {\r\n const topDuplicates = duplicates.slice(0, 3).map((d) => d.name);\r\n\r\n return {\r\n title: \"优化重复依赖\",\r\n description: `检测到 ${duplicates.length} 个重复打包的依赖,建议提取到公共 chunk`,\r\n priority: \"high\",\r\n impact: `可减少 ${(\r\n duplicates.reduce((sum, d) => sum + d.size, 0) /\r\n 1024 /\r\n 1024\r\n ).toFixed(2)}MB`,\r\n difficulty: \"简单\",\r\n code: {\r\n language: \"typescript\",\r\n content: `// vite.config.ts\r\nexport default defineConfig({\r\n build: {\r\n rollupOptions: {\r\n output: {\r\n manualChunks: {\r\n // 提取重复依赖到公共 chunk\r\n 'common-vendor': [\r\n ${topDuplicates.map((name) => `'${name}'`).join(\",\\n \")}\r\n ],\r\n },\r\n },\r\n },\r\n },\r\n \r\n // 或者使用自动分割策略\r\n optimizeDeps: {\r\n include: [\r\n ${topDuplicates.map((name) => `'${name}'`).join(\",\\n \")}\r\n ],\r\n },\r\n});`,\r\n },\r\n relatedFiles: duplicates.flatMap((d) => d.usedBy).slice(0, 5),\r\n };\r\n }\r\n\r\n /**\r\n * 压缩优化示例\r\n */\r\n private getCompressionExample(): OptimizationExample {\r\n return {\r\n title: \"启用 Gzip/Brotli 压缩\",\r\n description: \"使用压缩插件减少传输大小\",\r\n priority: \"medium\",\r\n impact: \"可减少 60-70% 的传输大小\",\r\n difficulty: \"简单\",\r\n code: {\r\n language: \"bash\",\r\n content: `# 1. 安装压缩插件\r\nnpm install vite-plugin-compression -D\r\n\r\n# 2. 配置插件\r\n# vite.config.ts\r\nimport viteCompression from 'vite-plugin-compression';\r\n\r\nexport default defineConfig({\r\n plugins: [\r\n // Gzip 压缩\r\n viteCompression({\r\n algorithm: 'gzip',\r\n ext: '.gz',\r\n threshold: 10240, // 大于 10KB 才压缩\r\n deleteOriginFile: false,\r\n }),\r\n \r\n // Brotli 压缩(可选,压缩率更高)\r\n viteCompression({\r\n algorithm: 'brotliCompress',\r\n ext: '.br',\r\n threshold: 10240,\r\n deleteOriginFile: false,\r\n }),\r\n ],\r\n});\r\n\r\n# 3. Nginx 配置(服务器端)\r\n# nginx.conf\r\ngzip on;\r\ngzip_types text/plain text/css application/json application/javascript;\r\ngzip_min_length 1024;`,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * 图片优化示例\r\n */\r\n private getImageOptimizationExample(): OptimizationExample {\r\n return {\r\n title: \"优化图片资源\",\r\n description: \"压缩图片并转换为现代格式\",\r\n priority: \"medium\",\r\n impact: \"可减少 50-80% 的图片大小\",\r\n difficulty: \"简单\",\r\n code: {\r\n language: \"bash\",\r\n content: `# 1. 安装图片优化插件\r\nnpm install vite-plugin-imagemin -D\r\n\r\n# 2. 配置插件\r\n# vite.config.ts\r\nimport viteImagemin from 'vite-plugin-imagemin';\r\n\r\nexport default defineConfig({\r\n plugins: [\r\n viteImagemin({\r\n // GIF 优化\r\n gifsicle: {\r\n optimizationLevel: 7,\r\n interlaced: false,\r\n },\r\n \r\n // PNG 优化\r\n optipng: {\r\n optimizationLevel: 7,\r\n },\r\n \r\n // JPEG 优化\r\n mozjpeg: {\r\n quality: 80,\r\n },\r\n \r\n // SVG 优化\r\n svgo: {\r\n plugins: [\r\n { name: 'removeViewBox', active: false },\r\n { name: 'removeEmptyAttrs', active: true },\r\n ],\r\n },\r\n \r\n // WebP 转换\r\n webp: {\r\n quality: 80,\r\n },\r\n }),\r\n ],\r\n});\r\n\r\n# 3. 使用 WebP 格式(HTML)\r\n<picture>\r\n <source srcset=\"image.webp\" type=\"image/webp\">\r\n <img src=\"image.jpg\" alt=\"description\">\r\n</picture>`,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * 动态导入示例\r\n */\r\n private getDynamicImportExample(): OptimizationExample {\r\n return {\r\n title: \"使用动态导入(懒加载)\",\r\n description: \"按需加载组件和路由,减少初始加载大小\",\r\n priority: \"high\",\r\n impact: \"可减少 40-60% 的初始加载大小\",\r\n difficulty: \"中等\",\r\n code: {\r\n language: \"typescript\",\r\n content: `// 1. 路由懒加载\r\n// router/index.ts\r\nconst routes = [\r\n {\r\n path: '/dashboard',\r\n name: 'Dashboard',\r\n // 使用动态导入\r\n component: () => import('@/views/Dashboard.vue'),\r\n },\r\n {\r\n path: '/settings',\r\n name: 'Settings',\r\n component: () => import('@/views/Settings.vue'),\r\n },\r\n];\r\n\r\n// 2. 组件懒加载\r\n// App.vue\r\n<script setup>\r\nimport { defineAsyncComponent } from 'vue';\r\n\r\n// 异步组件\r\nconst HeavyComponent = defineAsyncComponent(() =>\r\n import('./components/HeavyComponent.vue')\r\n);\r\n\r\n// 带加载状态的异步组件\r\nconst AsyncComponent = defineAsyncComponent({\r\n loader: () => import('./components/AsyncComponent.vue'),\r\n loadingComponent: LoadingSpinner,\r\n errorComponent: ErrorComponent,\r\n delay: 200,\r\n timeout: 3000,\r\n});\r\n</script>\r\n\r\n// 3. 条件加载\r\n<script setup>\r\nconst loadCharts = async () => {\r\n if (needCharts) {\r\n const { default: ECharts } = await import('echarts');\r\n // 使用 ECharts\r\n }\r\n};\r\n</script>`,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * Tree-shaking 优化示例\r\n */\r\n private getTreeShakingExample(): OptimizationExample {\r\n return {\r\n title: \"优化 Tree-shaking\",\r\n description: \"确保未使用的代码被正确移除\",\r\n priority: \"medium\",\r\n impact: \"可减少 10-30% 的代码大小\",\r\n difficulty: \"中等\",\r\n code: {\r\n language: \"typescript\",\r\n content: `// 1. 使用 ES6 模块导入(支持 tree-shaking)\r\n// ❌ 不推荐\r\nimport _ from 'lodash';\r\nconst result = _.debounce(fn, 300);\r\n\r\n// ✅ 推荐\r\nimport { debounce } from 'lodash-es';\r\nconst result = debounce(fn, 300);\r\n\r\n// 2. 配置 package.json\r\n{\r\n \"sideEffects\": false, // 标记为无副作用\r\n // 或指定有副作用的文件\r\n \"sideEffects\": [\"*.css\", \"*.scss\"]\r\n}\r\n\r\n// 3. Vite 配置\r\n// vite.config.ts\r\nexport default defineConfig({\r\n build: {\r\n // 启用 tree-shaking\r\n minify: 'terser',\r\n terserOptions: {\r\n compress: {\r\n drop_console: true, // 移除 console\r\n drop_debugger: true, // 移除 debugger\r\n pure_funcs: ['console.log'], // 移除特定函数\r\n },\r\n },\r\n },\r\n});\r\n\r\n// 4. 避免副作用导入\r\n// ❌ 不推荐(会导入整个模块)\r\nimport 'some-library';\r\n\r\n// ✅ 推荐(明确导入需要的部分)\r\nimport { specificFunction } from 'some-library';`,\r\n },\r\n };\r\n }\r\n}\r\n","/**\r\n * 性能分析报告生成器\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport pc from \"picocolors\";\r\nimport type { AnalysisResult, PerformanceIssue } from \"./types\";\r\n\r\nexport interface ReporterOptions {\r\n console?: boolean;\r\n html?: boolean;\r\n json?: boolean;\r\n}\r\n\r\nexport class PerfReporter {\r\n /**\r\n * 生成报告\r\n */\r\n async generate(\r\n result: AnalysisResult,\r\n options: ReporterOptions = {},\r\n ): Promise<void> {\r\n if (options.html) {\r\n await this.generateHTMLReport(result);\r\n }\r\n\r\n if (options.json) {\r\n await this.generateJSONReport(result);\r\n }\r\n }\r\n\r\n /**\r\n * 控制台输出\r\n */\r\n printConsole(result: AnalysisResult): void {\r\n console.log(\"\\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\");\r\n console.log(\"⚡ 性能分析报告\");\r\n console.log(\"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n\");\r\n\r\n // 总体统计\r\n console.log(\"📊 总体统计:\");\r\n console.log(\r\n ` 总大小: ${(result.summary.totalSize / 1024 / 1024).toFixed(2)}MB`,\r\n );\r\n console.log(\r\n ` Gzip后: ${(result.summary.totalGzipSize / 1024 / 1024).toFixed(2)}MB`,\r\n );\r\n console.log(` 文件数: ${result.summary.fileCount}\\n`);\r\n\r\n // 历史对比\r\n if (result.comparison) {\r\n console.log(\"📈 历史对比:\");\r\n const { totalSize, fileCount, newFiles, removedFiles } =\r\n result.comparison;\r\n const sizeIcon = totalSize.trend === \"increased\" ? \"📈\" : \"📉\";\r\n console.log(\r\n ` ${sizeIcon} 总大小: ${totalSize.trend === \"increased\" ? \"+\" : \"\"}${(\r\n totalSize.diff /\r\n 1024 /\r\n 1024\r\n ).toFixed(2)}MB (${\r\n totalSize.diffPercent > 0 ? \"+\" : \"\"\r\n }${totalSize.diffPercent.toFixed(2)}%)`,\r\n );\r\n console.log(\r\n ` 📊 文件数: ${fileCount.diff > 0 ? \"+\" : \"\"}${fileCount.diff}`,\r\n );\r\n if (newFiles.length > 0) {\r\n console.log(` ✨ 新增: ${newFiles.slice(0, 3).join(\", \")}`);\r\n }\r\n if (removedFiles.length > 0) {\r\n console.log(` 🗑️ 删除: ${removedFiles.slice(0, 3).join(\", \")}`);\r\n }\r\n console.log();\r\n }\r\n\r\n // 依赖分析\r\n if (result.dependencies) {\r\n console.log(\"📦 依赖分析:\");\r\n console.log(` 总依赖数: ${result.dependencies.total}`);\r\n if (result.dependencies.duplicates.length > 0) {\r\n console.log(\r\n ` ⚠️ 重复依赖: ${result.dependencies.duplicates.length} 个`,\r\n );\r\n result.dependencies.duplicates.slice(0, 3).forEach((dep) => {\r\n console.log(\r\n ` - ${dep.name}: 被 ${dep.usedBy.length} 个文件使用`,\r\n );\r\n });\r\n }\r\n console.log();\r\n }\r\n\r\n // 最大文件\r\n console.log(\"📦 最大的文件:\");\r\n result.summary.largestFiles.slice(0, 5).forEach((file, index) => {\r\n console.log(\r\n ` ${index + 1}. ${file.name}: ${(file.size / 1024).toFixed(2)}KB`,\r\n );\r\n });\r\n console.log();\r\n\r\n // 按类型统计\r\n console.log(\"📋 按类型统计:\");\r\n Object.entries(result.summary.byType).forEach(([type, info]) => {\r\n console.log(\r\n ` ${type}: ${info.count} 个, ${(info.size / 1024).toFixed(2)}KB`,\r\n );\r\n });\r\n console.log();\r\n\r\n // 性能问题\r\n if (result.issues.length > 0) {\r\n console.log(\"⚠️ 性能问题:\");\r\n result.issues.forEach((issue, index) => {\r\n const icon = this.getSeverityIcon(issue.severity);\r\n console.log(` ${index + 1}. ${icon} ${issue.title}`);\r\n console.log(` ${issue.description}`);\r\n if (issue.suggestion) {\r\n console.log(` 💡 ${issue.suggestion}`);\r\n }\r\n });\r\n console.log();\r\n } else {\r\n console.log(\"✅ 未发现明显的性能问题\\n\");\r\n }\r\n\r\n // 优化建议\r\n if (result.suggestions.length > 0) {\r\n console.log(\"💡 优化建议:\");\r\n result.suggestions.forEach((suggestion, index) => {\r\n console.log(` ${index + 1}. ${suggestion}`);\r\n });\r\n console.log();\r\n }\r\n\r\n // 优化示例\r\n if (result.optimizationExamples && result.optimizationExamples.length > 0) {\r\n console.log(\"🔧 优化示例:\");\r\n result.optimizationExamples.slice(0, 3).forEach((example, index) => {\r\n console.log(` ${index + 1}. ${example.title} (${example.priority})`);\r\n console.log(` ${example.description}`);\r\n });\r\n console.log();\r\n }\r\n\r\n // AI 分析\r\n if (result.aiAnalysis) {\r\n console.log(\"🤖 AI 分析:\");\r\n console.log(\r\n result.aiAnalysis\r\n .split(\"\\n\")\r\n .map((line) => ` ${line}`)\r\n .join(\"\\n\"),\r\n );\r\n console.log();\r\n }\r\n\r\n console.log(\"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n\");\r\n }\r\n\r\n /**\r\n * 生成 HTML 报告\r\n */\r\n private async generateHTMLReport(result: AnalysisResult): Promise<void> {\r\n const html = `\r\n<!DOCTYPE html>\r\n<html lang=\"zh-CN\">\r\n<head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <title>性能分析报告</title>\r\n <style>\r\n * { margin: 0; padding: 0; box-sizing: border-box; }\r\n body {\r\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\r\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\r\n padding: 20px;\r\n min-height: 100vh;\r\n }\r\n .container {\r\n max-width: 1200px;\r\n margin: 0 auto;\r\n background: white;\r\n border-radius: 12px;\r\n box-shadow: 0 8px 32px rgba(0,0,0,0.2);\r\n overflow: hidden;\r\n }\r\n .header {\r\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\r\n color: white;\r\n padding: 30px;\r\n text-align: center;\r\n }\r\n .header h1 {\r\n font-size: 32px;\r\n margin-bottom: 10px;\r\n }\r\n .header .time {\r\n font-size: 14px;\r\n opacity: 0.9;\r\n }\r\n .content {\r\n padding: 30px;\r\n }\r\n .section {\r\n margin-bottom: 30px;\r\n }\r\n .section-title {\r\n font-size: 20px;\r\n font-weight: bold;\r\n color: #333;\r\n margin-bottom: 15px;\r\n padding-bottom: 10px;\r\n border-bottom: 2px solid #667eea;\r\n }\r\n .stats-grid {\r\n display: grid;\r\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\r\n gap: 20px;\r\n margin-bottom: 20px;\r\n }\r\n .stat-card {\r\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\r\n color: white;\r\n padding: 20px;\r\n border-radius: 12px;\r\n text-align: center;\r\n }\r\n .stat-value {\r\n font-size: 32px;\r\n font-weight: bold;\r\n margin: 10px 0;\r\n }\r\n .stat-label {\r\n font-size: 14px;\r\n opacity: 0.9;\r\n }\r\n .file-list {\r\n background: #f7fafc;\r\n border-radius: 8px;\r\n padding: 15px;\r\n }\r\n .file-item {\r\n display: flex;\r\n justify-content: space-between;\r\n padding: 10px;\r\n border-bottom: 1px solid #e2e8f0;\r\n }\r\n .file-item:last-child {\r\n border-bottom: none;\r\n }\r\n .file-name {\r\n font-family: monospace;\r\n color: #333;\r\n }\r\n .file-size {\r\n color: #667eea;\r\n font-weight: bold;\r\n }\r\n .issue {\r\n background: #fff5f5;\r\n border-left: 4px solid #f56565;\r\n padding: 15px;\r\n border-radius: 8px;\r\n margin-bottom: 15px;\r\n }\r\n .issue.medium {\r\n background: #fffaf0;\r\n border-left-color: #ed8936;\r\n }\r\n .issue.low {\r\n background: #f0f9ff;\r\n border-left-color: #4299e1;\r\n }\r\n .issue-title {\r\n font-weight: bold;\r\n color: #333;\r\n margin-bottom: 8px;\r\n }\r\n .issue-desc {\r\n color: #666;\r\n margin-bottom: 8px;\r\n }\r\n .issue-suggestion {\r\n background: #e6fffa;\r\n padding: 10px;\r\n border-radius: 6px;\r\n margin-top: 10px;\r\n color: #234e52;\r\n }\r\n .suggestion-list {\r\n background: #f0fdf4;\r\n border-radius: 8px;\r\n padding: 15px;\r\n }\r\n .suggestion-item {\r\n padding: 10px;\r\n border-bottom: 1px solid #d1fae5;\r\n color: #065f46;\r\n }\r\n .suggestion-item:last-child {\r\n border-bottom: none;\r\n }\r\n .ai-analysis {\r\n background: #f0f9ff;\r\n border-left: 4px solid #3b82f6;\r\n padding: 20px;\r\n border-radius: 8px;\r\n line-height: 1.8;\r\n color: #1e40af;\r\n white-space: pre-wrap;\r\n }\r\n .footer {\r\n background: #f7fafc;\r\n padding: 20px;\r\n text-align: center;\r\n color: #718096;\r\n font-size: 14px;\r\n }\r\n .trend-up {\r\n background: linear-gradient(135deg, #f56565 0%, #ed8936 100%) !important;\r\n }\r\n .trend-down {\r\n background: linear-gradient(135deg, #48bb78 0%, #38a169 100%) !important;\r\n }\r\n .optimization-example {\r\n background: #f7fafc;\r\n border-radius: 12px;\r\n padding: 20px;\r\n margin-bottom: 20px;\r\n border: 1px solid #e2e8f0;\r\n }\r\n .example-header {\r\n margin-bottom: 15px;\r\n }\r\n .example-title {\r\n font-size: 18px;\r\n font-weight: bold;\r\n color: #333;\r\n margin-bottom: 8px;\r\n display: flex;\r\n align-items: center;\r\n gap: 10px;\r\n }\r\n .priority-badge {\r\n font-size: 12px;\r\n padding: 4px 12px;\r\n border-radius: 12px;\r\n font-weight: normal;\r\n }\r\n .priority-high {\r\n background: #fed7d7;\r\n color: #c53030;\r\n }\r\n .priority-medium {\r\n background: #feebc8;\r\n color: #c05621;\r\n }\r\n .priority-low {\r\n background: #bee3f8;\r\n color: #2c5282;\r\n }\r\n .example-meta {\r\n display: flex;\r\n gap: 15px;\r\n font-size: 14px;\r\n color: #666;\r\n }\r\n .example-desc {\r\n color: #666;\r\n margin-bottom: 15px;\r\n line-height: 1.6;\r\n }\r\n .code-block {\r\n background: #1e293b;\r\n border-radius: 8px;\r\n overflow: hidden;\r\n margin-top: 10px;\r\n }\r\n .code-header {\r\n background: #334155;\r\n color: #94a3b8;\r\n padding: 8px 15px;\r\n font-size: 12px;\r\n font-family: monospace;\r\n }\r\n .code-block pre {\r\n margin: 0;\r\n padding: 15px;\r\n overflow-x: auto;\r\n }\r\n .code-block code {\r\n font-family: 'Consolas', 'Monaco', 'Courier New', monospace;\r\n font-size: 13px;\r\n line-height: 1.6;\r\n color: #e2e8f0;\r\n }\r\n .related-files {\r\n margin-top: 15px;\r\n padding: 10px;\r\n background: #e6fffa;\r\n border-radius: 6px;\r\n font-size: 14px;\r\n color: #234e52;\r\n }\r\n </style>\r\n</head>\r\n<body>\r\n <div class=\"container\">\r\n <div class=\"header\">\r\n <h1>⚡ 性能分析报告</h1>\r\n <div class=\"time\">生成时间: ${result.timestamp}</div>\r\n </div>\r\n\r\n <div class=\"content\">\r\n <!-- 总体统计 -->\r\n <div class=\"section\">\r\n <div class=\"section-title\">📊 总体统计</div>\r\n <div class=\"stats-grid\">\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">总大小</div>\r\n <div class=\"stat-value\">${(\r\n result.summary.totalSize /\r\n 1024 /\r\n 1024\r\n ).toFixed(2)}MB</div>\r\n </div>\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">Gzip 后</div>\r\n <div class=\"stat-value\">${(\r\n result.summary.totalGzipSize /\r\n 1024 /\r\n 1024\r\n ).toFixed(2)}MB</div>\r\n </div>\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">文件数量</div>\r\n <div class=\"stat-value\">${result.summary.fileCount}</div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 最大文件 -->\r\n <div class=\"section\">\r\n <div class=\"section-title\">📦 最大的文件</div>\r\n <div class=\"file-list\">\r\n ${result.summary.largestFiles\r\n .slice(0, 10)\r\n .map(\r\n (file) => `\r\n <div class=\"file-item\">\r\n <span class=\"file-name\">${file.name}</span>\r\n <span class=\"file-size\">${(file.size / 1024).toFixed(2)}KB</span>\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n </div>\r\n\r\n <!-- 按类型统计 -->\r\n <div class=\"section\">\r\n <div class=\"section-title\">📋 按类型统计</div>\r\n <div class=\"file-list\">\r\n ${Object.entries(result.summary.byType)\r\n .map(\r\n ([type, info]) => `\r\n <div class=\"file-item\">\r\n <span class=\"file-name\">${type}</span>\r\n <span class=\"file-size\">${info.count} 个, ${(\r\n info.size / 1024\r\n ).toFixed(2)}KB</span>\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n </div>\r\n\r\n <!-- 历史对比 -->\r\n ${\r\n result.comparison\r\n ? `\r\n <div class=\"section\">\r\n <div class=\"section-title\">📈 历史对比</div>\r\n <div class=\"stats-grid\">\r\n <div class=\"stat-card ${\r\n result.comparison.totalSize.trend === \"increased\"\r\n ? \"trend-up\"\r\n : \"trend-down\"\r\n }\">\r\n <div class=\"stat-label\">总大小变化</div>\r\n <div class=\"stat-value\">${\r\n result.comparison.totalSize.diffPercent > 0 ? \"+\" : \"\"\r\n }${result.comparison.totalSize.diffPercent.toFixed(2)}%</div>\r\n <div class=\"stat-label\">${\r\n result.comparison.totalSize.trend === \"increased\" ? \"+\" : \"\"\r\n }${(result.comparison.totalSize.diff / 1024 / 1024).toFixed(\r\n 2,\r\n )}MB</div>\r\n </div>\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">文件数变化</div>\r\n <div class=\"stat-value\">${\r\n result.comparison.fileCount.diff > 0 ? \"+\" : \"\"\r\n }${result.comparison.fileCount.diff}</div>\r\n </div>\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">新增文件</div>\r\n <div class=\"stat-value\">${result.comparison.newFiles.length}</div>\r\n </div>\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">删除文件</div>\r\n <div class=\"stat-value\">${\r\n result.comparison.removedFiles.length\r\n }</div>\r\n </div>\r\n </div>\r\n ${\r\n result.comparison.largestFileChanges.length > 0\r\n ? `\r\n <div class=\"file-list\" style=\"margin-top: 15px;\">\r\n <div style=\"font-weight: bold; padding: 10px; color: #333;\">文件大小变化 Top 5:</div>\r\n ${result.comparison.largestFileChanges\r\n .slice(0, 5)\r\n .map(\r\n (change) => `\r\n <div class=\"file-item\">\r\n <span class=\"file-name\">${change.name}</span>\r\n <span class=\"file-size ${\r\n change.diff > 0 ? \"trend-up\" : \"trend-down\"\r\n }\">\r\n ${change.diff > 0 ? \"+\" : \"\"}${(change.diff / 1024).toFixed(\r\n 2,\r\n )}KB\r\n </span>\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n\r\n <!-- 依赖分析 -->\r\n ${\r\n result.dependencies\r\n ? `\r\n <div class=\"section\">\r\n <div class=\"section-title\">📦 依赖分析</div>\r\n <div class=\"stats-grid\">\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">总依赖数</div>\r\n <div class=\"stat-value\">${result.dependencies.total}</div>\r\n </div>\r\n <div class=\"stat-card ${\r\n result.dependencies.duplicates.length > 0 ? \"trend-up\" : \"\"\r\n }\">\r\n <div class=\"stat-label\">重复依赖</div>\r\n <div class=\"stat-value\">${\r\n result.dependencies.duplicates.length\r\n }</div>\r\n </div>\r\n </div>\r\n ${\r\n result.dependencies.duplicates.length > 0\r\n ? `\r\n <div class=\"file-list\" style=\"margin-top: 15px;\">\r\n <div style=\"font-weight: bold; padding: 10px; color: #333;\">重复打包的依赖:</div>\r\n ${result.dependencies.duplicates\r\n .slice(0, 10)\r\n .map(\r\n (dep) => `\r\n <div class=\"file-item\">\r\n <span class=\"file-name\">${dep.name}</span>\r\n <span class=\"file-size\">\r\n ${(dep.size / 1024).toFixed(2)}KB · 被 ${\r\n dep.usedBy.length\r\n } 个文件使用\r\n </span>\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n ${\r\n result.dependencies.largest.length > 0\r\n ? `\r\n <div class=\"file-list\" style=\"margin-top: 15px;\">\r\n <div style=\"font-weight: bold; padding: 10px; color: #333;\">最大的依赖:</div>\r\n ${result.dependencies.largest\r\n .slice(0, 10)\r\n .map(\r\n (dep) => `\r\n <div class=\"file-item\">\r\n <span class=\"file-name\">${dep.name}</span>\r\n <span class=\"file-size\">${(dep.size / 1024).toFixed(\r\n 2,\r\n )}KB</span>\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n\r\n <!-- 性能问题 -->\r\n ${\r\n result.issues.length > 0\r\n ? `\r\n <div class=\"section\">\r\n <div class=\"section-title\">⚠️ 性能问题</div>\r\n ${result.issues\r\n .map(\r\n (issue) => `\r\n <div class=\"issue ${issue.severity}\">\r\n <div class=\"issue-title\">${this.getSeverityIcon(\r\n issue.severity,\r\n )} ${issue.title}</div>\r\n <div class=\"issue-desc\">${issue.description}</div>\r\n ${\r\n issue.files\r\n ? `<div class=\"issue-desc\">相关文件: ${issue.files.join(\r\n \", \",\r\n )}</div>`\r\n : \"\"\r\n }\r\n ${\r\n issue.suggestion\r\n ? `<div class=\"issue-suggestion\">💡 ${issue.suggestion}</div>`\r\n : \"\"\r\n }\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n `\r\n : '<div class=\"section\"><div class=\"section-title\">✅ 未发现明显的性能问题</div></div>'\r\n }\r\n\r\n <!-- 优化建议 -->\r\n ${\r\n result.suggestions.length > 0\r\n ? `\r\n <div class=\"section\">\r\n <div class=\"section-title\">💡 优化建议</div>\r\n <div class=\"suggestion-list\">\r\n ${result.suggestions\r\n .map(\r\n (suggestion, index) => `\r\n <div class=\"suggestion-item\">${index + 1}. ${suggestion}</div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n\r\n <!-- 优化示例 -->\r\n ${\r\n result.optimizationExamples && result.optimizationExamples.length > 0\r\n ? `\r\n <div class=\"section\">\r\n <div class=\"section-title\">🔧 优化示例</div>\r\n ${result.optimizationExamples\r\n .map(\r\n (example) => `\r\n <div class=\"optimization-example\">\r\n <div class=\"example-header\">\r\n <div class=\"example-title\">\r\n ${example.title}\r\n <span class=\"priority-badge priority-${example.priority}\">${\r\n example.priority === \"high\"\r\n ? \"高优先级\"\r\n : example.priority === \"medium\"\r\n ? \"中优先级\"\r\n : \"低优先级\"\r\n }</span>\r\n </div>\r\n <div class=\"example-meta\">\r\n <span>💪 ${example.difficulty}</span>\r\n <span>📈 ${example.impact}</span>\r\n </div>\r\n </div>\r\n <div class=\"example-desc\">${example.description}</div>\r\n <div class=\"code-block\">\r\n <div class=\"code-header\">${example.code.language}</div>\r\n <pre><code>${this.escapeHtml(example.code.content)}</code></pre>\r\n </div>\r\n ${\r\n example.relatedFiles && example.relatedFiles.length > 0\r\n ? `\r\n <div class=\"related-files\">\r\n <strong>相关文件:</strong> ${example.relatedFiles.join(\", \")}\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n\r\n <!-- AI 分析 -->\r\n ${\r\n result.aiAnalysis\r\n ? `\r\n <div class=\"section\">\r\n <div class=\"section-title\">🤖 AI 分析</div>\r\n <div class=\"ai-analysis\">${this.escapeHtml(result.aiAnalysis)}</div>\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n </div>\r\n\r\n <div class=\"footer\">\r\n <p>AI Performance Analyzer v1.0.0</p>\r\n <p>Powered by Vite & OpenAI</p>\r\n </div>\r\n </div>\r\n</body>\r\n</html>\r\n `.trim();\r\n\r\n // 创建报告目录\r\n const reportsDir = path.resolve(process.cwd(), \"ai-reports\");\r\n if (!fs.existsSync(reportsDir)) {\r\n fs.mkdirSync(reportsDir, { recursive: true });\r\n }\r\n\r\n const reportPath = path.resolve(reportsDir, \"performance-report.html\");\r\n fs.writeFileSync(reportPath, html, \"utf-8\");\r\n console.log(pc.green(`\\n📄 性能报告已生成: ${pc.cyan(reportPath)}\\n`));\r\n }\r\n\r\n /**\r\n * 生成 JSON 报告\r\n */\r\n private async generateJSONReport(result: AnalysisResult): Promise<void> {\r\n const reportsDir = path.resolve(process.cwd(), \"ai-reports\");\r\n if (!fs.existsSync(reportsDir)) {\r\n fs.mkdirSync(reportsDir, { recursive: true });\r\n }\r\n\r\n const reportPath = path.resolve(reportsDir, \"performance-report.json\");\r\n fs.writeFileSync(reportPath, JSON.stringify(result, null, 2), \"utf-8\");\r\n console.log(pc.green(`📄 JSON 报告已生成: ${pc.cyan(reportPath)}`));\r\n }\r\n\r\n /**\r\n * 获取严重程度图标\r\n */\r\n private getSeverityIcon(severity: string): string {\r\n const icons: Record<string, string> = {\r\n high: \"🔴\",\r\n medium: \"🟡\",\r\n low: \"🔵\",\r\n };\r\n return icons[severity] || \"⚪\";\r\n }\r\n\r\n /**\r\n * 转义 HTML\r\n */\r\n private escapeHtml(text: string): string {\r\n const map: Record<string, string> = {\r\n \"&\": \"&amp;\",\r\n \"<\": \"&lt;\",\r\n \">\": \"&gt;\",\r\n '\"': \"&quot;\",\r\n \"'\": \"&#039;\",\r\n };\r\n return text.replace(/[&<>\"']/g, (m) => map[m]);\r\n }\r\n}\r\n"],"mappings":"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","names":["pc","fs","path","fs","path","path","fs","fs","path","pc"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/analyzer.ts","../src/dependency-analyzer.ts","../src/history-analyzer.ts","../src/optimization-examples.ts","../src/reporter.ts"],"sourcesContent":["/**\r\n * AI 性能分析插件\r\n *\r\n * 功能:\r\n * - 分析构建产物大小\r\n * - 检测性能问题\r\n * - 提供优化建议\r\n */\r\n\r\nimport type { Plugin } from \"vite\";\r\nimport pc from \"picocolors\";\r\nimport { PerfAnalyzer } from \"./analyzer\";\r\nimport { PerfReporter } from \"./reporter\";\r\nimport type { AnalysisResult } from \"./types\";\r\n\r\nexport interface PerfAnalyzerOptions {\r\n // AI 配置\r\n apiKey?: string;\r\n apiUrl?: string;\r\n model?: string;\r\n temperature?: number;\r\n maxTokens?: number;\r\n // 分析配置\r\n enabled?: boolean;\r\n threshold?: {\r\n bundleSize?: number; // 单个文件大小阈值 (KB)\r\n totalSize?: number; // 总大小阈值 (MB)\r\n chunkCount?: number; // chunk 数量阈值\r\n };\r\n // 输出配置\r\n output?: {\r\n console?: boolean;\r\n html?: boolean;\r\n json?: boolean;\r\n };\r\n}\r\n\r\nexport function vitePluginAIPerfAnalyzer(\r\n options: PerfAnalyzerOptions = {},\r\n): Plugin {\r\n const {\r\n apiKey = process.env.OPENAI_API_KEY || \"\",\r\n apiUrl = process.env.OPENAI_API_URL || \"https://api.openai.com/v1\",\r\n model = process.env.OPENAI_MODEL || \"gpt-4\",\r\n temperature = 0.2,\r\n maxTokens = 4000,\r\n enabled = true,\r\n threshold = {\r\n bundleSize: 500, // 500KB\r\n totalSize: 5, // 5MB\r\n chunkCount: 20,\r\n },\r\n output = {\r\n console: true,\r\n html: true,\r\n json: false,\r\n },\r\n } = options;\r\n\r\n const analyzer = new PerfAnalyzer({\r\n apiKey,\r\n apiUrl,\r\n model,\r\n threshold,\r\n temperature,\r\n maxTokens,\r\n });\r\n const reporter = new PerfReporter();\r\n\r\n let analysisResult: AnalysisResult | null = null;\r\n\r\n return {\r\n name: \"vite-plugin-ai-perf-analyzer\",\r\n enforce: \"post\",\r\n\r\n configResolved(config) {\r\n if (!enabled) return;\r\n\r\n console.log(pc.cyan(\"\\n⚡ AI 性能分析插件已启动...\"));\r\n console.log(`📊 分析阈值:`);\r\n console.log(` 单文件: ${pc.yellow(threshold.bundleSize + \"KB\")}`);\r\n console.log(` 总大小: ${pc.yellow(threshold.totalSize + \"MB\")}`);\r\n console.log(\r\n ` Chunk数: ${pc.yellow((threshold.chunkCount ?? 10).toString())}`,\r\n );\r\n console.log(`🔑 API Key: ${apiKey ? \"已配置\" : \"未配置\"}\\n`);\r\n },\r\n\r\n async closeBundle() {\r\n if (!enabled) return;\r\n\r\n console.log(\"\\n⚡ 正在分析构建产物...\\n\");\r\n\r\n try {\r\n // 分析构建产物\r\n analysisResult = await analyzer.analyze();\r\n\r\n // 生成报告\r\n await reporter.generate(analysisResult, output);\r\n\r\n // 控制台输出\r\n if (output.console) {\r\n reporter.printConsole(analysisResult);\r\n }\r\n } catch (error: any) {\r\n console.error(\"❌ 性能分析失败:\", error.message);\r\n }\r\n },\r\n };\r\n}\r\n\r\n// 默认导出\r\nexport default vitePluginAIPerfAnalyzer;\r\n","/**\r\n * 性能分析器\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport { gzipSync } from \"zlib\";\r\nimport { ChatOpenAI } from \"@langchain/openai\";\r\nimport { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\r\nimport type {\r\n AnalysisResult,\r\n BundleInfo,\r\n PerformanceIssue,\r\n AnalyzerOptions,\r\n} from \"./types\";\r\nimport { DependencyAnalyzer } from \"./dependency-analyzer\";\r\nimport { HistoryAnalyzer } from \"./history-analyzer\";\r\nimport { OptimizationExamples } from \"./optimization-examples\";\r\n\r\nexport class PerfAnalyzer {\r\n private llm: ChatOpenAI | null = null;\r\n private options: AnalyzerOptions;\r\n private depAnalyzer: DependencyAnalyzer;\r\n private historyAnalyzer: HistoryAnalyzer;\r\n private examplesGenerator: OptimizationExamples;\r\n\r\n constructor(options: AnalyzerOptions) {\r\n this.options = options;\r\n this.depAnalyzer = new DependencyAnalyzer();\r\n this.historyAnalyzer = new HistoryAnalyzer();\r\n this.examplesGenerator = new OptimizationExamples();\r\n\r\n if (options.apiKey) {\r\n this.llm = new ChatOpenAI({\r\n openAIApiKey: options.apiKey,\r\n configuration: { baseURL: options.apiUrl },\r\n modelName: options.model,\r\n temperature: options.temperature ?? 0.2,\r\n maxTokens: options.maxTokens ?? 4000,\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * 分析构建产物\r\n */\r\n async analyze(): Promise<AnalysisResult> {\r\n const distDir = path.resolve(process.cwd(), \"dist\");\r\n\r\n if (!fs.existsSync(distDir)) {\r\n throw new Error(\"构建目录不存在,请先执行构建\");\r\n }\r\n\r\n // 收集文件信息\r\n const bundles = this.collectBundles(distDir);\r\n\r\n // 计算统计信息\r\n const summary = this.calculateSummary(bundles);\r\n\r\n // 依赖分析\r\n console.log(\"📦 正在分析依赖...\");\r\n const dependencies = this.depAnalyzer.analyzeDependencies(bundles);\r\n\r\n // 历史对比\r\n console.log(\"📊 正在对比历史记录...\");\r\n const comparison = this.historyAnalyzer.compare(\r\n bundles,\r\n summary.totalSize,\r\n summary.fileCount,\r\n );\r\n\r\n // 检测性能问题\r\n const issues = this.detectIssues(bundles, summary, dependencies);\r\n\r\n // 生成基础建议\r\n const suggestions = this.generateSuggestions(issues);\r\n\r\n // 生成优化示例\r\n console.log(\"💡 正在生成优化示例...\");\r\n const optimizationExamples = this.examplesGenerator.generate(\r\n issues,\r\n dependencies.duplicates,\r\n );\r\n\r\n // AI 分析(如果配置了 API Key)\r\n let aiAnalysis: string | undefined;\r\n if (this.llm) {\r\n console.log(\"🤖 正在使用 AI 分析性能...\\n\");\r\n aiAnalysis = await this.performAIAnalysis(\r\n bundles,\r\n summary,\r\n issues,\r\n dependencies,\r\n comparison,\r\n );\r\n }\r\n\r\n return {\r\n timestamp: new Date().toLocaleString(\"zh-CN\"),\r\n summary,\r\n dependencies,\r\n comparison,\r\n issues,\r\n suggestions,\r\n optimizationExamples,\r\n aiAnalysis,\r\n };\r\n }\r\n\r\n /**\r\n * 收集构建产物信息\r\n */\r\n private collectBundles(dir: string, baseDir: string = dir): BundleInfo[] {\r\n const bundles: BundleInfo[] = [];\r\n const files = fs.readdirSync(dir);\r\n\r\n for (const file of files) {\r\n const filePath = path.join(dir, file);\r\n const stat = fs.statSync(filePath);\r\n\r\n if (stat.isDirectory()) {\r\n bundles.push(...this.collectBundles(filePath, baseDir));\r\n } else {\r\n const content = fs.readFileSync(filePath);\r\n const gzipSize = gzipSync(content).length;\r\n const relativePath = path.relative(baseDir, filePath);\r\n\r\n bundles.push({\r\n name: file,\r\n size: stat.size,\r\n gzipSize,\r\n type: this.getFileType(file),\r\n path: relativePath,\r\n });\r\n }\r\n }\r\n\r\n return bundles;\r\n }\r\n\r\n /**\r\n * 获取文件类型\r\n */\r\n private getFileType(filename: string): string {\r\n const ext = path.extname(filename).toLowerCase();\r\n const typeMap: Record<string, string> = {\r\n \".js\": \"javascript\",\r\n \".mjs\": \"javascript\",\r\n \".css\": \"stylesheet\",\r\n \".html\": \"html\",\r\n \".png\": \"image\",\r\n \".jpg\": \"image\",\r\n \".jpeg\": \"image\",\r\n \".gif\": \"image\",\r\n \".svg\": \"image\",\r\n \".webp\": \"image\",\r\n \".woff\": \"font\",\r\n \".woff2\": \"font\",\r\n \".ttf\": \"font\",\r\n \".eot\": \"font\",\r\n \".json\": \"data\",\r\n \".map\": \"sourcemap\",\r\n };\r\n return typeMap[ext] || \"other\";\r\n }\r\n\r\n /**\r\n * 计算统计信息\r\n */\r\n private calculateSummary(bundles: BundleInfo[]) {\r\n const totalSize = bundles.reduce((sum, b) => sum + b.size, 0);\r\n const totalGzipSize = bundles.reduce(\r\n (sum, b) => sum + (b.gzipSize || 0),\r\n 0,\r\n );\r\n\r\n // 按大小排序,取前 10\r\n const largestFiles = [...bundles]\r\n .sort((a, b) => b.size - a.size)\r\n .slice(0, 10);\r\n\r\n // 按类型分组\r\n const byType: Record<string, { count: number; size: number }> = {};\r\n bundles.forEach((bundle) => {\r\n if (!byType[bundle.type]) {\r\n byType[bundle.type] = { count: 0, size: 0 };\r\n }\r\n byType[bundle.type].count++;\r\n byType[bundle.type].size += bundle.size;\r\n });\r\n\r\n return {\r\n totalSize,\r\n totalGzipSize,\r\n fileCount: bundles.length,\r\n largestFiles,\r\n byType,\r\n };\r\n }\r\n\r\n /**\r\n * 检测性能问题\r\n */\r\n private detectIssues(\r\n bundles: BundleInfo[],\r\n summary: any,\r\n dependencies?: {\r\n total: number;\r\n duplicates: DependencyInfo[];\r\n largest: DependencyInfo[];\r\n },\r\n ): PerformanceIssue[] {\r\n const issues: PerformanceIssue[] = [];\r\n const { threshold } = this.options;\r\n\r\n // 检查总大小\r\n const totalSizeMB = summary.totalSize / 1024 / 1024;\r\n if (threshold.totalSize && totalSizeMB > threshold.totalSize) {\r\n issues.push({\r\n type: \"size\",\r\n severity: \"high\",\r\n title: \"构建产物总大小过大\",\r\n description: `总大小 ${totalSizeMB.toFixed(2)}MB 超过阈值 ${\r\n threshold.totalSize\r\n }MB`,\r\n suggestion: \"考虑代码分割、tree-shaking、压缩等优化手段\",\r\n });\r\n }\r\n\r\n // 检查单个文件大小\r\n const largeBundles = bundles.filter(\r\n (b) => b.size / 1024 > (threshold.bundleSize || 500),\r\n );\r\n if (largeBundles.length > 0) {\r\n issues.push({\r\n type: \"size\",\r\n severity: \"medium\",\r\n title: \"存在过大的单个文件\",\r\n description: `发现 ${largeBundles.length} 个文件超过 ${threshold.bundleSize}KB`,\r\n files: largeBundles.map(\r\n (b) => `${b.name} (${(b.size / 1024).toFixed(2)}KB)`,\r\n ),\r\n suggestion: \"考虑拆分大文件,使用动态导入或代码分割\",\r\n });\r\n }\r\n\r\n // 检查 chunk 数量\r\n const jsFiles = bundles.filter((b) => b.type === \"javascript\");\r\n if (threshold.chunkCount && jsFiles.length > threshold.chunkCount) {\r\n issues.push({\r\n type: \"count\",\r\n severity: \"low\",\r\n title: \"JavaScript 文件数量过多\",\r\n description: `共有 ${jsFiles.length} 个 JS 文件,超过阈值 ${threshold.chunkCount}`,\r\n suggestion: \"过多的文件会增加 HTTP 请求数,考虑合并小文件\",\r\n });\r\n }\r\n\r\n // 检查未压缩的图片\r\n const images = bundles.filter((b) => b.type === \"image\");\r\n const largeImages = images.filter((img) => img.size > 100 * 1024); // 100KB\r\n if (largeImages.length > 0) {\r\n issues.push({\r\n type: \"optimization\",\r\n severity: \"medium\",\r\n title: \"存在未优化的图片\",\r\n description: `发现 ${largeImages.length} 个大于 100KB 的图片`,\r\n files: largeImages.map(\r\n (img) => `${img.name} (${(img.size / 1024).toFixed(2)}KB)`,\r\n ),\r\n suggestion: \"使用图片压缩工具,或转换为 WebP 格式\",\r\n });\r\n }\r\n\r\n // 检查 sourcemap\r\n const sourcemaps = bundles.filter((b) => b.type === \"sourcemap\");\r\n if (sourcemaps.length > 0) {\r\n const totalMapSize = sourcemaps.reduce((sum, m) => sum + m.size, 0);\r\n issues.push({\r\n type: \"optimization\",\r\n severity: \"low\",\r\n title: \"生产环境包含 sourcemap\",\r\n description: `Sourcemap 文件占用 ${(totalMapSize / 1024 / 1024).toFixed(\r\n 2,\r\n )}MB`,\r\n suggestion: \"生产环境建议禁用 sourcemap 或使用外部 sourcemap\",\r\n });\r\n }\r\n\r\n // 检查重复依赖\r\n if (dependencies && dependencies.duplicates.length > 0) {\r\n const topDuplicates = dependencies.duplicates.slice(0, 3);\r\n issues.push({\r\n type: \"dependency\",\r\n severity: \"medium\",\r\n title: \"检测到重复打包的依赖\",\r\n description: `发现 ${dependencies.duplicates.length} 个依赖被多次打包`,\r\n files: topDuplicates.map(\r\n (d) =>\r\n `${d.name} (被 ${d.usedBy.length} 个文件使用, ${(\r\n d.size / 1024\r\n ).toFixed(2)}KB)`,\r\n ),\r\n suggestion: \"将重复依赖提取到公共 chunk 中\",\r\n });\r\n }\r\n\r\n return issues;\r\n }\r\n\r\n /**\r\n * 生成基础建议\r\n */\r\n private generateSuggestions(issues: PerformanceIssue[]): string[] {\r\n const suggestions: string[] = [];\r\n\r\n if (issues.some((i) => i.type === \"size\")) {\r\n suggestions.push(\"启用 gzip/brotli 压缩\");\r\n suggestions.push(\"配置 Vite 的 build.rollupOptions 进行代码分割\");\r\n suggestions.push(\"使用 vite-plugin-compression 插件\");\r\n }\r\n\r\n if (issues.some((i) => i.type === \"optimization\")) {\r\n suggestions.push(\"使用 vite-plugin-imagemin 优化图片\");\r\n suggestions.push(\"配置 CSS 压缩和 tree-shaking\");\r\n }\r\n\r\n if (issues.length === 0) {\r\n suggestions.push(\"构建产物已经很优秀,继续保持!\");\r\n }\r\n\r\n return suggestions;\r\n }\r\n\r\n /**\r\n * AI 性能分析\r\n */\r\n private async performAIAnalysis(\r\n bundles: BundleInfo[],\r\n summary: any,\r\n issues: PerformanceIssue[],\r\n dependencies?: {\r\n total: number;\r\n duplicates: DependencyInfo[];\r\n largest: DependencyInfo[];\r\n },\r\n comparison?: any,\r\n ): Promise<string> {\r\n const systemPrompt = new SystemMessage(\r\n \"你是一个专业的前端性能优化专家,精通 Vite、Webpack 等构建工具。请分析构建产物并提供专业的优化建议。\",\r\n );\r\n\r\n const bundlesSummary = summary.largestFiles\r\n .slice(0, 5)\r\n .map((b: BundleInfo) => `- ${b.name}: ${(b.size / 1024).toFixed(2)}KB`)\r\n .join(\"\\n\");\r\n\r\n const typesSummary = Object.entries(summary.byType)\r\n .map(\r\n ([type, info]: [string, any]) =>\r\n `- ${type}: ${info.count} 个文件, ${(info.size / 1024).toFixed(2)}KB`,\r\n )\r\n .join(\"\\n\");\r\n\r\n const issuesSummary = issues\r\n .map(\r\n (issue) => `- [${issue.severity}] ${issue.title}: ${issue.description}`,\r\n )\r\n .join(\"\\n\");\r\n\r\n // 依赖分析摘要\r\n let dependencySummary = \"\";\r\n if (dependencies) {\r\n dependencySummary = `\r\n## 依赖分析\r\n- 总依赖数: ${dependencies.total}\r\n- 重复依赖: ${dependencies.duplicates.length} 个\r\n- 最大依赖: ${dependencies.largest\r\n .slice(0, 3)\r\n .map((d) => `${d.name} (${(d.size / 1024).toFixed(2)}KB)`)\r\n .join(\", \")}`;\r\n }\r\n\r\n // 历史对比摘要\r\n let comparisonSummary = \"\";\r\n if (comparison) {\r\n const sizeChange =\r\n comparison.totalSize.trend === \"increased\" ? \"增加\" : \"减少\";\r\n comparisonSummary = `\r\n## 历史对比\r\n- 总大小${sizeChange}: ${Math.abs(comparison.totalSize.diffPercent).toFixed(2)}%\r\n- 文件数量变化: ${comparison.fileCount.diff > 0 ? \"+\" : \"\"}${\r\n comparison.fileCount.diff\r\n }\r\n- 新增文件: ${comparison.newFiles.length} 个\r\n- 删除文件: ${comparison.removedFiles.length} 个`;\r\n }\r\n\r\n const userPrompt = new HumanMessage(`\r\n请分析以下构建产物信息:\r\n\r\n## 总体统计\r\n- 总大小: ${(summary.totalSize / 1024 / 1024).toFixed(2)}MB\r\n- Gzip 后: ${(summary.totalGzipSize / 1024 / 1024).toFixed(2)}MB\r\n- 文件数量: ${summary.fileCount}\r\n\r\n## 最大的文件\r\n${bundlesSummary}\r\n\r\n## 按类型分组\r\n${typesSummary}\r\n${dependencySummary}\r\n${comparisonSummary}\r\n\r\n## 检测到的问题\r\n${issuesSummary || \"无明显问题\"}\r\n\r\n请提供:\r\n1. 性能评估(3-5 句话)\r\n2. 具体优化建议(3-5 条,每条简洁明了)\r\n3. 优先级排序\r\n\r\n请用简洁专业的语言回答,不要过于冗长。\r\n`);\r\n\r\n const response = await this.llm!.invoke([systemPrompt, userPrompt]);\r\n return response.content.toString();\r\n }\r\n}\r\n","/**\r\n * 依赖分析器\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport type { BundleInfo, DependencyInfo } from \"./types\";\r\n\r\nexport class DependencyAnalyzer {\r\n /**\r\n * 分析依赖\r\n */\r\n analyzeDependencies(bundles: BundleInfo[]): {\r\n total: number;\r\n duplicates: DependencyInfo[];\r\n largest: DependencyInfo[];\r\n } {\r\n const jsFiles = bundles.filter((b) => b.type === \"javascript\");\r\n const dependencies = this.extractDependencies(jsFiles);\r\n\r\n // 检测重复依赖\r\n const duplicates = this.findDuplicates(dependencies);\r\n\r\n // 找出最大的依赖\r\n const largest = [...dependencies]\r\n .sort((a, b) => b.size - a.size)\r\n .slice(0, 10);\r\n\r\n return {\r\n total: dependencies.length,\r\n duplicates,\r\n largest,\r\n };\r\n }\r\n\r\n /**\r\n * 从 JS 文件中提取依赖信息\r\n */\r\n private extractDependencies(jsFiles: BundleInfo[]): DependencyInfo[] {\r\n const depMap = new Map<string, DependencyInfo>();\r\n\r\n jsFiles.forEach((file) => {\r\n // 尝试解析文件内容,提取依赖\r\n const imports = this.parseImports(file);\r\n\r\n imports.forEach((depName) => {\r\n if (!depMap.has(depName)) {\r\n depMap.set(depName, {\r\n name: depName,\r\n size: 0,\r\n usedBy: [],\r\n isDuplicate: false,\r\n });\r\n }\r\n\r\n const dep = depMap.get(depName)!;\r\n dep.usedBy.push(file.name);\r\n // 估算依赖大小(简化处理)\r\n dep.size += file.size / imports.length;\r\n });\r\n });\r\n\r\n return Array.from(depMap.values());\r\n }\r\n\r\n /**\r\n * 解析文件中的 import 语句\r\n */\r\n private parseImports(file: BundleInfo): string[] {\r\n const imports: string[] = [];\r\n\r\n try {\r\n const distDir = path.resolve(process.cwd(), \"dist\");\r\n const filePath = path.join(distDir, file.path);\r\n\r\n if (!fs.existsSync(filePath)) {\r\n return imports;\r\n }\r\n\r\n const content = fs.readFileSync(filePath, \"utf-8\");\r\n\r\n // 匹配常见的依赖模式\r\n const patterns = [\r\n // node_modules 依赖\r\n /from\\s+[\"']([^\"']+)[\"']/g,\r\n /require\\([\"']([^\"']+)[\"']\\)/g,\r\n // Vite 特殊标记\r\n /__vite_ssr_import__\\(\"([^\"]+)\"\\)/g,\r\n ];\r\n\r\n patterns.forEach((pattern) => {\r\n let match;\r\n while ((match = pattern.exec(content)) !== null) {\r\n const depName = match[1];\r\n // 只保留 node_modules 依赖\r\n if (!depName.startsWith(\".\") && !depName.startsWith(\"/\")) {\r\n // 提取包名(去掉子路径)\r\n const pkgName = depName\r\n .split(\"/\")\r\n .slice(0, depName.startsWith(\"@\") ? 2 : 1)\r\n .join(\"/\");\r\n if (!imports.includes(pkgName)) {\r\n imports.push(pkgName);\r\n }\r\n }\r\n }\r\n });\r\n } catch (error) {\r\n // 忽略解析错误\r\n }\r\n\r\n return imports;\r\n }\r\n\r\n /**\r\n * 查找重复打包的依赖\r\n */\r\n private findDuplicates(dependencies: DependencyInfo[]): DependencyInfo[] {\r\n return dependencies\r\n .filter((dep) => dep.usedBy.length > 1)\r\n .map((dep) => ({\r\n ...dep,\r\n isDuplicate: true,\r\n }))\r\n .sort((a, b) => b.usedBy.length - a.usedBy.length)\r\n .slice(0, 10);\r\n }\r\n}\r\n","/**\r\n * 历史对比分析器\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport type {\r\n BundleInfo,\r\n HistoryComparison,\r\n HistoryRecord,\r\n ComparisonResult,\r\n} from \"./types\";\r\n\r\nexport class HistoryAnalyzer {\r\n private historyFile: string;\r\n\r\n constructor() {\r\n const reportsDir = path.resolve(process.cwd(), \"ai-reports\");\r\n this.historyFile = path.join(reportsDir, \".perf-history.json\");\r\n }\r\n\r\n /**\r\n * 对比当前构建与历史记录\r\n */\r\n compare(\r\n currentBundles: BundleInfo[],\r\n currentTotalSize: number,\r\n currentFileCount: number\r\n ): HistoryComparison | undefined {\r\n const history = this.loadHistory();\r\n\r\n if (history.length === 0) {\r\n // 没有历史记录,保存当前记录\r\n this.saveHistory(currentBundles, currentTotalSize, currentFileCount);\r\n return undefined;\r\n }\r\n\r\n // 获取上一次记录\r\n const previous = history[history.length - 1];\r\n\r\n // 对比总大小\r\n const totalSize = this.compareValue(currentTotalSize, previous.totalSize);\r\n\r\n // 对比文件数量\r\n const fileCount = this.compareValue(currentFileCount, previous.fileCount);\r\n\r\n // 对比最大文件变化\r\n const largestFileChanges = this.compareLargestFiles(\r\n currentBundles,\r\n previous.files\r\n );\r\n\r\n // 检测新增和删除的文件\r\n const currentFileNames = new Set(currentBundles.map((b) => b.name));\r\n const previousFileNames = new Set(previous.files.map((f) => f.name));\r\n\r\n const newFiles = currentBundles\r\n .filter((b) => !previousFileNames.has(b.name))\r\n .map((b) => b.name);\r\n\r\n const removedFiles = previous.files\r\n .filter((f) => !currentFileNames.has(f.name))\r\n .map((f) => f.name);\r\n\r\n // 保存当前记录\r\n this.saveHistory(currentBundles, currentTotalSize, currentFileCount);\r\n\r\n return {\r\n totalSize,\r\n fileCount,\r\n largestFileChanges,\r\n newFiles,\r\n removedFiles,\r\n };\r\n }\r\n\r\n /**\r\n * 对比数值\r\n */\r\n private compareValue(current: number, previous: number): ComparisonResult {\r\n const diff = current - previous;\r\n const diffPercent = previous === 0 ? 0 : (diff / previous) * 100;\r\n\r\n let trend: \"increased\" | \"decreased\" | \"unchanged\";\r\n if (Math.abs(diffPercent) < 1) {\r\n trend = \"unchanged\";\r\n } else if (diff > 0) {\r\n trend = \"increased\";\r\n } else {\r\n trend = \"decreased\";\r\n }\r\n\r\n return {\r\n current,\r\n previous,\r\n diff,\r\n diffPercent,\r\n trend,\r\n };\r\n }\r\n\r\n /**\r\n * 对比最大文件的变化\r\n */\r\n private compareLargestFiles(\r\n currentBundles: BundleInfo[],\r\n previousFiles: Array<{ name: string; size: number }>\r\n ): Array<{\r\n name: string;\r\n current: number;\r\n previous: number;\r\n diff: number;\r\n }> {\r\n const changes: Array<{\r\n name: string;\r\n current: number;\r\n previous: number;\r\n diff: number;\r\n }> = [];\r\n\r\n // 创建文件映射\r\n const previousMap = new Map(previousFiles.map((f) => [f.name, f.size]));\r\n\r\n currentBundles.forEach((bundle) => {\r\n const previousSize = previousMap.get(bundle.name);\r\n if (previousSize !== undefined) {\r\n const diff = bundle.size - previousSize;\r\n // 只记录变化超过 10KB 的文件\r\n if (Math.abs(diff) > 10 * 1024) {\r\n changes.push({\r\n name: bundle.name,\r\n current: bundle.size,\r\n previous: previousSize,\r\n diff,\r\n });\r\n }\r\n }\r\n });\r\n\r\n // 按变化幅度排序\r\n return changes\r\n .sort((a, b) => Math.abs(b.diff) - Math.abs(a.diff))\r\n .slice(0, 10);\r\n }\r\n\r\n /**\r\n * 加载历史记录\r\n */\r\n private loadHistory(): HistoryRecord[] {\r\n if (!fs.existsSync(this.historyFile)) {\r\n return [];\r\n }\r\n\r\n try {\r\n const content = fs.readFileSync(this.historyFile, \"utf-8\");\r\n const history = JSON.parse(content);\r\n // 只保留最近 10 次记录\r\n return history.slice(-10);\r\n } catch (error) {\r\n console.warn(\"⚠️ 无法读取历史记录\");\r\n return [];\r\n }\r\n }\r\n\r\n /**\r\n * 保存历史记录\r\n */\r\n private saveHistory(\r\n bundles: BundleInfo[],\r\n totalSize: number,\r\n fileCount: number\r\n ): void {\r\n const history = this.loadHistory();\r\n\r\n const record: HistoryRecord = {\r\n timestamp: new Date().toISOString(),\r\n totalSize,\r\n fileCount,\r\n files: bundles.map((b) => ({\r\n name: b.name,\r\n size: b.size,\r\n })),\r\n };\r\n\r\n history.push(record);\r\n\r\n // 确保目录存在\r\n const dir = path.dirname(this.historyFile);\r\n if (!fs.existsSync(dir)) {\r\n fs.mkdirSync(dir, { recursive: true });\r\n }\r\n\r\n // 只保留最近 10 次记录\r\n const recentHistory = history.slice(-10);\r\n\r\n fs.writeFileSync(\r\n this.historyFile,\r\n JSON.stringify(recentHistory, null, 2),\r\n \"utf-8\"\r\n );\r\n }\r\n\r\n /**\r\n * 清除历史记录\r\n */\r\n clearHistory(): void {\r\n if (fs.existsSync(this.historyFile)) {\r\n fs.unlinkSync(this.historyFile);\r\n }\r\n }\r\n}\r\n","/**\r\n * 优化示例生成器\r\n */\r\n\r\nimport type {\r\n OptimizationExample,\r\n PerformanceIssue,\r\n DependencyInfo,\r\n} from \"./types\";\r\n\r\nexport class OptimizationExamples {\r\n /**\r\n * 根据问题生成优化示例\r\n */\r\n generate(\r\n issues: PerformanceIssue[],\r\n duplicateDeps?: DependencyInfo[]\r\n ): OptimizationExample[] {\r\n const examples: OptimizationExample[] = [];\r\n\r\n // 代码分割示例\r\n if (issues.some((i) => i.type === \"size\")) {\r\n examples.push(this.getCodeSplittingExample());\r\n }\r\n\r\n // 重复依赖优化\r\n if (duplicateDeps && duplicateDeps.length > 0) {\r\n examples.push(this.getDependencyOptimizationExample(duplicateDeps));\r\n }\r\n\r\n // 压缩优化\r\n if (issues.some((i) => i.type === \"optimization\")) {\r\n examples.push(this.getCompressionExample());\r\n }\r\n\r\n // 图片优化\r\n if (issues.some((i) => i.description.includes(\"图片\"))) {\r\n examples.push(this.getImageOptimizationExample());\r\n }\r\n\r\n // 动态导入\r\n if (issues.some((i) => i.type === \"size\" && i.severity === \"high\")) {\r\n examples.push(this.getDynamicImportExample());\r\n }\r\n\r\n // Tree-shaking 优化\r\n examples.push(this.getTreeShakingExample());\r\n\r\n return examples;\r\n }\r\n\r\n /**\r\n * 代码分割示例\r\n */\r\n private getCodeSplittingExample(): OptimizationExample {\r\n return {\r\n title: \"配置代码分割\",\r\n description: \"将大文件拆分成多个小文件,提高加载性能\",\r\n priority: \"high\",\r\n impact: \"可减少 30-50% 的初始加载大小\",\r\n difficulty: \"简单\",\r\n code: {\r\n language: \"typescript\",\r\n content: `// vite.config.ts\r\nexport default defineConfig({\r\n build: {\r\n rollupOptions: {\r\n output: {\r\n manualChunks: {\r\n // 提取 Vue 核心库\r\n 'vue-vendor': ['vue', 'vue-router', 'pinia'],\r\n \r\n // 提取 UI 组件库\r\n 'ui-vendor': ['element-plus', '@element-plus/icons-vue'],\r\n \r\n // 提取工具库\r\n 'utils': ['lodash-es', 'dayjs', 'axios'],\r\n \r\n // 提取图表库(如果使用)\r\n 'charts': ['echarts', 'chart.js'],\r\n },\r\n \r\n // 自动分割大于 500KB 的文件\r\n chunkFileNames: (chunkInfo) => {\r\n const facadeModuleId = chunkInfo.facadeModuleId \r\n ? chunkInfo.facadeModuleId.split('/').pop() \r\n : 'chunk';\r\n return \\`js/\\${facadeModuleId}-[hash].js\\`;\r\n },\r\n },\r\n },\r\n \r\n // 设置 chunk 大小警告阈值\r\n chunkSizeWarningLimit: 500,\r\n },\r\n});`,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * 重复依赖优化示例\r\n */\r\n private getDependencyOptimizationExample(\r\n duplicates: DependencyInfo[]\r\n ): OptimizationExample {\r\n const topDuplicates = duplicates.slice(0, 3).map((d) => d.name);\r\n\r\n return {\r\n title: \"优化重复依赖\",\r\n description: `检测到 ${duplicates.length} 个重复打包的依赖,建议提取到公共 chunk`,\r\n priority: \"high\",\r\n impact: `可减少 ${(\r\n duplicates.reduce((sum, d) => sum + d.size, 0) /\r\n 1024 /\r\n 1024\r\n ).toFixed(2)}MB`,\r\n difficulty: \"简单\",\r\n code: {\r\n language: \"typescript\",\r\n content: `// vite.config.ts\r\nexport default defineConfig({\r\n build: {\r\n rollupOptions: {\r\n output: {\r\n manualChunks: {\r\n // 提取重复依赖到公共 chunk\r\n 'common-vendor': [\r\n ${topDuplicates.map((name) => `'${name}'`).join(\",\\n \")}\r\n ],\r\n },\r\n },\r\n },\r\n },\r\n \r\n // 或者使用自动分割策略\r\n optimizeDeps: {\r\n include: [\r\n ${topDuplicates.map((name) => `'${name}'`).join(\",\\n \")}\r\n ],\r\n },\r\n});`,\r\n },\r\n relatedFiles: duplicates.flatMap((d) => d.usedBy).slice(0, 5),\r\n };\r\n }\r\n\r\n /**\r\n * 压缩优化示例\r\n */\r\n private getCompressionExample(): OptimizationExample {\r\n return {\r\n title: \"启用 Gzip/Brotli 压缩\",\r\n description: \"使用压缩插件减少传输大小\",\r\n priority: \"medium\",\r\n impact: \"可减少 60-70% 的传输大小\",\r\n difficulty: \"简单\",\r\n code: {\r\n language: \"bash\",\r\n content: `# 1. 安装压缩插件\r\nnpm install vite-plugin-compression -D\r\n\r\n# 2. 配置插件\r\n# vite.config.ts\r\nimport viteCompression from 'vite-plugin-compression';\r\n\r\nexport default defineConfig({\r\n plugins: [\r\n // Gzip 压缩\r\n viteCompression({\r\n algorithm: 'gzip',\r\n ext: '.gz',\r\n threshold: 10240, // 大于 10KB 才压缩\r\n deleteOriginFile: false,\r\n }),\r\n \r\n // Brotli 压缩(可选,压缩率更高)\r\n viteCompression({\r\n algorithm: 'brotliCompress',\r\n ext: '.br',\r\n threshold: 10240,\r\n deleteOriginFile: false,\r\n }),\r\n ],\r\n});\r\n\r\n# 3. Nginx 配置(服务器端)\r\n# nginx.conf\r\ngzip on;\r\ngzip_types text/plain text/css application/json application/javascript;\r\ngzip_min_length 1024;`,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * 图片优化示例\r\n */\r\n private getImageOptimizationExample(): OptimizationExample {\r\n return {\r\n title: \"优化图片资源\",\r\n description: \"压缩图片并转换为现代格式\",\r\n priority: \"medium\",\r\n impact: \"可减少 50-80% 的图片大小\",\r\n difficulty: \"简单\",\r\n code: {\r\n language: \"bash\",\r\n content: `# 1. 安装图片优化插件\r\nnpm install vite-plugin-imagemin -D\r\n\r\n# 2. 配置插件\r\n# vite.config.ts\r\nimport viteImagemin from 'vite-plugin-imagemin';\r\n\r\nexport default defineConfig({\r\n plugins: [\r\n viteImagemin({\r\n // GIF 优化\r\n gifsicle: {\r\n optimizationLevel: 7,\r\n interlaced: false,\r\n },\r\n \r\n // PNG 优化\r\n optipng: {\r\n optimizationLevel: 7,\r\n },\r\n \r\n // JPEG 优化\r\n mozjpeg: {\r\n quality: 80,\r\n },\r\n \r\n // SVG 优化\r\n svgo: {\r\n plugins: [\r\n { name: 'removeViewBox', active: false },\r\n { name: 'removeEmptyAttrs', active: true },\r\n ],\r\n },\r\n \r\n // WebP 转换\r\n webp: {\r\n quality: 80,\r\n },\r\n }),\r\n ],\r\n});\r\n\r\n# 3. 使用 WebP 格式(HTML)\r\n<picture>\r\n <source srcset=\"image.webp\" type=\"image/webp\">\r\n <img src=\"image.jpg\" alt=\"description\">\r\n</picture>`,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * 动态导入示例\r\n */\r\n private getDynamicImportExample(): OptimizationExample {\r\n return {\r\n title: \"使用动态导入(懒加载)\",\r\n description: \"按需加载组件和路由,减少初始加载大小\",\r\n priority: \"high\",\r\n impact: \"可减少 40-60% 的初始加载大小\",\r\n difficulty: \"中等\",\r\n code: {\r\n language: \"typescript\",\r\n content: `// 1. 路由懒加载\r\n// router/index.ts\r\nconst routes = [\r\n {\r\n path: '/dashboard',\r\n name: 'Dashboard',\r\n // 使用动态导入\r\n component: () => import('@/views/Dashboard.vue'),\r\n },\r\n {\r\n path: '/settings',\r\n name: 'Settings',\r\n component: () => import('@/views/Settings.vue'),\r\n },\r\n];\r\n\r\n// 2. 组件懒加载\r\n// App.vue\r\n<script setup>\r\nimport { defineAsyncComponent } from 'vue';\r\n\r\n// 异步组件\r\nconst HeavyComponent = defineAsyncComponent(() =>\r\n import('./components/HeavyComponent.vue')\r\n);\r\n\r\n// 带加载状态的异步组件\r\nconst AsyncComponent = defineAsyncComponent({\r\n loader: () => import('./components/AsyncComponent.vue'),\r\n loadingComponent: LoadingSpinner,\r\n errorComponent: ErrorComponent,\r\n delay: 200,\r\n timeout: 3000,\r\n});\r\n</script>\r\n\r\n// 3. 条件加载\r\n<script setup>\r\nconst loadCharts = async () => {\r\n if (needCharts) {\r\n const { default: ECharts } = await import('echarts');\r\n // 使用 ECharts\r\n }\r\n};\r\n</script>`,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * Tree-shaking 优化示例\r\n */\r\n private getTreeShakingExample(): OptimizationExample {\r\n return {\r\n title: \"优化 Tree-shaking\",\r\n description: \"确保未使用的代码被正确移除\",\r\n priority: \"medium\",\r\n impact: \"可减少 10-30% 的代码大小\",\r\n difficulty: \"中等\",\r\n code: {\r\n language: \"typescript\",\r\n content: `// 1. 使用 ES6 模块导入(支持 tree-shaking)\r\n// ❌ 不推荐\r\nimport _ from 'lodash';\r\nconst result = _.debounce(fn, 300);\r\n\r\n// ✅ 推荐\r\nimport { debounce } from 'lodash-es';\r\nconst result = debounce(fn, 300);\r\n\r\n// 2. 配置 package.json\r\n{\r\n \"sideEffects\": false, // 标记为无副作用\r\n // 或指定有副作用的文件\r\n \"sideEffects\": [\"*.css\", \"*.scss\"]\r\n}\r\n\r\n// 3. Vite 配置\r\n// vite.config.ts\r\nexport default defineConfig({\r\n build: {\r\n // 启用 tree-shaking\r\n minify: 'terser',\r\n terserOptions: {\r\n compress: {\r\n drop_console: true, // 移除 console\r\n drop_debugger: true, // 移除 debugger\r\n pure_funcs: ['console.log'], // 移除特定函数\r\n },\r\n },\r\n },\r\n});\r\n\r\n// 4. 避免副作用导入\r\n// ❌ 不推荐(会导入整个模块)\r\nimport 'some-library';\r\n\r\n// ✅ 推荐(明确导入需要的部分)\r\nimport { specificFunction } from 'some-library';`,\r\n },\r\n };\r\n }\r\n}\r\n","/**\r\n * 性能分析报告生成器\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport pc from \"picocolors\";\r\nimport type { AnalysisResult, PerformanceIssue } from \"./types\";\r\n\r\nexport interface ReporterOptions {\r\n console?: boolean;\r\n html?: boolean;\r\n json?: boolean;\r\n}\r\n\r\nexport class PerfReporter {\r\n /**\r\n * 生成报告\r\n */\r\n async generate(\r\n result: AnalysisResult,\r\n options: ReporterOptions = {},\r\n ): Promise<void> {\r\n if (options.html) {\r\n await this.generateHTMLReport(result);\r\n }\r\n\r\n if (options.json) {\r\n await this.generateJSONReport(result);\r\n }\r\n }\r\n\r\n /**\r\n * 控制台输出\r\n */\r\n printConsole(result: AnalysisResult): void {\r\n console.log(\"\\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\");\r\n console.log(\"⚡ 性能分析报告\");\r\n console.log(\"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n\");\r\n\r\n // 总体统计\r\n console.log(\"📊 总体统计:\");\r\n console.log(\r\n ` 总大小: ${(result.summary.totalSize / 1024 / 1024).toFixed(2)}MB`,\r\n );\r\n console.log(\r\n ` Gzip后: ${(result.summary.totalGzipSize / 1024 / 1024).toFixed(2)}MB`,\r\n );\r\n console.log(` 文件数: ${result.summary.fileCount}\\n`);\r\n\r\n // 历史对比\r\n if (result.comparison) {\r\n console.log(\"📈 历史对比:\");\r\n const { totalSize, fileCount, newFiles, removedFiles } =\r\n result.comparison;\r\n const sizeIcon = totalSize.trend === \"increased\" ? \"📈\" : \"📉\";\r\n console.log(\r\n ` ${sizeIcon} 总大小: ${totalSize.trend === \"increased\" ? \"+\" : \"\"}${(\r\n totalSize.diff /\r\n 1024 /\r\n 1024\r\n ).toFixed(2)}MB (${\r\n totalSize.diffPercent > 0 ? \"+\" : \"\"\r\n }${totalSize.diffPercent.toFixed(2)}%)`,\r\n );\r\n console.log(\r\n ` 📊 文件数: ${fileCount.diff > 0 ? \"+\" : \"\"}${fileCount.diff}`,\r\n );\r\n if (newFiles.length > 0) {\r\n console.log(` ✨ 新增: ${newFiles.slice(0, 3).join(\", \")}`);\r\n }\r\n if (removedFiles.length > 0) {\r\n console.log(` 🗑️ 删除: ${removedFiles.slice(0, 3).join(\", \")}`);\r\n }\r\n console.log();\r\n }\r\n\r\n // 依赖分析\r\n if (result.dependencies) {\r\n console.log(\"📦 依赖分析:\");\r\n console.log(` 总依赖数: ${result.dependencies.total}`);\r\n if (result.dependencies.duplicates.length > 0) {\r\n console.log(\r\n ` ⚠️ 重复依赖: ${result.dependencies.duplicates.length} 个`,\r\n );\r\n result.dependencies.duplicates.slice(0, 3).forEach((dep) => {\r\n console.log(\r\n ` - ${dep.name}: 被 ${dep.usedBy.length} 个文件使用`,\r\n );\r\n });\r\n }\r\n console.log();\r\n }\r\n\r\n // 最大文件\r\n console.log(\"📦 最大的文件:\");\r\n result.summary.largestFiles.slice(0, 5).forEach((file, index) => {\r\n console.log(\r\n ` ${index + 1}. ${file.name}: ${(file.size / 1024).toFixed(2)}KB`,\r\n );\r\n });\r\n console.log();\r\n\r\n // 按类型统计\r\n console.log(\"📋 按类型统计:\");\r\n Object.entries(result.summary.byType).forEach(([type, info]) => {\r\n console.log(\r\n ` ${type}: ${info.count} 个, ${(info.size / 1024).toFixed(2)}KB`,\r\n );\r\n });\r\n console.log();\r\n\r\n // 性能问题\r\n if (result.issues.length > 0) {\r\n console.log(\"⚠️ 性能问题:\");\r\n result.issues.forEach((issue, index) => {\r\n const icon = this.getSeverityIcon(issue.severity);\r\n console.log(` ${index + 1}. ${icon} ${issue.title}`);\r\n console.log(` ${issue.description}`);\r\n if (issue.suggestion) {\r\n console.log(` 💡 ${issue.suggestion}`);\r\n }\r\n });\r\n console.log();\r\n } else {\r\n console.log(\"✅ 未发现明显的性能问题\\n\");\r\n }\r\n\r\n // 优化建议\r\n if (result.suggestions.length > 0) {\r\n console.log(\"💡 优化建议:\");\r\n result.suggestions.forEach((suggestion, index) => {\r\n console.log(` ${index + 1}. ${suggestion}`);\r\n });\r\n console.log();\r\n }\r\n\r\n // 优化示例\r\n if (result.optimizationExamples && result.optimizationExamples.length > 0) {\r\n console.log(\"🔧 优化示例:\");\r\n result.optimizationExamples.slice(0, 3).forEach((example, index) => {\r\n console.log(` ${index + 1}. ${example.title} (${example.priority})`);\r\n console.log(` ${example.description}`);\r\n });\r\n console.log();\r\n }\r\n\r\n // AI 分析\r\n if (result.aiAnalysis) {\r\n console.log(\"🤖 AI 分析:\");\r\n console.log(\r\n result.aiAnalysis\r\n .split(\"\\n\")\r\n .map((line) => ` ${line}`)\r\n .join(\"\\n\"),\r\n );\r\n console.log();\r\n }\r\n\r\n console.log(\"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n\");\r\n }\r\n\r\n /**\r\n * 生成 HTML 报告\r\n */\r\n private async generateHTMLReport(result: AnalysisResult): Promise<void> {\r\n const html = `\r\n<!DOCTYPE html>\r\n<html lang=\"zh-CN\">\r\n<head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <title>性能分析报告</title>\r\n <style>\r\n * { margin: 0; padding: 0; box-sizing: border-box; }\r\n body {\r\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\r\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\r\n padding: 20px;\r\n min-height: 100vh;\r\n }\r\n .container {\r\n max-width: 1200px;\r\n margin: 0 auto;\r\n background: white;\r\n border-radius: 12px;\r\n box-shadow: 0 8px 32px rgba(0,0,0,0.2);\r\n overflow: hidden;\r\n }\r\n .header {\r\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\r\n color: white;\r\n padding: 30px;\r\n text-align: center;\r\n }\r\n .header h1 {\r\n font-size: 32px;\r\n margin-bottom: 10px;\r\n }\r\n .header .time {\r\n font-size: 14px;\r\n opacity: 0.9;\r\n }\r\n .content {\r\n padding: 30px;\r\n }\r\n .section {\r\n margin-bottom: 30px;\r\n }\r\n .section-title {\r\n font-size: 20px;\r\n font-weight: bold;\r\n color: #333;\r\n margin-bottom: 15px;\r\n padding-bottom: 10px;\r\n border-bottom: 2px solid #667eea;\r\n }\r\n .stats-grid {\r\n display: grid;\r\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\r\n gap: 20px;\r\n margin-bottom: 20px;\r\n }\r\n .stat-card {\r\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\r\n color: white;\r\n padding: 20px;\r\n border-radius: 12px;\r\n text-align: center;\r\n }\r\n .stat-value {\r\n font-size: 32px;\r\n font-weight: bold;\r\n margin: 10px 0;\r\n }\r\n .stat-label {\r\n font-size: 14px;\r\n opacity: 0.9;\r\n }\r\n .file-list {\r\n background: #f7fafc;\r\n border-radius: 8px;\r\n padding: 15px;\r\n }\r\n .file-item {\r\n display: flex;\r\n justify-content: space-between;\r\n padding: 10px;\r\n border-bottom: 1px solid #e2e8f0;\r\n }\r\n .file-item:last-child {\r\n border-bottom: none;\r\n }\r\n .file-name {\r\n font-family: monospace;\r\n color: #333;\r\n }\r\n .file-size {\r\n color: #667eea;\r\n font-weight: bold;\r\n }\r\n .issue {\r\n background: #fff5f5;\r\n border-left: 4px solid #f56565;\r\n padding: 15px;\r\n border-radius: 8px;\r\n margin-bottom: 15px;\r\n }\r\n .issue.medium {\r\n background: #fffaf0;\r\n border-left-color: #ed8936;\r\n }\r\n .issue.low {\r\n background: #f0f9ff;\r\n border-left-color: #4299e1;\r\n }\r\n .issue-title {\r\n font-weight: bold;\r\n color: #333;\r\n margin-bottom: 8px;\r\n }\r\n .issue-desc {\r\n color: #666;\r\n margin-bottom: 8px;\r\n }\r\n .issue-suggestion {\r\n background: #e6fffa;\r\n padding: 10px;\r\n border-radius: 6px;\r\n margin-top: 10px;\r\n color: #234e52;\r\n }\r\n .suggestion-list {\r\n background: #f0fdf4;\r\n border-radius: 8px;\r\n padding: 15px;\r\n }\r\n .suggestion-item {\r\n padding: 10px;\r\n border-bottom: 1px solid #d1fae5;\r\n color: #065f46;\r\n }\r\n .suggestion-item:last-child {\r\n border-bottom: none;\r\n }\r\n .ai-analysis {\r\n background: #f0f9ff;\r\n border-left: 4px solid #3b82f6;\r\n padding: 20px;\r\n border-radius: 8px;\r\n line-height: 1.8;\r\n color: #1e40af;\r\n white-space: pre-wrap;\r\n }\r\n .footer {\r\n background: #f7fafc;\r\n padding: 20px;\r\n text-align: center;\r\n color: #718096;\r\n font-size: 14px;\r\n }\r\n .trend-up {\r\n background: linear-gradient(135deg, #f56565 0%, #ed8936 100%) !important;\r\n }\r\n .trend-down {\r\n background: linear-gradient(135deg, #48bb78 0%, #38a169 100%) !important;\r\n }\r\n .optimization-example {\r\n background: #f7fafc;\r\n border-radius: 12px;\r\n padding: 20px;\r\n margin-bottom: 20px;\r\n border: 1px solid #e2e8f0;\r\n }\r\n .example-header {\r\n margin-bottom: 15px;\r\n }\r\n .example-title {\r\n font-size: 18px;\r\n font-weight: bold;\r\n color: #333;\r\n margin-bottom: 8px;\r\n display: flex;\r\n align-items: center;\r\n gap: 10px;\r\n }\r\n .priority-badge {\r\n font-size: 12px;\r\n padding: 4px 12px;\r\n border-radius: 12px;\r\n font-weight: normal;\r\n }\r\n .priority-high {\r\n background: #fed7d7;\r\n color: #c53030;\r\n }\r\n .priority-medium {\r\n background: #feebc8;\r\n color: #c05621;\r\n }\r\n .priority-low {\r\n background: #bee3f8;\r\n color: #2c5282;\r\n }\r\n .example-meta {\r\n display: flex;\r\n gap: 15px;\r\n font-size: 14px;\r\n color: #666;\r\n }\r\n .example-desc {\r\n color: #666;\r\n margin-bottom: 15px;\r\n line-height: 1.6;\r\n }\r\n .code-block {\r\n background: #1e293b;\r\n border-radius: 8px;\r\n overflow: hidden;\r\n margin-top: 10px;\r\n }\r\n .code-header {\r\n background: #334155;\r\n color: #94a3b8;\r\n padding: 8px 15px;\r\n font-size: 12px;\r\n font-family: monospace;\r\n }\r\n .code-block pre {\r\n margin: 0;\r\n padding: 15px;\r\n overflow-x: auto;\r\n }\r\n .code-block code {\r\n font-family: 'Consolas', 'Monaco', 'Courier New', monospace;\r\n font-size: 13px;\r\n line-height: 1.6;\r\n color: #e2e8f0;\r\n }\r\n .related-files {\r\n margin-top: 15px;\r\n padding: 10px;\r\n background: #e6fffa;\r\n border-radius: 6px;\r\n font-size: 14px;\r\n color: #234e52;\r\n }\r\n </style>\r\n</head>\r\n<body>\r\n <div class=\"container\">\r\n <div class=\"header\">\r\n <h1>⚡ 性能分析报告</h1>\r\n <div class=\"time\">生成时间: ${result.timestamp}</div>\r\n </div>\r\n\r\n <div class=\"content\">\r\n <!-- 总体统计 -->\r\n <div class=\"section\">\r\n <div class=\"section-title\">📊 总体统计</div>\r\n <div class=\"stats-grid\">\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">总大小</div>\r\n <div class=\"stat-value\">${(\r\n result.summary.totalSize /\r\n 1024 /\r\n 1024\r\n ).toFixed(2)}MB</div>\r\n </div>\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">Gzip 后</div>\r\n <div class=\"stat-value\">${(\r\n result.summary.totalGzipSize /\r\n 1024 /\r\n 1024\r\n ).toFixed(2)}MB</div>\r\n </div>\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">文件数量</div>\r\n <div class=\"stat-value\">${result.summary.fileCount}</div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 最大文件 -->\r\n <div class=\"section\">\r\n <div class=\"section-title\">📦 最大的文件</div>\r\n <div class=\"file-list\">\r\n ${result.summary.largestFiles\r\n .slice(0, 10)\r\n .map(\r\n (file) => `\r\n <div class=\"file-item\">\r\n <span class=\"file-name\">${file.name}</span>\r\n <span class=\"file-size\">${(file.size / 1024).toFixed(2)}KB</span>\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n </div>\r\n\r\n <!-- 按类型统计 -->\r\n <div class=\"section\">\r\n <div class=\"section-title\">📋 按类型统计</div>\r\n <div class=\"file-list\">\r\n ${Object.entries(result.summary.byType)\r\n .map(\r\n ([type, info]) => `\r\n <div class=\"file-item\">\r\n <span class=\"file-name\">${type}</span>\r\n <span class=\"file-size\">${info.count} 个, ${(\r\n info.size / 1024\r\n ).toFixed(2)}KB</span>\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n </div>\r\n\r\n <!-- 历史对比 -->\r\n ${\r\n result.comparison\r\n ? `\r\n <div class=\"section\">\r\n <div class=\"section-title\">📈 历史对比</div>\r\n <div class=\"stats-grid\">\r\n <div class=\"stat-card ${\r\n result.comparison.totalSize.trend === \"increased\"\r\n ? \"trend-up\"\r\n : \"trend-down\"\r\n }\">\r\n <div class=\"stat-label\">总大小变化</div>\r\n <div class=\"stat-value\">${\r\n result.comparison.totalSize.diffPercent > 0 ? \"+\" : \"\"\r\n }${result.comparison.totalSize.diffPercent.toFixed(2)}%</div>\r\n <div class=\"stat-label\">${\r\n result.comparison.totalSize.trend === \"increased\" ? \"+\" : \"\"\r\n }${(result.comparison.totalSize.diff / 1024 / 1024).toFixed(\r\n 2,\r\n )}MB</div>\r\n </div>\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">文件数变化</div>\r\n <div class=\"stat-value\">${\r\n result.comparison.fileCount.diff > 0 ? \"+\" : \"\"\r\n }${result.comparison.fileCount.diff}</div>\r\n </div>\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">新增文件</div>\r\n <div class=\"stat-value\">${result.comparison.newFiles.length}</div>\r\n </div>\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">删除文件</div>\r\n <div class=\"stat-value\">${\r\n result.comparison.removedFiles.length\r\n }</div>\r\n </div>\r\n </div>\r\n ${\r\n result.comparison.largestFileChanges.length > 0\r\n ? `\r\n <div class=\"file-list\" style=\"margin-top: 15px;\">\r\n <div style=\"font-weight: bold; padding: 10px; color: #333;\">文件大小变化 Top 5:</div>\r\n ${result.comparison.largestFileChanges\r\n .slice(0, 5)\r\n .map(\r\n (change) => `\r\n <div class=\"file-item\">\r\n <span class=\"file-name\">${change.name}</span>\r\n <span class=\"file-size ${\r\n change.diff > 0 ? \"trend-up\" : \"trend-down\"\r\n }\">\r\n ${change.diff > 0 ? \"+\" : \"\"}${(change.diff / 1024).toFixed(\r\n 2,\r\n )}KB\r\n </span>\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n\r\n <!-- 依赖分析 -->\r\n ${\r\n result.dependencies\r\n ? `\r\n <div class=\"section\">\r\n <div class=\"section-title\">📦 依赖分析</div>\r\n <div class=\"stats-grid\">\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">总依赖数</div>\r\n <div class=\"stat-value\">${result.dependencies.total}</div>\r\n </div>\r\n <div class=\"stat-card ${\r\n result.dependencies.duplicates.length > 0 ? \"trend-up\" : \"\"\r\n }\">\r\n <div class=\"stat-label\">重复依赖</div>\r\n <div class=\"stat-value\">${\r\n result.dependencies.duplicates.length\r\n }</div>\r\n </div>\r\n </div>\r\n ${\r\n result.dependencies.duplicates.length > 0\r\n ? `\r\n <div class=\"file-list\" style=\"margin-top: 15px;\">\r\n <div style=\"font-weight: bold; padding: 10px; color: #333;\">重复打包的依赖:</div>\r\n ${result.dependencies.duplicates\r\n .slice(0, 10)\r\n .map(\r\n (dep) => `\r\n <div class=\"file-item\">\r\n <span class=\"file-name\">${dep.name}</span>\r\n <span class=\"file-size\">\r\n ${(dep.size / 1024).toFixed(2)}KB · 被 ${\r\n dep.usedBy.length\r\n } 个文件使用\r\n </span>\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n ${\r\n result.dependencies.largest.length > 0\r\n ? `\r\n <div class=\"file-list\" style=\"margin-top: 15px;\">\r\n <div style=\"font-weight: bold; padding: 10px; color: #333;\">最大的依赖:</div>\r\n ${result.dependencies.largest\r\n .slice(0, 10)\r\n .map(\r\n (dep) => `\r\n <div class=\"file-item\">\r\n <span class=\"file-name\">${dep.name}</span>\r\n <span class=\"file-size\">${(dep.size / 1024).toFixed(\r\n 2,\r\n )}KB</span>\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n\r\n <!-- 性能问题 -->\r\n ${\r\n result.issues.length > 0\r\n ? `\r\n <div class=\"section\">\r\n <div class=\"section-title\">⚠️ 性能问题</div>\r\n ${result.issues\r\n .map(\r\n (issue) => `\r\n <div class=\"issue ${issue.severity}\">\r\n <div class=\"issue-title\">${this.getSeverityIcon(\r\n issue.severity,\r\n )} ${issue.title}</div>\r\n <div class=\"issue-desc\">${issue.description}</div>\r\n ${\r\n issue.files\r\n ? `<div class=\"issue-desc\">相关文件: ${issue.files.join(\r\n \", \",\r\n )}</div>`\r\n : \"\"\r\n }\r\n ${\r\n issue.suggestion\r\n ? `<div class=\"issue-suggestion\">💡 ${issue.suggestion}</div>`\r\n : \"\"\r\n }\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n `\r\n : '<div class=\"section\"><div class=\"section-title\">✅ 未发现明显的性能问题</div></div>'\r\n }\r\n\r\n <!-- 优化建议 -->\r\n ${\r\n result.suggestions.length > 0\r\n ? `\r\n <div class=\"section\">\r\n <div class=\"section-title\">💡 优化建议</div>\r\n <div class=\"suggestion-list\">\r\n ${result.suggestions\r\n .map(\r\n (suggestion, index) => `\r\n <div class=\"suggestion-item\">${index + 1}. ${suggestion}</div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n\r\n <!-- 优化示例 -->\r\n ${\r\n result.optimizationExamples && result.optimizationExamples.length > 0\r\n ? `\r\n <div class=\"section\">\r\n <div class=\"section-title\">🔧 优化示例</div>\r\n ${result.optimizationExamples\r\n .map(\r\n (example) => `\r\n <div class=\"optimization-example\">\r\n <div class=\"example-header\">\r\n <div class=\"example-title\">\r\n ${example.title}\r\n <span class=\"priority-badge priority-${example.priority}\">${\r\n example.priority === \"high\"\r\n ? \"高优先级\"\r\n : example.priority === \"medium\"\r\n ? \"中优先级\"\r\n : \"低优先级\"\r\n }</span>\r\n </div>\r\n <div class=\"example-meta\">\r\n <span>💪 ${example.difficulty}</span>\r\n <span>📈 ${example.impact}</span>\r\n </div>\r\n </div>\r\n <div class=\"example-desc\">${example.description}</div>\r\n <div class=\"code-block\">\r\n <div class=\"code-header\">${example.code.language}</div>\r\n <pre><code>${this.escapeHtml(example.code.content)}</code></pre>\r\n </div>\r\n ${\r\n example.relatedFiles && example.relatedFiles.length > 0\r\n ? `\r\n <div class=\"related-files\">\r\n <strong>相关文件:</strong> ${example.relatedFiles.join(\", \")}\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n </div>\r\n `,\r\n )\r\n .join(\"\")}\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n\r\n <!-- AI 分析 -->\r\n ${\r\n result.aiAnalysis\r\n ? `\r\n <div class=\"section\">\r\n <div class=\"section-title\">🤖 AI 分析</div>\r\n <div class=\"ai-analysis\">${this.escapeHtml(result.aiAnalysis)}</div>\r\n </div>\r\n `\r\n : \"\"\r\n }\r\n </div>\r\n\r\n <div class=\"footer\">\r\n <p>AI Performance Analyzer v1.0.0</p>\r\n <p>Powered by Vite & OpenAI</p>\r\n </div>\r\n </div>\r\n</body>\r\n</html>\r\n `.trim();\r\n\r\n // 创建报告目录\r\n const reportsDir = path.resolve(process.cwd(), \"ai-reports\");\r\n if (!fs.existsSync(reportsDir)) {\r\n fs.mkdirSync(reportsDir, { recursive: true });\r\n }\r\n\r\n const reportPath = path.resolve(reportsDir, \"performance-report.html\");\r\n fs.writeFileSync(reportPath, html, \"utf-8\");\r\n console.log(pc.green(`\\n📄 性能报告已生成: ${pc.cyan(reportPath)}\\n`));\r\n }\r\n\r\n /**\r\n * 生成 JSON 报告\r\n */\r\n private async generateJSONReport(result: AnalysisResult): Promise<void> {\r\n const reportsDir = path.resolve(process.cwd(), \"ai-reports\");\r\n if (!fs.existsSync(reportsDir)) {\r\n fs.mkdirSync(reportsDir, { recursive: true });\r\n }\r\n\r\n const reportPath = path.resolve(reportsDir, \"performance-report.json\");\r\n fs.writeFileSync(reportPath, JSON.stringify(result, null, 2), \"utf-8\");\r\n console.log(pc.green(`📄 JSON 报告已生成: ${pc.cyan(reportPath)}`));\r\n }\r\n\r\n /**\r\n * 获取严重程度图标\r\n */\r\n private getSeverityIcon(severity: string): string {\r\n const icons: Record<string, string> = {\r\n high: \"🔴\",\r\n medium: \"🟡\",\r\n low: \"🔵\",\r\n };\r\n return icons[severity] || \"⚪\";\r\n }\r\n\r\n /**\r\n * 转义 HTML\r\n */\r\n private escapeHtml(text: string): string {\r\n const map: Record<string, string> = {\r\n \"&\": \"&amp;\",\r\n \"<\": \"&lt;\",\r\n \">\": \"&gt;\",\r\n '\"': \"&quot;\",\r\n \"'\": \"&#039;\",\r\n };\r\n return text.replace(/[&<>\"']/g, (m) => map[m]);\r\n }\r\n}\r\n"],"mappings":"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","names":["pc","fs","path","fs","path","path","fs","fs","path","pc"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-ai-perf-analyzer",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "AI-powered performance analyzer for Vite - Analyze bundle size and suggest optimizations",
5
5
  "keywords": [
6
6
  "vite-plugin-ai-perf-analyzer",