UNPKG

code-insight-analyst

Version:
1,621 lines (1,613 loc) 76.3 kB
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 = { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&#34;", "'": "&#39;" }; var _MATCH_HTML = /[&<>'"]/g; function encode_char(c) { return _ENCODE_HTML_RULES[c] || c; } var escapeFuncStr = `var _ENCODE_HTML_RULES = { "&": "&amp;" , "<": "&lt;" , ">": "&gt;" , '"': "&#34;" , "'": "&#39;" } , _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) {