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