ai-worktool 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/user.js ADDED
@@ -0,0 +1,472 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.setEnvironment = setEnvironment;
40
+ exports.getEnvironment = getEnvironment;
41
+ exports.setTokenStorage = setTokenStorage;
42
+ exports.getTokenStorage = getTokenStorage;
43
+ exports.openExternalUrl = openExternalUrl;
44
+ exports.login = login;
45
+ exports.cancelLogin = cancelLogin;
46
+ exports.logout = logout;
47
+ exports.isLoggedIn = isLoggedIn;
48
+ exports.getUserData = getUserData;
49
+ exports.decodeJwt = decodeJwt;
50
+ exports.isJwtExpired = isJwtExpired;
51
+ const http = __importStar(require("http"));
52
+ const url_1 = require("url");
53
+ const crypto_1 = __importDefault(require("crypto"));
54
+ const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
55
+ const util_1 = __importDefault(require("util"));
56
+ const child_process_1 = require("child_process");
57
+ // 转换exec为Promise形式,便于async/await使用
58
+ const execAsync = util_1.default.promisify(child_process_1.exec);
59
+ let tokenStorage;
60
+ let environment;
61
+ function setEnvironment(env) {
62
+ environment = env;
63
+ }
64
+ function getEnvironment() {
65
+ return environment;
66
+ }
67
+ function setTokenStorage(storage) {
68
+ tokenStorage = storage;
69
+ }
70
+ function getTokenStorage() {
71
+ return tokenStorage;
72
+ }
73
+ // 生成随机字符串用于CSRF保护
74
+ function generateState() {
75
+ return crypto_1.default.randomBytes(16).toString('hex');
76
+ }
77
+ let lastLoginServer = null;
78
+ let lastReject = null;
79
+ /**
80
+ * 启动临时HTTP服务器接收登录回调
81
+ * @param state 用于验证的state值
82
+ * @returns 包含token的Promise
83
+ */
84
+ function startCallbackServer(state, port) {
85
+ return new Promise((resolve, reject) => {
86
+ lastReject = reject;
87
+ const server = (lastLoginServer = http.createServer((req, res) => {
88
+ if (!req.url) {
89
+ // eslint-disable-next-line @typescript-eslint/naming-convention
90
+ res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
91
+ res.end('Bad Request');
92
+ return;
93
+ }
94
+ const parsedUrl = new url_1.URL(req.url, `http://${req.headers.host}`);
95
+ const callbackState = parsedUrl.searchParams.get('state');
96
+ const token = parsedUrl.searchParams.get('token');
97
+ // 验证state防止CSRF攻击
98
+ if (callbackState !== state) {
99
+ // eslint-disable-next-line @typescript-eslint/naming-convention
100
+ res.writeHead(403, { 'Content-Type': 'text/html; charset=utf-8' });
101
+ res.end(`
102
+ <html>
103
+ <head>
104
+ <style>
105
+ body {
106
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
107
+ display: flex;
108
+ flex-direction: column;
109
+ align-items: center;
110
+ justify-content: center;
111
+ height: 100vh;
112
+ margin: 0;
113
+ background-color: #f8f9fa;
114
+ color: #333;
115
+ }
116
+ .card {
117
+ background-color: white;
118
+ border-radius: 12px;
119
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
120
+ padding: 2rem;
121
+ text-align: center;
122
+ max-width: 400px;
123
+ width: 90%;
124
+ border-left: 4px solid #dc3545;
125
+ }
126
+ h1 {
127
+ color: #dc3545;
128
+ margin-bottom: 1rem;
129
+ font-size: 1.8rem;
130
+ }
131
+ p {
132
+ margin: 0.5rem 0;
133
+ color: #6c757d;
134
+ }
135
+ .countdown {
136
+ font-weight: bold;
137
+ color: #dc3545;
138
+ font-size: 1.2rem;
139
+ }
140
+ </style>
141
+ </head>
142
+ <body>
143
+ <div class="card">
144
+ <h1>登录验证失败</h1>
145
+ <p>无效的状态参数,可能存在安全风险</p>
146
+ </div>
147
+ </body>
148
+ </html>
149
+ `);
150
+ reject(new Error('登录验证失败:无效的状态参数'));
151
+ server.close();
152
+ return;
153
+ }
154
+ // 处理token
155
+ if (token) {
156
+ // 构建清理后的URL(移除token和state参数)
157
+ parsedUrl.searchParams.delete('token');
158
+ parsedUrl.searchParams.delete('state');
159
+ const cleanUrl = parsedUrl.toString();
160
+ // 返回包含自动刷新和关闭逻辑的HTML
161
+ // eslint-disable-next-line @typescript-eslint/naming-convention
162
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
163
+ res.end(`
164
+ <html>
165
+ <head>
166
+ <title>登录成功</title>
167
+ <style>
168
+ body {
169
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
170
+ display: flex;
171
+ flex-direction: column;
172
+ align-items: center;
173
+ justify-content: center;
174
+ height: 100vh;
175
+ margin: 0;
176
+ background-color: #f8f9fa;
177
+ color: #333;
178
+ }
179
+ .card {
180
+ background-color: white;
181
+ border-radius: 12px;
182
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
183
+ padding: 2rem;
184
+ text-align: center;
185
+ max-width: 400px;
186
+ width: 90%;
187
+ border-left: 4px solid #28a745;
188
+ }
189
+ h1 {
190
+ color: #28a745;
191
+ margin-bottom: 1rem;
192
+ font-size: 1.8rem;
193
+ }
194
+ p {
195
+ margin: 0.5rem 0;
196
+ color: #6c757d;
197
+ }
198
+ .countdown {
199
+ font-weight: bold;
200
+ color: #28a745;
201
+ font-size: 1.2rem;
202
+ }
203
+ .checkmark {
204
+ font-size: 3rem;
205
+ color: #28a745;
206
+ margin-bottom: 1rem;
207
+ }
208
+ </style>
209
+ </head>
210
+ <body>
211
+ <div class="card">
212
+ <div class="checkmark">✓</div>
213
+ <h1>登录成功!</h1>
214
+ <p>您可以关闭此窗口返回应用。</p>
215
+ </div>
216
+ </body>
217
+ </html>
218
+ `);
219
+ resolve(token);
220
+ }
221
+ else {
222
+ // eslint-disable-next-line @typescript-eslint/naming-convention
223
+ res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
224
+ res.end(`
225
+ <html>
226
+ <head>
227
+ <style>
228
+ body {
229
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
230
+ display: flex;
231
+ flex-direction: column;
232
+ align-items: center;
233
+ justify-content: center;
234
+ height: 100vh;
235
+ margin: 0;
236
+ background-color: #f8f9fa;
237
+ color: #333;
238
+ }
239
+ .card {
240
+ background-color: white;
241
+ border-radius: 12px;
242
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
243
+ padding: 2rem;
244
+ text-align: center;
245
+ max-width: 400px;
246
+ width: 90%;
247
+ border-left: 4px solid #dc3545;
248
+ }
249
+ h1 {
250
+ color: #dc3545;
251
+ margin-bottom: 1rem;
252
+ font-size: 1.8rem;
253
+ }
254
+ p {
255
+ margin: 0.5rem 0;
256
+ color: #6c757d;
257
+ }
258
+ .countdown {
259
+ font-weight: bold;
260
+ color: #dc3545;
261
+ font-size: 1.2rem;
262
+ }
263
+ .error-icon {
264
+ font-size: 3rem;
265
+ color: #dc3545;
266
+ margin-bottom: 1rem;
267
+ }
268
+ </style>
269
+ </head>
270
+ <body>
271
+ <div class="card">
272
+ <div class="error-icon">✗</div>
273
+ <h1>登录失败</h1>
274
+ <p>未收到有效的认证信息</p>
275
+ </div>
276
+ </body>
277
+ </html>
278
+ `);
279
+ reject(new Error('登录失败:未收到token'));
280
+ }
281
+ server.close();
282
+ }));
283
+ // 监听随机可用端口
284
+ server.listen(port, 'localhost', () => {
285
+ const address = server.address();
286
+ if (typeof address === 'object' && address) {
287
+ console.log(`回调服务器启动在端口 ${address.port}`);
288
+ }
289
+ });
290
+ // 设置超时(5分钟)
291
+ setTimeout(() => {
292
+ server.close();
293
+ reject(new Error('登录超时,请重试'));
294
+ }, 5 * 60 * 1000);
295
+ });
296
+ }
297
+ /**
298
+ * 获取可用端口
299
+ * @returns 可用端口号
300
+ */
301
+ function getAvailablePort() {
302
+ return new Promise((resolve, reject) => {
303
+ const tempServer = http.createServer();
304
+ tempServer.listen(0, 'localhost', () => {
305
+ const address = tempServer.address();
306
+ if (typeof address === 'object' && address && address.port) {
307
+ const port = address.port;
308
+ tempServer.close(() => resolve(port));
309
+ }
310
+ else {
311
+ tempServer.close();
312
+ reject(new Error('无法获取可用端口'));
313
+ }
314
+ });
315
+ });
316
+ }
317
+ async function openExternalUrl(url) {
318
+ try {
319
+ // 根据不同操作系统,使用相应的命令打开浏览器
320
+ switch (process.platform) {
321
+ case 'darwin': // macOS
322
+ await execAsync(`open "${url}"`);
323
+ break;
324
+ case 'win32': // Windows
325
+ await execAsync(`start "" "${url}"`);
326
+ break;
327
+ default: // Linux及其他类Unix系统
328
+ await execAsync(`xdg-open "${url}"`);
329
+ break;
330
+ }
331
+ return true;
332
+ }
333
+ catch (error) {
334
+ console.error('无法打开外部浏览器:', error);
335
+ return false;
336
+ }
337
+ }
338
+ /**
339
+ * 执行登录流程
340
+ * @param env 环境接口
341
+ * @param tokenStorage token存储接口
342
+ * @param authServerUrl 认证服务器URL
343
+ * @param clientId 客户端ID
344
+ * @returns 登录成功后的token
345
+ */
346
+ async function login(authServerUrl, clientId) {
347
+ const env = getEnvironment();
348
+ const tokenStorage = getTokenStorage();
349
+ const state = generateState();
350
+ const port = await getAvailablePort();
351
+ const callbackUrl = `http://localhost:${port}/callback`;
352
+ await cancelLogin();
353
+ // 启动回调服务器
354
+ const serverPromise = startCallbackServer(state, port);
355
+ // 构建认证服务器URL
356
+ const authUrl = new url_1.URL(authServerUrl);
357
+ authUrl.searchParams.set('clientId', clientId);
358
+ authUrl.searchParams.set('redirectUrl', callbackUrl);
359
+ authUrl.searchParams.set('state', state);
360
+ authUrl.searchParams.set('responseType', 'token');
361
+ // 打开外部登录页面
362
+ const opened = await openExternalUrl(authUrl.toString());
363
+ if (!opened) {
364
+ throw new Error('无法打开外部浏览器进行登录');
365
+ }
366
+ // 等待回调返回token
367
+ const token = await serverPromise;
368
+ // 存储token
369
+ await tokenStorage.set(token);
370
+ env.showInfoMessage('登录成功!');
371
+ return token;
372
+ }
373
+ async function cancelLogin() {
374
+ if (lastLoginServer) {
375
+ lastLoginServer.close();
376
+ lastLoginServer = null;
377
+ }
378
+ if (lastReject) {
379
+ lastReject(new Error('登录已取消'));
380
+ lastReject = null;
381
+ }
382
+ }
383
+ /**
384
+ * 执行登出操作
385
+ * @param tokenStorage token存储接口
386
+ * @param env 环境接口
387
+ */
388
+ async function logout() {
389
+ const env = getEnvironment();
390
+ const tokenStorage = getTokenStorage();
391
+ await tokenStorage.clear();
392
+ env.showInfoMessage('已成功登出');
393
+ }
394
+ /**
395
+ * 检查用户是否已登录
396
+ * @param tokenStorage token存储接口
397
+ * @returns 是否已登录
398
+ */
399
+ function isLoggedIn() {
400
+ const tokenStorage = getTokenStorage();
401
+ const token = tokenStorage.get();
402
+ if (!token) {
403
+ return false;
404
+ }
405
+ if (isJwtExpired(token)) {
406
+ return false;
407
+ }
408
+ return true;
409
+ }
410
+ function getUserData() {
411
+ const token = getTokenStorage().get();
412
+ if (!token) {
413
+ return null;
414
+ }
415
+ try {
416
+ const decoded = decodeJwt(token);
417
+ return decoded;
418
+ }
419
+ catch (error) {
420
+ console.error('解析用户数据失败:', error);
421
+ }
422
+ return null;
423
+ }
424
+ /**
425
+ * 解析JWT获取其中的信息
426
+ * @param token JWT字符串
427
+ * @returns 解析后的payload对象
428
+ */
429
+ function decodeJwt(token) {
430
+ try {
431
+ // 不验证签名,仅解码获取信息
432
+ const decoded = jsonwebtoken_1.default.decode(token);
433
+ return decoded;
434
+ }
435
+ catch (error) {
436
+ console.error('JWT解码失败:', error);
437
+ return null;
438
+ }
439
+ }
440
+ /**
441
+ * 验证JWT是否过期
442
+ * @param token JWT字符串 或 已解析的JWT payload
443
+ * @returns 如果过期返回true,否则返回false
444
+ */
445
+ function isJwtExpired(token) {
446
+ try {
447
+ let payload;
448
+ // 如果传入的是字符串,则先解析
449
+ if (typeof token === 'string') {
450
+ payload = decodeJwt(token);
451
+ if (!payload) {
452
+ return true;
453
+ } // 解析失败视为过期
454
+ }
455
+ else {
456
+ payload = token;
457
+ }
458
+ // 检查是否包含过期时间
459
+ if (!payload.exp) {
460
+ return false;
461
+ } // 没有过期时间,视为不过期
462
+ // 计算当前时间(秒级时间戳)
463
+ const currentTime = Math.floor(Date.now() / 1000);
464
+ // 比较当前时间和过期时间
465
+ return currentTime > payload.exp;
466
+ }
467
+ catch (error) {
468
+ console.error('JWT过期检查失败:', error);
469
+ return true; // 发生错误时视为过期
470
+ }
471
+ }
472
+ //# sourceMappingURL=user.js.map
package/dist/view.js ADDED
@@ -0,0 +1,216 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.COVERAGE_FILE_PATH = void 0;
7
+ exports.showCoverageView = showCoverageView;
8
+ exports.stopMonitoring = stopMonitoring;
9
+ exports.loadAndDisplayCoverage = loadAndDisplayCoverage;
10
+ const fs_1 = __importDefault(require("fs"));
11
+ const path_1 = __importDefault(require("path"));
12
+ const table_1 = require("table");
13
+ const chalk_1 = __importDefault(require("chalk"));
14
+ let REFRESH_INTERVAL = 3000;
15
+ // 存储上一次的覆盖率数据
16
+ let previousCoverage = null;
17
+ // 存储变化的单元格位置
18
+ let changedCells = [];
19
+ // 文件监听对象
20
+ let fileWatcher = null;
21
+ // 定时器ID
22
+ let refreshTimer = null;
23
+ const output = console.log;
24
+ exports.COVERAGE_FILE_PATH = 'coverage-summary.json';
25
+ // 主函数
26
+ async function showCoverageView(rootDir, coverageFilePath = exports.COVERAGE_FILE_PATH) {
27
+ const coveragePath = path_1.default.join(rootDir, coverageFilePath);
28
+ console.log(chalk_1.default.cyan('开始监控覆盖率数据...' + coveragePath));
29
+ // 初始加载
30
+ await loadAndDisplayCoverage(coveragePath);
31
+ // 设置文件监听
32
+ fileWatcher = fs_1.default.watch(coveragePath, async (eventType, filename) => {
33
+ if (filename && eventType === 'change') {
34
+ await loadAndDisplayCoverage(coveragePath);
35
+ }
36
+ });
37
+ // 定时刷新,确保捕获所有变化
38
+ // refreshTimer = setInterval(() => loadAndDisplayCoverage(coveragePath), refershTime) as any;
39
+ }
40
+ // 停止监控函数
41
+ function stopMonitoring() {
42
+ // 清理文件监听器
43
+ if (fileWatcher) {
44
+ fileWatcher.close();
45
+ fileWatcher = null;
46
+ }
47
+ // 清理定时器
48
+ if (refreshTimer) {
49
+ clearInterval(refreshTimer);
50
+ refreshTimer = null;
51
+ }
52
+ }
53
+ // 加载并显示覆盖率数据
54
+ async function loadAndDisplayCoverage(coveragePath, refreshTimeout = REFRESH_INTERVAL) {
55
+ try {
56
+ const coverage = await readCoverageFile(coveragePath);
57
+ // 生成表格数据
58
+ const tableData = generateTableData(coverage);
59
+ // 比较并标记变化
60
+ if (previousCoverage && refreshTimeout > 0) {
61
+ changedCells = compareCoverage(previousCoverage, coverage);
62
+ // 如果有变化,3秒后清除标记
63
+ if (changedCells.length > 0) {
64
+ setTimeout(() => {
65
+ changedCells = [];
66
+ loadAndDisplayCoverage(coveragePath);
67
+ }, refreshTimeout);
68
+ }
69
+ }
70
+ // 存储当前覆盖率数据供下次比较
71
+ previousCoverage = coverage;
72
+ // 渲染表格
73
+ renderTable(tableData);
74
+ }
75
+ catch (error) {
76
+ console.error(chalk_1.default.red(`加载覆盖率数据时出错: ${error.message}`));
77
+ }
78
+ }
79
+ // 读取覆盖率文件
80
+ async function readCoverageFile(coveragePath) {
81
+ return new Promise((resolve, reject) => {
82
+ fs_1.default.readFile(coveragePath, 'utf8', (err, data) => {
83
+ if (err) {
84
+ reject(new Error(`无法读取覆盖率文件: ${err.message}`));
85
+ return;
86
+ }
87
+ try {
88
+ const coverage = JSON.parse(data);
89
+ resolve(coverage);
90
+ }
91
+ catch (parseError) {
92
+ reject(new Error(`解析覆盖率文件失败: ${parseError.message}`));
93
+ }
94
+ });
95
+ });
96
+ }
97
+ // 生成表格数据
98
+ function generateTableData(coverage) {
99
+ const tableData = [['文件路径', '行覆盖率(%)', '语句覆盖率(%)', '函数覆盖率(%)', '分支覆盖率(%)']];
100
+ // 提取并排序文件路径
101
+ const filePaths = Object.keys(coverage).filter((key) => key !== 'total');
102
+ filePaths.sort();
103
+ // 添加总计行
104
+ tableData.push([
105
+ chalk_1.default.bold('总计'),
106
+ formatPercentage(coverage.total.lines.pct),
107
+ formatPercentage(coverage.total.statements.pct),
108
+ formatPercentage(coverage.total.functions.pct),
109
+ formatPercentage(coverage.total.branches.pct)
110
+ ]);
111
+ // 添加分隔线
112
+ tableData.push(['---', '---', '---', '---', '---']);
113
+ // 添加每个文件的覆盖率数据
114
+ filePaths.forEach((filePath) => {
115
+ const fileCoverage = coverage[filePath];
116
+ tableData.push([
117
+ path_1.default.basename(filePath),
118
+ formatPercentage(fileCoverage.lines.pct),
119
+ formatPercentage(fileCoverage.statements.pct),
120
+ formatPercentage(fileCoverage.functions.pct),
121
+ formatPercentage(fileCoverage.branches.pct)
122
+ ]);
123
+ });
124
+ return tableData;
125
+ }
126
+ // 格式化百分比并应用颜色
127
+ function formatPercentage(pct) {
128
+ if (pct === 'Unknown')
129
+ return chalk_1.default.gray('Unknown');
130
+ let formatted = pct.toFixed(2) + '%';
131
+ // 根据覆盖率设置颜色
132
+ if (pct < 50) {
133
+ formatted = chalk_1.default.red(formatted);
134
+ }
135
+ else if (pct < 80) {
136
+ formatted = chalk_1.default.yellow(formatted);
137
+ }
138
+ else {
139
+ formatted = chalk_1.default.green(formatted);
140
+ }
141
+ return formatted;
142
+ }
143
+ // 比较覆盖率数据,找出变化的单元格
144
+ function compareCoverage(oldData, newData) {
145
+ const changes = [];
146
+ // 比较总计
147
+ const compareMetrics = (oldMetrics, newMetrics, rowIndex) => {
148
+ if (oldMetrics.lines.pct !== newMetrics.lines.pct) {
149
+ changes.push([rowIndex, 1]);
150
+ }
151
+ if (oldMetrics.statements.pct !== newMetrics.statements.pct) {
152
+ changes.push([rowIndex, 2]);
153
+ }
154
+ if (oldMetrics.functions.pct !== newMetrics.functions.pct) {
155
+ changes.push([rowIndex, 3]);
156
+ }
157
+ if (oldMetrics.branches.pct !== newMetrics.branches.pct) {
158
+ changes.push([rowIndex, 4]);
159
+ }
160
+ };
161
+ // 比较总计行
162
+ compareMetrics(oldData.total, newData.total, 1);
163
+ // 比较文件行
164
+ const filePaths = Object.keys(oldData).filter((key) => key !== 'total');
165
+ filePaths.forEach((filePath, index) => {
166
+ if (newData[filePath]) {
167
+ compareMetrics(oldData[filePath], newData[filePath], index + 3);
168
+ }
169
+ });
170
+ return changes;
171
+ }
172
+ // 渲染表格
173
+ function renderTable(tableData) {
174
+ // 应用变化标记
175
+ const markedData = tableData.map((row, rowIndex) => {
176
+ return row.map((cell, colIndex) => {
177
+ // 检查是否是变化的单元格
178
+ if (changedCells.some(([r, c]) => r === rowIndex && c === colIndex)) {
179
+ return chalk_1.default.bgRed.white(cell);
180
+ }
181
+ return cell;
182
+ });
183
+ });
184
+ // 配置表格样式
185
+ const tableConfig = {
186
+ columns: {
187
+ 0: { width: 30 },
188
+ 1: { width: 15, alignment: 'right' },
189
+ 2: { width: 15, alignment: 'right' },
190
+ 3: { width: 15, alignment: 'right' },
191
+ 4: { width: 15, alignment: 'right' }
192
+ },
193
+ border: {
194
+ topBody: `─`,
195
+ topJoin: `┬`,
196
+ topLeft: `┌`,
197
+ topRight: `┐`,
198
+ bottomBody: `─`,
199
+ bottomJoin: `┴`,
200
+ bottomLeft: `└`,
201
+ bottomRight: `┘`,
202
+ bodyLeft: `│`,
203
+ bodyRight: `│`,
204
+ bodyJoin: `│`,
205
+ joinBody: `─`,
206
+ joinLeft: `├`,
207
+ joinRight: `┤`,
208
+ joinJoin: `┼`
209
+ }
210
+ };
211
+ // 清除控制台并打印表格
212
+ console.clear();
213
+ console.log(chalk_1.default.cyan('单测覆盖率监控工具 - 最后更新: ' + new Date().toLocaleString()));
214
+ output((0, table_1.table)(markedData, tableConfig));
215
+ }
216
+ //# sourceMappingURL=view.js.map