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