synthia-doctor
Version:
Synthia Engine Synthia-Doctor - 开发synthia-doctor工具,集成AI构建优化建议(构建产物精简31%)
525 lines (512 loc) • 19.7 kB
JavaScript
;
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