elit 3.6.5 → 3.6.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/Cargo.lock +1 -1
- package/Cargo.toml +1 -1
- package/README.md +6 -0
- package/dist/build.cjs +421 -331
- package/dist/build.d.ts +1 -16
- package/dist/build.js +420 -330
- package/dist/build.mjs +420 -330
- package/dist/chokidar.cjs +219 -182
- package/dist/chokidar.d.ts +25 -10
- package/dist/chokidar.js +217 -182
- package/dist/chokidar.mjs +218 -183
- package/dist/cli.cjs +21608 -20241
- package/dist/cli.d.ts +19 -37
- package/dist/cli.mjs +21262 -19910
- package/dist/config.cjs +357 -350
- package/dist/config.d.ts +19 -240
- package/dist/config.js +520 -515
- package/dist/config.mjs +346 -341
- package/dist/contracts-BeW9k0yZ.d.ts +54 -0
- package/dist/contracts-D7KIS-TK.d.ts +36 -0
- package/dist/coverage.cjs +448 -485
- package/dist/coverage.d.ts +13 -59
- package/dist/coverage.js +447 -484
- package/dist/coverage.mjs +447 -484
- package/dist/database.cjs +819 -828
- package/dist/database.d.ts +8 -24
- package/dist/database.js +818 -829
- package/dist/database.mjs +818 -829
- package/dist/desktop-auto-render.cjs +1700 -1522
- package/dist/desktop-auto-render.d.ts +4 -9
- package/dist/desktop-auto-render.js +1695 -1517
- package/dist/desktop-auto-render.mjs +1696 -1518
- package/dist/desktop.cjs +3 -1
- package/dist/desktop.d.ts +4 -1
- package/dist/desktop.js +1 -1
- package/dist/desktop.mjs +1 -1
- package/dist/dev-build.cjs +830 -0
- package/dist/dev-build.d.ts +53 -0
- package/dist/dev-build.js +3318 -0
- package/dist/dev-build.mjs +797 -0
- package/dist/dom.cjs +717 -590
- package/dist/dom.d.ts +2 -15
- package/dist/dom.js +714 -587
- package/dist/dom.mjs +716 -589
- package/dist/el.cjs +62 -52
- package/dist/el.d.ts +5 -10
- package/dist/el.js +60 -52
- package/dist/el.mjs +60 -52
- package/dist/fs.cjs +72 -63
- package/dist/fs.d.ts +22 -19
- package/dist/fs.js +71 -62
- package/dist/fs.mjs +71 -62
- package/dist/hmr.cjs +40 -14
- package/dist/hmr.d.ts +11 -23
- package/dist/hmr.js +38 -14
- package/dist/hmr.mjs +38 -14
- package/dist/http.cjs +251 -99
- package/dist/http.d.ts +38 -104
- package/dist/http.js +249 -99
- package/dist/http.mjs +249 -99
- package/dist/https.cjs +524 -228
- package/dist/https.d.ts +44 -36
- package/dist/https.js +520 -226
- package/dist/https.mjs +522 -228
- package/dist/index.cjs +7502 -7690
- package/dist/index.d.ts +8 -3
- package/dist/index.js +7486 -7676
- package/dist/index.mjs +7497 -7686
- package/dist/mime-types.cjs +10 -4
- package/dist/mime-types.d.ts +8 -11
- package/dist/mime-types.js +9 -3
- package/dist/mime-types.mjs +9 -3
- package/dist/native.cjs +8616 -8869
- package/dist/native.d.ts +7 -8
- package/dist/native.js +8682 -8935
- package/dist/native.mjs +8615 -8868
- package/dist/path.cjs +83 -77
- package/dist/path.d.ts +29 -29
- package/dist/path.js +82 -76
- package/dist/path.mjs +82 -76
- package/dist/pm.cjs +3300 -0
- package/dist/pm.d.ts +256 -0
- package/dist/pm.js +5638 -0
- package/dist/pm.mjs +3196 -0
- package/dist/preview-build.cjs +712 -0
- package/dist/preview-build.d.ts +59 -0
- package/dist/preview-build.js +3194 -0
- package/dist/preview-build.mjs +676 -0
- package/dist/render-context.cjs +13 -2
- package/dist/render-context.d.ts +9 -31
- package/dist/render-context.js +11 -2
- package/dist/render-context.mjs +11 -2
- package/dist/router.cjs +787 -645
- package/dist/router.d.ts +8 -12
- package/dist/router.js +786 -644
- package/dist/router.mjs +786 -644
- package/dist/runtime.cjs +1 -1
- package/dist/runtime.js +1 -1
- package/dist/runtime.mjs +1 -1
- package/dist/server.cjs +3315 -2603
- package/dist/server.d.ts +49 -4
- package/dist/server.js +7611 -2834
- package/dist/server.mjs +3317 -2607
- package/dist/smtp-server.cjs +128 -0
- package/dist/smtp-server.d.ts +27 -0
- package/dist/smtp-server.js +4199 -0
- package/dist/smtp-server.mjs +100 -0
- package/dist/state-DvEkDehk.d.ts +195 -0
- package/dist/state.cjs +768 -658
- package/dist/state.d.ts +11 -69
- package/dist/state.js +760 -650
- package/dist/state.mjs +767 -657
- package/dist/style.cjs +1011 -968
- package/dist/style.d.ts +13 -127
- package/dist/style.js +1009 -970
- package/dist/style.mjs +1011 -971
- package/dist/test-reporter.cjs +332 -316
- package/dist/test-reporter.d.ts +28 -33
- package/dist/test-reporter.js +328 -312
- package/dist/test-reporter.mjs +328 -312
- package/dist/test-runtime.cjs +927 -968
- package/dist/test-runtime.d.ts +24 -99
- package/dist/test-runtime.js +922 -965
- package/dist/test-runtime.mjs +922 -965
- package/dist/test.cjs +4428 -4273
- package/dist/test.d.ts +2 -8
- package/dist/test.js +4307 -4154
- package/dist/test.mjs +4419 -4267
- package/dist/types-BONVzPtp.d.ts +59 -0
- package/dist/types-BR4wMiVx.d.ts +32 -0
- package/dist/types-C4gKykuG.d.ts +23 -0
- package/dist/types-CIhpN1-K.d.ts +64 -0
- package/dist/types-Ckj8md_j.d.ts +84 -0
- package/dist/types-CpjQTAkX.d.ts +24 -0
- package/dist/types-D0LjrYjS.d.ts +14 -0
- package/dist/types-DAisuVr5.d.ts +75 -0
- package/dist/types-tJn88E1N.d.ts +242 -0
- package/dist/types.d.ts +71 -226
- package/dist/universal.cjs +1 -1
- package/dist/universal.d.ts +1 -5
- package/dist/universal.js +1 -1
- package/dist/universal.mjs +1 -1
- package/dist/websocket-XfyK23zD.d.ts +119 -0
- package/dist/ws.cjs +129 -108
- package/dist/ws.d.ts +21 -131
- package/dist/ws.js +128 -109
- package/dist/ws.mjs +128 -109
- package/dist/wss.cjs +757 -479
- package/dist/wss.d.ts +31 -28
- package/dist/wss.js +755 -479
- package/dist/wss.mjs +758 -482
- package/package.json +16 -1
- package/vendor/epaint-0.31.1/src/image.rs +418 -0
- package/dist/server-CcBFc2F5.d.ts +0 -449
package/dist/coverage.cjs
CHANGED
|
@@ -17,7 +17,7 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
17
|
};
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
|
|
20
|
-
// src/coverage.ts
|
|
20
|
+
// src/test/coverage/index.ts
|
|
21
21
|
var coverage_exports = {};
|
|
22
22
|
__export(coverage_exports, {
|
|
23
23
|
calculateUncoveredLines: () => calculateUncoveredLines,
|
|
@@ -34,7 +34,7 @@ __export(coverage_exports, {
|
|
|
34
34
|
});
|
|
35
35
|
module.exports = __toCommonJS(coverage_exports);
|
|
36
36
|
|
|
37
|
-
// src/runtime.ts
|
|
37
|
+
// src/shares/runtime.ts
|
|
38
38
|
var runtime = (() => {
|
|
39
39
|
if (typeof Deno !== "undefined") return "deno";
|
|
40
40
|
if (typeof Bun !== "undefined") return "bun";
|
|
@@ -44,7 +44,15 @@ var isNode = runtime === "node";
|
|
|
44
44
|
var isBun = runtime === "bun";
|
|
45
45
|
var isDeno = runtime === "deno";
|
|
46
46
|
|
|
47
|
-
// src/fs.ts
|
|
47
|
+
// src/server/fs/node-modules.ts
|
|
48
|
+
var fs;
|
|
49
|
+
var fsPromises;
|
|
50
|
+
if (isNode || isBun) {
|
|
51
|
+
fs = require("fs");
|
|
52
|
+
fsPromises = require("fs/promises");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// src/server/fs/utils.ts
|
|
48
56
|
function parseOptions(options, defaultValue) {
|
|
49
57
|
return typeof options === "string" ? { encoding: options } : options || defaultValue;
|
|
50
58
|
}
|
|
@@ -63,6 +71,18 @@ function dataToUint8Array(data) {
|
|
|
63
71
|
}
|
|
64
72
|
return data;
|
|
65
73
|
}
|
|
74
|
+
function createDirentFromDenoEntry(entry) {
|
|
75
|
+
return {
|
|
76
|
+
name: entry.name,
|
|
77
|
+
isFile: () => entry.isFile,
|
|
78
|
+
isDirectory: () => entry.isDirectory,
|
|
79
|
+
isBlockDevice: () => false,
|
|
80
|
+
isCharacterDevice: () => false,
|
|
81
|
+
isSymbolicLink: () => entry.isSymlink || false,
|
|
82
|
+
isFIFO: () => false,
|
|
83
|
+
isSocket: () => false
|
|
84
|
+
};
|
|
85
|
+
}
|
|
66
86
|
function processDenoEntries(iterator, withFileTypes) {
|
|
67
87
|
const entries = [];
|
|
68
88
|
for (const entry of iterator) {
|
|
@@ -74,12 +94,37 @@ function processDenoEntries(iterator, withFileTypes) {
|
|
|
74
94
|
}
|
|
75
95
|
return entries;
|
|
76
96
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
97
|
+
function createStatsFromDenoFileInfo(info) {
|
|
98
|
+
return {
|
|
99
|
+
isFile: () => info.isFile,
|
|
100
|
+
isDirectory: () => info.isDirectory,
|
|
101
|
+
isBlockDevice: () => false,
|
|
102
|
+
isCharacterDevice: () => false,
|
|
103
|
+
isSymbolicLink: () => info.isSymlink || false,
|
|
104
|
+
isFIFO: () => false,
|
|
105
|
+
isSocket: () => false,
|
|
106
|
+
dev: info.dev || 0,
|
|
107
|
+
ino: info.ino || 0,
|
|
108
|
+
mode: info.mode || 0,
|
|
109
|
+
nlink: info.nlink || 1,
|
|
110
|
+
uid: info.uid || 0,
|
|
111
|
+
gid: info.gid || 0,
|
|
112
|
+
rdev: 0,
|
|
113
|
+
size: info.size,
|
|
114
|
+
blksize: info.blksize || 4096,
|
|
115
|
+
blocks: info.blocks || Math.ceil(info.size / 512),
|
|
116
|
+
atimeMs: info.atime?.getTime() || Date.now(),
|
|
117
|
+
mtimeMs: info.mtime?.getTime() || Date.now(),
|
|
118
|
+
ctimeMs: info.birthtime?.getTime() || Date.now(),
|
|
119
|
+
birthtimeMs: info.birthtime?.getTime() || Date.now(),
|
|
120
|
+
atime: info.atime || /* @__PURE__ */ new Date(),
|
|
121
|
+
mtime: info.mtime || /* @__PURE__ */ new Date(),
|
|
122
|
+
ctime: info.birthtime || /* @__PURE__ */ new Date(),
|
|
123
|
+
birthtime: info.birthtime || /* @__PURE__ */ new Date()
|
|
124
|
+
};
|
|
82
125
|
}
|
|
126
|
+
|
|
127
|
+
// src/server/fs/file-ops.ts
|
|
83
128
|
function readFileSync(path, options) {
|
|
84
129
|
const opts = parseOptions(options, {});
|
|
85
130
|
if (isNode || isBun) {
|
|
@@ -115,6 +160,8 @@ function statSync(path) {
|
|
|
115
160
|
}
|
|
116
161
|
throw new Error("Unsupported runtime");
|
|
117
162
|
}
|
|
163
|
+
|
|
164
|
+
// src/server/fs/directory-ops.ts
|
|
118
165
|
function mkdirSync(path, options) {
|
|
119
166
|
const opts = typeof options === "number" ? { mode: options } : options || {};
|
|
120
167
|
if (isNode || isBun) {
|
|
@@ -132,79 +179,87 @@ function readdirSync(path, options) {
|
|
|
132
179
|
}
|
|
133
180
|
throw new Error("Unsupported runtime");
|
|
134
181
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
ctimeMs: info.birthtime?.getTime() || Date.now(),
|
|
157
|
-
birthtimeMs: info.birthtime?.getTime() || Date.now(),
|
|
158
|
-
atime: info.atime || /* @__PURE__ */ new Date(),
|
|
159
|
-
mtime: info.mtime || /* @__PURE__ */ new Date(),
|
|
160
|
-
ctime: info.birthtime || /* @__PURE__ */ new Date(),
|
|
161
|
-
birthtime: info.birthtime || /* @__PURE__ */ new Date()
|
|
162
|
-
};
|
|
182
|
+
|
|
183
|
+
// src/test/coverage/tracking.ts
|
|
184
|
+
var executedLinesMap = /* @__PURE__ */ new Map();
|
|
185
|
+
var totalLinesMap = /* @__PURE__ */ new Map();
|
|
186
|
+
function getExecutableLines(filePath) {
|
|
187
|
+
const executableLines = /* @__PURE__ */ new Set();
|
|
188
|
+
try {
|
|
189
|
+
const sourceCode = readFileSync(filePath, "utf-8").toString();
|
|
190
|
+
const lines = sourceCode.split("\n");
|
|
191
|
+
for (let index = 0; index < lines.length; index++) {
|
|
192
|
+
const line = lines[index].trim();
|
|
193
|
+
if (!line || line.startsWith("//") || line.startsWith("*") || line.startsWith("/*") || line.startsWith("*/") || line === "{" || line === "}" || /^import\s+.*$/.test(line) || /^export\s+(interface|type|enum)\s+.*$/.test(line) || /^interface\s+.*$/.test(line) || /^type\s+.*$/.test(line) || /^enum\s+.*$/.test(line)) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
executableLines.add(index + 1);
|
|
197
|
+
}
|
|
198
|
+
} catch {
|
|
199
|
+
return /* @__PURE__ */ new Set();
|
|
200
|
+
}
|
|
201
|
+
totalLinesMap.set(filePath, executableLines.size);
|
|
202
|
+
return executableLines;
|
|
163
203
|
}
|
|
164
|
-
function
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
204
|
+
function markFileAsCovered(_filePath) {
|
|
205
|
+
}
|
|
206
|
+
function markLineExecuted(filePath, lineNumber) {
|
|
207
|
+
let executedLines = executedLinesMap.get(filePath);
|
|
208
|
+
if (!executedLines) {
|
|
209
|
+
executedLines = /* @__PURE__ */ new Set();
|
|
210
|
+
executedLinesMap.set(filePath, executedLines);
|
|
211
|
+
}
|
|
212
|
+
executedLines.add(lineNumber);
|
|
213
|
+
}
|
|
214
|
+
function getExecutedLines(filePath) {
|
|
215
|
+
return executedLinesMap.get(filePath) || /* @__PURE__ */ new Set();
|
|
216
|
+
}
|
|
217
|
+
function calculateUncoveredLines(filePath) {
|
|
218
|
+
const executableLines = getExecutableLines(filePath);
|
|
219
|
+
const executedLines = getExecutedLines(filePath);
|
|
220
|
+
const uncoveredLines = [];
|
|
221
|
+
for (const line of executableLines) {
|
|
222
|
+
if (!executedLines.has(line)) {
|
|
223
|
+
uncoveredLines.push(line);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return uncoveredLines.sort((left, right) => left - right);
|
|
227
|
+
}
|
|
228
|
+
function resetCoverageTracking() {
|
|
229
|
+
executedLinesMap.clear();
|
|
230
|
+
totalLinesMap.clear();
|
|
231
|
+
}
|
|
232
|
+
function initializeCoverageTracking() {
|
|
233
|
+
resetCoverageTracking();
|
|
175
234
|
}
|
|
176
235
|
|
|
177
|
-
// src/path.ts
|
|
236
|
+
// src/server/path/platform.ts
|
|
178
237
|
function getSeparator(isWin) {
|
|
179
238
|
return isWin ? "\\" : "/";
|
|
180
239
|
}
|
|
181
240
|
function getCwd() {
|
|
182
241
|
if (isNode || isBun) {
|
|
183
242
|
return process.cwd();
|
|
184
|
-
}
|
|
243
|
+
}
|
|
244
|
+
if (isDeno) {
|
|
185
245
|
return Deno.cwd();
|
|
186
246
|
}
|
|
187
247
|
return "/";
|
|
188
248
|
}
|
|
249
|
+
var isWindows = (() => {
|
|
250
|
+
if (isNode) {
|
|
251
|
+
return process.platform === "win32";
|
|
252
|
+
}
|
|
253
|
+
if (isDeno) {
|
|
254
|
+
return Deno.build.os === "windows";
|
|
255
|
+
}
|
|
256
|
+
return typeof process !== "undefined" && process.platform === "win32";
|
|
257
|
+
})();
|
|
258
|
+
|
|
259
|
+
// src/server/path/operations.ts
|
|
189
260
|
function findLastSeparator(path) {
|
|
190
261
|
return Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
|
|
191
262
|
}
|
|
192
|
-
function createPathOps(isWin) {
|
|
193
|
-
return {
|
|
194
|
-
sep: getSeparator(isWin),
|
|
195
|
-
delimiter: isWin ? ";" : ":",
|
|
196
|
-
normalize: (path) => normalizePath(path, isWin),
|
|
197
|
-
join: (...paths) => joinPaths(paths, isWin),
|
|
198
|
-
resolve: (...paths) => resolvePaths(paths, isWin),
|
|
199
|
-
isAbsolute: (path) => isWin ? isAbsoluteWin(path) : isAbsolutePosix(path),
|
|
200
|
-
relative: (from, to) => relativePath(from, to, isWin),
|
|
201
|
-
dirname: (path) => getDirname(path, isWin),
|
|
202
|
-
basename: (path, ext) => getBasename(path, ext, isWin),
|
|
203
|
-
extname: (path) => getExtname(path),
|
|
204
|
-
parse: (path) => parsePath(path, isWin),
|
|
205
|
-
format: (pathObject) => formatPath(pathObject, isWin)
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
263
|
function isAbsolutePosix(path) {
|
|
209
264
|
return path.length > 0 && path[0] === "/";
|
|
210
265
|
}
|
|
@@ -225,28 +280,18 @@ function isAbsoluteWin(path) {
|
|
|
225
280
|
}
|
|
226
281
|
return false;
|
|
227
282
|
}
|
|
228
|
-
var isWindows = (() => {
|
|
229
|
-
if (isNode) {
|
|
230
|
-
return process.platform === "win32";
|
|
231
|
-
} else if (isDeno) {
|
|
232
|
-
return Deno.build.os === "windows";
|
|
233
|
-
}
|
|
234
|
-
return typeof process !== "undefined" && process.platform === "win32";
|
|
235
|
-
})();
|
|
236
|
-
var posix = createPathOps(false);
|
|
237
|
-
var win32 = createPathOps(true);
|
|
238
283
|
function normalizePath(path, isWin) {
|
|
239
284
|
if (path.length === 0) return ".";
|
|
240
285
|
const separator = getSeparator(isWin);
|
|
241
286
|
const isAbsolute = isWin ? isAbsoluteWin(path) : isAbsolutePosix(path);
|
|
242
287
|
const trailingSeparator = path[path.length - 1] === separator || isWin && path[path.length - 1] === "/";
|
|
243
|
-
|
|
288
|
+
const normalized = path.replace(isWin ? /[\/\\]+/g : /\/+/g, separator);
|
|
244
289
|
const parts = normalized.split(separator);
|
|
245
290
|
const result = [];
|
|
246
|
-
for (let
|
|
247
|
-
const part = parts[
|
|
291
|
+
for (let index = 0; index < parts.length; index++) {
|
|
292
|
+
const part = parts[index];
|
|
248
293
|
if (part === "" || part === ".") {
|
|
249
|
-
if (
|
|
294
|
+
if (index === 0 && isAbsolute) result.push("");
|
|
250
295
|
continue;
|
|
251
296
|
}
|
|
252
297
|
if (part === "..") {
|
|
@@ -261,21 +306,21 @@ function normalizePath(path, isWin) {
|
|
|
261
306
|
result.push(part);
|
|
262
307
|
}
|
|
263
308
|
}
|
|
264
|
-
let
|
|
265
|
-
if (
|
|
309
|
+
let finalPath = result.join(separator);
|
|
310
|
+
if (finalPath.length === 0) {
|
|
266
311
|
return isAbsolute ? separator : ".";
|
|
267
312
|
}
|
|
268
|
-
if (trailingSeparator &&
|
|
269
|
-
|
|
313
|
+
if (trailingSeparator && finalPath[finalPath.length - 1] !== separator) {
|
|
314
|
+
finalPath += separator;
|
|
270
315
|
}
|
|
271
|
-
return
|
|
316
|
+
return finalPath;
|
|
272
317
|
}
|
|
273
318
|
function joinPaths(paths, isWin) {
|
|
274
319
|
if (paths.length === 0) return ".";
|
|
275
320
|
const separator = getSeparator(isWin);
|
|
276
321
|
let joined = "";
|
|
277
|
-
for (let
|
|
278
|
-
const path = paths[
|
|
322
|
+
for (let index = 0; index < paths.length; index++) {
|
|
323
|
+
const path = paths[index];
|
|
279
324
|
if (path && path.length > 0) {
|
|
280
325
|
if (joined.length === 0) {
|
|
281
326
|
joined = path;
|
|
@@ -290,43 +335,43 @@ function joinPaths(paths, isWin) {
|
|
|
290
335
|
function resolvePaths(paths, isWin) {
|
|
291
336
|
const separator = getSeparator(isWin);
|
|
292
337
|
let resolved = "";
|
|
293
|
-
let
|
|
294
|
-
for (let
|
|
295
|
-
const path = paths[
|
|
338
|
+
let absolute = false;
|
|
339
|
+
for (let index = paths.length - 1; index >= 0 && !absolute; index--) {
|
|
340
|
+
const path = paths[index];
|
|
296
341
|
if (path && path.length > 0) {
|
|
297
342
|
resolved = path + (resolved.length > 0 ? separator + resolved : "");
|
|
298
|
-
|
|
343
|
+
absolute = isWin ? isAbsoluteWin(resolved) : isAbsolutePosix(resolved);
|
|
299
344
|
}
|
|
300
345
|
}
|
|
301
|
-
if (!
|
|
346
|
+
if (!absolute) {
|
|
302
347
|
const cwd = getCwd();
|
|
303
348
|
resolved = cwd + (resolved.length > 0 ? separator + resolved : "");
|
|
304
349
|
}
|
|
305
350
|
return normalizePath(resolved, isWin);
|
|
306
351
|
}
|
|
307
352
|
function relativePath(from, to, isWin) {
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
if (
|
|
353
|
+
const resolvedFrom = resolvePaths([from], isWin);
|
|
354
|
+
const resolvedTo = resolvePaths([to], isWin);
|
|
355
|
+
if (resolvedFrom === resolvedTo) return "";
|
|
311
356
|
const separator = getSeparator(isWin);
|
|
312
|
-
const fromParts =
|
|
313
|
-
const toParts =
|
|
314
|
-
let commonLength = 0;
|
|
357
|
+
const fromParts = resolvedFrom.split(separator).filter((part) => part.length > 0);
|
|
358
|
+
const toParts = resolvedTo.split(separator).filter((part) => part.length > 0);
|
|
315
359
|
const minLength = Math.min(fromParts.length, toParts.length);
|
|
316
|
-
|
|
317
|
-
|
|
360
|
+
let commonLength = 0;
|
|
361
|
+
for (let index = 0; index < minLength; index++) {
|
|
362
|
+
if (fromParts[index] === toParts[index]) {
|
|
318
363
|
commonLength++;
|
|
319
364
|
} else {
|
|
320
365
|
break;
|
|
321
366
|
}
|
|
322
367
|
}
|
|
323
|
-
const upCount = fromParts.length - commonLength;
|
|
324
368
|
const result = [];
|
|
325
|
-
|
|
369
|
+
const upCount = fromParts.length - commonLength;
|
|
370
|
+
for (let index = 0; index < upCount; index++) {
|
|
326
371
|
result.push("..");
|
|
327
372
|
}
|
|
328
|
-
for (let
|
|
329
|
-
result.push(toParts[
|
|
373
|
+
for (let index = commonLength; index < toParts.length; index++) {
|
|
374
|
+
result.push(toParts[index]);
|
|
330
375
|
}
|
|
331
376
|
return result.join(separator) || ".";
|
|
332
377
|
}
|
|
@@ -334,15 +379,15 @@ function getDirname(path, isWin) {
|
|
|
334
379
|
if (path.length === 0) return ".";
|
|
335
380
|
const separator = getSeparator(isWin);
|
|
336
381
|
const normalized = normalizePath(path, isWin);
|
|
337
|
-
const
|
|
338
|
-
if (
|
|
339
|
-
if (
|
|
340
|
-
return normalized.slice(0,
|
|
382
|
+
const lastSeparatorIndex = normalized.lastIndexOf(separator);
|
|
383
|
+
if (lastSeparatorIndex === -1) return ".";
|
|
384
|
+
if (lastSeparatorIndex === 0) return separator;
|
|
385
|
+
return normalized.slice(0, lastSeparatorIndex);
|
|
341
386
|
}
|
|
342
387
|
function getBasename(path, ext, isWin) {
|
|
343
388
|
if (path.length === 0) return "";
|
|
344
|
-
const
|
|
345
|
-
let base =
|
|
389
|
+
const lastSeparatorIndex = isWin ? findLastSeparator(path) : path.lastIndexOf("/");
|
|
390
|
+
let base = lastSeparatorIndex === -1 ? path : path.slice(lastSeparatorIndex + 1);
|
|
346
391
|
if (ext && base.endsWith(ext)) {
|
|
347
392
|
base = base.slice(0, base.length - ext.length);
|
|
348
393
|
}
|
|
@@ -350,8 +395,8 @@ function getBasename(path, ext, isWin) {
|
|
|
350
395
|
}
|
|
351
396
|
function getExtname(path) {
|
|
352
397
|
const lastDotIndex = path.lastIndexOf(".");
|
|
353
|
-
const
|
|
354
|
-
if (lastDotIndex === -1 || lastDotIndex <
|
|
398
|
+
const lastSeparatorIndex = findLastSeparator(path);
|
|
399
|
+
if (lastDotIndex === -1 || lastDotIndex < lastSeparatorIndex || lastDotIndex === path.length - 1) {
|
|
355
400
|
return "";
|
|
356
401
|
}
|
|
357
402
|
return path.slice(lastDotIndex);
|
|
@@ -367,10 +412,8 @@ function parsePath(path, isWin) {
|
|
|
367
412
|
} else if (path[0] === "\\" || path[0] === "/") {
|
|
368
413
|
root = "\\";
|
|
369
414
|
}
|
|
370
|
-
} else {
|
|
371
|
-
|
|
372
|
-
root = "/";
|
|
373
|
-
}
|
|
415
|
+
} else if (path[0] === "/") {
|
|
416
|
+
root = "/";
|
|
374
417
|
}
|
|
375
418
|
const dir = getDirname(path, isWin);
|
|
376
419
|
const base = getBasename(path, void 0, isWin);
|
|
@@ -386,6 +429,26 @@ function formatPath(pathObject, isWin) {
|
|
|
386
429
|
if (dir === pathObject.root) return dir + base;
|
|
387
430
|
return dir + separator + base;
|
|
388
431
|
}
|
|
432
|
+
function createPathOps(isWin) {
|
|
433
|
+
return {
|
|
434
|
+
sep: getSeparator(isWin),
|
|
435
|
+
delimiter: isWin ? ";" : ":",
|
|
436
|
+
normalize: (path) => normalizePath(path, isWin),
|
|
437
|
+
join: (...paths) => joinPaths(paths, isWin),
|
|
438
|
+
resolve: (...paths) => resolvePaths(paths, isWin),
|
|
439
|
+
isAbsolute: (path) => isWin ? isAbsoluteWin(path) : isAbsolutePosix(path),
|
|
440
|
+
relative: (from, to) => relativePath(from, to, isWin),
|
|
441
|
+
dirname: (path) => getDirname(path, isWin),
|
|
442
|
+
basename: (path, ext) => getBasename(path, ext, isWin),
|
|
443
|
+
extname: (path) => getExtname(path),
|
|
444
|
+
parse: (path) => parsePath(path, isWin),
|
|
445
|
+
format: (pathObject) => formatPath(pathObject, isWin)
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// src/server/path/index.ts
|
|
450
|
+
var posix = createPathOps(false);
|
|
451
|
+
var win32 = createPathOps(true);
|
|
389
452
|
function join(...paths) {
|
|
390
453
|
return joinPaths(paths, isWindows);
|
|
391
454
|
}
|
|
@@ -396,119 +459,24 @@ function dirname(path) {
|
|
|
396
459
|
return getDirname(path, isWindows);
|
|
397
460
|
}
|
|
398
461
|
|
|
399
|
-
// src/coverage.ts
|
|
400
|
-
var executedLinesMap = /* @__PURE__ */ new Map();
|
|
401
|
-
var totalLinesMap = /* @__PURE__ */ new Map();
|
|
402
|
-
function getExecutableLines(filePath) {
|
|
403
|
-
const executableLines = /* @__PURE__ */ new Set();
|
|
404
|
-
try {
|
|
405
|
-
const sourceCode = readFileSync(filePath, "utf-8").toString();
|
|
406
|
-
const lines = sourceCode.split("\n");
|
|
407
|
-
for (let i = 0; i < lines.length; i++) {
|
|
408
|
-
const line = lines[i];
|
|
409
|
-
const trimmed = line.trim();
|
|
410
|
-
if (!trimmed || trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("/*") || trimmed.startsWith("*/") || trimmed.startsWith("import ") || trimmed.startsWith("export ") && !trimmed.includes("function") && !trimmed.includes("class") && !trimmed.includes("const") && !trimmed.includes("let") && !trimmed.includes("var") || trimmed.startsWith("interface ") || trimmed.startsWith("type ") || trimmed.startsWith("enum ") || trimmed.match(/^class\s+\w+.*{?\s*$/) || trimmed === "{" || trimmed === "}" || trimmed === "();") {
|
|
411
|
-
continue;
|
|
412
|
-
}
|
|
413
|
-
executableLines.add(i + 1);
|
|
414
|
-
}
|
|
415
|
-
} catch (e) {
|
|
416
|
-
}
|
|
417
|
-
return executableLines;
|
|
418
|
-
}
|
|
419
|
-
function markFileAsCovered(_filePath) {
|
|
420
|
-
}
|
|
421
|
-
function markLineExecuted(filePath, lineNumber) {
|
|
422
|
-
if (!executedLinesMap.has(filePath)) {
|
|
423
|
-
executedLinesMap.set(filePath, /* @__PURE__ */ new Set());
|
|
424
|
-
}
|
|
425
|
-
executedLinesMap.get(filePath).add(lineNumber);
|
|
426
|
-
}
|
|
427
|
-
function getExecutedLines(filePath) {
|
|
428
|
-
return executedLinesMap.get(filePath) || /* @__PURE__ */ new Set();
|
|
429
|
-
}
|
|
430
|
-
function calculateUncoveredLines(filePath) {
|
|
431
|
-
const executableLines = getExecutableLines(filePath);
|
|
432
|
-
const executedLines = getExecutedLines(filePath);
|
|
433
|
-
const uncovered = [];
|
|
434
|
-
for (const line of executableLines) {
|
|
435
|
-
if (!executedLines.has(line)) {
|
|
436
|
-
uncovered.push(line);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
return uncovered.sort((a, b) => a - b);
|
|
440
|
-
}
|
|
441
|
-
function resetCoverageTracking() {
|
|
442
|
-
executedLinesMap.clear();
|
|
443
|
-
totalLinesMap.clear();
|
|
444
|
-
}
|
|
445
|
-
function initializeCoverageTracking() {
|
|
446
|
-
resetCoverageTracking();
|
|
447
|
-
}
|
|
462
|
+
// src/test/coverage/matching.ts
|
|
448
463
|
function globToRegex(pattern) {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
const char = pattern[i];
|
|
452
|
-
switch (char) {
|
|
453
|
-
case ".":
|
|
454
|
-
regexStr += "\\.";
|
|
455
|
-
break;
|
|
456
|
-
case "*":
|
|
457
|
-
if (i + 1 < pattern.length && pattern[i + 1] === "*") {
|
|
458
|
-
regexStr += "(?:[^/]*(?:/|$))*";
|
|
459
|
-
i++;
|
|
460
|
-
} else {
|
|
461
|
-
regexStr += "[^/]*";
|
|
462
|
-
}
|
|
463
|
-
break;
|
|
464
|
-
case "?":
|
|
465
|
-
regexStr += "[^/]";
|
|
466
|
-
break;
|
|
467
|
-
case "/":
|
|
468
|
-
regexStr += "/";
|
|
469
|
-
break;
|
|
470
|
-
// Escape special regex characters
|
|
471
|
-
case "^":
|
|
472
|
-
case "$":
|
|
473
|
-
case "+":
|
|
474
|
-
case "(":
|
|
475
|
-
case ")":
|
|
476
|
-
case "[":
|
|
477
|
-
case "]":
|
|
478
|
-
case "{":
|
|
479
|
-
case "}":
|
|
480
|
-
case "|":
|
|
481
|
-
case "\\":
|
|
482
|
-
regexStr += "\\" + char;
|
|
483
|
-
break;
|
|
484
|
-
default:
|
|
485
|
-
regexStr += char;
|
|
486
|
-
break;
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
regexStr += "$";
|
|
490
|
-
return new RegExp(regexStr);
|
|
464
|
+
const regexPattern = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "__DOUBLE_STAR__").replace(/\*/g, "[^/\\\\]*").replace(/__DOUBLE_STAR__/g, ".*");
|
|
465
|
+
return new RegExp(`^${regexPattern.replace(/\\/g, "[\\\\/]")}$`);
|
|
491
466
|
}
|
|
492
467
|
function matchesInclude(filePath, include) {
|
|
493
|
-
if (include.length === 0) return true;
|
|
494
468
|
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
495
|
-
|
|
469
|
+
return include.some((pattern) => {
|
|
496
470
|
const regex = globToRegex(pattern);
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
return false;
|
|
471
|
+
return regex.test(normalizedPath);
|
|
472
|
+
});
|
|
502
473
|
}
|
|
503
474
|
function matchesExclude(filePath, exclude) {
|
|
504
475
|
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
505
|
-
|
|
476
|
+
return exclude.some((pattern) => {
|
|
506
477
|
const regex = globToRegex(pattern);
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
return false;
|
|
478
|
+
return regex.test(normalizedPath);
|
|
479
|
+
});
|
|
512
480
|
}
|
|
513
481
|
function findAllTypeScriptFiles(dir, include, exclude) {
|
|
514
482
|
const files = [];
|
|
@@ -518,21 +486,30 @@ function findAllTypeScriptFiles(dir, include, exclude) {
|
|
|
518
486
|
try {
|
|
519
487
|
const entries = readdirSync(dir, { withFileTypes: true });
|
|
520
488
|
for (const entry of entries) {
|
|
521
|
-
if (typeof entry === "string")
|
|
489
|
+
if (typeof entry === "string") {
|
|
490
|
+
const fullPath2 = join(dir, entry);
|
|
491
|
+
if (fullPath2.endsWith(".ts") && matchesInclude(fullPath2, include) && !matchesExclude(fullPath2, exclude)) {
|
|
492
|
+
files.push(fullPath2);
|
|
493
|
+
}
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
522
496
|
const fullPath = join(dir, entry.name);
|
|
523
497
|
if (entry.isDirectory()) {
|
|
524
|
-
if (matchesExclude(fullPath, exclude))
|
|
525
|
-
|
|
498
|
+
if (!matchesExclude(fullPath + "/", exclude)) {
|
|
499
|
+
files.push(...findAllTypeScriptFiles(fullPath, include, exclude));
|
|
500
|
+
}
|
|
526
501
|
} else if (entry.isFile() && fullPath.endsWith(".ts")) {
|
|
527
502
|
if (matchesInclude(fullPath, include) && !matchesExclude(fullPath, exclude)) {
|
|
528
503
|
files.push(fullPath);
|
|
529
504
|
}
|
|
530
505
|
}
|
|
531
506
|
}
|
|
532
|
-
} catch
|
|
507
|
+
} catch {
|
|
533
508
|
}
|
|
534
509
|
return files;
|
|
535
510
|
}
|
|
511
|
+
|
|
512
|
+
// src/test/coverage/process.ts
|
|
536
513
|
function analyzeSourceFile(filePath) {
|
|
537
514
|
try {
|
|
538
515
|
const sourceCode = readFileSync(filePath, "utf-8").toString();
|
|
@@ -567,10 +544,28 @@ function analyzeSourceFile(filePath) {
|
|
|
567
544
|
}
|
|
568
545
|
}
|
|
569
546
|
return { statements, branches, functions, lines: executableLines };
|
|
570
|
-
} catch
|
|
547
|
+
} catch {
|
|
571
548
|
return { statements: 0, branches: 0, functions: 0, lines: 0 };
|
|
572
549
|
}
|
|
573
550
|
}
|
|
551
|
+
function createCoverageRecord(filePath, isCovered) {
|
|
552
|
+
const analysis = analyzeSourceFile(filePath);
|
|
553
|
+
const executableLines = getExecutableLines(filePath);
|
|
554
|
+
const executedLines = isCovered ? executableLines : /* @__PURE__ */ new Set();
|
|
555
|
+
const uncoveredLines = Array.from(executableLines).filter((line) => !executedLines.has(line)).sort((left, right) => left - right);
|
|
556
|
+
return {
|
|
557
|
+
path: filePath,
|
|
558
|
+
statements: analysis.statements,
|
|
559
|
+
coveredStatements: isCovered ? analysis.statements : 0,
|
|
560
|
+
branches: analysis.branches,
|
|
561
|
+
coveredBranches: isCovered ? analysis.branches : 0,
|
|
562
|
+
functions: analysis.functions,
|
|
563
|
+
coveredFunctions: isCovered ? analysis.functions : 0,
|
|
564
|
+
lines: executableLines.size,
|
|
565
|
+
coveredLines: executedLines.size,
|
|
566
|
+
uncoveredLines: uncoveredLines.length > 0 ? uncoveredLines : void 0
|
|
567
|
+
};
|
|
568
|
+
}
|
|
574
569
|
async function processCoverage(options) {
|
|
575
570
|
const {
|
|
576
571
|
include = ["**/*.ts"],
|
|
@@ -581,108 +576,109 @@ async function processCoverage(options) {
|
|
|
581
576
|
const allTsFiles = findAllTypeScriptFiles(process.cwd(), include, exclude);
|
|
582
577
|
for (const tsFile of allTsFiles) {
|
|
583
578
|
const isCovered = coveredFiles?.has(tsFile) || false;
|
|
584
|
-
|
|
585
|
-
const executableLines = getExecutableLines(tsFile);
|
|
586
|
-
const executedLines = isCovered ? executableLines : /* @__PURE__ */ new Set();
|
|
587
|
-
const uncoveredLinesArray = [];
|
|
588
|
-
for (const line of executableLines) {
|
|
589
|
-
if (!executedLines.has(line)) {
|
|
590
|
-
uncoveredLinesArray.push(line);
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
const uncoveredLines = uncoveredLinesArray.length > 0 ? uncoveredLinesArray.sort((a, b) => a - b) : void 0;
|
|
594
|
-
const coveredLinesCount = executedLines.size;
|
|
595
|
-
coverageMap.set(tsFile, {
|
|
596
|
-
path: tsFile,
|
|
597
|
-
statements: analysis.statements,
|
|
598
|
-
coveredStatements: isCovered ? analysis.statements : 0,
|
|
599
|
-
branches: analysis.branches,
|
|
600
|
-
coveredBranches: isCovered ? analysis.branches : 0,
|
|
601
|
-
functions: analysis.functions,
|
|
602
|
-
coveredFunctions: isCovered ? analysis.functions : 0,
|
|
603
|
-
lines: executableLines.size,
|
|
604
|
-
coveredLines: coveredLinesCount,
|
|
605
|
-
uncoveredLines
|
|
606
|
-
});
|
|
579
|
+
coverageMap.set(tsFile, createCoverageRecord(tsFile, isCovered));
|
|
607
580
|
}
|
|
608
581
|
if (coveredFiles) {
|
|
609
582
|
for (const coveredFile of coveredFiles) {
|
|
610
|
-
if (coverageMap.has(coveredFile))
|
|
583
|
+
if (coverageMap.has(coveredFile)) {
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
611
586
|
const relativePath2 = relative(process.cwd(), coveredFile);
|
|
612
587
|
const isOutsideProject = relativePath2.startsWith("..");
|
|
613
588
|
if (!coveredFile.includes("node_modules") && !coveredFile.includes("dist") && !isOutsideProject) {
|
|
614
|
-
|
|
615
|
-
const executableLines = getExecutableLines(coveredFile);
|
|
616
|
-
const executedLines = executableLines;
|
|
617
|
-
const uncoveredLinesArray = [];
|
|
618
|
-
for (const line of executableLines) {
|
|
619
|
-
if (!executedLines.has(line)) {
|
|
620
|
-
uncoveredLinesArray.push(line);
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
const uncoveredLines = uncoveredLinesArray.length > 0 ? uncoveredLinesArray.sort((a, b) => a - b) : void 0;
|
|
624
|
-
const coveredLinesCount = executedLines.size;
|
|
625
|
-
coverageMap.set(coveredFile, {
|
|
626
|
-
path: coveredFile,
|
|
627
|
-
statements: analysis.statements,
|
|
628
|
-
coveredStatements: analysis.statements,
|
|
629
|
-
branches: analysis.branches,
|
|
630
|
-
coveredBranches: analysis.branches,
|
|
631
|
-
functions: analysis.functions,
|
|
632
|
-
coveredFunctions: analysis.functions,
|
|
633
|
-
lines: executableLines.size,
|
|
634
|
-
coveredLines: coveredLinesCount,
|
|
635
|
-
uncoveredLines
|
|
636
|
-
});
|
|
589
|
+
coverageMap.set(coveredFile, createCoverageRecord(coveredFile, true));
|
|
637
590
|
}
|
|
638
591
|
}
|
|
639
592
|
}
|
|
640
593
|
return coverageMap;
|
|
641
594
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
red: "\x1B[31m",
|
|
647
|
-
green: "\x1B[32m",
|
|
648
|
-
yellow: "\x1B[33m",
|
|
649
|
-
cyan: "\x1B[36m"
|
|
650
|
-
};
|
|
651
|
-
function getColorForPercentage(pct) {
|
|
652
|
-
if (pct >= 80) return colors.green;
|
|
653
|
-
if (pct >= 50) return colors.yellow;
|
|
654
|
-
return colors.red;
|
|
595
|
+
|
|
596
|
+
// src/test/coverage/shared.ts
|
|
597
|
+
function calculatePercentage(covered, total) {
|
|
598
|
+
return total > 0 ? covered / total * 100 : 0;
|
|
655
599
|
}
|
|
656
600
|
function calculateFileCoverage(file) {
|
|
657
|
-
const stmtPct = file.statements > 0 ? file.coveredStatements / file.statements * 100 : 0;
|
|
658
|
-
const branchPct = file.branches > 0 ? file.coveredBranches / file.branches * 100 : 0;
|
|
659
|
-
const funcPct = file.functions > 0 ? file.coveredFunctions / file.functions * 100 : 0;
|
|
660
|
-
const linePct = file.lines > 0 ? file.coveredLines / file.lines * 100 : 0;
|
|
661
601
|
return {
|
|
662
|
-
statements: {
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
602
|
+
statements: {
|
|
603
|
+
total: file.statements,
|
|
604
|
+
covered: file.coveredStatements,
|
|
605
|
+
percentage: calculatePercentage(file.coveredStatements, file.statements)
|
|
606
|
+
},
|
|
607
|
+
branches: {
|
|
608
|
+
total: file.branches,
|
|
609
|
+
covered: file.coveredBranches,
|
|
610
|
+
percentage: calculatePercentage(file.coveredBranches, file.branches)
|
|
611
|
+
},
|
|
612
|
+
functions: {
|
|
613
|
+
total: file.functions,
|
|
614
|
+
covered: file.coveredFunctions,
|
|
615
|
+
percentage: calculatePercentage(file.coveredFunctions, file.functions)
|
|
616
|
+
},
|
|
617
|
+
lines: {
|
|
618
|
+
total: file.lines,
|
|
619
|
+
covered: file.coveredLines,
|
|
620
|
+
percentage: calculatePercentage(file.coveredLines, file.lines)
|
|
621
|
+
}
|
|
666
622
|
};
|
|
667
623
|
}
|
|
668
|
-
function
|
|
669
|
-
|
|
624
|
+
function calculateCoverageTotals(coverageMap) {
|
|
625
|
+
let totalStatements = 0;
|
|
626
|
+
let coveredStatements = 0;
|
|
627
|
+
let totalBranches = 0;
|
|
628
|
+
let coveredBranches = 0;
|
|
629
|
+
let totalFunctions = 0;
|
|
630
|
+
let coveredFunctions = 0;
|
|
631
|
+
let totalLines = 0;
|
|
632
|
+
let coveredLines = 0;
|
|
633
|
+
let coveredFiles = 0;
|
|
634
|
+
for (const coverage of coverageMap.values()) {
|
|
635
|
+
totalStatements += coverage.statements;
|
|
636
|
+
coveredStatements += coverage.coveredStatements;
|
|
637
|
+
totalBranches += coverage.branches;
|
|
638
|
+
coveredBranches += coverage.coveredBranches;
|
|
639
|
+
totalFunctions += coverage.functions;
|
|
640
|
+
coveredFunctions += coverage.coveredFunctions;
|
|
641
|
+
totalLines += coverage.lines;
|
|
642
|
+
coveredLines += coverage.coveredLines;
|
|
643
|
+
if (coverage.coveredStatements > 0) {
|
|
644
|
+
coveredFiles++;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
const statements = {
|
|
648
|
+
total: totalStatements,
|
|
649
|
+
covered: coveredStatements,
|
|
650
|
+
percentage: calculatePercentage(coveredStatements, totalStatements)
|
|
651
|
+
};
|
|
652
|
+
const branches = {
|
|
653
|
+
total: totalBranches,
|
|
654
|
+
covered: coveredBranches,
|
|
655
|
+
percentage: calculatePercentage(coveredBranches, totalBranches)
|
|
656
|
+
};
|
|
657
|
+
const functions = {
|
|
658
|
+
total: totalFunctions,
|
|
659
|
+
covered: coveredFunctions,
|
|
660
|
+
percentage: calculatePercentage(coveredFunctions, totalFunctions)
|
|
661
|
+
};
|
|
662
|
+
const lines = {
|
|
663
|
+
total: totalLines,
|
|
664
|
+
covered: coveredLines,
|
|
665
|
+
percentage: calculatePercentage(coveredLines, totalLines)
|
|
666
|
+
};
|
|
667
|
+
return {
|
|
668
|
+
statements,
|
|
669
|
+
branches,
|
|
670
|
+
functions,
|
|
671
|
+
lines,
|
|
672
|
+
overallPercentage: (statements.percentage + branches.percentage + functions.percentage + lines.percentage) / 4,
|
|
673
|
+
totalFiles: coverageMap.size,
|
|
674
|
+
coveredFiles
|
|
675
|
+
};
|
|
670
676
|
}
|
|
671
|
-
function
|
|
672
|
-
return
|
|
677
|
+
function toRelativePath(cwd, filePath) {
|
|
678
|
+
return relative(cwd, filePath).replace(/\\/g, "/");
|
|
673
679
|
}
|
|
674
|
-
function
|
|
675
|
-
|
|
676
|
-
const pctStr = percentage.toFixed(2);
|
|
677
|
-
const pct = color + pctStr + "%" + colors.reset;
|
|
678
|
-
const coveredPadded = covered.toString().padStart(4);
|
|
679
|
-
const totalPadded = total.toString().padStart(4);
|
|
680
|
-
const count = `${colors.dim}${coveredPadded}${colors.reset}/${totalPadded}`;
|
|
681
|
-
const metric = `${pct} (${count})`;
|
|
682
|
-
const visibleWidth = getVisibleWidth(metric);
|
|
683
|
-
const padding = " ".repeat(Math.max(0, 19 - visibleWidth));
|
|
684
|
-
const separator = includeSeparator ? `${colors.dim}\u2502${colors.reset}` : " ";
|
|
685
|
-
return metric + padding + separator;
|
|
680
|
+
function getSafeReportFileName(filePath) {
|
|
681
|
+
return filePath.replace(/[\/\\]/g, "_") + ".html";
|
|
686
682
|
}
|
|
687
683
|
function formatUncoveredLines(uncoveredLines) {
|
|
688
684
|
if (!uncoveredLines || uncoveredLines.length === 0) {
|
|
@@ -691,18 +687,18 @@ function formatUncoveredLines(uncoveredLines) {
|
|
|
691
687
|
const ranges = [];
|
|
692
688
|
let start = uncoveredLines[0];
|
|
693
689
|
let end = uncoveredLines[0];
|
|
694
|
-
for (let
|
|
695
|
-
if (uncoveredLines[
|
|
696
|
-
end = uncoveredLines[
|
|
690
|
+
for (let index = 1; index < uncoveredLines.length; index++) {
|
|
691
|
+
if (uncoveredLines[index] === end + 1) {
|
|
692
|
+
end = uncoveredLines[index];
|
|
693
|
+
continue;
|
|
694
|
+
}
|
|
695
|
+
if (start === end) {
|
|
696
|
+
ranges.push(start.toString());
|
|
697
697
|
} else {
|
|
698
|
-
|
|
699
|
-
ranges.push(start.toString());
|
|
700
|
-
} else {
|
|
701
|
-
ranges.push(`${start}-${end}`);
|
|
702
|
-
}
|
|
703
|
-
start = uncoveredLines[i];
|
|
704
|
-
end = uncoveredLines[i];
|
|
698
|
+
ranges.push(`${start}-${end}`);
|
|
705
699
|
}
|
|
700
|
+
start = uncoveredLines[index];
|
|
701
|
+
end = uncoveredLines[index];
|
|
706
702
|
}
|
|
707
703
|
if (start === end) {
|
|
708
704
|
ranges.push(start.toString());
|
|
@@ -711,41 +707,62 @@ function formatUncoveredLines(uncoveredLines) {
|
|
|
711
707
|
}
|
|
712
708
|
return ranges.join(",");
|
|
713
709
|
}
|
|
710
|
+
function escapeHtml(text) {
|
|
711
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
712
|
+
}
|
|
713
|
+
function escapeXml(text) {
|
|
714
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// src/test/coverage/text-report.ts
|
|
718
|
+
var colors = {
|
|
719
|
+
reset: "\x1B[0m",
|
|
720
|
+
bold: "\x1B[1m",
|
|
721
|
+
dim: "\x1B[2m",
|
|
722
|
+
red: "\x1B[31m",
|
|
723
|
+
green: "\x1B[32m",
|
|
724
|
+
yellow: "\x1B[33m",
|
|
725
|
+
cyan: "\x1B[36m"
|
|
726
|
+
};
|
|
727
|
+
function getColorForPercentage(percentage) {
|
|
728
|
+
if (percentage >= 80) return colors.green;
|
|
729
|
+
if (percentage >= 50) return colors.yellow;
|
|
730
|
+
return colors.red;
|
|
731
|
+
}
|
|
732
|
+
function stripAnsi(value) {
|
|
733
|
+
return value.replace(/\x1b\[[0-9;]*m/g, "");
|
|
734
|
+
}
|
|
735
|
+
function getVisibleWidth(value) {
|
|
736
|
+
return stripAnsi(value).length;
|
|
737
|
+
}
|
|
738
|
+
function formatMetricFixedWidth(covered, total, percentage, includeSeparator = false) {
|
|
739
|
+
const color = getColorForPercentage(percentage);
|
|
740
|
+
const pct = `${color}${percentage.toFixed(2)}%${colors.reset}`;
|
|
741
|
+
const count = `${colors.dim}${covered.toString().padStart(4)}${colors.reset}/${total.toString().padStart(4)}`;
|
|
742
|
+
const metric = `${pct} (${count})`;
|
|
743
|
+
const padding = " ".repeat(Math.max(0, 19 - getVisibleWidth(metric)));
|
|
744
|
+
const separator = includeSeparator ? `${colors.dim}\u2502${colors.reset}` : " ";
|
|
745
|
+
return metric + padding + separator;
|
|
746
|
+
}
|
|
714
747
|
function generateTextReport(coverageMap, testResults) {
|
|
715
748
|
let output = "\n";
|
|
716
749
|
void testResults;
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
coveredBranches += coverage.coveredBranches;
|
|
726
|
-
totalFunctions += coverage.functions;
|
|
727
|
-
coveredFunctions += coverage.coveredFunctions;
|
|
728
|
-
totalLines += coverage.lines;
|
|
729
|
-
coveredLines += coverage.coveredLines;
|
|
730
|
-
}
|
|
731
|
-
const pctStmts = totalStatements > 0 ? coveredStatements / totalStatements * 100 : 0;
|
|
732
|
-
const pctBranch = totalBranches > 0 ? coveredBranches / totalBranches * 100 : 0;
|
|
733
|
-
const pctFunc = totalFunctions > 0 ? coveredFunctions / totalFunctions * 100 : 0;
|
|
734
|
-
const pctLines = totalLines > 0 ? coveredLines / totalLines * 100 : 0;
|
|
735
|
-
output += `${colors.bold}% Coverage report from v8\x1B[0m
|
|
736
|
-
`;
|
|
737
|
-
output += `
|
|
750
|
+
const totals = calculateCoverageTotals(coverageMap);
|
|
751
|
+
const cwd = process.cwd();
|
|
752
|
+
const namePadding = Math.max(
|
|
753
|
+
45,
|
|
754
|
+
...Array.from(coverageMap.keys()).map((filePath) => toRelativePath(cwd, filePath).length + 2)
|
|
755
|
+
);
|
|
756
|
+
output += `${colors.bold}% Coverage report from v8${colors.reset}
|
|
757
|
+
|
|
738
758
|
`;
|
|
739
|
-
output += `${colors.dim}${colors.bold}All files
|
|
740
|
-
const maxFileNameLength = Math.max(...Array.from(coverageMap.keys()).map((f) => relative(process.cwd(), f).length));
|
|
741
|
-
const namePadding = Math.max(45, maxFileNameLength + 2);
|
|
759
|
+
output += `${colors.dim}${colors.bold}All files${colors.reset}`;
|
|
742
760
|
output += " ".repeat(namePadding - 9);
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
output +=
|
|
748
|
-
`;
|
|
761
|
+
output += formatMetricFixedWidth(totals.statements.covered, totals.statements.total, totals.statements.percentage, true);
|
|
762
|
+
output += formatMetricFixedWidth(totals.branches.covered, totals.branches.total, totals.branches.percentage, true);
|
|
763
|
+
output += formatMetricFixedWidth(totals.functions.covered, totals.functions.total, totals.functions.percentage, true);
|
|
764
|
+
output += formatMetricFixedWidth(totals.lines.covered, totals.lines.total, totals.lines.percentage, true);
|
|
765
|
+
output += "\n";
|
|
749
766
|
output += `${colors.dim}`;
|
|
750
767
|
output += " ".repeat(namePadding);
|
|
751
768
|
output += " ".repeat(5) + "Statements";
|
|
@@ -776,101 +793,58 @@ function generateTextReport(coverageMap, testResults) {
|
|
|
776
793
|
}
|
|
777
794
|
groupedFiles.get(dir).push({ path: filePath, coverage });
|
|
778
795
|
}
|
|
779
|
-
const cwd = process.cwd();
|
|
780
|
-
const toRelative = (path) => relative(cwd, path).replace(/\\/g, "/");
|
|
781
796
|
for (const [dir, files] of groupedFiles.entries()) {
|
|
782
|
-
const
|
|
783
|
-
if (
|
|
797
|
+
const relativeDir = toRelativePath(cwd, dir);
|
|
798
|
+
if (relativeDir !== ".") {
|
|
784
799
|
output += `
|
|
785
|
-
${colors.cyan}${
|
|
800
|
+
${colors.cyan}${relativeDir}/${colors.reset}
|
|
786
801
|
`;
|
|
787
802
|
}
|
|
788
803
|
for (const { path, coverage } of files) {
|
|
789
804
|
const stats = calculateFileCoverage(coverage);
|
|
790
|
-
const
|
|
791
|
-
let displayName =
|
|
805
|
+
const relativePath2 = toRelativePath(cwd, path);
|
|
806
|
+
let displayName = relativePath2;
|
|
792
807
|
if (displayName.length > namePadding - 2) {
|
|
793
808
|
displayName = "..." + displayName.slice(-(namePadding - 5));
|
|
794
809
|
}
|
|
795
810
|
output += displayName.padEnd(namePadding);
|
|
796
|
-
output += formatMetricFixedWidth(
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
// Include separator
|
|
802
|
-
);
|
|
803
|
-
output += formatMetricFixedWidth(
|
|
804
|
-
stats.branches.covered,
|
|
805
|
-
stats.branches.total,
|
|
806
|
-
stats.branches.percentage,
|
|
807
|
-
true
|
|
808
|
-
// Include separator
|
|
809
|
-
);
|
|
810
|
-
output += formatMetricFixedWidth(
|
|
811
|
-
stats.functions.covered,
|
|
812
|
-
stats.functions.total,
|
|
813
|
-
stats.functions.percentage,
|
|
814
|
-
true
|
|
815
|
-
// Include separator
|
|
816
|
-
);
|
|
817
|
-
output += formatMetricFixedWidth(
|
|
818
|
-
stats.lines.covered,
|
|
819
|
-
stats.lines.total,
|
|
820
|
-
stats.lines.percentage,
|
|
821
|
-
true
|
|
822
|
-
// Include separator
|
|
823
|
-
);
|
|
824
|
-
const uncoveredStr = formatUncoveredLines(coverage.uncoveredLines);
|
|
825
|
-
output += `${colors.red}${uncoveredStr}${colors.reset}`;
|
|
811
|
+
output += formatMetricFixedWidth(stats.statements.covered, stats.statements.total, stats.statements.percentage, true);
|
|
812
|
+
output += formatMetricFixedWidth(stats.branches.covered, stats.branches.total, stats.branches.percentage, true);
|
|
813
|
+
output += formatMetricFixedWidth(stats.functions.covered, stats.functions.total, stats.functions.percentage, true);
|
|
814
|
+
output += formatMetricFixedWidth(stats.lines.covered, stats.lines.total, stats.lines.percentage, true);
|
|
815
|
+
output += `${colors.red}${formatUncoveredLines(coverage.uncoveredLines)}${colors.reset}`;
|
|
826
816
|
output += "\n";
|
|
827
817
|
}
|
|
828
818
|
}
|
|
829
|
-
output +=
|
|
830
|
-
|
|
831
|
-
output += `${colors.dim}${colors.bold}Test Files\x1B[0m ${coverageMap.size} passed (100%)
|
|
819
|
+
output += "\n";
|
|
820
|
+
output += `${colors.dim}${colors.bold}Test Files${colors.reset} ${coverageMap.size} passed (100%)
|
|
832
821
|
`;
|
|
833
|
-
output += `${colors.dim}${colors.bold}Tests
|
|
834
|
-
|
|
835
|
-
output += `
|
|
822
|
+
output += `${colors.dim}${colors.bold}Tests${colors.reset} ${coverageMap.size} passed (100%)
|
|
823
|
+
|
|
836
824
|
`;
|
|
837
|
-
output += `${colors.dim}${colors.bold}Statements
|
|
825
|
+
output += `${colors.dim}${colors.bold}Statements${colors.reset} ${colors.green}${totals.statements.covered}${colors.reset} ${colors.dim}/${colors.reset} ${totals.statements.total}
|
|
838
826
|
`;
|
|
839
|
-
output += `${colors.dim}${colors.bold}Branches
|
|
827
|
+
output += `${colors.dim}${colors.bold}Branches${colors.reset} ${colors.green}${totals.branches.covered}${colors.reset} ${colors.dim}/${colors.reset} ${totals.branches.total}
|
|
840
828
|
`;
|
|
841
|
-
output += `${colors.dim}${colors.bold}Functions
|
|
829
|
+
output += `${colors.dim}${colors.bold}Functions${colors.reset} ${colors.green}${totals.functions.covered}${colors.reset} ${colors.dim}/${colors.reset} ${totals.functions.total}
|
|
842
830
|
`;
|
|
843
|
-
output += `${colors.dim}${colors.bold}Lines
|
|
831
|
+
output += `${colors.dim}${colors.bold}Lines${colors.reset} ${colors.green}${totals.lines.covered}${colors.reset} ${colors.dim}/${colors.reset} ${totals.lines.total}
|
|
844
832
|
`;
|
|
845
833
|
return output;
|
|
846
834
|
}
|
|
835
|
+
|
|
836
|
+
// src/test/coverage/html-report.ts
|
|
837
|
+
function getCoverageClass(percentage) {
|
|
838
|
+
if (percentage >= 80) return "high";
|
|
839
|
+
if (percentage >= 50) return "medium";
|
|
840
|
+
return "low";
|
|
841
|
+
}
|
|
847
842
|
function generateHtmlReport(coverageMap, reportsDir) {
|
|
848
843
|
if (!existsSync(reportsDir)) {
|
|
849
844
|
mkdirSync(reportsDir, { recursive: true });
|
|
850
845
|
}
|
|
851
|
-
|
|
852
|
-
let totalBranches = 0, coveredBranches = 0;
|
|
853
|
-
let totalFunctions = 0, coveredFunctions = 0;
|
|
854
|
-
let totalLines = 0, coveredLines = 0;
|
|
855
|
-
for (const coverage of coverageMap.values()) {
|
|
856
|
-
totalStatements += coverage.statements;
|
|
857
|
-
coveredStatements += coverage.coveredStatements;
|
|
858
|
-
totalBranches += coverage.branches;
|
|
859
|
-
coveredBranches += coverage.coveredBranches;
|
|
860
|
-
totalFunctions += coverage.functions;
|
|
861
|
-
coveredFunctions += coverage.coveredFunctions;
|
|
862
|
-
totalLines += coverage.lines;
|
|
863
|
-
coveredLines += coverage.coveredLines;
|
|
864
|
-
}
|
|
865
|
-
const pctStmts = totalStatements > 0 ? coveredStatements / totalStatements * 100 : 0;
|
|
866
|
-
const pctBranch = totalBranches > 0 ? coveredBranches / totalBranches * 100 : 0;
|
|
867
|
-
const pctFunc = totalFunctions > 0 ? coveredFunctions / totalFunctions * 100 : 0;
|
|
868
|
-
const pctLines = totalLines > 0 ? coveredLines / totalLines * 100 : 0;
|
|
869
|
-
const overallPct = (pctStmts + pctBranch + pctFunc + pctLines) / 4;
|
|
870
|
-
const totalFiles = coverageMap.size;
|
|
871
|
-
const coveredFiles = Array.from(coverageMap.values()).filter((c) => c.coveredStatements > 0).length;
|
|
846
|
+
const totals = calculateCoverageTotals(coverageMap);
|
|
872
847
|
const cwd = process.cwd();
|
|
873
|
-
const toRelative = (path) => relative(cwd, path).replace(/\\/g, "/");
|
|
874
848
|
const indexHtml = `<!DOCTYPE html>
|
|
875
849
|
<html>
|
|
876
850
|
<head>
|
|
@@ -920,7 +894,7 @@ function generateHtmlReport(coverageMap, reportsDir) {
|
|
|
920
894
|
}
|
|
921
895
|
.overall-bar-fill {
|
|
922
896
|
height: 100%;
|
|
923
|
-
background: ${
|
|
897
|
+
background: ${totals.overallPercentage >= 80 ? "#3fb950" : totals.overallPercentage >= 50 ? "#d29922" : "#f85149"};
|
|
924
898
|
display: flex;
|
|
925
899
|
align-items: center;
|
|
926
900
|
justify-content: center;
|
|
@@ -1007,11 +981,6 @@ function generateHtmlReport(coverageMap, reportsDir) {
|
|
|
1007
981
|
.file-row:last-child { border-bottom: none; }
|
|
1008
982
|
.file-name { color: #58a6ff; text-decoration: none; cursor: pointer; }
|
|
1009
983
|
.file-name:hover { text-decoration: underline; }
|
|
1010
|
-
.percentage { font-weight: 600; }
|
|
1011
|
-
.percentage.high { color: #3fb950; }
|
|
1012
|
-
.percentage.medium { color: #d29922; }
|
|
1013
|
-
.percentage.low { color: #f85149; }
|
|
1014
|
-
.metric-detail { font-size: 11px; color: #8b949e; margin-top: 2px; }
|
|
1015
984
|
.badge { display: inline-block; padding: 2px 8px; border-radius: 12px; font-size: 11px; font-weight: 600; margin-left: 8px; }
|
|
1016
985
|
.badge.covered { background: #238636; color: #fff; }
|
|
1017
986
|
.badge.uncovered { background: #da3633; color: #fff; }
|
|
@@ -1063,11 +1032,11 @@ function generateHtmlReport(coverageMap, reportsDir) {
|
|
|
1063
1032
|
<div class="overall-bar-inner">
|
|
1064
1033
|
<div class="overall-bar-label">Overall Coverage</div>
|
|
1065
1034
|
<div class="overall-bar-visual">
|
|
1066
|
-
<div class="overall-bar-fill" style="width: ${
|
|
1067
|
-
<div class="overall-bar-text">${
|
|
1035
|
+
<div class="overall-bar-fill" style="width: ${totals.overallPercentage}%"></div>
|
|
1036
|
+
<div class="overall-bar-text">${totals.overallPercentage.toFixed(2)}%</div>
|
|
1068
1037
|
</div>
|
|
1069
1038
|
</div>
|
|
1070
|
-
<div class="files-info"><span>${coveredFiles}</span> of ${totalFiles} files covered</div>
|
|
1039
|
+
<div class="files-info"><span>${totals.coveredFiles}</span> of ${totals.totalFiles} files covered</div>
|
|
1071
1040
|
</div>
|
|
1072
1041
|
|
|
1073
1042
|
<div class="summary">
|
|
@@ -1075,35 +1044,35 @@ function generateHtmlReport(coverageMap, reportsDir) {
|
|
|
1075
1044
|
<div class="metrics">
|
|
1076
1045
|
<div class="metric">
|
|
1077
1046
|
<div class="metric-label">Statements</div>
|
|
1078
|
-
<div class="metric-value ${
|
|
1047
|
+
<div class="metric-value ${getCoverageClass(totals.statements.percentage)}">${totals.statements.percentage.toFixed(2)}%</div>
|
|
1079
1048
|
<div class="progress-bar">
|
|
1080
|
-
<div class="progress-fill ${
|
|
1049
|
+
<div class="progress-fill ${getCoverageClass(totals.statements.percentage)}" style="width: ${totals.statements.percentage}%"></div>
|
|
1081
1050
|
</div>
|
|
1082
|
-
<div class="metric-count">${
|
|
1051
|
+
<div class="metric-count">${totals.statements.covered}/${totals.statements.total}</div>
|
|
1083
1052
|
</div>
|
|
1084
1053
|
<div class="metric">
|
|
1085
1054
|
<div class="metric-label">Branches</div>
|
|
1086
|
-
<div class="metric-value ${
|
|
1055
|
+
<div class="metric-value ${getCoverageClass(totals.branches.percentage)}">${totals.branches.percentage.toFixed(2)}%</div>
|
|
1087
1056
|
<div class="progress-bar">
|
|
1088
|
-
<div class="progress-fill ${
|
|
1057
|
+
<div class="progress-fill ${getCoverageClass(totals.branches.percentage)}" style="width: ${totals.branches.percentage}%"></div>
|
|
1089
1058
|
</div>
|
|
1090
|
-
<div class="metric-count">${
|
|
1059
|
+
<div class="metric-count">${totals.branches.covered}/${totals.branches.total}</div>
|
|
1091
1060
|
</div>
|
|
1092
1061
|
<div class="metric">
|
|
1093
1062
|
<div class="metric-label">Functions</div>
|
|
1094
|
-
<div class="metric-value ${
|
|
1063
|
+
<div class="metric-value ${getCoverageClass(totals.functions.percentage)}">${totals.functions.percentage.toFixed(2)}%</div>
|
|
1095
1064
|
<div class="progress-bar">
|
|
1096
|
-
<div class="progress-fill ${
|
|
1065
|
+
<div class="progress-fill ${getCoverageClass(totals.functions.percentage)}" style="width: ${totals.functions.percentage}%"></div>
|
|
1097
1066
|
</div>
|
|
1098
|
-
<div class="metric-count">${
|
|
1067
|
+
<div class="metric-count">${totals.functions.covered}/${totals.functions.total}</div>
|
|
1099
1068
|
</div>
|
|
1100
1069
|
<div class="metric">
|
|
1101
1070
|
<div class="metric-label">Lines</div>
|
|
1102
|
-
<div class="metric-value ${
|
|
1071
|
+
<div class="metric-value ${getCoverageClass(totals.lines.percentage)}">${totals.lines.percentage.toFixed(2)}%</div>
|
|
1103
1072
|
<div class="progress-bar">
|
|
1104
|
-
<div class="progress-fill ${
|
|
1073
|
+
<div class="progress-fill ${getCoverageClass(totals.lines.percentage)}" style="width: ${totals.lines.percentage}%"></div>
|
|
1105
1074
|
</div>
|
|
1106
|
-
<div class="metric-count">${
|
|
1075
|
+
<div class="metric-count">${totals.lines.covered}/${totals.lines.total}</div>
|
|
1107
1076
|
</div>
|
|
1108
1077
|
</div>
|
|
1109
1078
|
</div>
|
|
@@ -1123,8 +1092,8 @@ function generateHtmlReport(coverageMap, reportsDir) {
|
|
|
1123
1092
|
<div id="file-rows">
|
|
1124
1093
|
${Array.from(coverageMap.entries()).map(([filePath, coverage]) => {
|
|
1125
1094
|
const stats = calculateFileCoverage(coverage);
|
|
1126
|
-
const fileName =
|
|
1127
|
-
const safeFileName = fileName
|
|
1095
|
+
const fileName = toRelativePath(cwd, filePath);
|
|
1096
|
+
const safeFileName = getSafeReportFileName(fileName);
|
|
1128
1097
|
const isCovered = coverage.coveredStatements > 0;
|
|
1129
1098
|
return `
|
|
1130
1099
|
<div class="file-row" onclick="window.location.href='${safeFileName}'">
|
|
@@ -1133,19 +1102,19 @@ function generateHtmlReport(coverageMap, reportsDir) {
|
|
|
1133
1102
|
${isCovered ? '<span class="badge covered">Covered</span>' : '<span class="badge uncovered">Not Covered</span>'}
|
|
1134
1103
|
</div>
|
|
1135
1104
|
<div class="coverage-cell">
|
|
1136
|
-
<div class="coverage-percent ${stats.statements.percentage
|
|
1105
|
+
<div class="coverage-percent ${getCoverageClass(stats.statements.percentage)}">${stats.statements.percentage.toFixed(2)}%</div>
|
|
1137
1106
|
<div class="coverage-count">${coverage.coveredStatements}/${coverage.statements}</div>
|
|
1138
1107
|
</div>
|
|
1139
1108
|
<div class="coverage-cell">
|
|
1140
|
-
<div class="coverage-percent ${stats.branches.percentage
|
|
1109
|
+
<div class="coverage-percent ${getCoverageClass(stats.branches.percentage)}">${stats.branches.percentage.toFixed(2)}%</div>
|
|
1141
1110
|
<div class="coverage-count">${coverage.coveredBranches}/${coverage.branches}</div>
|
|
1142
1111
|
</div>
|
|
1143
1112
|
<div class="coverage-cell">
|
|
1144
|
-
<div class="coverage-percent ${stats.functions.percentage
|
|
1113
|
+
<div class="coverage-percent ${getCoverageClass(stats.functions.percentage)}">${stats.functions.percentage.toFixed(2)}%</div>
|
|
1145
1114
|
<div class="coverage-count">${coverage.coveredFunctions}/${coverage.functions}</div>
|
|
1146
1115
|
</div>
|
|
1147
1116
|
<div class="coverage-cell">
|
|
1148
|
-
<div class="coverage-percent ${stats.lines.percentage
|
|
1117
|
+
<div class="coverage-percent ${getCoverageClass(stats.lines.percentage)}">${stats.lines.percentage.toFixed(2)}%</div>
|
|
1149
1118
|
<div class="coverage-count">${coverage.coveredLines}/${coverage.lines}</div>
|
|
1150
1119
|
</div>
|
|
1151
1120
|
</div>
|
|
@@ -1187,18 +1156,18 @@ function generateHtmlReport(coverageMap, reportsDir) {
|
|
|
1187
1156
|
</html>`;
|
|
1188
1157
|
writeFileSync(join(reportsDir, "index.html"), indexHtml, "utf-8");
|
|
1189
1158
|
for (const [filePath, coverage] of coverageMap.entries()) {
|
|
1190
|
-
generateFileDetailPage(filePath, coverage, reportsDir,
|
|
1159
|
+
generateFileDetailPage(filePath, coverage, reportsDir, cwd);
|
|
1191
1160
|
}
|
|
1192
1161
|
}
|
|
1193
|
-
function generateFileDetailPage(filePath, coverage, reportsDir,
|
|
1194
|
-
const fileName =
|
|
1195
|
-
const safeFileName = fileName
|
|
1162
|
+
function generateFileDetailPage(filePath, coverage, reportsDir, cwd) {
|
|
1163
|
+
const fileName = toRelativePath(cwd, filePath);
|
|
1164
|
+
const safeFileName = getSafeReportFileName(fileName);
|
|
1196
1165
|
const stats = calculateFileCoverage(coverage);
|
|
1197
1166
|
let sourceLines = [];
|
|
1198
1167
|
try {
|
|
1199
1168
|
const sourceCode = readFileSync(filePath, "utf-8").toString();
|
|
1200
1169
|
sourceLines = sourceCode.split("\n");
|
|
1201
|
-
} catch
|
|
1170
|
+
} catch {
|
|
1202
1171
|
sourceLines = ["// Unable to read source file"];
|
|
1203
1172
|
}
|
|
1204
1173
|
const uncoveredSet = new Set(coverage.uncoveredLines || []);
|
|
@@ -1326,33 +1295,33 @@ function generateFileDetailPage(filePath, coverage, reportsDir, toRelative) {
|
|
|
1326
1295
|
<div class="metrics">
|
|
1327
1296
|
<div class="metric">
|
|
1328
1297
|
<div class="metric-label">Statements</div>
|
|
1329
|
-
<div class="metric-value ${stats.statements.percentage
|
|
1298
|
+
<div class="metric-value ${getCoverageClass(stats.statements.percentage)}">${stats.statements.percentage.toFixed(2)}%</div>
|
|
1330
1299
|
<div class="progress-bar">
|
|
1331
|
-
<div class="progress-fill ${stats.statements.percentage
|
|
1300
|
+
<div class="progress-fill ${getCoverageClass(stats.statements.percentage)}" style="width: ${stats.statements.percentage}%"></div>
|
|
1332
1301
|
</div>
|
|
1333
1302
|
<div class="metric-count">${coverage.coveredStatements}/${coverage.statements}</div>
|
|
1334
1303
|
</div>
|
|
1335
1304
|
<div class="metric">
|
|
1336
1305
|
<div class="metric-label">Branches</div>
|
|
1337
|
-
<div class="metric-value ${stats.branches.percentage
|
|
1306
|
+
<div class="metric-value ${getCoverageClass(stats.branches.percentage)}">${stats.branches.percentage.toFixed(2)}%</div>
|
|
1338
1307
|
<div class="progress-bar">
|
|
1339
|
-
<div class="progress-fill ${stats.branches.percentage
|
|
1308
|
+
<div class="progress-fill ${getCoverageClass(stats.branches.percentage)}" style="width: ${stats.branches.percentage}%"></div>
|
|
1340
1309
|
</div>
|
|
1341
1310
|
<div class="metric-count">${coverage.coveredBranches}/${coverage.branches}</div>
|
|
1342
1311
|
</div>
|
|
1343
1312
|
<div class="metric">
|
|
1344
1313
|
<div class="metric-label">Functions</div>
|
|
1345
|
-
<div class="metric-value ${stats.functions.percentage
|
|
1314
|
+
<div class="metric-value ${getCoverageClass(stats.functions.percentage)}">${stats.functions.percentage.toFixed(2)}%</div>
|
|
1346
1315
|
<div class="progress-bar">
|
|
1347
|
-
<div class="progress-fill ${stats.functions.percentage
|
|
1316
|
+
<div class="progress-fill ${getCoverageClass(stats.functions.percentage)}" style="width: ${stats.functions.percentage}%"></div>
|
|
1348
1317
|
</div>
|
|
1349
1318
|
<div class="metric-count">${coverage.coveredFunctions}/${coverage.functions}</div>
|
|
1350
1319
|
</div>
|
|
1351
1320
|
<div class="metric">
|
|
1352
1321
|
<div class="metric-label">Lines</div>
|
|
1353
|
-
<div class="metric-value ${stats.lines.percentage
|
|
1322
|
+
<div class="metric-value ${getCoverageClass(stats.lines.percentage)}">${stats.lines.percentage.toFixed(2)}%</div>
|
|
1354
1323
|
<div class="progress-bar">
|
|
1355
|
-
<div class="progress-fill ${stats.lines.percentage
|
|
1324
|
+
<div class="progress-fill ${getCoverageClass(stats.lines.percentage)}" style="width: ${stats.lines.percentage}%"></div>
|
|
1356
1325
|
</div>
|
|
1357
1326
|
<div class="metric-count">${coverage.coveredLines}/${coverage.lines}</div>
|
|
1358
1327
|
</div>
|
|
@@ -1376,13 +1345,13 @@ function generateFileDetailPage(filePath, coverage, reportsDir, toRelative) {
|
|
|
1376
1345
|
</div>
|
|
1377
1346
|
<table class="code-table">
|
|
1378
1347
|
${sourceLines.map((line, index) => {
|
|
1379
|
-
const
|
|
1380
|
-
const isUncovered = uncoveredSet.has(
|
|
1381
|
-
const
|
|
1382
|
-
const rowClass =
|
|
1348
|
+
const lineNumber = index + 1;
|
|
1349
|
+
const isUncovered = uncoveredSet.has(lineNumber);
|
|
1350
|
+
const hasCoverage = coverage.lines > 0;
|
|
1351
|
+
const rowClass = hasCoverage ? isUncovered ? "uncovered" : "covered" : "";
|
|
1383
1352
|
return `
|
|
1384
1353
|
<tr class="${rowClass}">
|
|
1385
|
-
<td class="line-number">${
|
|
1354
|
+
<td class="line-number">${lineNumber}</td>
|
|
1386
1355
|
<td class="line-content">${escapeHtml(line) || " "}</td>
|
|
1387
1356
|
</tr>
|
|
1388
1357
|
`;
|
|
@@ -1394,31 +1363,26 @@ function generateFileDetailPage(filePath, coverage, reportsDir, toRelative) {
|
|
|
1394
1363
|
</html>`;
|
|
1395
1364
|
writeFileSync(join(reportsDir, safeFileName), fileHtml, "utf-8");
|
|
1396
1365
|
}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
}
|
|
1400
|
-
function escapeXml(text) {
|
|
1401
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1402
|
-
}
|
|
1366
|
+
|
|
1367
|
+
// src/test/coverage/machine-reports.ts
|
|
1403
1368
|
function generateCoverageFinalJson(coverageMap, reportsDir) {
|
|
1369
|
+
const cwd = process.cwd();
|
|
1404
1370
|
const coverageData = {};
|
|
1405
1371
|
for (const [filePath, coverage] of coverageMap.entries()) {
|
|
1406
|
-
const relativePath2 =
|
|
1372
|
+
const relativePath2 = toRelativePath(cwd, filePath);
|
|
1407
1373
|
const lineMap = {};
|
|
1408
1374
|
const executableLines = getExecutableLines(filePath);
|
|
1375
|
+
const isCovered = coverage.coveredStatements > 0;
|
|
1409
1376
|
for (const line of executableLines) {
|
|
1410
|
-
const isCovered = coverage.coveredStatements > 0;
|
|
1411
1377
|
lineMap[line] = isCovered ? 1 : 0;
|
|
1412
1378
|
}
|
|
1413
|
-
coverageData[relativePath2] = {
|
|
1414
|
-
lines: lineMap
|
|
1415
|
-
};
|
|
1379
|
+
coverageData[relativePath2] = { lines: lineMap };
|
|
1416
1380
|
}
|
|
1417
|
-
|
|
1418
|
-
writeFileSync(join(reportsDir, "coverage-final.json"), jsonData, "utf-8");
|
|
1381
|
+
writeFileSync(join(reportsDir, "coverage-final.json"), JSON.stringify(coverageData, null, 2), "utf-8");
|
|
1419
1382
|
}
|
|
1420
1383
|
function generateCloverXml(coverageMap, reportsDir) {
|
|
1421
1384
|
const timestamp = Date.now();
|
|
1385
|
+
const cwd = process.cwd();
|
|
1422
1386
|
let totalFiles = 0;
|
|
1423
1387
|
let totalClasses = 0;
|
|
1424
1388
|
let totalElements = 0;
|
|
@@ -1431,9 +1395,13 @@ function generateCloverXml(coverageMap, reportsDir) {
|
|
|
1431
1395
|
let coveredFunctions = 0;
|
|
1432
1396
|
let totalLines = 0;
|
|
1433
1397
|
let coveredLines = 0;
|
|
1398
|
+
const coveredClassCount = Array.from(coverageMap.values()).filter((coverage) => coverage.coveredStatements > 0).length;
|
|
1434
1399
|
const fileEntries = [];
|
|
1435
1400
|
for (const [filePath, coverage] of coverageMap.entries()) {
|
|
1436
|
-
const relativePath2 =
|
|
1401
|
+
const relativePath2 = toRelativePath(cwd, filePath);
|
|
1402
|
+
const escapedPath = escapeXml(relativePath2);
|
|
1403
|
+
const fileElements = coverage.statements + coverage.branches + coverage.functions;
|
|
1404
|
+
const fileCoveredElements = coverage.coveredStatements + coverage.coveredBranches + coverage.coveredFunctions;
|
|
1437
1405
|
totalFiles++;
|
|
1438
1406
|
totalClasses++;
|
|
1439
1407
|
totalStatements += coverage.statements;
|
|
@@ -1444,11 +1412,8 @@ function generateCloverXml(coverageMap, reportsDir) {
|
|
|
1444
1412
|
coveredFunctions += coverage.coveredFunctions;
|
|
1445
1413
|
totalLines += coverage.lines;
|
|
1446
1414
|
coveredLines += coverage.coveredLines;
|
|
1447
|
-
const fileElements = coverage.statements + coverage.branches + coverage.functions;
|
|
1448
|
-
const fileCoveredElements = coverage.coveredStatements + coverage.coveredBranches + coverage.coveredFunctions;
|
|
1449
1415
|
totalElements += fileElements;
|
|
1450
1416
|
coveredElements += fileCoveredElements;
|
|
1451
|
-
const escapedPath = escapeXml(relativePath2);
|
|
1452
1417
|
fileEntries.push(`
|
|
1453
1418
|
<file name="${escapedPath}">
|
|
1454
1419
|
<class name="${escapedPath}">
|
|
@@ -1459,24 +1424,22 @@ function generateCloverXml(coverageMap, reportsDir) {
|
|
|
1459
1424
|
<line num="1" type="stmt" count="${coverage.coveredStatements > 0 ? 1 : 0}" />
|
|
1460
1425
|
</file>`);
|
|
1461
1426
|
}
|
|
1462
|
-
const complexity = 0;
|
|
1463
|
-
const elements = totalElements;
|
|
1464
1427
|
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
1465
1428
|
<coverage generated="${timestamp}" clover="3.2.0">
|
|
1466
1429
|
<project timestamp="${timestamp}" name="Coverage">
|
|
1467
|
-
<metrics complexity="
|
|
1430
|
+
<metrics complexity="0" elements="${totalElements}" coveredelements="${coveredElements}"
|
|
1468
1431
|
conditionals="${totalBranches}" coveredconditionals="${coveredBranches}"
|
|
1469
1432
|
statements="${totalStatements}" coveredstatements="${coveredStatements}"
|
|
1470
1433
|
methods="${totalFunctions}" coveredmethods="${coveredFunctions}"
|
|
1471
|
-
classes="${totalClasses}" coveredclasses="${
|
|
1434
|
+
classes="${totalClasses}" coveredclasses="${coveredClassCount}"
|
|
1472
1435
|
files="${totalFiles}" loc="${totalLines}" ncloc="${totalLines - coveredLines}"
|
|
1473
1436
|
packages="${totalFiles}" classes="${totalClasses}" />
|
|
1474
1437
|
<package name="root">
|
|
1475
|
-
<metrics complexity="
|
|
1438
|
+
<metrics complexity="0" elements="${totalElements}" coveredelements="${coveredElements}"
|
|
1476
1439
|
conditionals="${totalBranches}" coveredconditionals="${coveredBranches}"
|
|
1477
1440
|
statements="${totalStatements}" coveredstatements="${coveredStatements}"
|
|
1478
1441
|
methods="${totalFunctions}" coveredmethods="${coveredFunctions}"
|
|
1479
|
-
classes="${totalClasses}" coveredclasses="${
|
|
1442
|
+
classes="${totalClasses}" coveredclasses="${coveredClassCount}"
|
|
1480
1443
|
files="${totalFiles}" loc="${totalLines}" ncloc="${totalLines - coveredLines}" />
|
|
1481
1444
|
${fileEntries.join("")}
|
|
1482
1445
|
</package>
|