code-insight-analyst
Version:
1,621 lines (1,613 loc) • 76.3 kB
JavaScript
import { __commonJS, __require, __toESM } from './chunk-HD6MG2RU.js';
export { run } from './chunk-HD6MG2RU.js';
import * as os from 'os';
import { Worker } from 'worker_threads';
import * as path4 from 'path';
import * as events from 'events';
import { fileURLToPath } from 'url';
import * as fs3 from 'fs';
import { promises } from 'fs';
import chalk from 'chalk';
import { Table } from 'console-table-printer';
import * as crypto from 'crypto';
import { execSync } from 'child_process';
// node_modules/.pnpm/ejs@3.1.10/node_modules/ejs/lib/utils.js
var require_utils = __commonJS({
"node_modules/.pnpm/ejs@3.1.10/node_modules/ejs/lib/utils.js"(exports) {
var regExpChars = /[|\\{}()[\]^$+*?.]/g;
var hasOwnProperty = Object.prototype.hasOwnProperty;
var hasOwn = function(obj, key) {
return hasOwnProperty.apply(obj, [key]);
};
exports.escapeRegExpChars = function(string) {
if (!string) {
return "";
}
return String(string).replace(regExpChars, "\\$&");
};
var _ENCODE_HTML_RULES = {
"&": "&",
"<": "<",
">": ">",
'"': """,
"'": "'"
};
var _MATCH_HTML = /[&<>'"]/g;
function encode_char(c) {
return _ENCODE_HTML_RULES[c] || c;
}
var escapeFuncStr = `var _ENCODE_HTML_RULES = {
"&": "&"
, "<": "<"
, ">": ">"
, '"': """
, "'": "'"
}
, _MATCH_HTML = /[&<>'"]/g;
function encode_char(c) {
return _ENCODE_HTML_RULES[c] || c;
};
`;
exports.escapeXML = function(markup) {
return markup == void 0 ? "" : String(markup).replace(_MATCH_HTML, encode_char);
};
function escapeXMLToString() {
return Function.prototype.toString.call(this) + ";\n" + escapeFuncStr;
}
try {
if (typeof Object.defineProperty === "function") {
Object.defineProperty(exports.escapeXML, "toString", { value: escapeXMLToString });
} else {
exports.escapeXML.toString = escapeXMLToString;
}
} catch (err) {
console.warn("Unable to set escapeXML.toString (is the Function prototype frozen?)");
}
exports.shallowCopy = function(to, from) {
from = from || {};
if (to !== null && to !== void 0) {
for (var p in from) {
if (!hasOwn(from, p)) {
continue;
}
if (p === "__proto__" || p === "constructor") {
continue;
}
to[p] = from[p];
}
}
return to;
};
exports.shallowCopyFromList = function(to, from, list) {
list = list || [];
from = from || {};
if (to !== null && to !== void 0) {
for (var i = 0; i < list.length; i++) {
var p = list[i];
if (typeof from[p] != "undefined") {
if (!hasOwn(from, p)) {
continue;
}
if (p === "__proto__" || p === "constructor") {
continue;
}
to[p] = from[p];
}
}
}
return to;
};
exports.cache = {
_data: {},
set: function(key, val) {
this._data[key] = val;
},
get: function(key) {
return this._data[key];
},
remove: function(key) {
delete this._data[key];
},
reset: function() {
this._data = {};
}
};
exports.hyphenToCamel = function(str) {
return str.replace(/-[a-z]/g, function(match) {
return match[1].toUpperCase();
});
};
exports.createNullProtoObjWherePossible = function() {
if (typeof Object.create == "function") {
return function() {
return /* @__PURE__ */ Object.create(null);
};
}
if (!({ __proto__: null } instanceof Object)) {
return function() {
return { __proto__: null };
};
}
return function() {
return {};
};
}();
exports.hasOwnOnlyObject = function(obj) {
var o = exports.createNullProtoObjWherePossible();
for (var p in obj) {
if (hasOwn(obj, p)) {
o[p] = obj[p];
}
}
return o;
};
}
});
// node_modules/.pnpm/ejs@3.1.10/node_modules/ejs/package.json
var require_package = __commonJS({
"node_modules/.pnpm/ejs@3.1.10/node_modules/ejs/package.json"(exports, module) {
module.exports = {
name: "ejs",
description: "Embedded JavaScript templates",
keywords: [
"template",
"engine",
"ejs"
],
version: "3.1.10",
author: "Matthew Eernisse <mde@fleegix.org> (http://fleegix.org)",
license: "Apache-2.0",
bin: {
ejs: "./bin/cli.js"
},
main: "./lib/ejs.js",
jsdelivr: "ejs.min.js",
unpkg: "ejs.min.js",
repository: {
type: "git",
url: "git://github.com/mde/ejs.git"
},
bugs: "https://github.com/mde/ejs/issues",
homepage: "https://github.com/mde/ejs",
dependencies: {
jake: "^10.8.5"
},
devDependencies: {
browserify: "^16.5.1",
eslint: "^6.8.0",
"git-directory-deploy": "^1.5.1",
jsdoc: "^4.0.2",
"lru-cache": "^4.0.1",
mocha: "^10.2.0",
"uglify-js": "^3.3.16"
},
engines: {
node: ">=0.10.0"
},
scripts: {
test: "npx jake test"
}
};
}
});
// node_modules/.pnpm/ejs@3.1.10/node_modules/ejs/lib/ejs.js
var require_ejs = __commonJS({
"node_modules/.pnpm/ejs@3.1.10/node_modules/ejs/lib/ejs.js"(exports) {
var fs4 = __require("fs");
var path6 = __require("path");
var utils = require_utils();
var scopeOptionWarned = false;
var _VERSION_STRING = require_package().version;
var _DEFAULT_OPEN_DELIMITER = "<";
var _DEFAULT_CLOSE_DELIMITER = ">";
var _DEFAULT_DELIMITER = "%";
var _DEFAULT_LOCALS_NAME = "locals";
var _NAME = "ejs";
var _REGEX_STRING = "(<%%|%%>|<%=|<%-|<%_|<%#|<%|%>|-%>|_%>)";
var _OPTS_PASSABLE_WITH_DATA = [
"delimiter",
"scope",
"context",
"debug",
"compileDebug",
"client",
"_with",
"rmWhitespace",
"strict",
"filename",
"async"
];
var _OPTS_PASSABLE_WITH_DATA_EXPRESS = _OPTS_PASSABLE_WITH_DATA.concat("cache");
var _BOM = /^\uFEFF/;
var _JS_IDENTIFIER = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/;
exports.cache = utils.cache;
exports.fileLoader = fs4.readFileSync;
exports.localsName = _DEFAULT_LOCALS_NAME;
exports.promiseImpl = new Function("return this;")().Promise;
exports.resolveInclude = function(name, filename, isDir) {
var dirname4 = path6.dirname;
var extname2 = path6.extname;
var resolve = path6.resolve;
var includePath = resolve(isDir ? filename : dirname4(filename), name);
var ext = extname2(name);
if (!ext) {
includePath += ".ejs";
}
return includePath;
};
function resolvePaths(name, paths) {
var filePath;
if (paths.some(function(v) {
filePath = exports.resolveInclude(name, v, true);
return fs4.existsSync(filePath);
})) {
return filePath;
}
}
function getIncludePath(path7, options) {
var includePath;
var filePath;
var views = options.views;
var match = /^[A-Za-z]+:\\|^\//.exec(path7);
if (match && match.length) {
path7 = path7.replace(/^\/*/, "");
if (Array.isArray(options.root)) {
includePath = resolvePaths(path7, options.root);
} else {
includePath = exports.resolveInclude(path7, options.root || "/", true);
}
} else {
if (options.filename) {
filePath = exports.resolveInclude(path7, options.filename);
if (fs4.existsSync(filePath)) {
includePath = filePath;
}
}
if (!includePath && Array.isArray(views)) {
includePath = resolvePaths(path7, views);
}
if (!includePath && typeof options.includer !== "function") {
throw new Error('Could not find the include file "' + options.escapeFunction(path7) + '"');
}
}
return includePath;
}
function handleCache(options, template) {
var func;
var filename = options.filename;
var hasTemplate = arguments.length > 1;
if (options.cache) {
if (!filename) {
throw new Error("cache option requires a filename");
}
func = exports.cache.get(filename);
if (func) {
return func;
}
if (!hasTemplate) {
template = fileLoader(filename).toString().replace(_BOM, "");
}
} else if (!hasTemplate) {
if (!filename) {
throw new Error("Internal EJS error: no file name or template provided");
}
template = fileLoader(filename).toString().replace(_BOM, "");
}
func = exports.compile(template, options);
if (options.cache) {
exports.cache.set(filename, func);
}
return func;
}
function tryHandleCache(options, data, cb) {
var result;
if (!cb) {
if (typeof exports.promiseImpl == "function") {
return new exports.promiseImpl(function(resolve, reject) {
try {
result = handleCache(options)(data);
resolve(result);
} catch (err) {
reject(err);
}
});
} else {
throw new Error("Please provide a callback function");
}
} else {
try {
result = handleCache(options)(data);
} catch (err) {
return cb(err);
}
cb(null, result);
}
}
function fileLoader(filePath) {
return exports.fileLoader(filePath);
}
function includeFile(path7, options) {
var opts = utils.shallowCopy(utils.createNullProtoObjWherePossible(), options);
opts.filename = getIncludePath(path7, opts);
if (typeof options.includer === "function") {
var includerResult = options.includer(path7, opts.filename);
if (includerResult) {
if (includerResult.filename) {
opts.filename = includerResult.filename;
}
if (includerResult.template) {
return handleCache(opts, includerResult.template);
}
}
}
return handleCache(opts);
}
function rethrow(err, str, flnm, lineno, esc) {
var lines = str.split("\n");
var start = Math.max(lineno - 3, 0);
var end = Math.min(lines.length, lineno + 3);
var filename = esc(flnm);
var context = lines.slice(start, end).map(function(line, i) {
var curr = i + start + 1;
return (curr == lineno ? " >> " : " ") + curr + "| " + line;
}).join("\n");
err.path = filename;
err.message = (filename || "ejs") + ":" + lineno + "\n" + context + "\n\n" + err.message;
throw err;
}
function stripSemi(str) {
return str.replace(/;(\s*$)/, "$1");
}
exports.compile = function compile(template, opts) {
var templ;
if (opts && opts.scope) {
if (!scopeOptionWarned) {
console.warn("`scope` option is deprecated and will be removed in EJS 3");
scopeOptionWarned = true;
}
if (!opts.context) {
opts.context = opts.scope;
}
delete opts.scope;
}
templ = new Template(template, opts);
return templ.compile();
};
exports.render = function(template, d, o) {
var data = d || utils.createNullProtoObjWherePossible();
var opts = o || utils.createNullProtoObjWherePossible();
if (arguments.length == 2) {
utils.shallowCopyFromList(opts, data, _OPTS_PASSABLE_WITH_DATA);
}
return handleCache(opts, template)(data);
};
exports.renderFile = function() {
var args = Array.prototype.slice.call(arguments);
var filename = args.shift();
var cb;
var opts = { filename };
var data;
var viewOpts;
if (typeof arguments[arguments.length - 1] == "function") {
cb = args.pop();
}
if (args.length) {
data = args.shift();
if (args.length) {
utils.shallowCopy(opts, args.pop());
} else {
if (data.settings) {
if (data.settings.views) {
opts.views = data.settings.views;
}
if (data.settings["view cache"]) {
opts.cache = true;
}
viewOpts = data.settings["view options"];
if (viewOpts) {
utils.shallowCopy(opts, viewOpts);
}
}
utils.shallowCopyFromList(opts, data, _OPTS_PASSABLE_WITH_DATA_EXPRESS);
}
opts.filename = filename;
} else {
data = utils.createNullProtoObjWherePossible();
}
return tryHandleCache(opts, data, cb);
};
exports.Template = Template;
exports.clearCache = function() {
exports.cache.reset();
};
function Template(text, optsParam) {
var opts = utils.hasOwnOnlyObject(optsParam);
var options = utils.createNullProtoObjWherePossible();
this.templateText = text;
this.mode = null;
this.truncate = false;
this.currentLine = 1;
this.source = "";
options.client = opts.client || false;
options.escapeFunction = opts.escape || opts.escapeFunction || utils.escapeXML;
options.compileDebug = opts.compileDebug !== false;
options.debug = !!opts.debug;
options.filename = opts.filename;
options.openDelimiter = opts.openDelimiter || exports.openDelimiter || _DEFAULT_OPEN_DELIMITER;
options.closeDelimiter = opts.closeDelimiter || exports.closeDelimiter || _DEFAULT_CLOSE_DELIMITER;
options.delimiter = opts.delimiter || exports.delimiter || _DEFAULT_DELIMITER;
options.strict = opts.strict || false;
options.context = opts.context;
options.cache = opts.cache || false;
options.rmWhitespace = opts.rmWhitespace;
options.root = opts.root;
options.includer = opts.includer;
options.outputFunctionName = opts.outputFunctionName;
options.localsName = opts.localsName || exports.localsName || _DEFAULT_LOCALS_NAME;
options.views = opts.views;
options.async = opts.async;
options.destructuredLocals = opts.destructuredLocals;
options.legacyInclude = typeof opts.legacyInclude != "undefined" ? !!opts.legacyInclude : true;
if (options.strict) {
options._with = false;
} else {
options._with = typeof opts._with != "undefined" ? opts._with : true;
}
this.opts = options;
this.regex = this.createRegex();
}
Template.modes = {
EVAL: "eval",
ESCAPED: "escaped",
RAW: "raw",
COMMENT: "comment",
LITERAL: "literal"
};
Template.prototype = {
createRegex: function() {
var str = _REGEX_STRING;
var delim = utils.escapeRegExpChars(this.opts.delimiter);
var open = utils.escapeRegExpChars(this.opts.openDelimiter);
var close = utils.escapeRegExpChars(this.opts.closeDelimiter);
str = str.replace(/%/g, delim).replace(/</g, open).replace(/>/g, close);
return new RegExp(str);
},
compile: function() {
var src;
var fn;
var opts = this.opts;
var prepended = "";
var appended = "";
var escapeFn = opts.escapeFunction;
var ctor;
var sanitizedFilename = opts.filename ? JSON.stringify(opts.filename) : "undefined";
if (!this.source) {
this.generateSource();
prepended += ' var __output = "";\n function __append(s) { if (s !== undefined && s !== null) __output += s }\n';
if (opts.outputFunctionName) {
if (!_JS_IDENTIFIER.test(opts.outputFunctionName)) {
throw new Error("outputFunctionName is not a valid JS identifier.");
}
prepended += " var " + opts.outputFunctionName + " = __append;\n";
}
if (opts.localsName && !_JS_IDENTIFIER.test(opts.localsName)) {
throw new Error("localsName is not a valid JS identifier.");
}
if (opts.destructuredLocals && opts.destructuredLocals.length) {
var destructuring = " var __locals = (" + opts.localsName + " || {}),\n";
for (var i = 0; i < opts.destructuredLocals.length; i++) {
var name = opts.destructuredLocals[i];
if (!_JS_IDENTIFIER.test(name)) {
throw new Error("destructuredLocals[" + i + "] is not a valid JS identifier.");
}
if (i > 0) {
destructuring += ",\n ";
}
destructuring += name + " = __locals." + name;
}
prepended += destructuring + ";\n";
}
if (opts._with !== false) {
prepended += " with (" + opts.localsName + " || {}) {\n";
appended += " }\n";
}
appended += " return __output;\n";
this.source = prepended + this.source + appended;
}
if (opts.compileDebug) {
src = "var __line = 1\n , __lines = " + JSON.stringify(this.templateText) + "\n , __filename = " + sanitizedFilename + ";\ntry {\n" + this.source + "} catch (e) {\n rethrow(e, __lines, __filename, __line, escapeFn);\n}\n";
} else {
src = this.source;
}
if (opts.client) {
src = "escapeFn = escapeFn || " + escapeFn.toString() + ";\n" + src;
if (opts.compileDebug) {
src = "rethrow = rethrow || " + rethrow.toString() + ";\n" + src;
}
}
if (opts.strict) {
src = '"use strict";\n' + src;
}
if (opts.debug) {
console.log(src);
}
if (opts.compileDebug && opts.filename) {
src = src + "\n//# sourceURL=" + sanitizedFilename + "\n";
}
try {
if (opts.async) {
try {
ctor = new Function("return (async function(){}).constructor;")();
} catch (e) {
if (e instanceof SyntaxError) {
throw new Error("This environment does not support async/await");
} else {
throw e;
}
}
} else {
ctor = Function;
}
fn = new ctor(opts.localsName + ", escapeFn, include, rethrow", src);
} catch (e) {
if (e instanceof SyntaxError) {
if (opts.filename) {
e.message += " in " + opts.filename;
}
e.message += " while compiling ejs\n\n";
e.message += "If the above error is not helpful, you may want to try EJS-Lint:\n";
e.message += "https://github.com/RyanZim/EJS-Lint";
if (!opts.async) {
e.message += "\n";
e.message += "Or, if you meant to create an async function, pass `async: true` as an option.";
}
}
throw e;
}
var returnedFn = opts.client ? fn : function anonymous(data) {
var include = function(path7, includeData) {
var d = utils.shallowCopy(utils.createNullProtoObjWherePossible(), data);
if (includeData) {
d = utils.shallowCopy(d, includeData);
}
return includeFile(path7, opts)(d);
};
return fn.apply(
opts.context,
[data || utils.createNullProtoObjWherePossible(), escapeFn, include, rethrow]
);
};
if (opts.filename && typeof Object.defineProperty === "function") {
var filename = opts.filename;
var basename = path6.basename(filename, path6.extname(filename));
try {
Object.defineProperty(returnedFn, "name", {
value: basename,
writable: false,
enumerable: false,
configurable: true
});
} catch (e) {
}
}
return returnedFn;
},
generateSource: function() {
var opts = this.opts;
if (opts.rmWhitespace) {
this.templateText = this.templateText.replace(/[\r\n]+/g, "\n").replace(/^\s+|\s+$/gm, "");
}
this.templateText = this.templateText.replace(/[ \t]*<%_/gm, "<%_").replace(/_%>[ \t]*/gm, "_%>");
var self = this;
var matches = this.parseTemplateText();
var d = this.opts.delimiter;
var o = this.opts.openDelimiter;
var c = this.opts.closeDelimiter;
if (matches && matches.length) {
matches.forEach(function(line, index) {
var closing;
if (line.indexOf(o + d) === 0 && line.indexOf(o + d + d) !== 0) {
closing = matches[index + 2];
if (!(closing == d + c || closing == "-" + d + c || closing == "_" + d + c)) {
throw new Error('Could not find matching close tag for "' + line + '".');
}
}
self.scanLine(line);
});
}
},
parseTemplateText: function() {
var str = this.templateText;
var pat = this.regex;
var result = pat.exec(str);
var arr = [];
var firstPos;
while (result) {
firstPos = result.index;
if (firstPos !== 0) {
arr.push(str.substring(0, firstPos));
str = str.slice(firstPos);
}
arr.push(result[0]);
str = str.slice(result[0].length);
result = pat.exec(str);
}
if (str) {
arr.push(str);
}
return arr;
},
_addOutput: function(line) {
if (this.truncate) {
line = line.replace(/^(?:\r\n|\r|\n)/, "");
this.truncate = false;
}
if (!line) {
return line;
}
line = line.replace(/\\/g, "\\\\");
line = line.replace(/\n/g, "\\n");
line = line.replace(/\r/g, "\\r");
line = line.replace(/"/g, '\\"');
this.source += ' ; __append("' + line + '")\n';
},
scanLine: function(line) {
var self = this;
var d = this.opts.delimiter;
var o = this.opts.openDelimiter;
var c = this.opts.closeDelimiter;
var newLineCount = 0;
newLineCount = line.split("\n").length - 1;
switch (line) {
case o + d:
case o + d + "_":
this.mode = Template.modes.EVAL;
break;
case o + d + "=":
this.mode = Template.modes.ESCAPED;
break;
case o + d + "-":
this.mode = Template.modes.RAW;
break;
case o + d + "#":
this.mode = Template.modes.COMMENT;
break;
case o + d + d:
this.mode = Template.modes.LITERAL;
this.source += ' ; __append("' + line.replace(o + d + d, o + d) + '")\n';
break;
case d + d + c:
this.mode = Template.modes.LITERAL;
this.source += ' ; __append("' + line.replace(d + d + c, d + c) + '")\n';
break;
case d + c:
case "-" + d + c:
case "_" + d + c:
if (this.mode == Template.modes.LITERAL) {
this._addOutput(line);
}
this.mode = null;
this.truncate = line.indexOf("-") === 0 || line.indexOf("_") === 0;
break;
default:
if (this.mode) {
switch (this.mode) {
case Template.modes.EVAL:
case Template.modes.ESCAPED:
case Template.modes.RAW:
if (line.lastIndexOf("//") > line.lastIndexOf("\n")) {
line += "\n";
}
}
switch (this.mode) {
case Template.modes.EVAL:
this.source += " ; " + line + "\n";
break;
case Template.modes.ESCAPED:
this.source += " ; __append(escapeFn(" + stripSemi(line) + "))\n";
break;
case Template.modes.RAW:
this.source += " ; __append(" + stripSemi(line) + ")\n";
break;
case Template.modes.COMMENT:
break;
case Template.modes.LITERAL:
this._addOutput(line);
break;
}
} else {
this._addOutput(line);
}
}
if (self.opts.compileDebug && newLineCount) {
this.currentLine += newLineCount;
this.source += " ; __line = " + this.currentLine + "\n";
}
}
};
exports.escapeXML = utils.escapeXML;
exports.__express = exports.renderFile;
exports.VERSION = _VERSION_STRING;
exports.name = _NAME;
if (typeof window != "undefined") {
window.ejs = exports;
}
}
});
// src/core/engine.ts
var AnalysisEngine = class {
constructor(options) {
this.options = options;
}
/**
* 执行分析
*/
async analyze() {
console.log(`\u5206\u6790\u76EE\u5F55: ${this.options.directory}`);
console.log(`\u5206\u6790\u7C7B\u578B: ${this.options.analysisTypes.join(", ")}`);
return {};
}
};
var __filename = fileURLToPath(import.meta.url);
var __dirname = path4.dirname(__filename);
var ParallelProcessor = class {
/**
* 构造函数
*
* @param maxWorkers - 最大工作线程数
* @param workerScript - 工作线程脚本路径
*/
constructor(maxWorkers = os.cpus().length, workerScript = path4.join(__dirname, "worker.js")) {
this.maxWorkers = maxWorkers;
this.workerScript = workerScript;
/**
* 工作线程池
*/
this.workers = [];
/**
* 任务队列
*/
this.taskQueue = [];
/**
* 任务结果
*/
this.results = /* @__PURE__ */ new Map();
/**
* 事件发射器
*/
this.emitter = new events.EventEmitter();
/**
* 正在处理中的任务数
*/
this.processingTasks = 0;
/**
* 是否正在运行
*/
this.isRunning = false;
this.emitter.setMaxListeners(this.maxWorkers * 2);
}
/**
* 初始化工作线程池
*/
initializeWorkers() {
for (let i = 0; i < this.maxWorkers; i++) {
const worker = new Worker(this.workerScript);
worker.on("message", (result) => {
this.results.set(result.taskId, result);
this.emitter.emit(`task-completed:${result.taskId}`, result);
this.emitter.emit("task-completed", result);
this.processingTasks--;
this.processNextTask(worker);
});
worker.on("error", (err) => {
console.error(`\u5DE5\u4F5C\u7EBF\u7A0B\u9519\u8BEF: ${err}`);
worker.terminate().catch(console.error);
const newWorker = new Worker(this.workerScript);
this.workers[this.workers.indexOf(worker)] = newWorker;
this.processingTasks--;
});
this.workers.push(worker);
}
}
/**
* 添加任务
*
* @param task - 任务
* @returns 任务ID
*/
addTask(task) {
this.taskQueue.push(task);
this.taskQueue.sort((a, b) => (a.priority || 0) - (b.priority || 0));
return task.id;
}
/**
* 添加多个任务
*
* @param tasks - 任务列表
* @returns 任务ID列表
*/
addTasks(tasks) {
const taskIds = tasks.map((task) => task.id);
this.taskQueue.push(...tasks);
this.taskQueue.sort((a, b) => (a.priority || 0) - (b.priority || 0));
return taskIds;
}
/**
* 处理下一个任务
*
* @param worker - 工作线程
*/
processNextTask(worker) {
if (!this.isRunning || this.taskQueue.length === 0) {
return;
}
const task = this.taskQueue.shift();
if (!task) {
return;
}
this.processingTasks++;
worker.postMessage(task);
}
/**
* 启动处理器
*/
start() {
if (this.isRunning) {
return;
}
this.isRunning = true;
if (this.workers.length === 0) {
this.initializeWorkers();
}
for (const worker of this.workers) {
if (this.taskQueue.length > 0) {
this.processNextTask(worker);
}
}
}
/**
* 停止处理器
*/
async stop() {
this.isRunning = false;
await Promise.all(this.workers.map((worker) => worker.terminate()));
this.workers = [];
}
/**
* 等待任务完成
*
* @param taskId - 任务ID
* @returns 任务结果
*/
async waitForTask(taskId) {
if (this.results.has(taskId)) {
return this.results.get(taskId);
}
return new Promise((resolve) => {
this.emitter.once(`task-completed:${taskId}`, (result) => {
resolve(result);
});
});
}
/**
* 等待所有任务完成
*
* @returns 所有任务的结果
*/
async waitForAll() {
if (this.taskQueue.length === 0 && this.processingTasks === 0) {
return Array.from(this.results.values());
}
return new Promise((resolve) => {
const checkComplete = () => {
if (this.taskQueue.length === 0 && this.processingTasks === 0) {
resolve(Array.from(this.results.values()));
}
};
checkComplete();
this.emitter.on("task-completed", () => {
checkComplete();
});
});
}
/**
* 执行任务集合
*
* @param tasks - 任务列表
* @returns 任务结果列表
*/
async execute(tasks) {
this.results.clear();
this.addTasks(tasks);
this.start();
const results = await this.waitForAll();
return results;
}
/**
* 获取待处理任务数量
*
* @returns 待处理任务数量
*/
getPendingTaskCount() {
return this.taskQueue.length;
}
/**
* 获取处理中任务数量
*
* @returns 处理中任务数量
*/
getProcessingTaskCount() {
return this.processingTasks;
}
};
// src/report/formatters/html.ts
var ejs = __toESM(require_ejs(), 1);
var BaseReportGenerator = class {
constructor(options = {}) {
this.options = {
outputPath: "./reports",
title: "\u4EE3\u7801\u5206\u6790\u62A5\u544A",
detailed: true,
includeCharts: true,
...options,
timestamp: options.timestamp || /* @__PURE__ */ new Date()
};
}
/**
* 确保输出目录存在
*/
async ensureOutputDir() {
const outputDir = this.options.outputPath || "./reports";
if (!fs3.existsSync(outputDir)) {
await promises.mkdir(outputDir, { recursive: true });
}
}
/**
* 格式化日期时间
* @param date 日期对象
* @returns 格式化的日期时间字符串
*/
formatDateTime(date) {
return date.toISOString().replace(/T/, " ").replace(/\..+/, "");
}
/**
* 格式化持续时间
* @param ms 毫秒数
* @returns 格式化的持续时间字符串
*/
formatDuration(ms) {
if (ms < 1e3) {
return `${ms}ms`;
}
const seconds = Math.floor(ms / 1e3);
if (seconds < 60) {
return `${seconds}s`;
}
const minutes = Math.floor(seconds / 60);
const remainingSeconds = seconds % 60;
return `${minutes}m ${remainingSeconds}s`;
}
/**
* 生成报告文件名
* @param extension 文件扩展名
* @returns 报告文件名
*/
getReportFileName(extension) {
const timestamp = this.options.timestamp || /* @__PURE__ */ new Date();
const dateString = timestamp.toISOString().replace(/:/g, "-").replace(/\..+/, "");
const projectName = this.options.projectName || "project";
const sanitizedProjectName = projectName.replace(/[^\w-]/g, "_");
return `${sanitizedProjectName}-report-${dateString}.${extension}`;
}
/**
* 生成报告完整路径
* @param fileName 文件名
* @returns 报告完整路径
*/
getReportPath(fileName) {
return path4.join(this.options.outputPath || "./reports", fileName);
}
/**
* 分析代码覆盖率图表数据
* @param results 分析结果
* @returns 覆盖率图表数据
*/
getCoverageChartData(results) {
if (!results.coverage || results.coverage.length === 0) {
return null;
}
const avgCoverage = {
line: 0,
statement: 0,
branch: 0,
function: 0
};
results.coverage.forEach((file) => {
avgCoverage.line += file.lineCoverage;
avgCoverage.statement += file.statementCoverage;
avgCoverage.branch += file.branchCoverage;
avgCoverage.function += file.functionCoverage;
});
const fileCount = results.coverage.length;
avgCoverage.line /= fileCount;
avgCoverage.statement /= fileCount;
avgCoverage.branch /= fileCount;
avgCoverage.function /= fileCount;
return {
title: "\u4EE3\u7801\u8986\u76D6\u7387\u6982\u89C8",
type: "bar",
labels: ["\u884C\u8986\u76D6\u7387", "\u8BED\u53E5\u8986\u76D6\u7387", "\u5206\u652F\u8986\u76D6\u7387", "\u51FD\u6570\u8986\u76D6\u7387"],
datasets: [
{
label: "\u8986\u76D6\u7387\u767E\u5206\u6BD4",
data: [
avgCoverage.line * 100,
avgCoverage.statement * 100,
avgCoverage.branch * 100,
avgCoverage.function * 100
],
backgroundColor: [
"rgba(54, 162, 235, 0.7)",
"rgba(75, 192, 192, 0.7)",
"rgba(255, 206, 86, 0.7)",
"rgba(153, 102, 255, 0.7)"
]
}
]
};
}
/**
* 分析重复代码图表数据
* @param results 分析结果
* @returns 重复代码图表数据
*/
getDuplicatesChartData(results) {
if (!results.duplicates) {
return null;
}
const dupeRate = results.duplicates.totalDuplicationRate * 100;
const uniqueRate = 100 - dupeRate;
return {
title: "\u4EE3\u7801\u91CD\u590D\u5EA6\u5206\u6790",
type: "pie",
labels: ["\u552F\u4E00\u4EE3\u7801", "\u91CD\u590D\u4EE3\u7801"],
datasets: [
{
data: [uniqueRate, dupeRate],
backgroundColor: [
"rgba(75, 192, 192, 0.7)",
"rgba(255, 99, 132, 0.7)"
]
}
]
};
}
/**
* 分析未使用代码图表数据
* @param results 分析结果
* @returns 未使用代码图表数据
*/
getUnusedCodeChartData(results) {
if (!results.unusedCode) {
return null;
}
const unusedCode = results.unusedCode;
return {
title: "\u672A\u4F7F\u7528\u4EE3\u7801\u5206\u6790",
type: "bar",
labels: [
"\u672A\u4F7F\u7528\u5BFC\u5165",
"\u672A\u4F7F\u7528\u53D8\u91CF",
"\u672A\u4F7F\u7528\u51FD\u6570",
"\u672A\u4F7F\u7528\u7C7B",
"\u672A\u4F7F\u7528\u5BFC\u51FA"
],
datasets: [
{
label: "\u6570\u91CF",
data: [
unusedCode.unusedImports.length,
unusedCode.unusedVariables.length,
unusedCode.unusedFunctions.length,
unusedCode.unusedClasses.length,
unusedCode.unusedExports.length
],
backgroundColor: "rgba(255, 99, 132, 0.7)"
}
]
};
}
/**
* 分析依赖图表数据
* @param results 分析结果
* @returns 依赖图表数据
*/
getDependencyChartData(results) {
if (!results.dependencies) {
return null;
}
const dependencies = results.dependencies;
return {
title: "\u4F9D\u8D56\u5206\u6790",
type: "bar",
labels: ["\u5FAA\u73AF\u4F9D\u8D56", "\u672A\u4F7F\u7528\u4F9D\u8D56", "\u7F3A\u5931\u4F9D\u8D56"],
datasets: [
{
label: "\u6570\u91CF",
data: [
dependencies.circularDependencies.length,
dependencies.unusedDependencies.length,
dependencies.missingDependencies.length
],
backgroundColor: [
"rgba(255, 99, 132, 0.7)",
"rgba(255, 206, 86, 0.7)",
"rgba(255, 159, 64, 0.7)"
]
}
]
};
}
/**
* 分析风险图表数据
* @param results 分析结果
* @returns 风险图表数据
*/
getRiskChartData(results) {
const memoryLeakCount = results.memoryLeaks?.potentialLeaks.length || 0;
const infiniteLoopCount = results.infiniteLoops?.potentialInfiniteLoops.length || 0;
if (memoryLeakCount === 0 && infiniteLoopCount === 0) {
return null;
}
const riskLevels = {
low: 0,
medium: 0,
high: 0
};
if (results.memoryLeaks) {
results.memoryLeaks.potentialLeaks.forEach((leak) => {
riskLevels[leak.riskLevel]++;
});
}
if (results.infiniteLoops) {
results.infiniteLoops.potentialInfiniteLoops.forEach((loop) => {
riskLevels[loop.riskLevel]++;
});
}
return {
title: "\u4EE3\u7801\u98CE\u9669\u5206\u6790",
type: "doughnut",
labels: ["\u9AD8\u98CE\u9669", "\u4E2D\u98CE\u9669", "\u4F4E\u98CE\u9669"],
datasets: [
{
data: [riskLevels.high, riskLevels.medium, riskLevels.low],
backgroundColor: [
"rgba(255, 99, 132, 0.7)",
"rgba(255, 206, 86, 0.7)",
"rgba(75, 192, 192, 0.7)"
]
}
]
};
}
/**
* 分析增量变化图表数据
* @param results 分析结果
* @returns 增量变化图表数据
*/
getIncrementalChartData(results) {
if (!results.incrementalInfo?.trends) {
return null;
}
const trends = results.incrementalInfo.trends;
const trendValues = [
trends.coverageTrend || 0,
trends.duplicationTrend || 0,
(trends.unusedCodeTrend || 0) * -1,
// 反转,使负值表示好的变化
(trends.circularDependenciesTrend || 0) * -1
// 反转,使负值表示好的变化
];
return {
title: "\u4E0E\u4E0A\u6B21\u5206\u6790\u76F8\u6BD4\u7684\u53D8\u5316\u8D8B\u52BF",
type: "bar",
labels: ["\u8986\u76D6\u7387", "\u91CD\u590D\u4EE3\u7801", "\u672A\u4F7F\u7528\u4EE3\u7801", "\u5FAA\u73AF\u4F9D\u8D56"],
datasets: [
{
label: "\u53D8\u5316\u767E\u5206\u6BD4",
data: trendValues.map((v) => parseFloat(v.toFixed(2))),
backgroundColor: trendValues.map(
(value) => value >= 0 ? "rgba(75, 192, 192, 0.7)" : "rgba(255, 99, 132, 0.7)"
)
}
]
};
}
/**
* 准备所有图表数据
* @param results 分析结果
* @returns 所有图表数据
*/
prepareChartData(results) {
if (!this.options.includeCharts) {
return [];
}
const charts = [
this.getCoverageChartData(results),
this.getDuplicatesChartData(results),
this.getUnusedCodeChartData(results),
this.getDependencyChartData(results),
this.getRiskChartData(results)
];
if (results.incrementalInfo?.trends) {
const incrementalChart = this.getIncrementalChartData(results);
if (incrementalChart) {
charts.push(incrementalChart);
}
}
return charts.filter((chart) => chart !== null);
}
};
// src/report/visualizers/chart-visualizer.ts
var ChartVisualizer = class {
/**
* 生成Chart.js配置
* @param chartData 图表数据
* @returns Chart.js配置对象
*/
static generateChartConfig(chartData) {
const config = {
type: chartData.type,
data: {
labels: chartData.labels,
datasets: chartData.datasets
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: chartData.title,
font: {
size: 16,
weight: "bold"
}
},
legend: {
display: chartData.type !== "bar" || chartData.datasets.length > 1,
position: "bottom"
},
datalabels: {
display: true,
color: "#333",
font: {
weight: "bold"
},
formatter: (value) => {
return chartData.type === "pie" || chartData.type === "doughnut" ? `${value.toFixed(1)}%` : value.toFixed(1);
},
anchor: "end",
align: "end"
},
tooltip: {
enabled: true
}
}
}
};
switch (chartData.type) {
case "bar":
config.options.scales = {
y: {
beginAtZero: true
}
};
break;
case "pie":
case "doughnut":
config.options.cutout = chartData.type === "doughnut" ? "50%" : 0;
break;
}
return config;
}
/**
* 生成HTML内容初始化图表
* @param chartData 图表数据列表
* @returns 包含初始化脚本的HTML字符串
*/
static generateChartInitScript(chartData) {
let script = `
<script>
document.addEventListener('DOMContentLoaded', function() {
// \u6CE8\u518C\u6570\u636E\u6807\u7B7E\u63D2\u4EF6
Chart.register(ChartDataLabels);
`;
chartData.forEach((chart, index) => {
script += `
// \u521D\u59CB\u5316 ${chart.title} \u56FE\u8868
(function() {
const ctx = document.getElementById('chart-${index}').getContext('2d');
const config = ${JSON.stringify(this.generateChartConfig(chart))};
new Chart(ctx, config);
})();
`;
});
script += `
});
</script>
`;
return script;
}
/**
* 生成图表HTML容器
* @param index 图表索引
* @param chartData 图表数据
* @returns 图表容器HTML
*/
static generateChartContainer(index, chartData) {
return `
<div class="chart-container">
<div class="chart-header">
<h2>${chartData.title}</h2>
</div>
<div class="chart-canvas-container">
<canvas id="chart-${index}" class="chart-canvas"></canvas>
</div>
</div>
`;
}
/**
* 将所有图表包装在一个容器中
* @param chartData 图表数据列表
* @returns 包含所有图表的HTML
*/
static wrapChartsInContainer(chartData) {
if (chartData.length === 0) {
return "";
}
const chartRows = this.arrangeChartsInRows(chartData);
return `
<div class="charts-section">
<h2 class="section-title">\u5206\u6790\u56FE\u8868\u53EF\u89C6\u5316</h2>
${chartRows}
</div>
${this.generateChartInitScript(chartData)}
`;
}
/**
* 将图表排列成行
* @param chartData 图表数据列表
* @param chartsPerRow 每行图表数量
* @returns 排列后的HTML
*/
static arrangeChartsInRows(chartData, chartsPerRow = 2) {
let html = "";
for (let i = 0; i < chartData.length; i += chartsPerRow) {
html += '<div class="chart-row">';
for (let j = i; j < i + chartsPerRow && j < chartData.length; j++) {
html += this.generateChartContainer(j, chartData[j]);
}
html += "</div>";
}
return html;
}
};
// src/report/formatters/html.ts
var __filename2 = fileURLToPath(import.meta.url);
var __dirname2 = path4.dirname(__filename2);
var HTMLReportGenerator = class extends BaseReportGenerator {
constructor(options = {}) {
super(options);
this.defaultTemplatePath = path4.join(
__dirname2,
"../templates/html-report.ejs"
);
}
/**
* 生成HTML报告
* @param results 分析结果
* @returns 报告文件路径
*/
async generate(results) {
try {
await this.ensureOutputDir();
const templatePath = this.options.templatePath || this.defaultTemplatePath;
const chartData = this.prepareChartData(results);
const fileName = this.getReportFileName("html");
const outputPath = this.getReportPath(fileName);
const templateContent = await promises.readFile(templatePath, "utf-8");
const chartsHtml = ChartVisualizer.wrapChartsInContainer(chartData);
const htmlContent = await ejs.render(
templateContent,
{
title: this.options.title || "\u4EE3\u7801\u5206\u6790\u62A5\u544A",
projectName: results.projectName,
timestamp: this.formatDateTime(this.options.timestamp || /* @__PURE__ */ new Date()),
results,
chartData: JSON.stringify(chartData),
chartsHtml,
detailed: this.options.detailed,
duration: this.formatDuration(results.stats.duration)
},
{ async: true }
);
await promises.writeFile(outputPath, htmlContent);
return outputPath;
} catch (error) {
console.error("\u751F\u6210HTML\u62A5\u544A\u5931\u8D25:", error);
return null;
}
}
};
var JSONReportGenerator = class extends BaseReportGenerator {
constructor(options = {}) {
super(options);
}
/**
* 生成JSON报告
* @param results 分析结果
* @returns 报告文件路径
*/
async generate(results) {
try {
await this.ensureOutputDir();
const fileName = this.getReportFileName("json");
const outputPath = this.getReportPath(fileName);
const exportData = {
projectName: results.projectName,
timestamp: this.formatDateTime(this.options.timestamp || /* @__PURE__ */ new Date()),
stats: results.stats,
coverage: results.coverage,
duplicates: results.duplicates,
unusedCode: results.unusedCode,
dependencies: results.dependencies,
memoryLeaks: results.memoryLeaks,
infiniteLoops: results.infiniteLoops,
customRules: results.customRules,
incrementalInfo: results.incrementalInfo
};
await promises.writeFile(
outputPath,
JSON.stringify(exportData, null, 2)
);
return outputPath;
} catch (error) {
console.error("\u751F\u6210JSON\u62A5\u544A\u5931\u8D25:", error);
return null;
}
}
};
var ConsoleReportGenerator = class extends BaseReportGenerator {
constructor(options = {}) {
super(options);
}
/**
* 生成控制台报告
* @param results 分析结果
* @returns null (控制台输出不生成文件)
*/
async generate(results) {
try {
this.printHeader(results);
this.printSummary(results);
if (results.coverage && results.coverage.length > 0) {
this.printCoverageSummary(results);
}
if (results.duplicates) {
this.printDuplicatesSummary(results.duplicates);
}
if (results.unusedCode) {
this.printUnusedCodeSummary(results.unusedCode);
}
if (results.dependencies) {
this.printDependenciesSummary(results.dependencies);
}
if (results.memoryLeaks) {
this.printRiskSummary(
"\u5185\u5B58\u6CC4\u6F0F\u98CE\u9669",
results.memoryLeaks.potentialLeaks
);
}
if (results.infiniteLoops) {
this.printRiskSummary(
"\u6F5C\u5728\u6B7B\u5FAA\u73AF",
results.infiniteLoops.potentialInfiniteLoops
);
}
if (results.incrementalInfo?.trends) {
this.printIncrementalInfo(results.incrementalInfo);
}
this.printFooter();
return null;
} catch (error) {
console.error("\u751F\u6210\u63A7\u5236\u53F0\u62A5\u544A\u5931\u8D25:", error);
return null;
}
}
/**
* 打印报告头部
* @param results 分析结果
*/
printHeader(results) {
console.log("\n");
console.log(chalk.bgBlue.white.bold(" \u4EE3\u7801\u5206\u6790\u62A5\u544A "));
console.log(chalk.blue("=".repeat(50)));
console.log(`${chalk.bold("\u9879\u76EE:")} ${results.projectName}`);
console.log(
`${chalk.bold("\u65F6\u95F4:")} ${this.formatDateTime(this.options.timestamp || /* @__PURE__ */ new Date())}`
);
console.log(
`${chalk.bold("\u5206\u6790\u8017\u65F6:")} ${this.formatDuration(results.stats.duration)}`
);
console.log(chalk.blue("=".repeat(50)));
console.log("\n");
}
/**
* 打印总体摘要
* @param results 分析结果
*/
printSummary(results) {
console.log(chalk.bgYellow.black.bold(" \u5206\u6790\u6458\u8981 "));
console.log(`\u603B\u6587\u4EF6\u6570: ${chalk.bold(results.stats.totalFiles.toString())}`);
console.log(`\u603B\u4EE3\u7801\u884C: ${chalk.bold(results.stats.totalLines.toString())}`);
const unusedCount = results.unusedCode ? results.unusedCode.unusedImports.length + results.unusedCode.unusedVariables.length + results.unusedCode.unusedFunctions.length + results.unusedCode.unusedClasses.length + results.unusedCode.unusedExports.length : 0;
const duplicatesCount = results.duplicates ? results.duplicates.duplicates.length : 0;
const circularCount = results.dependencies ? results.dependencies.circularDependencies.length : 0;
const memoryLeaksCount = results.memoryLeaks ? results.memoryLeaks.potentialLeaks.length : 0;
const infiniteLoopsCount = results.infiniteLoops ? results.infiniteLoops.potentialInfiniteLoops.length : 0;
const totalIssues = unusedCount + duplicatesCount + circularCount + memoryLeaksCount + infiniteLoopsCount;
console.log(`\u53D1\u73B0\u95EE\u9898: ${chalk.bold.red(totalIssues.toString())}`);
console.log("\n");
}
/**
* 打印覆盖率摘要
* @param results 分析结果
*/
printCoverageSummary(results) {
if (!results.coverage || results.coverage.length === 0)
return;
console.log(chalk.bgGreen.black.bold(" \u4EE3\u7801\u8986\u76D6\u7387 "));
const avgCoverage = {
line: 0,
statement: 0,
branch: 0,
function: 0
};
results.coverage.forEach((file) => {
avgCoverage.line += file.lineCoverage;
avgCoverage.statement += file.statementCoverage;
avgCoverage.branch += file.branchCoverage;
avgCoverage.function += file.functionCoverage;
});
const fileCount = results.coverage.length;
avgCoverage.line /= fileCount;
avgCoverage.statement /= fileCount;
avgCoverage.branch /= fileCount;
avgCoverage.function /= fileCount;
const formatPercent = (value) => {
const percent = (value * 100).toFixed(2);
const color = value < 0.7 ? "red" : value < 0.8 ? "yellow" : "green";
return chalk[color](`${percent}%`);
};
console.log(`\u884C\u8986\u76D6\u7387: ${formatPercent(avgCoverage.line)}`);
console.log(`\u8BED\u53E5\u8986\u76D6\u7387: ${formatPercent(avgCoverage.statement)}`);
console.log(`\u5206\u652F\u8986\u76D6\u7387: ${formatPercent(avgCoverage.branch)}`);
console.log(`\u51FD\u6570\u8986\u76D6\u7387: ${formatPercent(avgCoverage.function)}`);
console.log("\n");
}
/**
* 打印重复代码摘要
* @param duplicates 重复代码结果
*/
printDuplicatesSummary(duplicates) {
if (!duplicates)
return;
console.log(chalk.bgMagenta.black.bold(" \u4EE3\u7801\u91CD\u590D\u5206\u6790 "));
console.log(
`\u603B\u91CD\u590D\u7387: ${chalk.bold((duplicates.totalDuplicationRate * 100).toFixed(2) + "%")}`
);
console.log(
`\u91CD\u590D\u5757\u6570: ${chalk.bold(duplicates.duplicates.length.toString())}`
);
if (duplicates.duplicates.length > 0 && this.options.detailed) {