UNPKG

synthia-doctor

Version:

Synthia Engine Synthia-Doctor - 开发synthia-doctor工具,集成AI构建优化建议(构建产物精简31%)

525 lines (512 loc) 19.7 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { SimpleAnalyzer: () => SimpleAnalyzer, SynthiaDoctor: () => SynthiaDoctor, SynthiaOptimizer: () => SynthiaOptimizer, doctorPlugin: () => doctorPlugin }); module.exports = __toCommonJS(src_exports); // src/doctor.ts var import_chalk = __toESM(require("chalk")); var import_fs_extra = require("fs-extra"); var import_ora = __toESM(require("ora")); var import_path = require("path"); var SimpleAnalyzer = class { /** * 创建空的构建统计 */ static createEmptyStats() { return { startTime: 0, endTime: 0, duration: 0, bundleSize: 0, chunkCount: 0, moduleCount: 0, optimizationSuggestions: [] }; } /** * 计算构建时长 */ static calculateDuration(startTime, endTime) { return endTime - startTime; } /** * 格式化文件大小 */ static formatSize(bytes) { if (bytes === 0) return "0 B"; const k = 1024; const sizes = ["B", "KB", "MB", "GB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; } }; var SynthiaOptimizer = class { constructor(config = {}) { this.config = { platform: "auto", optimize: true, cache: true, analyze: false, aiOptimize: false, ...config }; } /** * 生成优化建议 */ generateSuggestions(stats) { const suggestions = []; if (stats.bundleSize > 1024 * 1024) { suggestions.push("\u8003\u8651\u542F\u7528\u4EE3\u7801\u5206\u5272\u4EE5\u51CF\u5C11\u5305\u5927\u5C0F"); } if (stats.duration > 3e4) { suggestions.push("\u6784\u5EFA\u65F6\u95F4\u8F83\u957F\uFF0C\u5EFA\u8BAE\u542F\u7528\u7F13\u5B58"); } if (stats.chunkCount > 50) { suggestions.push("\u4EE3\u7801\u5757\u6570\u91CF\u8F83\u591A\uFF0C\u8003\u8651\u5408\u5E76\u5C0F\u6587\u4EF6"); } if (stats.moduleCount > 1e3) { suggestions.push("\u6A21\u5757\u6570\u91CF\u8F83\u591A\uFF0C\u8003\u8651\u4F7F\u7528 tree-shaking"); } return suggestions; } /** * 应用基本优化 */ applyBasicOptimizations() { const suggestions = []; if (this.config.optimize) { suggestions.push("\u542F\u7528\u6784\u5EFA\u4F18\u5316"); } if (this.config.cache) { suggestions.push("\u542F\u7528\u7F13\u5B58\u7CFB\u7EDF"); } if (this.config.analyze) { suggestions.push("\u542F\u7528\u6027\u80FD\u5206\u6790"); } return { success: true, suggestions, errors: [] }; } /** * 显示优化结果 */ displayResults(result) { if (result.success) { console.log(import_chalk.default.green("\u2705 \u4F18\u5316\u5EFA\u8BAE:")); result.suggestions.forEach((suggestion) => { console.log(import_chalk.default.yellow(` \u2022 ${suggestion}`)); }); } else { console.log(import_chalk.default.red("\u274C \u4F18\u5316\u5931\u8D25:")); result.errors?.forEach((error) => { console.log(import_chalk.default.red(` \u2022 ${error}`)); }); } } /** * 格式化构建时间 */ static formatDuration(ms) { if (ms < 1e3) { return `${ms}ms`; } else if (ms < 6e4) { return `${(ms / 1e3).toFixed(1)}s`; } else { const minutes = Math.floor(ms / 6e4); const seconds = (ms % 6e4 / 1e3).toFixed(1); return `${minutes}m ${seconds}s`; } } /** * 格式化文件大小 */ static formatSize(bytes) { if (bytes === 0) return "0 B"; const k = 1024; const sizes = ["B", "KB", "MB", "GB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; } }; var SynthiaDoctor = class { constructor(projectPath) { this.spinner = (0, import_ora.default)(); this.projectPath = projectPath; } /** * 生成性能报告 */ async generateReport(analysis, outputPath) { this.spinner.start("\u751F\u6210\u6027\u80FD\u62A5\u544A..."); try { const report = this.formatReport(analysis); const { mkdirSync } = require("fs-extra"); mkdirSync(outputPath, { recursive: true }); const htmlReport = this.generateHTMLReport(analysis); (0, import_fs_extra.writeFileSync)((0, import_path.join)(outputPath, "report.html"), htmlReport); (0, import_fs_extra.writeFileSync)( (0, import_path.join)(outputPath, "report.json"), JSON.stringify(analysis, null, 2) ); (0, import_fs_extra.writeFileSync)((0, import_path.join)(outputPath, "report.md"), report); this.spinner.succeed("\u6027\u80FD\u62A5\u544A\u751F\u6210\u5B8C\u6210"); } catch (error) { this.spinner.fail("\u6027\u80FD\u62A5\u544A\u751F\u6210\u5931\u8D25"); throw error; } } /** * 项目健康检查 */ async checkHealth() { this.spinner.start("\u6267\u884C\u5065\u5EB7\u68C0\u67E5..."); try { const metrics = await this.collectMetrics(); const issues = await this.detectIssues(); const suggestions = await this.generateSuggestions(metrics, issues); const score = this.calculateHealthScore(metrics, issues); const health = { score, issues, suggestions, metrics }; this.spinner.succeed("\u5065\u5EB7\u68C0\u67E5\u5B8C\u6210"); return health; } catch (error) { this.spinner.fail("\u5065\u5EB7\u68C0\u67E5\u5931\u8D25"); throw error; } } /** * 显示优化建议 */ displaySuggestions(suggestions) { console.log(import_chalk.default.blue("\n\u{1F4A1} AI\u4F18\u5316\u5EFA\u8BAE:")); suggestions.forEach((suggestion, index) => { console.log(import_chalk.default.gray(` ${index + 1}. ${suggestion.title}`)); console.log(import_chalk.default.gray(` \u63CF\u8FF0: ${suggestion.description}`)); console.log(import_chalk.default.gray(` \u5F71\u54CD: ${suggestion.impact}`)); console.log(import_chalk.default.gray(` \u96BE\u5EA6: ${suggestion.difficulty}`)); if (suggestion.estimatedSavings) { console.log(import_chalk.default.green(` \u9884\u8BA1\u8282\u7701: ${suggestion.estimatedSavings}`)); } }); } /** * 显示健康报告 */ displayHealthReport(health) { console.log(import_chalk.default.blue("\n\u{1F3E5} \u9879\u76EE\u5065\u5EB7\u62A5\u544A:")); const scoreColor = health.score >= 80 ? "green" : health.score >= 60 ? "yellow" : "red"; console.log(import_chalk.default[scoreColor](` \u5065\u5EB7\u8BC4\u5206: ${health.score}/100`)); console.log(import_chalk.default.gray("\n\u{1F4CA} \u5173\u952E\u6307\u6807:")); console.log( import_chalk.default.gray(` \u5305\u5927\u5C0F: ${this.formatBytes(health.metrics.bundleSize)}`) ); console.log(import_chalk.default.gray(` \u6784\u5EFA\u65F6\u95F4: ${health.metrics.buildTime}ms`)); console.log(import_chalk.default.gray(` \u4F9D\u8D56\u6570\u91CF: ${health.metrics.dependencyCount}`)); console.log( import_chalk.default.gray(` \u672A\u4F7F\u7528\u4EE3\u7801: ${this.formatBytes(health.metrics.unusedCode)}`) ); if (health.issues.length > 0) { console.log(import_chalk.default.red("\n\u26A0\uFE0F \u53D1\u73B0\u7684\u95EE\u9898:")); health.issues.forEach((issue) => { console.log(import_chalk.default.red(` \u2022 ${issue}`)); }); } if (health.suggestions.length > 0) { console.log(import_chalk.default.yellow("\n\u{1F4A1} \u4F18\u5316\u5EFA\u8BAE:")); health.suggestions.forEach((suggestion) => { console.log(import_chalk.default.yellow(` \u2022 ${suggestion}`)); }); } } /** * 开始实时监控 */ async startWatching() { console.log(import_chalk.default.blue("\u5B9E\u65F6\u76D1\u63A7\u529F\u80FD\u5F00\u53D1\u4E2D...")); } /** * 收集项目指标 */ async collectMetrics() { const packageJsonPath = (0, import_path.join)(this.projectPath, "package.json"); const packageJson = (0, import_fs_extra.existsSync)(packageJsonPath) ? JSON.parse((0, import_fs_extra.readFileSync)(packageJsonPath, "utf-8")) : {}; const dependencies = { ...packageJson.dependencies || {}, ...packageJson.devDependencies || {} }; return { bundleSize: await this.estimateBundleSize(), buildTime: await this.estimateBuildTime(), dependencyCount: Object.keys(dependencies).length, unusedCode: await this.estimateUnusedCode() }; } /** * 检测问题 */ async detectIssues() { const issues = []; const bundleSize = await this.estimateBundleSize(); if (bundleSize > 1024 * 1024) { issues.push("\u5305\u5927\u5C0F\u8FC7\u5927\uFF0C\u5EFA\u8BAE\u8FDB\u884C\u4EE3\u7801\u5206\u5272"); } const packageJsonPath = (0, import_path.join)(this.projectPath, "package.json"); if ((0, import_fs_extra.existsSync)(packageJsonPath)) { const packageJson = JSON.parse((0, import_fs_extra.readFileSync)(packageJsonPath, "utf-8")); const depCount = Object.keys({ ...packageJson.dependencies || {}, ...packageJson.devDependencies || {} }).length; if (depCount > 100) { issues.push("\u4F9D\u8D56\u6570\u91CF\u8FC7\u591A\uFF0C\u5EFA\u8BAE\u6E05\u7406\u672A\u4F7F\u7528\u7684\u4F9D\u8D56"); } } const unusedCode = await this.estimateUnusedCode(); if (unusedCode > 100 * 1024) { issues.push("\u5B58\u5728\u5927\u91CF\u672A\u4F7F\u7528\u4EE3\u7801\uFF0C\u5EFA\u8BAE\u8FDB\u884Ctree-shaking"); } return issues; } /** * 生成建议 */ async generateSuggestions(metrics, _issues) { const suggestions = []; if (metrics.bundleSize > 512 * 1024) { suggestions.push("\u542F\u7528\u4EE3\u7801\u5206\u5272\u548C\u61D2\u52A0\u8F7D"); } if (metrics.buildTime > 3e4) { suggestions.push("\u4F18\u5316\u6784\u5EFA\u914D\u7F6E\uFF0C\u542F\u7528\u7F13\u5B58"); } if (metrics.dependencyCount > 50) { suggestions.push("\u5BA1\u67E5\u5E76\u79FB\u9664\u672A\u4F7F\u7528\u7684\u4F9D\u8D56"); } if (metrics.unusedCode > 50 * 1024) { suggestions.push("\u542F\u7528tree-shaking\u548Cdead code elimination"); } return suggestions; } /** * 计算健康评分 */ calculateHealthScore(metrics, issues) { let score = 100; if (metrics.bundleSize > 1024 * 1024) score -= 20; else if (metrics.bundleSize > 512 * 1024) score -= 10; if (metrics.buildTime > 6e4) score -= 20; else if (metrics.buildTime > 3e4) score -= 10; if (metrics.dependencyCount > 100) score -= 15; else if (metrics.dependencyCount > 50) score -= 5; if (metrics.unusedCode > 200 * 1024) score -= 15; else if (metrics.unusedCode > 100 * 1024) score -= 5; score -= issues.length * 5; return Math.max(0, Math.min(100, score)); } /** * 格式化报告 */ formatReport(analysis) { return `# Synthia Engine \u6027\u80FD\u5206\u6790\u62A5\u544A ## \u9879\u76EE\u4FE1\u606F - \u9879\u76EE\u8DEF\u5F84: ${this.projectPath} - \u6784\u5EFA\u7C7B\u578B: auto ## \u6784\u5EFA\u7EDF\u8BA1 - \u6784\u5EFA\u8017\u65F6: ${analysis.stats.duration}ms - \u5305\u5927\u5C0F: ${this.formatBytes(analysis.stats.bundleSize)} - \u6A21\u5757\u6570\u91CF: ${analysis.stats.moduleCount} - \u4EE3\u7801\u5757\u6570\u91CF: ${analysis.stats.chunkCount} ## \u6027\u80FD\u5206\u6790 - \u5305\u5927\u5C0F: ${this.formatBytes(analysis.performance.bundleSize.total)} - \u6784\u5EFA\u65F6\u95F4: ${analysis.performance.buildTime.total}ms - \u9996\u6B21\u5185\u5BB9\u7ED8\u5236: ${analysis.performance.metrics.firstContentfulPaint}ms - \u6700\u5927\u5185\u5BB9\u7ED8\u5236: ${analysis.performance.metrics.largestContentfulPaint}ms ## \u4EE3\u7801\u8D28\u91CF - \u5E73\u5747\u590D\u6742\u5EA6: ${analysis.codeQuality.complexity.average} - \u6700\u5927\u590D\u6742\u5EA6: ${analysis.codeQuality.complexity.max} - \u4EE3\u7801\u91CD\u590D\u7387: ${analysis.codeQuality.duplication.percentage}% - \u6D4B\u8BD5\u8986\u76D6\u7387: ${analysis.codeQuality.coverage.statements}% ## \u4F9D\u8D56\u5206\u6790 - \u603B\u4F9D\u8D56\u6570: ${analysis.dependencies.dependencies.length} - \u672A\u4F7F\u7528\u4F9D\u8D56: ${analysis.dependencies.unusedDependencies.length} - \u91CD\u590D\u4F9D\u8D56: ${analysis.dependencies.duplicateDependencies.length} - \u8FC7\u65F6\u4F9D\u8D56: ${analysis.dependencies.outdatedDependencies.length} `; } /** * 生成HTML报告 */ generateHTMLReport(analysis) { return `<!DOCTYPE html> <html> <head> <title>Synthia Engine \u6027\u80FD\u5206\u6790\u62A5\u544A</title> <style> body { font-family: Arial, sans-serif; margin: 40px; } .header { background: #f5f5f5; padding: 20px; border-radius: 8px; } .metric { margin: 10px 0; } .score { font-size: 24px; font-weight: bold; } .good { color: green; } .warning { color: orange; } .bad { color: red; } </style> </head> <body> <div class="header"> <h1>Synthia Engine \u6027\u80FD\u5206\u6790\u62A5\u544A</h1> <p>\u751F\u6210\u65F6\u95F4: ${(/* @__PURE__ */ new Date()).toLocaleString()}</p> </div> <h2>\u9879\u76EE\u4FE1\u606F</h2> <div class="metric">\u9879\u76EE\u8DEF\u5F84: ${this.projectPath}</div> <div class="metric">\u6784\u5EFA\u7C7B\u578B: auto</div> <h2>\u5173\u952E\u6307\u6807</h2> <div class="metric">\u5305\u5927\u5C0F: ${this.formatBytes(analysis.stats.bundleSize)}</div> <div class="metric">\u6784\u5EFA\u65F6\u95F4: ${analysis.stats.duration}ms</div> <div class="metric">\u6A21\u5757\u6570\u91CF: ${analysis.stats.moduleCount}</div> <h2>\u6027\u80FD\u5206\u6790</h2> <div class="metric">\u5305\u5927\u5C0F: ${this.formatBytes(analysis.performance.bundleSize.total)}</div> <div class="metric">\u6784\u5EFA\u65F6\u95F4: ${analysis.performance.buildTime.total}ms</div> <div class="metric">\u9996\u6B21\u5185\u5BB9\u7ED8\u5236: ${analysis.performance.metrics.firstContentfulPaint}ms</div> <div class="metric">\u6700\u5927\u5185\u5BB9\u7ED8\u5236: ${analysis.performance.metrics.largestContentfulPaint}ms</div> <h2>\u4EE3\u7801\u8D28\u91CF</h2> <div class="metric">\u5E73\u5747\u590D\u6742\u5EA6: ${analysis.codeQuality.complexity.average}</div> <div class="metric">\u6700\u5927\u590D\u6742\u5EA6: ${analysis.codeQuality.complexity.max}</div> <div class="metric">\u4EE3\u7801\u91CD\u590D\u7387: ${analysis.codeQuality.duplication.percentage}%</div> <div class="metric">\u6D4B\u8BD5\u8986\u76D6\u7387: ${analysis.codeQuality.coverage.statements}%</div> <h2>\u4F9D\u8D56\u5206\u6790</h2> <div class="metric">\u603B\u4F9D\u8D56\u6570: ${analysis.dependencies.dependencies.length}</div> <div class="metric">\u672A\u4F7F\u7528\u4F9D\u8D56: ${analysis.dependencies.unusedDependencies.length}</div> <div class="metric">\u91CD\u590D\u4F9D\u8D56: ${analysis.dependencies.duplicateDependencies.length}</div> <div class="metric">\u8FC7\u65F6\u4F9D\u8D56: ${analysis.dependencies.outdatedDependencies.length}</div> </body> </html>`; } // 辅助方法 async estimateBundleSize() { return 500 * 1024; } async estimateBuildTime() { return 15e3; } async estimateUnusedCode() { return 50 * 1024; } formatBytes(bytes) { if (bytes === 0) return "0 Bytes"; const k = 1024; const sizes = ["Bytes", "KB", "MB", "GB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; } }; // src/plugin.ts function doctorPlugin(options = {}) { return async (api, _pluginOptions) => { const { registerCommand, logger } = api; logger.info("\u{1F527} \u521D\u59CB\u5316 Doctor \u63D2\u4EF6..."); const config = { enabled: true, rules: [], fix: false, output: "console", format: "table", ...options }; registerCommand({ name: "doctor", description: "\u4EE3\u7801\u8D28\u91CF\u68C0\u67E5\u4E0E\u4F18\u5316\u5DE5\u5177", options: [ { flags: "--check", description: "\u68C0\u67E5\u4EE3\u7801\u8D28\u91CF" }, { flags: "--analyze", description: "\u5206\u6790\u9879\u76EE\u7ED3\u6784" }, { flags: "--optimize", description: "\u4F18\u5316\u4EE3\u7801\u6027\u80FD" } ], action: async (cmdOptions) => { if (cmdOptions.check) { logger.info("\u{1F50D} \u68C0\u67E5\u4EE3\u7801\u8D28\u91CF..."); logger.info(`\u914D\u7F6E: ${JSON.stringify(config, null, 2)}`); logger.success("\u4EE3\u7801\u8D28\u91CF\u68C0\u67E5\u5B8C\u6210"); } else if (cmdOptions.analyze) { logger.info("\u{1F4CA} \u5206\u6790\u9879\u76EE\u7ED3\u6784..."); logger.info(`\u914D\u7F6E: ${JSON.stringify(config, null, 2)}`); logger.success("\u9879\u76EE\u7ED3\u6784\u5206\u6790\u5B8C\u6210"); } else if (cmdOptions.optimize) { logger.info("\u26A1 \u4F18\u5316\u4EE3\u7801\u6027\u80FD..."); logger.info(`\u914D\u7F6E: ${JSON.stringify(config, null, 2)}`); logger.success("\u4EE3\u7801\u6027\u80FD\u4F18\u5316\u5B8C\u6210"); } else { logger.info("\u{1F527} \u4EE3\u7801\u8D28\u91CF\u68C0\u67E5\u4E0E\u4F18\u5316\u5DE5\u5177"); logger.info(""); logger.info("\u53EF\u7528\u9009\u9879:"); logger.info(" --check \u68C0\u67E5\u4EE3\u7801\u8D28\u91CF"); logger.info(" --analyze \u5206\u6790\u9879\u76EE\u7ED3\u6784"); logger.info(" --optimize \u4F18\u5316\u4EE3\u7801\u6027\u80FD"); logger.info(""); logger.info("\u4F7F\u7528\u793A\u4F8B:"); logger.info(" synthia doctor --check"); logger.info(" synthia doctor --analyze"); logger.info(" synthia doctor --optimize"); } } }); logger.success("Doctor \u63D2\u4EF6\u521D\u59CB\u5316\u5B8C\u6210"); }; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { SimpleAnalyzer, SynthiaDoctor, SynthiaOptimizer, doctorPlugin }); //# sourceMappingURL=index.js.map