elit 3.6.6 → 3.6.7

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