elit 3.6.5 → 3.6.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/README.md +6 -0
  4. package/dist/build.cjs +421 -331
  5. package/dist/build.d.ts +1 -16
  6. package/dist/build.js +420 -330
  7. package/dist/build.mjs +420 -330
  8. package/dist/chokidar.cjs +219 -182
  9. package/dist/chokidar.d.ts +25 -10
  10. package/dist/chokidar.js +217 -182
  11. package/dist/chokidar.mjs +218 -183
  12. package/dist/cli.cjs +21608 -20241
  13. package/dist/cli.d.ts +19 -37
  14. package/dist/cli.mjs +21262 -19910
  15. package/dist/config.cjs +357 -350
  16. package/dist/config.d.ts +19 -240
  17. package/dist/config.js +520 -515
  18. package/dist/config.mjs +346 -341
  19. package/dist/contracts-BeW9k0yZ.d.ts +54 -0
  20. package/dist/contracts-D7KIS-TK.d.ts +36 -0
  21. package/dist/coverage.cjs +448 -485
  22. package/dist/coverage.d.ts +13 -59
  23. package/dist/coverage.js +447 -484
  24. package/dist/coverage.mjs +447 -484
  25. package/dist/database.cjs +819 -828
  26. package/dist/database.d.ts +8 -24
  27. package/dist/database.js +818 -829
  28. package/dist/database.mjs +818 -829
  29. package/dist/desktop-auto-render.cjs +1700 -1522
  30. package/dist/desktop-auto-render.d.ts +4 -9
  31. package/dist/desktop-auto-render.js +1695 -1517
  32. package/dist/desktop-auto-render.mjs +1696 -1518
  33. package/dist/desktop.cjs +3 -1
  34. package/dist/desktop.d.ts +4 -1
  35. package/dist/desktop.js +1 -1
  36. package/dist/desktop.mjs +1 -1
  37. package/dist/dev-build.cjs +830 -0
  38. package/dist/dev-build.d.ts +53 -0
  39. package/dist/dev-build.js +3318 -0
  40. package/dist/dev-build.mjs +797 -0
  41. package/dist/dom.cjs +717 -590
  42. package/dist/dom.d.ts +2 -15
  43. package/dist/dom.js +714 -587
  44. package/dist/dom.mjs +716 -589
  45. package/dist/el.cjs +62 -52
  46. package/dist/el.d.ts +5 -10
  47. package/dist/el.js +60 -52
  48. package/dist/el.mjs +60 -52
  49. package/dist/fs.cjs +72 -63
  50. package/dist/fs.d.ts +22 -19
  51. package/dist/fs.js +71 -62
  52. package/dist/fs.mjs +71 -62
  53. package/dist/hmr.cjs +40 -14
  54. package/dist/hmr.d.ts +11 -23
  55. package/dist/hmr.js +38 -14
  56. package/dist/hmr.mjs +38 -14
  57. package/dist/http.cjs +251 -99
  58. package/dist/http.d.ts +38 -104
  59. package/dist/http.js +249 -99
  60. package/dist/http.mjs +249 -99
  61. package/dist/https.cjs +524 -228
  62. package/dist/https.d.ts +44 -36
  63. package/dist/https.js +520 -226
  64. package/dist/https.mjs +522 -228
  65. package/dist/index.cjs +7502 -7690
  66. package/dist/index.d.ts +8 -3
  67. package/dist/index.js +7486 -7676
  68. package/dist/index.mjs +7497 -7686
  69. package/dist/mime-types.cjs +10 -4
  70. package/dist/mime-types.d.ts +8 -11
  71. package/dist/mime-types.js +9 -3
  72. package/dist/mime-types.mjs +9 -3
  73. package/dist/native.cjs +8616 -8869
  74. package/dist/native.d.ts +7 -8
  75. package/dist/native.js +8682 -8935
  76. package/dist/native.mjs +8615 -8868
  77. package/dist/path.cjs +83 -77
  78. package/dist/path.d.ts +29 -29
  79. package/dist/path.js +82 -76
  80. package/dist/path.mjs +82 -76
  81. package/dist/pm.cjs +3300 -0
  82. package/dist/pm.d.ts +256 -0
  83. package/dist/pm.js +5638 -0
  84. package/dist/pm.mjs +3196 -0
  85. package/dist/preview-build.cjs +712 -0
  86. package/dist/preview-build.d.ts +59 -0
  87. package/dist/preview-build.js +3194 -0
  88. package/dist/preview-build.mjs +676 -0
  89. package/dist/render-context.cjs +13 -2
  90. package/dist/render-context.d.ts +9 -31
  91. package/dist/render-context.js +11 -2
  92. package/dist/render-context.mjs +11 -2
  93. package/dist/router.cjs +787 -645
  94. package/dist/router.d.ts +8 -12
  95. package/dist/router.js +786 -644
  96. package/dist/router.mjs +786 -644
  97. package/dist/runtime.cjs +1 -1
  98. package/dist/runtime.js +1 -1
  99. package/dist/runtime.mjs +1 -1
  100. package/dist/server.cjs +3315 -2603
  101. package/dist/server.d.ts +49 -4
  102. package/dist/server.js +7611 -2834
  103. package/dist/server.mjs +3317 -2607
  104. package/dist/smtp-server.cjs +128 -0
  105. package/dist/smtp-server.d.ts +27 -0
  106. package/dist/smtp-server.js +4199 -0
  107. package/dist/smtp-server.mjs +100 -0
  108. package/dist/state-DvEkDehk.d.ts +195 -0
  109. package/dist/state.cjs +768 -658
  110. package/dist/state.d.ts +11 -69
  111. package/dist/state.js +760 -650
  112. package/dist/state.mjs +767 -657
  113. package/dist/style.cjs +1011 -968
  114. package/dist/style.d.ts +13 -127
  115. package/dist/style.js +1009 -970
  116. package/dist/style.mjs +1011 -971
  117. package/dist/test-reporter.cjs +332 -316
  118. package/dist/test-reporter.d.ts +28 -33
  119. package/dist/test-reporter.js +328 -312
  120. package/dist/test-reporter.mjs +328 -312
  121. package/dist/test-runtime.cjs +927 -968
  122. package/dist/test-runtime.d.ts +24 -99
  123. package/dist/test-runtime.js +922 -965
  124. package/dist/test-runtime.mjs +922 -965
  125. package/dist/test.cjs +4428 -4273
  126. package/dist/test.d.ts +2 -8
  127. package/dist/test.js +4307 -4154
  128. package/dist/test.mjs +4419 -4267
  129. package/dist/types-BONVzPtp.d.ts +59 -0
  130. package/dist/types-BR4wMiVx.d.ts +32 -0
  131. package/dist/types-C4gKykuG.d.ts +23 -0
  132. package/dist/types-CIhpN1-K.d.ts +64 -0
  133. package/dist/types-Ckj8md_j.d.ts +84 -0
  134. package/dist/types-CpjQTAkX.d.ts +24 -0
  135. package/dist/types-D0LjrYjS.d.ts +14 -0
  136. package/dist/types-DAisuVr5.d.ts +75 -0
  137. package/dist/types-tJn88E1N.d.ts +242 -0
  138. package/dist/types.d.ts +71 -226
  139. package/dist/universal.cjs +1 -1
  140. package/dist/universal.d.ts +1 -5
  141. package/dist/universal.js +1 -1
  142. package/dist/universal.mjs +1 -1
  143. package/dist/websocket-XfyK23zD.d.ts +119 -0
  144. package/dist/ws.cjs +129 -108
  145. package/dist/ws.d.ts +21 -131
  146. package/dist/ws.js +128 -109
  147. package/dist/ws.mjs +128 -109
  148. package/dist/wss.cjs +757 -479
  149. package/dist/wss.d.ts +31 -28
  150. package/dist/wss.js +755 -479
  151. package/dist/wss.mjs +758 -482
  152. package/package.json +16 -1
  153. package/vendor/epaint-0.31.1/src/image.rs +418 -0
  154. package/dist/server-CcBFc2F5.d.ts +0 -449
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
- var fs;
51
- var fsPromises;
52
- if (isNode || isBun) {
53
- fs = __require("fs");
54
- fsPromises = __require("fs/promises");
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
- function createStatsFromDenoFileInfo(info) {
109
- return {
110
- isFile: () => info.isFile,
111
- isDirectory: () => info.isDirectory,
112
- isBlockDevice: () => false,
113
- isCharacterDevice: () => false,
114
- isSymbolicLink: () => info.isSymlink || false,
115
- isFIFO: () => false,
116
- isSocket: () => false,
117
- dev: info.dev || 0,
118
- ino: info.ino || 0,
119
- mode: info.mode || 0,
120
- nlink: info.nlink || 1,
121
- uid: info.uid || 0,
122
- gid: info.gid || 0,
123
- rdev: 0,
124
- size: info.size,
125
- blksize: info.blksize || 4096,
126
- blocks: info.blocks || Math.ceil(info.size / 512),
127
- atimeMs: info.atime?.getTime() || Date.now(),
128
- mtimeMs: info.mtime?.getTime() || Date.now(),
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 createDirentFromDenoEntry(entry) {
138
- return {
139
- name: entry.name,
140
- isFile: () => entry.isFile,
141
- isDirectory: () => entry.isDirectory,
142
- isBlockDevice: () => false,
143
- isCharacterDevice: () => false,
144
- isSymbolicLink: () => entry.isSymlink || false,
145
- isFIFO: () => false,
146
- isSocket: () => false
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
- } else if (isDeno) {
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
- let normalized = path.replace(isWin ? /[\/\\]+/g : /\/+/g, separator);
261
+ const normalized = path.replace(isWin ? /[\/\\]+/g : /\/+/g, separator);
217
262
  const parts = normalized.split(separator);
218
263
  const result = [];
219
- for (let i = 0; i < parts.length; i++) {
220
- const part = parts[i];
264
+ for (let index = 0; index < parts.length; index++) {
265
+ const part = parts[index];
221
266
  if (part === "" || part === ".") {
222
- if (i === 0 && isAbsolute) result.push("");
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 final = result.join(separator);
238
- if (final.length === 0) {
282
+ let finalPath = result.join(separator);
283
+ if (finalPath.length === 0) {
239
284
  return isAbsolute ? separator : ".";
240
285
  }
241
- if (trailingSeparator && final[final.length - 1] !== separator) {
242
- final += separator;
286
+ if (trailingSeparator && finalPath[finalPath.length - 1] !== separator) {
287
+ finalPath += separator;
243
288
  }
244
- return final;
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 i = 0; i < paths.length; i++) {
251
- const path = paths[i];
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 isAbsolute = false;
267
- for (let i = paths.length - 1; i >= 0 && !isAbsolute; i--) {
268
- const path = paths[i];
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
- isAbsolute = isWin ? isAbsoluteWin(resolved) : isAbsolutePosix(resolved);
316
+ absolute = isWin ? isAbsoluteWin(resolved) : isAbsolutePosix(resolved);
272
317
  }
273
318
  }
274
- if (!isAbsolute) {
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
- from = resolvePaths([from], isWin);
282
- to = resolvePaths([to], isWin);
283
- if (from === to) return "";
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 = from.split(separator).filter((p) => p.length > 0);
286
- const toParts = to.split(separator).filter((p) => p.length > 0);
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
- for (let i = 0; i < minLength; i++) {
290
- if (fromParts[i] === toParts[i]) {
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
- for (let i = 0; i < upCount; i++) {
342
+ const upCount = fromParts.length - commonLength;
343
+ for (let index = 0; index < upCount; index++) {
299
344
  result.push("..");
300
345
  }
301
- for (let i = commonLength; i < toParts.length; i++) {
302
- result.push(toParts[i]);
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 lastSepIndex = normalized.lastIndexOf(separator);
311
- if (lastSepIndex === -1) return ".";
312
- if (lastSepIndex === 0) return separator;
313
- return normalized.slice(0, lastSepIndex);
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 lastSepIndex = isWin ? findLastSeparator(path) : path.lastIndexOf("/");
318
- let base = lastSepIndex === -1 ? path : path.slice(lastSepIndex + 1);
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 lastSepIndex = findLastSeparator(path);
327
- if (lastDotIndex === -1 || lastDotIndex < lastSepIndex || lastDotIndex === path.length - 1) {
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
- if (path[0] === "/") {
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
- let regexStr = "^";
423
- for (let i = 0; i < pattern.length; i++) {
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
- for (const pattern of include) {
442
+ return include.some((pattern) => {
469
443
  const regex = globToRegex(pattern);
470
- if (regex.test(normalizedPath)) {
471
- return true;
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
- for (const pattern of exclude) {
449
+ return exclude.some((pattern) => {
479
450
  const regex = globToRegex(pattern);
480
- if (regex.test(normalizedPath)) {
481
- return true;
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") continue;
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)) continue;
498
- files.push(...findAllTypeScriptFiles(fullPath, include, exclude));
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 (e) {
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 (e) {
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
- const analysis = analyzeSourceFile(tsFile);
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)) continue;
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
- const analysis = analyzeSourceFile(coveredFile);
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
- var colors = {
616
- reset: "\x1B[0m",
617
- bold: "\x1B[1m",
618
- dim: "\x1B[2m",
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: { total: file.statements, covered: file.coveredStatements, percentage: stmtPct },
636
- branches: { total: file.branches, covered: file.coveredBranches, percentage: branchPct },
637
- functions: { total: file.functions, covered: file.coveredFunctions, percentage: funcPct },
638
- lines: { total: file.lines, covered: file.coveredLines, percentage: linePct }
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 stripAnsi(str) {
642
- return str.replace(/\x1b\[[0-9;]*m/g, "");
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 getVisibleWidth(str) {
645
- return stripAnsi(str).length;
650
+ function toRelativePath(cwd, filePath) {
651
+ return relative(cwd, filePath).replace(/\\/g, "/");
646
652
  }
647
- function formatMetricFixedWidth(covered, total, percentage, includeSeparator = false) {
648
- const color = getColorForPercentage(percentage);
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 i = 1; i < uncoveredLines.length; i++) {
668
- if (uncoveredLines[i] === end + 1) {
669
- end = uncoveredLines[i];
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
- if (start === end) {
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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
685
+ }
686
+ function escapeXml(text) {
687
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
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
- let totalStatements = 0, coveredStatements = 0;
691
- let totalBranches = 0, coveredBranches = 0;
692
- let totalFunctions = 0, coveredFunctions = 0;
693
- let totalLines = 0, coveredLines = 0;
694
- for (const coverage of coverageMap.values()) {
695
- totalStatements += coverage.statements;
696
- coveredStatements += coverage.coveredStatements;
697
- totalBranches += coverage.branches;
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\x1B[0m`;
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
- const stmtsMetric = formatMetricFixedWidth(coveredStatements, totalStatements, pctStmts, true);
717
- const branchMetric = formatMetricFixedWidth(coveredBranches, totalBranches, pctBranch, true);
718
- const funcsMetric = formatMetricFixedWidth(coveredFunctions, totalFunctions, pctFunc, true);
719
- const linesMetric = formatMetricFixedWidth(coveredLines, totalLines, pctLines, true);
720
- output += `${stmtsMetric}${branchMetric}${funcsMetric}${linesMetric}
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 relDir = toRelative(dir);
756
- if (relDir !== ".") {
770
+ const relativeDir = toRelativePath(cwd, dir);
771
+ if (relativeDir !== ".") {
757
772
  output += `
758
- ${colors.cyan}${relDir}/${colors.reset}
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 relPath = toRelative(path);
764
- let displayName = relPath;
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
- stats.statements.covered,
771
- stats.statements.total,
772
- stats.statements.percentage,
773
- true
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\x1B[0m ${coverageMap.size} passed (100%)
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\x1B[0m ${colors.green}${coveredStatements}${colors.reset} ${colors.dim}/${colors.reset} ${totalStatements}
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\x1B[0m ${colors.green}${coveredBranches}${colors.reset} ${colors.dim}/${colors.reset} ${totalBranches}
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\x1B[0m ${colors.green}${coveredFunctions}${colors.reset} ${colors.dim}/${colors.reset} ${totalFunctions}
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\x1B[0m ${colors.green}${coveredLines}${colors.reset} ${colors.dim}/${colors.reset} ${totalLines}
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
- let totalStatements = 0, coveredStatements = 0;
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: ${overallPct >= 80 ? "#3fb950" : overallPct >= 50 ? "#d29922" : "#f85149"};
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: ${overallPct}%"></div>
1040
- <div class="overall-bar-text">${overallPct.toFixed(2)}%</div>
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 ${pctStmts >= 80 ? "high" : pctStmts >= 50 ? "medium" : "low"}">${pctStmts.toFixed(2)}%</div>
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 ${pctStmts >= 80 ? "high" : pctStmts >= 50 ? "medium" : "low"}" style="width: ${pctStmts}%"></div>
1022
+ <div class="progress-fill ${getCoverageClass(totals.statements.percentage)}" style="width: ${totals.statements.percentage}%"></div>
1054
1023
  </div>
1055
- <div class="metric-count">${coveredStatements}/${totalStatements}</div>
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 ${pctBranch >= 80 ? "high" : pctBranch >= 50 ? "medium" : "low"}">${pctBranch.toFixed(2)}%</div>
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 ${pctBranch >= 80 ? "high" : pctBranch >= 50 ? "medium" : "low"}" style="width: ${pctBranch}%"></div>
1030
+ <div class="progress-fill ${getCoverageClass(totals.branches.percentage)}" style="width: ${totals.branches.percentage}%"></div>
1062
1031
  </div>
1063
- <div class="metric-count">${coveredBranches}/${totalBranches}</div>
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 ${pctFunc >= 80 ? "high" : pctFunc >= 50 ? "medium" : "low"}">${pctFunc.toFixed(2)}%</div>
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 ${pctFunc >= 80 ? "high" : pctFunc >= 50 ? "medium" : "low"}" style="width: ${pctFunc}%"></div>
1038
+ <div class="progress-fill ${getCoverageClass(totals.functions.percentage)}" style="width: ${totals.functions.percentage}%"></div>
1070
1039
  </div>
1071
- <div class="metric-count">${coveredFunctions}/${totalFunctions}</div>
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 ${pctLines >= 80 ? "high" : pctLines >= 50 ? "medium" : "low"}">${pctLines.toFixed(2)}%</div>
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 ${pctLines >= 80 ? "high" : pctLines >= 50 ? "medium" : "low"}" style="width: ${pctLines}%"></div>
1046
+ <div class="progress-fill ${getCoverageClass(totals.lines.percentage)}" style="width: ${totals.lines.percentage}%"></div>
1078
1047
  </div>
1079
- <div class="metric-count">${coveredLines}/${totalLines}</div>
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 = toRelative(filePath);
1100
- const safeFileName = fileName.replace(/[\/\\]/g, "_") + ".html";
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 >= 80 ? "high" : stats.statements.percentage >= 50 ? "medium" : "low"}">${stats.statements.percentage.toFixed(2)}%</div>
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 >= 80 ? "high" : stats.branches.percentage >= 50 ? "medium" : "low"}">${stats.branches.percentage.toFixed(2)}%</div>
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 >= 80 ? "high" : stats.functions.percentage >= 50 ? "medium" : "low"}">${stats.functions.percentage.toFixed(2)}%</div>
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 >= 80 ? "high" : stats.lines.percentage >= 50 ? "medium" : "low"}">${stats.lines.percentage.toFixed(2)}%</div>
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, toRelative);
1132
+ generateFileDetailPage(filePath, coverage, reportsDir, cwd);
1164
1133
  }
1165
1134
  }
1166
- function generateFileDetailPage(filePath, coverage, reportsDir, toRelative) {
1167
- const fileName = toRelative(filePath);
1168
- const safeFileName = fileName.replace(/[\/\\]/g, "_") + ".html";
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 (e) {
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 >= 80 ? "high" : stats.statements.percentage >= 50 ? "medium" : "low"}">${stats.statements.percentage.toFixed(2)}%</div>
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 >= 80 ? "high" : stats.statements.percentage >= 50 ? "medium" : "low"}" style="width: ${stats.statements.percentage}%"></div>
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 >= 80 ? "high" : stats.branches.percentage >= 50 ? "medium" : "low"}">${stats.branches.percentage.toFixed(2)}%</div>
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 >= 80 ? "high" : stats.branches.percentage >= 50 ? "medium" : "low"}" style="width: ${stats.branches.percentage}%"></div>
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 >= 80 ? "high" : stats.functions.percentage >= 50 ? "medium" : "low"}">${stats.functions.percentage.toFixed(2)}%</div>
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 >= 80 ? "high" : stats.functions.percentage >= 50 ? "medium" : "low"}" style="width: ${stats.functions.percentage}%"></div>
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 >= 80 ? "high" : stats.lines.percentage >= 50 ? "medium" : "low"}">${stats.lines.percentage.toFixed(2)}%</div>
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 >= 80 ? "high" : stats.lines.percentage >= 50 ? "medium" : "low"}" style="width: ${stats.lines.percentage}%"></div>
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 lineNum = index + 1;
1353
- const isUncovered = uncoveredSet.has(lineNum);
1354
- const isExecutable = coverage.lines > 0;
1355
- const rowClass = isExecutable ? isUncovered ? "uncovered" : "covered" : "";
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">${lineNum}</td>
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
- function escapeHtml(text) {
1371
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
1372
- }
1373
- function escapeXml(text) {
1374
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
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 = relative(process.cwd(), filePath).replace(/\\/g, "/");
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
- const jsonData = JSON.stringify(coverageData, null, 2);
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 = relative(process.cwd(), filePath).replace(/\\/g, "/");
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="${complexity}" elements="${elements}" coveredelements="${coveredElements}"
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="${coverageMap.size > 0 ? Array.from(coverageMap.values()).filter((c) => c.coveredStatements > 0).length : 0}"
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="${complexity}" elements="${elements}" coveredelements="${coveredElements}"
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="${coverageMap.size > 0 ? Array.from(coverageMap.values()).filter((c) => c.coveredStatements > 0).length : 0}"
1415
+ classes="${totalClasses}" coveredclasses="${coveredClassCount}"
1453
1416
  files="${totalFiles}" loc="${totalLines}" ncloc="${totalLines - coveredLines}" />
1454
1417
  ${fileEntries.join("")}
1455
1418
  </package>