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