code-insight-analyst
Version:
1,674 lines (1,653 loc) • 145 kB
JavaScript
#!/usr/bin/env node
'use strict';
var commander = require('commander');
var path11 = require('path');
var fs7 = require('fs');
var os4 = require('os');
var util = require('util');
var chalk6 = require('chalk');
var inquirer3 = require('inquirer');
var fs9 = require('fs-extra');
var globModule = require('glob');
var Table2 = require('cli-table3');
var worker_threads = require('worker_threads');
var crypto = require('crypto');
var ora = require('ora');
var events = require('events');
var chokidar = require('chokidar');
var lodash = require('lodash');
var child_process = require('child_process');
var url = require('url');
var listr2 = require('listr2');
var tsMorph = require('ts-morph');
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var path11__namespace = /*#__PURE__*/_interopNamespace(path11);
var fs7__namespace = /*#__PURE__*/_interopNamespace(fs7);
var os4__namespace = /*#__PURE__*/_interopNamespace(os4);
var chalk6__default = /*#__PURE__*/_interopDefault(chalk6);
var inquirer3__default = /*#__PURE__*/_interopDefault(inquirer3);
var fs9__default = /*#__PURE__*/_interopDefault(fs9);
var globModule__namespace = /*#__PURE__*/_interopNamespace(globModule);
var Table2__default = /*#__PURE__*/_interopDefault(Table2);
var crypto__default = /*#__PURE__*/_interopDefault(crypto);
var ora__default = /*#__PURE__*/_interopDefault(ora);
var chokidar__default = /*#__PURE__*/_interopDefault(chokidar);
var lodash__default = /*#__PURE__*/_interopDefault(lodash);
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
}) : x)(function(x) {
if (typeof require !== "undefined")
return require.apply(this, arguments);
throw Error('Dynamic require of "' + x + '" is not supported');
});
var mkdir2 = util.promisify(fs7__namespace.mkdir);
var appendFile2 = util.promisify(fs7__namespace.appendFile);
var ErrorHandler = class _ErrorHandler {
/**
* 创建错误处理器实例
* @param options 错误处理选项
*/
constructor(options = {}) {
this.options = {
logToConsole: true,
logToFile: true,
logFilePath: path11__namespace.join(
os4__namespace.homedir(),
".code-insight",
"logs",
"error.log"
),
includeStack: true,
minLevel: "warning" /* WARNING */,
...options
};
this.ensureLogDirectory();
}
/**
* 获取单例实例
* @param options 错误处理选项
* @returns 错误处理器实例
*/
static getInstance(options) {
if (!_ErrorHandler.instance) {
_ErrorHandler.instance = new _ErrorHandler(options);
}
return _ErrorHandler.instance;
}
/**
* 确保日志目录存在
*/
async ensureLogDirectory() {
if (this.options.logToFile) {
const logDir = path11__namespace.dirname(this.options.logFilePath || "");
try {
await mkdir2(logDir, { recursive: true });
} catch (err) {
console.error(`\u65E0\u6CD5\u521B\u5EFA\u65E5\u5FD7\u76EE\u5F55: ${logDir}`, err);
}
}
}
/**
* 处理错误
* @param error 错误对象
* @param level 错误级别
* @param context 错误上下文信息
*/
async handle(error, level = "error" /* ERROR */, context) {
const errorLevelValues = {
["info" /* INFO */]: 0,
["warning" /* WARNING */]: 1,
["error" /* ERROR */]: 2,
["fatal" /* FATAL */]: 3
};
if (errorLevelValues[level] < errorLevelValues[this.options.minLevel || "warning" /* WARNING */]) {
return;
}
const errorObj = typeof error === "string" ? new Error(error) : error;
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
const errorMessage = errorObj.message;
const stack = this.options.includeStack ? errorObj.stack : void 0;
const errorInfo = {
timestamp,
level,
message: errorMessage,
stack,
context
};
if (this.options.logToConsole) {
this.logToConsole(errorInfo);
}
if (this.options.logToFile) {
await this.logToFile(errorInfo);
}
if (level === "fatal" /* FATAL */) {
console.error("\u9047\u5230\u81F4\u547D\u9519\u8BEF\uFF0C\u7A0B\u5E8F\u5C06\u9000\u51FA");
process.exit(1);
}
}
/**
* 控制台输出错误信息
* @param errorInfo 错误信息对象
*/
logToConsole(errorInfo) {
const { level, message, stack, context } = errorInfo;
let logMethod;
switch (level) {
case "info" /* INFO */:
logMethod = console.info;
break;
case "warning" /* WARNING */:
logMethod = console.warn;
break;
case "error" /* ERROR */:
case "fatal" /* FATAL */:
logMethod = console.error;
break;
default:
logMethod = console.log;
}
logMethod(`[${level.toUpperCase()}] ${message}`);
if (context && Object.keys(context).length > 0) {
logMethod("\u4E0A\u4E0B\u6587:", context);
}
if (stack && level !== "info" /* INFO */) {
logMethod("\u5806\u6808:", stack);
}
}
/**
* 将错误信息写入日志文件
* @param errorInfo 错误信息对象
*/
async logToFile(errorInfo) {
try {
const { timestamp, level, message, stack, context } = errorInfo;
let logEntry = `[${timestamp}] [${level.toUpperCase()}] ${message}
`;
if (context && Object.keys(context).length > 0) {
logEntry += `\u4E0A\u4E0B\u6587: ${JSON.stringify(context)}
`;
}
if (stack) {
logEntry += `\u5806\u6808: ${stack}
`;
}
logEntry += "----------------------\n";
await appendFile2(this.options.logFilePath || "", logEntry);
} catch (err) {
console.error("\u65E0\u6CD5\u5199\u5165\u9519\u8BEF\u65E5\u5FD7\u6587\u4EF6:", err);
}
}
/**
* 记录信息级别日志
* @param message 日志消息
* @param context 上下文信息
*/
async info(message, context) {
await this.handle(message, "info" /* INFO */, context);
}
/**
* 记录警告级别日志
* @param message 日志消息
* @param context 上下文信息
*/
async warning(message, context) {
await this.handle(message, "warning" /* WARNING */, context);
}
/**
* 记录错误级别日志
* @param error 错误对象或消息
* @param context 上下文信息
*/
async error(error, context) {
await this.handle(error, "error" /* ERROR */, context);
}
/**
* 记录致命级别日志
* @param error 错误对象或消息
* @param context 上下文信息
*/
async fatal(error, context) {
await this.handle(error, "fatal" /* FATAL */, context);
}
};
// src/cli/commands/analyze-command.ts
var AnalyzeCommand = class {
constructor() {
this.errorHandler = ErrorHandler.getInstance();
}
/**
* 注册analyze命令
* @param program Commander程序实例
*/
register(program) {
program.command("analyze").description("\u5206\u6790\u4EE3\u7801\u5E93\u5E76\u751F\u6210\u6D1E\u5BDF\u62A5\u544A").option("-p, --path <path>", "\u8981\u5206\u6790\u7684\u4EE3\u7801\u8DEF\u5F84", process.cwd()).option(
"-o, --output <output>",
"\u8F93\u51FA\u62A5\u544A\u7684\u8DEF\u5F84",
"./code-insight-report"
).option("--ignore <patterns...>", "\u8981\u5FFD\u7565\u7684\u6587\u4EF6\u6A21\u5F0F", [
"node_modules",
"dist",
".git"
]).action(async (options) => {
try {
const targetPath = path11__namespace.default.resolve(options.path);
const outputPath = path11__namespace.default.resolve(options.output);
console.log(`\u5F00\u59CB\u5206\u6790\u4EE3\u7801: ${targetPath}`);
console.log(`\u5206\u6790\u62A5\u544A\u5C06\u4FDD\u5B58\u5230: ${outputPath}`);
console.log("\u5206\u6790\u5B8C\u6210\uFF01");
} catch (error) {
this.errorHandler.error(
error instanceof Error ? error : String(error)
);
process.exit(1);
}
});
}
};
var Logger = class {
constructor() {
this.verbose = false;
}
/**
* 设置是否显示详细日志
*/
setVerbose(verbose) {
this.verbose = verbose;
}
/**
* 记录普通信息
*/
info(message, ...args) {
console.info(chalk6__default.default.blue("\u2139"), message, ...args);
}
/**
* 记录成功信息
*/
success(message, ...args) {
console.log(chalk6__default.default.green("\u2713"), message, ...args);
}
/**
* 记录警告信息
*/
warn(message, ...args) {
console.warn(chalk6__default.default.yellow("\u26A0"), message, ...args);
}
/**
* 记录错误信息
*/
error(message, ...args) {
console.error(chalk6__default.default.red("\u2717"), message, ...args);
}
/**
* 记录调试信息(仅在详细模式下显示)
*/
debug(message, ...args) {
if (this.verbose) {
console.debug(chalk6__default.default.magenta("\u{1F50D}"), message, ...args);
}
}
/**
* 显示进度信息
*/
progress(step, total, message) {
const percentage = Math.round(step / total * 100);
const progressBar = this.createProgressBar(percentage);
console.log(`${progressBar} ${percentage}% | ${message}`);
}
/**
* 创建进度条
*/
createProgressBar(percentage) {
const width = 20;
const completed = Math.floor(width * percentage / 100);
const remaining = width - completed;
const filledBar = "\u2588".repeat(completed);
const emptyBar = "\u2591".repeat(remaining);
return chalk6__default.default.cyan(`[${filledBar}${emptyBar}]`);
}
};
// src/core/permission-manager.ts
var PermissionManager = class {
constructor() {
this.configDir = path11__namespace.default.join(os4__namespace.default.homedir(), ".code-insight");
this.accessLogPath = path11__namespace.default.join(this.configDir, "access.log");
this.authorizedPaths = /* @__PURE__ */ new Set();
this.logger = new Logger();
this.init();
}
/**
* 初始化
*/
init() {
try {
fs9__default.default.ensureDirSync(this.configDir);
if (fs9__default.default.existsSync(this.accessLogPath)) {
const content = fs9__default.default.readFileSync(this.accessLogPath, "utf-8");
content.split("\n").filter(Boolean).forEach((line) => {
const [path18] = line.split(",");
if (path18) {
this.authorizedPaths.add(path18);
}
});
}
} catch (error) {
this.logger.error("\u521D\u59CB\u5316\u6743\u9650\u7BA1\u7406\u5668\u5931\u8D25:", error);
}
}
/**
* 请求访问权限
* 注意:这个方法本身不再实现交互逻辑,而是由专门的交互模块调用
* @param path 文件路径
* @returns 是否已获得授权
*/
async requestAccess(path18) {
if (this.isAuthorized(path18)) {
return true;
}
return false;
}
/**
* 检查是否已授权
* @param path 文件路径
*/
isAuthorized(path18) {
const resolvedPath = fs9__default.default.realpathSync(path18);
if (this.authorizedPaths.has(resolvedPath)) {
return true;
}
for (const authorizedPath of this.authorizedPaths) {
if (resolvedPath.startsWith(authorizedPath)) {
return true;
}
}
return false;
}
/**
* 记录访问
* @param path 文件路径
*/
recordAccess(path18) {
try {
const resolvedPath = fs9__default.default.realpathSync(path18);
this.authorizedPaths.add(resolvedPath);
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
const logEntry = `${resolvedPath},${timestamp}
`;
fs9__default.default.appendFileSync(this.accessLogPath, logEntry);
} catch (error) {
this.logger.error("\u8BB0\u5F55\u6743\u9650\u4FE1\u606F\u5931\u8D25:", error);
}
}
/**
* 清除授权记录
*/
clearAuthorizations() {
try {
this.authorizedPaths.clear();
fs9__default.default.writeFileSync(this.accessLogPath, "");
} catch (error) {
this.logger.error("\u6E05\u9664\u6743\u9650\u8BB0\u5F55\u5931\u8D25:", error);
}
}
};
var ConfigManager = class {
constructor() {
this.configDir = path11__namespace.default.join(os4__namespace.default.homedir(), ".code-insight");
this.configPath = path11__namespace.default.join(this.configDir, "config.json");
this.logger = new Logger();
this.config = {};
this.init();
}
/**
* 初始化
*/
init() {
try {
fs9__default.default.ensureDirSync(this.configDir);
if (fs9__default.default.existsSync(this.configPath)) {
this.config = fs9__default.default.readJsonSync(this.configPath);
} else {
this.config = {
permissions: {},
lastUsedOptions: [],
preferredMode: "single",
analyzers: {},
plugins: {},
watchMode: {
enabled: false,
interval: 5e3,
// 5秒
patterns: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"],
exclude: [
"**/node_modules/**",
"**/dist/**",
"**/build/**",
"**/.git/**"
],
analyzers: []
}
};
this.saveConfig();
}
} catch (error) {
this.logger.error("\u521D\u59CB\u5316\u914D\u7F6E\u7BA1\u7406\u5668\u5931\u8D25:", error);
}
}
/**
* 获取配置
*/
getConfig() {
return this.config;
}
/**
* 获取特定配置项
*/
get(key) {
return this.config[key];
}
/**
* 设置特定配置项
*/
set(key, value) {
this.config[key] = value;
this.saveConfig();
}
/**
* 设置上次使用的选项
*/
setLastUsedOptions(options) {
this.config.lastUsedOptions = options;
this.saveConfig();
}
/**
* 设置首选模式
*/
setPreferredMode(mode) {
this.config.preferredMode = mode;
this.saveConfig();
}
/**
* 保存配置
*/
saveConfig() {
try {
fs9__default.default.writeJsonSync(this.configPath, this.config, { spaces: 2 });
} catch (error) {
this.logger.error("\u4FDD\u5B58\u914D\u7F6E\u5931\u8D25:", error);
}
}
/**
* 重置配置
*/
resetConfig() {
this.config = {
permissions: {},
lastUsedOptions: [],
preferredMode: "single",
analyzers: {},
plugins: {},
watchMode: {
enabled: false,
interval: 5e3,
patterns: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"],
exclude: [
"**/node_modules/**",
"**/dist/**",
"**/build/**",
"**/.git/**"
],
analyzers: []
}
};
this.saveConfig();
}
/**
* 更新监测模式配置
* @param watchConfig 监测模式配置
*/
updateWatchConfig(watchConfig = {}) {
if (!this.config.watchMode) {
this.config.watchMode = {
enabled: false,
interval: 5e3,
patterns: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"],
exclude: [
"**/node_modules/**",
"**/dist/**",
"**/build/**",
"**/.git/**"
],
analyzers: []
};
}
this.config.watchMode = {
enabled: typeof watchConfig.enabled === "boolean" ? watchConfig.enabled : this.config.watchMode.enabled,
interval: typeof watchConfig.interval === "number" ? watchConfig.interval : this.config.watchMode.interval,
patterns: watchConfig.patterns || this.config.watchMode.patterns,
exclude: watchConfig.exclude || this.config.watchMode.exclude,
analyzers: watchConfig.analyzers || this.config.watchMode.analyzers
};
this.saveConfig();
}
/**
* 启用监测模式
*/
enableWatchMode() {
if (this.config.watchMode) {
this.config.watchMode.enabled = true;
this.saveConfig();
}
}
/**
* 禁用监测模式
*/
disableWatchMode() {
if (this.config.watchMode) {
this.config.watchMode.enabled = false;
this.saveConfig();
}
}
/**
* 获取插件配置
* @param pluginName 插件名称
*/
getPluginConfig(pluginName) {
const plugins = this.config.plugins || {};
return plugins[pluginName] || {};
}
/**
* 设置插件配置
* @param pluginName 插件名称
* @param config 插件配置
*/
setPluginConfig(pluginName, config) {
if (!this.config.plugins) {
this.config.plugins = {};
}
this.config.plugins[pluginName] = config;
this.saveConfig();
}
/**
* 删除插件配置
* @param pluginName 插件名称
*/
removePluginConfig(pluginName) {
if (this.config.plugins && this.config.plugins[pluginName]) {
delete this.config.plugins[pluginName];
this.saveConfig();
}
}
/**
* 获取配置文件路径
*/
getConfigPath() {
return this.configPath;
}
/**
* 获取配置目录
*/
getConfigDir() {
return this.configDir;
}
};
// src/cli/prompt/permission-prompt.ts
var PermissionPrompt = class {
/**
* 构造函数
*/
constructor() {
this.permissionManager = new PermissionManager();
this.configManager = new ConfigManager();
}
/**
* 执行三步授权流程
* @param projectPath 项目路径
* @returns 是否获得授权
*/
async requestPermission(projectPath) {
try {
if (this.permissionManager.isAuthorized(projectPath)) {
console.log(chalk6__default.default.green(`\u2713 \u5DF2\u6709\u8BBF\u95EE\u6388\u6743: ${projectPath}`));
return true;
}
console.log(chalk6__default.default.blue(`\u9700\u8981\u8BBF\u95EE\u4EE5\u4E0B\u76EE\u5F55: ${projectPath}`));
if (!fs9__default.default.existsSync(projectPath)) {
console.log(chalk6__default.default.red(`\u9519\u8BEF: \u76EE\u5F55\u4E0D\u5B58\u5728`));
return false;
}
const fileTypesInfo = this.collectFileTypeInfo(projectPath);
const totalFiles = fileTypesInfo.reduce(
(sum, info) => sum + info.count,
0
);
console.log(chalk6__default.default.blue("\u5C06\u5206\u6790\u4EE5\u4E0B\u7C7B\u578B\u7684\u6587\u4EF6:"));
fileTypesInfo.forEach((info) => {
console.log(
chalk6__default.default.blue(` ${info.extension.toUpperCase()} \u6587\u4EF6: ${info.count} \u4E2A`)
);
});
console.log(chalk6__default.default.blue(`\u5171\u8BA1 ${totalFiles} \u4E2A\u6587\u4EF6`));
console.log(chalk6__default.default.yellow("\u5206\u6790\u5C06\u5305\u62EC\u4EE5\u4E0B\u5185\u5BB9:"));
console.log(chalk6__default.default.yellow(" - \u6587\u4EF6\u5185\u5BB9 (\u7528\u4E8E\u4EE3\u7801\u5206\u6790)"));
console.log(chalk6__default.default.yellow(" - \u9879\u76EE\u7ED3\u6784 (\u7528\u4E8E\u4F9D\u8D56\u5206\u6790)"));
console.log(
chalk6__default.default.yellow(" - \u5F00\u53D1\u914D\u7F6E (package.json, tsconfig.json \u7B49)")
);
const { permission } = await inquirer3__default.default.prompt([
{
type: "confirm",
name: "permission",
message: "\u6388\u6743\u8BBF\u95EE?",
default: false
}
]);
if (permission) {
const { rememberPermission } = await inquirer3__default.default.prompt([
{
type: "confirm",
name: "rememberPermission",
message: "\u8BB0\u4F4F\u6B64\u6388\u6743?",
default: true
}
]);
this.permissionManager.recordAccess(projectPath);
if (rememberPermission) {
const permissions = this.configManager.get("permissions") || {};
permissions[projectPath] = true;
this.configManager.set("permissions", permissions);
}
console.log(chalk6__default.default.green(`\u2713 \u5DF2\u83B7\u5F97\u6388\u6743`));
return true;
} else {
console.log(chalk6__default.default.red(`\u2717 \u6388\u6743\u88AB\u62D2\u7EDD`));
return false;
}
} catch (error) {
console.error(chalk6__default.default.red(`\u8BF7\u6C42\u6388\u6743\u65F6\u51FA\u9519: ${error}`));
return false;
}
}
/**
* 收集项目中的文件类型信息
* @param projectPath 项目路径
* @returns 文件类型信息数组
*/
collectFileTypeInfo(projectPath) {
const extensions = [
"ts",
"js",
"tsx",
"jsx",
"json",
"css",
"scss",
"less",
"html",
"vue",
"md"
];
const fileTypesInfo = [];
for (const ext of extensions) {
const pattern = `**/*.${ext}`;
const files = globModule__namespace.sync(pattern, {
cwd: projectPath,
ignore: ["**/node_modules/**", "**/dist/**", "**/build/**"],
absolute: true
});
if (files.length > 0) {
fileTypesInfo.push({
extension: ext,
count: files.length
});
}
}
return fileTypesInfo.sort((a, b) => b.count - a.count);
}
};
var madge = async (projectPath) => {
console.info(`\u6A21\u62DF\u5206\u6790\u9879\u76EE\u4F9D\u8D56\u5173\u7CFB: ${projectPath}`);
const mockDependencies = {
"src/index.ts": [
"src/core/analyzers/dependency-analyzer.ts",
"src/services/dependency-service.ts"
],
"src/cli/index.ts": ["src/cli/app.ts"],
"src/cli/app.ts": [
"src/cli/commands/dependency-command.ts",
"src/cli/commands/watch-command.ts"
],
"src/cli/commands/dependency-command.ts": [
"src/services/dependency-service.ts"
],
"src/cli/commands/watch-command.ts": ["src/services/watch-service.ts"],
"src/services/dependency-service.ts": [
"src/core/analyzers/dependency-analyzer.ts",
"src/core/report/report-generator.ts"
],
"src/services/watch-service.ts": [
"src/core/analyzers/dependency-analyzer.ts"
],
"src/core/analyzers/dependency-analyzer.ts": [
"src/types/dependency-types.ts"
],
"src/core/report/report-generator.ts": ["src/types/dependency-types.ts"],
"src/types/dependency-types.ts": []
};
const mockCircular = [
[
"src/index.ts",
"src/core/analyzers/dependency-analyzer.ts",
"src/types/dependency-types.ts",
"src/index.ts"
]
];
return {
obj: () => mockDependencies,
circular: () => mockCircular
};
};
var DependencyAnalysisError = class extends Error {
constructor(message, code) {
super(`[${code}] ${message}`);
this.code = code;
this.name = "DependencyAnalysisError";
}
};
var DependencyAnalyzer = class {
/**
* 创建依赖分析器实例
* @param basePath - 项目根路径
* @param fileExtensions - 要分析的文件扩展名
*/
constructor(basePath, fileExtensions = ["js", "jsx", "ts", "tsx"]) {
this.graph = null;
this.basePath = path11__namespace.default.resolve(basePath);
this.fileExtensions = fileExtensions;
}
/**
* 构建项目的依赖关系图
* @returns 依赖图对象
*/
async buildGraph() {
if (!fs7__namespace.default.existsSync(this.basePath)) {
throw new DependencyAnalysisError(
`\u9879\u76EE\u8DEF\u5F84\u4E0D\u5B58\u5728: ${this.basePath}`,
"ERR_PATH_NOT_FOUND"
);
}
try {
const madgeResult = await madge(this.basePath);
const dependencyMap = madgeResult.obj();
this.graph = {
nodes: this.buildNodes(dependencyMap),
edges: this.buildEdges(dependencyMap),
circularDependencies: this.extractCircularDependencies(madgeResult)
};
return this.graph;
} catch (error) {
throw new DependencyAnalysisError(
`\u4F9D\u8D56\u5206\u6790\u5931\u8D25: ${error.message}`,
"ERR_ANALYSIS_FAILED"
);
}
}
/**
* 执行完整的依赖分析
* @returns 完整的依赖分析结果
*/
async analyze() {
if (!this.graph) {
await this.buildGraph();
}
const graph = this.graph;
const levels = await this.getDependencyLevels();
const counts = await this.getDependencyCounts();
const stats = this.computeStats(graph, levels, counts);
return {
graph,
levels,
counts,
stats
};
}
/**
* 查找项目中的循环依赖
* @returns 循环依赖列表
*/
async getCircularDependencies() {
if (!this.graph) {
await this.buildGraph();
}
return this.graph?.circularDependencies || [];
}
/**
* 获取依赖层级分析
* @returns 每个文件的依赖层级信息
*/
async getDependencyLevels() {
if (!this.graph) {
await this.buildGraph();
}
const levels = /* @__PURE__ */ new Map();
const { nodes, edges } = this.graph;
const calculateLevel = (nodeId, visited = /* @__PURE__ */ new Set()) => {
if (visited.has(nodeId)) {
return 0;
}
if (levels.has(nodeId)) {
return levels.get(nodeId);
}
const dependencies = edges.filter((edge) => edge.target === nodeId).map((edge) => edge.source);
if (dependencies.length === 0) {
levels.set(nodeId, 0);
return 0;
}
visited.add(nodeId);
const maxLevel = Math.max(
...dependencies.map(
(dep) => calculateLevel(dep, /* @__PURE__ */ new Set([...visited]))
)
);
const level = maxLevel + 1;
levels.set(nodeId, level);
return level;
};
nodes.forEach((node) => {
calculateLevel(node.id);
});
return levels;
}
/**
* 计算每个文件的依赖数量
* @returns 每个文件的依赖数量统计
*/
async getDependencyCounts() {
if (!this.graph) {
await this.buildGraph();
}
const counts = /* @__PURE__ */ new Map();
const { nodes, edges } = this.graph;
nodes.forEach((node) => {
counts.set(node.id, { incoming: 0, outgoing: 0 });
});
edges.forEach((edge) => {
const source = counts.get(edge.source);
const target = counts.get(edge.target);
if (source) {
source.outgoing += 1;
counts.set(edge.source, source);
}
if (target) {
target.incoming += 1;
counts.set(edge.target, target);
}
});
return counts;
}
/**
* 计算各种统计信息
*/
computeStats(graph, levels, counts) {
const totalFiles = graph.nodes.length;
const totalDependencies = graph.edges.length;
const circularDependencyCount = graph.circularDependencies.length;
let maxDependencyLevel = 0;
levels.forEach((level) => {
if (level > maxDependencyLevel) {
maxDependencyLevel = level;
}
});
let mostDepended = { id: "", count: 0 };
let mostDependsOn = { id: "", count: 0 };
counts.forEach((count, id) => {
if (count.incoming > mostDepended.count) {
mostDepended = { id, count: count.incoming };
}
if (count.outgoing > mostDependsOn.count) {
mostDependsOn = { id, count: count.outgoing };
}
});
return {
totalFiles,
totalDependencies,
circularDependencyCount,
maxDependencyLevel,
mostDepended,
mostDependsOn
};
}
/**
* 将madge依赖对象转换为节点列表
*/
buildNodes(dependencyMap) {
return Object.keys(dependencyMap).map((filePath) => ({
id: filePath,
path: path11__namespace.default.resolve(this.basePath, filePath),
size: this.getFileSize(path11__namespace.default.resolve(this.basePath, filePath))
}));
}
/**
* 将madge依赖对象转换为边列表
*/
buildEdges(dependencyMap) {
const edges = [];
Object.entries(dependencyMap).forEach(([file, dependencies]) => {
dependencies.forEach((dependency) => {
edges.push({
source: dependency,
target: file
});
});
});
return edges;
}
/**
* 从madge结果中提取循环依赖信息
*/
extractCircularDependencies(madgeResult) {
try {
const circular = madgeResult.circular();
return circular.map((dependencyCycle, index) => ({
id: `cycle-${index}`,
cycle: dependencyCycle,
length: dependencyCycle.length
}));
} catch (error) {
console.error("\u83B7\u53D6\u5FAA\u73AF\u4F9D\u8D56\u5931\u8D25:", error);
return [];
}
}
/**
* 获取文件大小
*/
getFileSize(filePath) {
try {
const stats = fs7__namespace.default.statSync(filePath);
return stats.size;
} catch {
return 0;
}
}
};
var ReportGenerator = class {
/**
* 生成报告
* @param data 分析结果数据
* @param options 报告选项
* @returns 报告文件路径或控制台输出的结果
*/
async generate(data, options) {
switch (options.type) {
case "console" /* CONSOLE */:
return this.generateConsoleReport(data, options);
case "html" /* HTML */:
return this.generateHtmlReport(data, options);
case "json" /* JSON */:
return this.generateJsonReport(data, options);
case "markdown" /* MARKDOWN */:
return this.generateMarkdownReport(data, options);
default:
throw new Error(`\u4E0D\u652F\u6301\u7684\u62A5\u544A\u7C7B\u578B: ${options.type}`);
}
}
/**
* 生成控制台报告
*/
generateConsoleReport(data, options) {
return this.formatDataForConsole(data, options);
}
/**
* 生成HTML报告
*/
generateHtmlReport(data, options) {
const outputPath = options.outputPath || "./report.html";
const htmlContent = this.formatDataForHtml(data, options);
fs7__namespace.default.writeFileSync(outputPath, htmlContent, "utf8");
return path11__namespace.default.resolve(outputPath);
}
/**
* 生成JSON报告
*/
generateJsonReport(data, options) {
const outputPath = options.outputPath || "./report.json";
const jsonContent = JSON.stringify(data, this.jsonReplacer, 2);
fs7__namespace.default.writeFileSync(outputPath, jsonContent, "utf8");
return path11__namespace.default.resolve(outputPath);
}
/**
* 生成Markdown报告
*/
generateMarkdownReport(data, options) {
const outputPath = options.outputPath || "./report.md";
const mdContent = this.formatDataForMarkdown(data, options);
fs7__namespace.default.writeFileSync(outputPath, mdContent, "utf8");
return path11__namespace.default.resolve(outputPath);
}
/**
* 格式化数据用于控制台输出
*/
formatDataForConsole(data, options) {
if (!data)
return "\u65E0\u6570\u636E";
const detailed = options.detailed || false;
const indent = detailed ? 2 : 1;
return JSON.stringify(data, null, indent);
}
/**
* 格式化数据用于HTML输出
*/
formatDataForHtml(data, options) {
const title = options.projectName ? `${options.projectName} - \u5206\u6790\u62A5\u544A` : "\u4EE3\u7801\u5206\u6790\u62A5\u544A";
return `
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${title}</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
margin: 0;
padding: 20px;
color: #333;
line-height: 1.5;
}
.container { max-width: 1200px; margin: 0 auto; }
.header { margin-bottom: 30px; }
.section { margin-bottom: 30px; }
h1 { color: #2c3e50; }
h2 { color: #3498db; border-bottom: 1px solid #eee; padding-bottom: 10px; }
table { width: 100%; border-collapse: collapse; margin: 20px 0; }
th, td { padding: 12px 15px; text-align: left; border-bottom: 1px solid #ddd; }
th { background-color: #f8f9fa; }
tr:hover { background-color: #f8f9fa; }
.summary { background-color: #f9f9f9; padding: 20px; border-radius: 5px; }
.warning { color: #e74c3c; }
.info { color: #3498db; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>${title}</h1>
<p>\u751F\u6210\u65F6\u95F4: ${(/* @__PURE__ */ new Date()).toLocaleString()}</p>
</div>
<div class="content">
${this.dataToHtml(data, options)}
</div>
</div>
</body>
</html>`;
}
/**
* 格式化数据用于Markdown输出
*/
formatDataForMarkdown(data, options) {
const title = options.projectName ? `${options.projectName} - \u5206\u6790\u62A5\u544A` : "\u4EE3\u7801\u5206\u6790\u62A5\u544A";
return `# ${title}
\u751F\u6210\u65F6\u95F4: ${(/* @__PURE__ */ new Date()).toLocaleString()}
${this.dataToMarkdown(data, options)}
`;
}
/**
* 将JSON数据转换为适合在控制台显示的表格格式
*/
jsonToTable(data) {
const table = new Table2__default.default({
head: ["\u5C5E\u6027", "\u503C"],
style: { head: ["cyan"] }
});
Object.entries(data).forEach(([key, value]) => {
table.push([key, this.formatValue(value)]);
});
return table.toString();
}
/**
* 将值格式化为适合显示的文本
*/
formatValue(value) {
if (value === null || value === void 0) {
return "-";
}
if (typeof value === "object") {
if (Array.isArray(value)) {
return value.length ? `[${value.length} \u9879]` : "[]";
} else {
return "{...}";
}
}
return String(value);
}
/**
* 将数据转换为HTML格式
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
dataToHtml(data, _options) {
if (!data)
return "<p>\u65E0\u6570\u636E</p>";
if (typeof data === "object") {
return `<pre>${JSON.stringify(data, null, 2)}</pre>`;
}
return `<p>${String(data)}</p>`;
}
/**
* 将数据转换为Markdown格式
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
dataToMarkdown(data, _options) {
if (!data)
return "\u65E0\u6570\u636E";
if (typeof data === "object") {
return "```json\n" + JSON.stringify(data, null, 2) + "\n```";
}
return String(data);
}
/**
* JSON.stringify 的替换函数,用于处理Map等特殊对象
*/
jsonReplacer(_key, value) {
if (value instanceof Map) {
return Object.fromEntries(value);
}
return value;
}
};
var DependencyReportGenerator = class extends ReportGenerator {
/**
* 格式化依赖分析结果用于控制台输出
* @override
*/
formatDataForConsole(data, options) {
const dependencyData = data;
if (!dependencyData)
return "\u65E0\u4F9D\u8D56\u5206\u6790\u6570\u636E";
const { stats, graph } = dependencyData;
let output = "";
output += `
${chalk6__default.default.bold.blue("\u4F9D\u8D56\u5173\u7CFB\u5206\u6790\u62A5\u544A")}
`;
output += chalk6__default.default.yellow("\u{1F4CA} \u7EDF\u8BA1\u6982\u89C8:\n");
const statsTable = new Table2__default.default({
style: { head: [], border: [] }
});
statsTable.push(
{ \u6587\u4EF6\u603B\u6570: stats.totalFiles },
{ \u4F9D\u8D56\u603B\u6570: stats.totalDependencies },
{ \u5FAA\u73AF\u4F9D\u8D56\u6570: stats.circularDependencyCount },
{ \u6700\u5927\u4F9D\u8D56\u6DF1\u5EA6: stats.maxDependencyLevel },
{
\u88AB\u4F9D\u8D56\u6700\u591A: `${stats.mostDepended.id} (${stats.mostDepended.count}\u6B21)`
},
{ \u4F9D\u8D56\u6700\u591A: `${stats.mostDependsOn.id} (${stats.mostDependsOn.count}\u6B21)` }
);
output += statsTable.toString() + "\n\n";
if (graph.circularDependencies.length > 0) {
output += chalk6__default.default.red("\u{1F504} \u68C0\u6D4B\u5230\u5FAA\u73AF\u4F9D\u8D56:\n");
const circularTable = new Table2__default.default({
head: ["\u5E8F\u53F7", "\u5FAA\u73AF\u8DEF\u5F84", "\u957F\u5EA6"],
style: { head: ["red"] }
});
graph.circularDependencies.slice(0, 5).forEach((dep, index) => {
circularTable.push([index + 1, dep.cycle.join(" \u2192 "), dep.length]);
});
if (graph.circularDependencies.length > 5) {
output += circularTable.toString() + "\n";
output += chalk6__default.default.yellow(
`...\u5171\u68C0\u6D4B\u5230 ${graph.circularDependencies.length} \u4E2A\u5FAA\u73AF\u4F9D\u8D56\u95EE\u9898
`
);
} else {
output += circularTable.toString() + "\n\n";
}
} else {
output += chalk6__default.default.green("\u2713 \u672A\u68C0\u6D4B\u5230\u5FAA\u73AF\u4F9D\u8D56\n\n");
}
if (options.detailed && dependencyData.counts) {
output += chalk6__default.default.yellow("\u{1F4CB} \u4F9D\u8D56\u6700\u591A\u7684\u6587\u4EF6 (Top 10):\n");
const countsArray = Array.from(dependencyData.counts.entries());
const topOutgoing = countsArray.sort((a, b) => b[1].outgoing - a[1].outgoing).slice(0, 10);
if (topOutgoing.length > 0) {
const topTable = new Table2__default.default({
head: ["\u6587\u4EF6", "\u4F9D\u8D56\u6570\u91CF"],
style: { head: ["cyan"] }
});
topOutgoing.forEach(([file, counts]) => {
topTable.push([file, counts.outgoing]);
});
output += topTable.toString() + "\n\n";
}
}
return output;
}
/**
* 格式化依赖数据用于HTML输出
* @override
*/
dataToHtml(data, options) {
const dependencyData = data;
if (!dependencyData)
return "<p>\u65E0\u4F9D\u8D56\u5206\u6790\u6570\u636E</p>";
const { stats, graph } = dependencyData;
let html = "";
html += `
<div class="section summary">
<h2>\u{1F4CA} \u7EDF\u8BA1\u6982\u89C8</h2>
<table>
<tr><td>\u6587\u4EF6\u603B\u6570</td><td>${stats.totalFiles}</td></tr>
<tr><td>\u4F9D\u8D56\u603B\u6570</td><td>${stats.totalDependencies}</td></tr>
<tr><td>\u5FAA\u73AF\u4F9D\u8D56\u6570</td><td>${stats.circularDependencyCount}</td></tr>
<tr><td>\u6700\u5927\u4F9D\u8D56\u6DF1\u5EA6</td><td>${stats.maxDependencyLevel}</td></tr>
<tr><td>\u88AB\u4F9D\u8D56\u6700\u591A</td><td>${stats.mostDepended.id} (${stats.mostDepended.count}\u6B21)</td></tr>
<tr><td>\u4F9D\u8D56\u6700\u591A</td><td>${stats.mostDependsOn.id} (${stats.mostDependsOn.count}\u6B21)</td></tr>
</table>
</div>`;
if (graph.circularDependencies.length > 0) {
html += `
<div class="section">
<h2 class="warning">\u{1F504} \u68C0\u6D4B\u5230\u5FAA\u73AF\u4F9D\u8D56</h2>
<table>
<thead>
<tr>
<th>\u5E8F\u53F7</th>
<th>\u5FAA\u73AF\u8DEF\u5F84</th>
<th>\u957F\u5EA6</th>
</tr>
</thead>
<tbody>`;
const displayCircular = graph.circularDependencies.slice(0, 20);
displayCircular.forEach((dep, index) => {
html += `
<tr>
<td>${index + 1}</td>
<td>${dep.cycle.join(" \u2192 ")}</td>
<td>${dep.length}</td>
</tr>`;
});
html += `
</tbody>
</table>
${graph.circularDependencies.length > 20 ? `<p class="info">...\u5171\u68C0\u6D4B\u5230 ${graph.circularDependencies.length} \u4E2A\u5FAA\u73AF\u4F9D\u8D56\u95EE\u9898</p>` : ""}
</div>`;
} else {
html += `
<div class="section">
<h2 class="info">\u2713 \u672A\u68C0\u6D4B\u5230\u5FAA\u73AF\u4F9D\u8D56</h2>
</div>`;
}
if (options.detailed && dependencyData.counts) {
html += `
<div class="section">
<h2>\u{1F4CB} \u4F9D\u8D56\u8BE6\u60C5</h2>`;
const countsArray = Array.from(dependencyData.counts.entries());
const topOutgoing = countsArray.sort((a, b) => b[1].outgoing - a[1].outgoing).slice(0, 20);
html += `
<h3>\u4F9D\u8D56\u6700\u591A\u7684\u6587\u4EF6 (Top 20)</h3>
<table>
<thead>
<tr>
<th>\u6587\u4EF6</th>
<th>\u4F9D\u8D56\u6570\u91CF</th>
</tr>
</thead>
<tbody>`;
topOutgoing.forEach(([file, counts]) => {
html += `
<tr>
<td>${file}</td>
<td>${counts.outgoing}</td>
</tr>`;
});
html += `
</tbody>
</table>
</div>`;
}
return html;
}
/**
* 格式化依赖数据用于Markdown输出
* @override
*/
dataToMarkdown(data, options) {
const dependencyData = data;
if (!dependencyData)
return "\u65E0\u4F9D\u8D56\u5206\u6790\u6570\u636E";
const { stats, graph } = dependencyData;
let md = "";
md += `## \u{1F4CA} \u7EDF\u8BA1\u6982\u89C8
| \u6307\u6807 | \u503C |
|------|-----|
| \u6587\u4EF6\u603B\u6570 | ${stats.totalFiles} |
| \u4F9D\u8D56\u603B\u6570 | ${stats.totalDependencies} |
| \u5FAA\u73AF\u4F9D\u8D56\u6570 | ${stats.circularDependencyCount} |
| \u6700\u5927\u4F9D\u8D56\u6DF1\u5EA6 | ${stats.maxDependencyLevel} |
| \u88AB\u4F9D\u8D56\u6700\u591A | ${stats.mostDepended.id} (${stats.mostDepended.count}\u6B21) |
| \u4F9D\u8D56\u6700\u591A | ${stats.mostDependsOn.id} (${stats.mostDependsOn.count}\u6B21) |
`;
if (graph.circularDependencies.length > 0) {
md += `## \u{1F504} \u68C0\u6D4B\u5230\u5FAA\u73AF\u4F9D\u8D56
| \u5E8F\u53F7 | \u5FAA\u73AF\u8DEF\u5F84 | \u957F\u5EA6 |
|------|---------|------|
`;
const displayCircular = graph.circularDependencies.slice(0, 10);
displayCircular.forEach((dep, index) => {
md += `| ${index + 1} | ${dep.cycle.join(" \u2192 ")} | ${dep.length} |
`;
});
if (graph.circularDependencies.length > 10) {
md += `
_...\u5171\u68C0\u6D4B\u5230 ${graph.circularDependencies.length} \u4E2A\u5FAA\u73AF\u4F9D\u8D56\u95EE\u9898_
`;
}
} else {
md += `## \u2713 \u672A\u68C0\u6D4B\u5230\u5FAA\u73AF\u4F9D\u8D56
`;
}
if (options.detailed && dependencyData.counts) {
md += `## \u{1F4CB} \u4F9D\u8D56\u8BE6\u60C5
`;
const countsArray = Array.from(dependencyData.counts.entries());
const topOutgoing = countsArray.sort((a, b) => b[1].outgoing - a[1].outgoing).slice(0, 10);
md += `### \u4F9D\u8D56\u6700\u591A\u7684\u6587\u4EF6 (Top 10)
`;
md += `| \u6587\u4EF6 | \u4F9D\u8D56\u6570\u91CF |
|------|--------|
`;
topOutgoing.forEach(([file, counts]) => {
md += `| ${file} | ${counts.outgoing} |
`;
});
md += "\n";
}
return md;
}
};
var PerformanceOptimizer = class {
/**
* 构造函数
* @param config 分析配置
*/
constructor(config) {
this.config = config;
this.cacheDir = path11__namespace.default.join(os4__namespace.default.homedir(), ".code-insight", "cache");
this.maxWorkers = config.performance?.maxWorkers || Math.max(1, os4__namespace.default.cpus().length - 1);
this.ensureCacheDir();
}
/**
* 使用缓存运行函数
* @param key 缓存键
* @param fn 要运行的函数
* @param options 缓存选项
* @returns 函数执行结果或缓存的结果
*/
async withCache(key, fn, options) {
if (!this.config.performance?.useCache) {
return fn();
}
const opts = {
ttl: this.config.performance?.cacheTTL || 86400,
cacheDir: this.cacheDir,
checkSourceChange: true,
...options
};
const cacheFilePath = this.getCacheFilePath(key);
try {
if (fs7__namespace.default.existsSync(cacheFilePath)) {
const cacheContent = fs7__namespace.default.readFileSync(cacheFilePath, "utf8");
const cache = JSON.parse(cacheContent);
const now = Date.now();
const cacheAge = now - cache.timestamp;
if (cacheAge < opts.ttl * 1e3) {
console.info(`\u4F7F\u7528\u7F13\u5B58: ${key}`);
return cache.data;
}
}
} catch (error) {
console.warn(`\u7F13\u5B58\u8BFB\u53D6\u5931\u8D25: ${error.message}`);
}
const result = await fn();
try {
const cache = {
timestamp: Date.now(),
data: result
};
fs7__namespace.default.writeFileSync(cacheFilePath, JSON.stringify(cache), "utf8");
console.info(`\u5DF2\u7F13\u5B58\u7ED3\u679C: ${key}`);
} catch (error) {
console.warn(`\u7F13\u5B58\u5199\u5165\u5931\u8D25: ${error.message}`);
}
return result;
}
/**
* 并行运行多个任务
* @param tasks 任务列表
* @returns 任务执行结果
*/
async runInParallel(tasks, workerScript, timeoutMs = 6e4) {
if (!this.config.performance?.useParallel || tasks.length <= 1) {
const result = {};
for (const task of tasks) {
result[task.id] = await this.runSingleTask(
task.data,
workerScript
);
}
return result;
}
const maxConcurrent = Math.min(tasks.length, this.maxWorkers);
const results = {};
const workQueue = [...tasks];
const runningWorkers = [];
const processTask = async () => {
if (workQueue.length === 0)
return;
const task = workQueue.shift();
console.info(`\u5F00\u59CB\u5904\u7406\u4EFB\u52A1: ${task.id}`);
try {
const resultPromise = this.runSingleTask(task.data, workerScript);
const timeoutPromise = new Promise((_, reject) => {
setTimeout(
() => reject(new Error(`\u4EFB\u52A1\u8D85\u65F6: ${task.id}`)),
timeoutMs
);
});
results[task.id] = await Promise.race([resultPromise, timeoutPromise]);
console.info(`\u4EFB\u52A1\u5B8C\u6210: ${task.id}`);
} catch (error) {
console.error(`\u4EFB\u52A1\u5931\u8D25 ${task.id}: ${error.message}`);
results[task.id] = { error: error.message };
}
if (workQueue.length > 0) {
runningWorkers.push(processTask());
}
};
for (let i = 0; i < maxConcurrent; i++) {
if (workQueue.length > 0) {
runningWorkers.push(processTask());
}
}
await Promise.all(runningWorkers);
return results;
}
/**
* 增量分析,只处理有变化的文件
* @param files 文件列表
* @param processFn 处理函数
* @returns 处理结果
*/
async incrementalProcess(files, processFn) {
const results = {};
for (const file of files) {
const cacheKey = `incremental:${path11__namespace.default.basename(file)}`;
results[file] = await this.withCache(cacheKey, () => processFn(file), {
checkSourceChange: true,
ttl: this.config.performance?.cacheTTL || 86400
});
}
return results;
}
/**
* 在工作线程中运行单个任务
*/
async runSingleTask(data, workerScript) {
return new Promise((resolve2, reject) => {
const worker = new worker_threads.Worker(workerScript, {
workerData: data
});
worker.on("message", (result) => {
resolve2(result);
});
worker.on("error", (err) => {
reject(err);
});
worker.on("exit", (code) => {
if (code !== 0) {
reject(new Error(`\u5DE5\u4F5C\u7EBF\u7A0B\u9000\u51FA\uFF0C\u9000\u51FA\u7801: ${code}`));
}
});
});
}
/**
* 获取缓存文件路径
*/
getCacheFilePath(key) {
const safeKey = key.replace(/[^a-z0-9]/gi, "_").toLowerCase();
return path11__namespace.default.join(this.cacheDir, `${safeKey}.json`);
}
/**
* 确保缓存目录存在
*/
ensureCacheDir() {
if (!fs7__namespace.default.existsSync(this.cacheDir)) {
fs7__namespace.default.mkdirSync(this.cacheDir, { recursive: true });
}
}
/**
* 获取文件的哈希值
*/
getFileHash(filePath) {
try {
const content = fs7__namespace.default.readFileSync(filePath);
return crypto__default.default.createHash("md5").update(content).digest("hex");
} catch (error) {
return "";
}
}
/**
* 清理过期缓存
* @param maxAgeDays 最大缓存时间(天)
*/
cleanCache(maxAgeDays = 30) {
if (!fs7__namespace.default.existsSync(this.cacheDir)) {
return;
}
const files = fs7__namespace.default.readdirSync(this.cacheDir);
const now = Date.now();
const maxAgeMs = maxAgeDays * 24 * 60 * 60 * 1e3;
let cleanedCount = 0;
for (const file of files) {
const filePath = path11__namespace.default.join(this.cacheDir, file);
try {
const stats = fs7__namespace.default.statSync(filePath);
const fileAge = now - stats.mtimeMs;
if (fileAge > maxAgeMs) {
fs7__namespace.default.unlinkSync(filePath);
cleanedCount++;
}
} catch (error) {
console.warn(`\u6E05\u7406\u7F13\u5B58\u5931\u8D25: ${error.message}`);
}
}
console.info(`\u6E05\u7406\u4E86 ${cleanedCount} \u4E2A\u8FC7\u671F\u7F13\u5B58\u6587\u4EF6`);
}
async computeHash(data) {
return new Promise((resolve2, reject) => {
try {
const dataHash = crypto__default.default.createHash("md5").update(data).digest("hex");
resolve2(dataHash);
} catch (error) {
reject(error);
}
});
}
};
// src/services/dependency-service.ts
var DependencyService = class {
/**
* 构造函数
* @param projectPath 项目路径
* @param config 分析配置
*/
constructor(projectPath, config) {
/**
* 依赖分析器
*/
this.analyzer = null;
this.projectPath = path11__namespace.default.resolve(projectPath);
this.config = config;
this.optimizer = new PerformanceOptimizer(config);
}
/**
* 运行依赖分析
* @returns 分析报告路径或控制台输出
*/
async analyze() {
console.info(`\u5F00\u59CB\u5206\u6790\u9879\u76EE: ${this.projectPath}`);
console.info("\u6B63\u5728\u68C0\u7D22\u9879\u76EE\u6587\u4EF6...");
try {
const files = await this.getProjectFiles();
console.info(`\u627E\u5230 ${files.length} \u4E2A\u6587\u4EF6\u9700\u8981\u5206\u6790`);
this.analyzer = new DependencyAnalyzer(
this.projectPath,
this.config.includeExtensions
);
console.info("\u6B63\u5728\u5206\u6790\u4F9D\u8D56\u5173\u7CFB...");
const analysisResult = await this.optimizer.withCache(
`dependency-analysis:${this.projectPath}`,
async () => {
console.info("\u5904\u7406\u4F9D\u8D56\u5173\u7CFB\u4E2D...");
const result = await this.analyzer.analyze();
console.info("\u4F9D\u8D56\u5206\u6790\u8BA1\u7B97\u5B8C\u6210");
return result;
},
{
ttl: this.config.performance?.cacheTTL || 86400
}
);
console.info("\u5206\u6790\u7ED3\u679C\u5904\u7406\u4E2D...");
console.info(`\u83B7\u53D6\u5230\u5206\u6790\u7ED3\u679C\u5BF9\u8C61: ${analysisResult ? "yes" : "no"}`);
if (analysisResult) {
console.info(`\u5206\u6790\u7ED3\u679C\u7C7B\u578B: ${typeof analysisResult}`);
console.info(`\u5206\u6790\u7ED3\u679C\u5C5E\u6027: ${Object.keys(analysisResult).join(", ")}`);
}
console.info("\u6B63\u5728\u751F\u6210\u62A5\u544A...");
const reportGenerator = new DependencyReportGenerator();
const outputFormat = this.config.outputFormat || "console";
const reportType = this.mapOutputFormatToReportType(outputFormat);
const detailed = true;
const reportPathOrContent = await reportGenerator.generate(
analysisResult,
{
type: reportType,
outputPath: `${this.config.outputPath || "./code-insight-report"}/dependency-analysis.${outputFormat}`,
projectName: this.config.projectName || path11__namespace.default.basename(this.projectPath),
detailed