elit 3.3.3 → 3.3.4

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 (143) hide show
  1. package/dist/build.d.mts +1 -1
  2. package/dist/build.js +1 -0
  3. package/dist/build.js.map +1 -0
  4. package/dist/build.mjs +1 -0
  5. package/dist/build.mjs.map +1 -0
  6. package/dist/chokidar.js +1 -0
  7. package/dist/chokidar.js.map +1 -0
  8. package/dist/chokidar.mjs +1 -0
  9. package/dist/chokidar.mjs.map +1 -0
  10. package/dist/cli.js +4687 -34
  11. package/dist/config.d.mts +3 -1
  12. package/dist/config.d.ts +3 -1
  13. package/dist/config.d.ts.map +1 -1
  14. package/dist/config.js +1 -0
  15. package/dist/config.js.map +1 -0
  16. package/dist/config.mjs +1 -0
  17. package/dist/config.mjs.map +1 -0
  18. package/dist/coverage.d.mts +85 -0
  19. package/dist/coverage.d.ts +76 -0
  20. package/dist/coverage.d.ts.map +1 -0
  21. package/dist/coverage.js +1549 -0
  22. package/dist/coverage.js.map +1 -0
  23. package/dist/coverage.mjs +1520 -0
  24. package/dist/coverage.mjs.map +1 -0
  25. package/dist/database.d.mts +31 -6
  26. package/dist/database.d.ts +31 -6
  27. package/dist/database.d.ts.map +1 -1
  28. package/dist/database.js +60 -33
  29. package/dist/database.js.map +1 -0
  30. package/dist/database.mjs +60 -33
  31. package/dist/database.mjs.map +1 -0
  32. package/dist/dom.js +1 -0
  33. package/dist/dom.js.map +1 -0
  34. package/dist/dom.mjs +1 -0
  35. package/dist/dom.mjs.map +1 -0
  36. package/dist/el.js +1 -0
  37. package/dist/el.js.map +1 -0
  38. package/dist/el.mjs +1 -0
  39. package/dist/el.mjs.map +1 -0
  40. package/dist/fs.js +1 -0
  41. package/dist/fs.js.map +1 -0
  42. package/dist/fs.mjs +1 -0
  43. package/dist/fs.mjs.map +1 -0
  44. package/dist/hmr.js +1 -0
  45. package/dist/hmr.js.map +1 -0
  46. package/dist/hmr.mjs +1 -0
  47. package/dist/hmr.mjs.map +1 -0
  48. package/dist/http.js +1 -0
  49. package/dist/http.js.map +1 -0
  50. package/dist/http.mjs +1 -0
  51. package/dist/http.mjs.map +1 -0
  52. package/dist/https.d.mts +1 -1
  53. package/dist/https.js +1 -0
  54. package/dist/https.js.map +1 -0
  55. package/dist/https.mjs +1 -0
  56. package/dist/https.mjs.map +1 -0
  57. package/dist/index.d.mts +1 -1
  58. package/dist/index.js +1 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/index.mjs +1 -0
  61. package/dist/index.mjs.map +1 -0
  62. package/dist/mime-types.js +1 -0
  63. package/dist/mime-types.js.map +1 -0
  64. package/dist/mime-types.mjs +1 -0
  65. package/dist/mime-types.mjs.map +1 -0
  66. package/dist/path.js +1 -0
  67. package/dist/path.js.map +1 -0
  68. package/dist/path.mjs +1 -0
  69. package/dist/path.mjs.map +1 -0
  70. package/dist/router.js +1 -0
  71. package/dist/router.js.map +1 -0
  72. package/dist/router.mjs +1 -0
  73. package/dist/router.mjs.map +1 -0
  74. package/dist/runtime.js +1 -0
  75. package/dist/runtime.js.map +1 -0
  76. package/dist/runtime.mjs +1 -0
  77. package/dist/runtime.mjs.map +1 -0
  78. package/dist/{server-Cz3z-5ls.d.mts → server-BFTzgJpO.d.mts} +62 -1
  79. package/dist/{server-BG2CaVMh.d.ts → server-CIXtexNS.d.ts} +62 -1
  80. package/dist/server.d.mts +1 -1
  81. package/dist/server.d.ts +9 -0
  82. package/dist/server.d.ts.map +1 -1
  83. package/dist/server.js +12 -0
  84. package/dist/server.js.map +1 -0
  85. package/dist/server.mjs +12 -0
  86. package/dist/server.mjs.map +1 -0
  87. package/dist/state.d.mts +1 -1
  88. package/dist/state.js +1 -0
  89. package/dist/state.js.map +1 -0
  90. package/dist/state.mjs +1 -0
  91. package/dist/state.mjs.map +1 -0
  92. package/dist/style.js +1 -0
  93. package/dist/style.js.map +1 -0
  94. package/dist/style.mjs +1 -0
  95. package/dist/style.mjs.map +1 -0
  96. package/dist/test-globals.d.ts +184 -0
  97. package/dist/test-reporter.d.mts +77 -0
  98. package/dist/test-reporter.d.ts +73 -0
  99. package/dist/test-reporter.d.ts.map +1 -0
  100. package/dist/test-reporter.js +726 -0
  101. package/dist/test-reporter.js.map +1 -0
  102. package/dist/test-reporter.mjs +696 -0
  103. package/dist/test-reporter.mjs.map +1 -0
  104. package/dist/test-runtime.d.mts +122 -0
  105. package/dist/test-runtime.d.ts +120 -0
  106. package/dist/test-runtime.d.ts.map +1 -0
  107. package/dist/test-runtime.js +1292 -0
  108. package/dist/test-runtime.js.map +1 -0
  109. package/dist/test-runtime.mjs +1269 -0
  110. package/dist/test-runtime.mjs.map +1 -0
  111. package/dist/test.d.mts +39 -0
  112. package/dist/test.d.ts +38 -0
  113. package/dist/test.d.ts.map +1 -0
  114. package/dist/test.js +4966 -0
  115. package/dist/test.js.map +1 -0
  116. package/dist/test.mjs +4944 -0
  117. package/dist/test.mjs.map +1 -0
  118. package/dist/types.d.mts +62 -1
  119. package/dist/types.d.ts +52 -0
  120. package/dist/types.d.ts.map +1 -1
  121. package/dist/types.js +1 -0
  122. package/dist/types.js.map +1 -0
  123. package/dist/types.mjs +1 -0
  124. package/dist/types.mjs.map +1 -0
  125. package/dist/ws.js +1 -0
  126. package/dist/ws.js.map +1 -0
  127. package/dist/ws.mjs +1 -0
  128. package/dist/ws.mjs.map +1 -0
  129. package/dist/wss.js +1 -0
  130. package/dist/wss.js.map +1 -0
  131. package/dist/wss.mjs +1 -0
  132. package/dist/wss.mjs.map +1 -0
  133. package/package.json +37 -5
  134. package/src/cli.ts +165 -1
  135. package/src/config.ts +3 -1
  136. package/src/coverage.ts +1479 -0
  137. package/src/database.ts +71 -35
  138. package/src/server.ts +12 -0
  139. package/src/test-globals.d.ts +184 -0
  140. package/src/test-reporter.ts +609 -0
  141. package/src/test-runtime.ts +1359 -0
  142. package/src/test.ts +368 -0
  143. package/src/types.ts +59 -0
package/src/test.ts ADDED
@@ -0,0 +1,368 @@
1
+ /**
2
+ * Jest-compatible Test Runner powered by esbuild
3
+ *
4
+ * Main entry point for running tests with Jest-like API.
5
+ */
6
+
7
+ import { readdirSync } from './fs';
8
+ import { join, relative } from './path';
9
+ import { runTests, setupGlobals, clearGlobals, resetCoveredFiles, getCoveredFiles } from './test-runtime';
10
+ import { TestReporter, DotReporter, JsonReporter, VerboseReporter } from './test-reporter';
11
+
12
+ export interface TestOptions {
13
+ files?: string[];
14
+ include?: string[];
15
+ exclude?: string[];
16
+ reporter?: 'default' | 'dot' | 'json' | 'verbose';
17
+ timeout?: number;
18
+ bail?: boolean;
19
+ run?: boolean;
20
+ watch?: boolean;
21
+ endToEnd?: boolean;
22
+ colors?: boolean;
23
+ globals?: boolean;
24
+ describePattern?: string;
25
+ testPattern?: string;
26
+ coverage?: {
27
+ enabled: boolean;
28
+ provider: 'v8' | 'istanbul';
29
+ reporter?: ('text' | 'html' | 'lcov' | 'json' | 'coverage-final.json' | 'clover')[];
30
+ include?: string[];
31
+ exclude?: string[];
32
+ };
33
+ }
34
+
35
+ /**
36
+ * Convert glob pattern to regex (safe from ReDoS)
37
+ */
38
+ function globToRegex(pattern: string): RegExp {
39
+ // Handle brace expansion: {ts,js} -> (ts|js)
40
+ // Use string operations instead of regex to avoid ReDoS
41
+ let expanded = pattern;
42
+ const openBraceIndex = pattern.indexOf('{');
43
+ if (openBraceIndex !== -1) {
44
+ const closeBraceIndex = pattern.indexOf('}', openBraceIndex);
45
+ if (closeBraceIndex !== -1) {
46
+ const options = pattern.slice(openBraceIndex + 1, closeBraceIndex).split(',');
47
+ const before = pattern.slice(0, openBraceIndex);
48
+ const after = pattern.slice(closeBraceIndex + 1);
49
+ expanded = before + '(' + options.join('|') + ')' + after;
50
+ }
51
+ }
52
+
53
+ // Convert to regex using character-by-character processing
54
+ // This avoids using .replace() with regex on user input
55
+ let regexStr = '^';
56
+ for (let i = 0; i < expanded.length; i++) {
57
+ const char = expanded[i];
58
+ switch (char) {
59
+ case '.':
60
+ regexStr += '\\.';
61
+ break;
62
+ case '*':
63
+ regexStr += '.*';
64
+ break;
65
+ case '?':
66
+ regexStr += '.';
67
+ break;
68
+ case '+':
69
+ case '^':
70
+ case '$':
71
+ case '|':
72
+ case '(':
73
+ case ')':
74
+ case '[':
75
+ case ']':
76
+ case '{':
77
+ case '}':
78
+ case '\\':
79
+ regexStr += '\\' + char;
80
+ break;
81
+ default:
82
+ regexStr += char;
83
+ }
84
+ }
85
+ regexStr += '$';
86
+
87
+ return new RegExp(regexStr);
88
+ }
89
+
90
+ /**
91
+ * Check if a file path matches a pattern (safe from ReDoS)
92
+ */
93
+ function matchesPattern(relativePath: string, pattern: string): boolean {
94
+ // Handle brace expansion: {test,spec} or {ts,js,tsx,etc}
95
+ // Use string operations instead of regex to avoid ReDoS
96
+ const openBraceIndex = pattern.indexOf('{');
97
+
98
+ if (openBraceIndex !== -1) {
99
+ const closeBraceIndex = pattern.indexOf('}', openBraceIndex);
100
+ if (closeBraceIndex !== -1) {
101
+ // Expand braces to multiple patterns
102
+ const options = pattern.slice(openBraceIndex + 1, closeBraceIndex).split(',');
103
+ const before = pattern.slice(0, openBraceIndex);
104
+ const after = pattern.slice(closeBraceIndex + 1);
105
+
106
+ for (const option of options) {
107
+ const testPattern = before + option + after;
108
+ if (matchesPattern(relativePath, testPattern)) {
109
+ return true;
110
+ }
111
+ }
112
+ return false;
113
+ }
114
+ }
115
+
116
+ // Simple glob to regex conversion
117
+ const regex = globToRegex(pattern);
118
+ return regex.test(relativePath);
119
+ }
120
+
121
+ /**
122
+ * Find all test files matching patterns
123
+ */
124
+ function findTestFiles(
125
+ root: string,
126
+ include: string[],
127
+ exclude: string[]
128
+ ): string[] {
129
+ const files: string[] = [];
130
+
131
+ // Normalize path to use forward slashes for pattern matching
132
+ // This ensures cross-platform compatibility
133
+ function normalizePathForPattern(path: string): string {
134
+ return path.replace(/\\/g, '/');
135
+ }
136
+
137
+ function scanDir(dir: string) {
138
+ try {
139
+ const entries = readdirSync(dir, { withFileTypes: true });
140
+
141
+ for (const entry of entries) {
142
+ if (typeof entry === 'string') continue;
143
+
144
+ const fullPath = join(dir, entry.name);
145
+
146
+ if (entry.isDirectory()) {
147
+ // Check if directory should be excluded
148
+ const relativePath = normalizePathForPattern(relative(root, fullPath));
149
+ if (exclude.some(pattern => matchesPattern(relativePath, pattern))) {
150
+ continue;
151
+ }
152
+ scanDir(fullPath);
153
+ } else if (entry.isFile()) {
154
+ // Check if file matches include patterns
155
+ const relativePath = normalizePathForPattern(relative(root, fullPath));
156
+ // First check if file should be excluded
157
+ if (exclude.some(pattern => matchesPattern(relativePath, pattern))) {
158
+ continue;
159
+ }
160
+ for (const pattern of include) {
161
+ if (matchesPattern(relativePath, pattern)) {
162
+ files.push(fullPath);
163
+ break;
164
+ }
165
+ }
166
+ }
167
+ }
168
+ } catch (error) {
169
+ // Ignore permission errors
170
+ }
171
+ }
172
+
173
+ scanDir(root);
174
+ return files;
175
+ }
176
+
177
+ /**
178
+ * Run tests with Jest-compatible interface
179
+ */
180
+ export async function runJestTests(options: TestOptions = {}) {
181
+ const {
182
+ include = ['**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
183
+ exclude = ['**/node_modules/**', '**/dist/**', '**/coverage/**', '**/.elit-tests-temp/**'],
184
+ reporter = 'default',
185
+ timeout = 5000,
186
+ bail = false,
187
+ globals = true,
188
+ } = options;
189
+
190
+ const root = process.cwd();
191
+ const files = options.files || findTestFiles(root, include, exclude);
192
+
193
+ if (files.length === 0) {
194
+ console.log('\n No test files found\n');
195
+ return {
196
+ success: true,
197
+ passed: 0,
198
+ failed: 0,
199
+ total: 0,
200
+ };
201
+ }
202
+
203
+ // Setup globals if enabled
204
+ if (globals) {
205
+ setupGlobals();
206
+ }
207
+
208
+ // Create reporter
209
+ let testReporter;
210
+ switch (reporter) {
211
+ case 'dot':
212
+ testReporter = new DotReporter();
213
+ break;
214
+ case 'json':
215
+ testReporter = new JsonReporter();
216
+ break;
217
+ case 'verbose':
218
+ testReporter = new VerboseReporter();
219
+ break;
220
+ default:
221
+ testReporter = new TestReporter({ colors: true });
222
+ }
223
+
224
+ // Notify start
225
+ if ('onRunStart' in testReporter) {
226
+ testReporter.onRunStart(files);
227
+ }
228
+
229
+ // Reset coverage tracking before running tests
230
+ resetCoveredFiles();
231
+
232
+ // Run tests
233
+ const results = await runTests({
234
+ files,
235
+ timeout,
236
+ bail,
237
+ describePattern: options.describePattern,
238
+ testPattern: options.testPattern,
239
+ });
240
+ // Notify individual test results
241
+ if ('onTestResult' in testReporter) {
242
+ for (const result of results.results) {
243
+ testReporter.onTestResult(result);
244
+ }
245
+ }
246
+
247
+ // Notify end
248
+ if ('onRunEnd' in testReporter) {
249
+ testReporter.onRunEnd(results.results);
250
+ }
251
+
252
+ // Clear globals
253
+ if (globals) {
254
+ clearGlobals();
255
+ }
256
+
257
+ // Generate coverage if enabled
258
+ if (options.coverage?.enabled) {
259
+ await generateCoverage(options.coverage, results.results);
260
+ }
261
+
262
+ return {
263
+ success: results.failed === 0,
264
+ passed: results.passed,
265
+ failed: results.failed,
266
+ total: results.passed + results.failed + results.skipped + results.todo,
267
+ };
268
+ }
269
+
270
+ /**
271
+ * Generate coverage report
272
+ */
273
+ async function generateCoverage(
274
+ options: {
275
+ provider: 'v8' | 'istanbul';
276
+ reporter?: ('text' | 'html' | 'lcov' | 'json' | 'coverage-final.json' | 'clover')[];
277
+ include?: string[];
278
+ exclude?: string[];
279
+ },
280
+ testResults?: any[]
281
+ ) {
282
+ const { processCoverage, generateTextReport, generateHtmlReport, generateCoverageFinalJson, generateCloverXml } = await import('./coverage');
283
+
284
+ // Get covered files from test execution
285
+ const coveredFilesForCoverage = getCoveredFiles();
286
+
287
+ const coverageMap = await processCoverage({
288
+ reportsDirectory: './coverage',
289
+ include: options.include || ['**/*.ts', '**/*.js'],
290
+ exclude: options.exclude || ['**/*.test.ts', '**/*.spec.ts', '**/node_modules/**'],
291
+ reporter: options.reporter || ['text', 'html'],
292
+ coveredFiles: coveredFilesForCoverage,
293
+ });
294
+
295
+ const reporters = options.reporter || ['text', 'html'];
296
+ if (reporters.includes('text')) {
297
+ console.log('\n' + generateTextReport(coverageMap, testResults));
298
+ }
299
+
300
+ if (reporters.includes('html')) {
301
+ generateHtmlReport(coverageMap, './coverage');
302
+ console.log(`\n Coverage report: coverage/index.html\n`);
303
+ }
304
+
305
+ if (reporters.includes('coverage-final.json')) {
306
+ generateCoverageFinalJson(coverageMap, './coverage');
307
+ console.log(`\n Coverage report: coverage/coverage-final.json\n`);
308
+ }
309
+
310
+ if (reporters.includes('clover')) {
311
+ generateCloverXml(coverageMap, './coverage');
312
+ console.log(`\n Coverage report: coverage/clover.xml\n`);
313
+ }
314
+ }
315
+
316
+ // ============================================================================
317
+ // Watch Mode
318
+ // ============================================================================
319
+
320
+ export async function runWatchMode(options: TestOptions = {}) {
321
+ const chokidar = await import('chokidar');
322
+
323
+ console.log('\n � watch mode - files will be re-run on change\n');
324
+
325
+ let isRunning = false;
326
+ let needsRerun = false;
327
+
328
+ const runTests = async () => {
329
+ if (isRunning) {
330
+ needsRerun = true;
331
+ return;
332
+ }
333
+
334
+ isRunning = true;
335
+ needsRerun = false;
336
+
337
+ console.clear();
338
+ await runJestTests(options);
339
+
340
+ isRunning = false;
341
+
342
+ if (needsRerun) {
343
+ await runTests();
344
+ }
345
+ };
346
+
347
+ // Initial run
348
+ await runTests();
349
+
350
+ const { include, exclude } = options;
351
+ const watchPatterns = include || ['**/*.test.ts', '**/*.test.js', '**/*.spec.ts', '**/*.spec.js'];
352
+ const ignoredPatterns = exclude || ['**/node_modules/**', '**/dist/**', '**/coverage/**'];
353
+
354
+ const watcher = chokidar.default.watch(watchPatterns, {
355
+ ignored: ignoredPatterns,
356
+ persistent: true,
357
+ });
358
+
359
+ watcher.on('change', async (path) => {
360
+ console.log(`\n 📄 ${path} changed\n`);
361
+ await runTests();
362
+ });
363
+
364
+ watcher.on('add', async (path) => {
365
+ console.log(`\n 📄 ${path} added\n`);
366
+ await runTests();
367
+ });
368
+ }
package/src/types.ts CHANGED
@@ -268,3 +268,62 @@ export interface PreviewOptions {
268
268
  /** Environment variables to inject (prefix with VITE_ for client access) */
269
269
  env?: Record<string, string>;
270
270
  }
271
+
272
+ // ===== Test Types =====
273
+
274
+ export type TestEnvironment = 'node' | 'jsdom' | 'happy-dom' | 'edge-runtime';
275
+
276
+ export type TestCoverageProvider = 'v8' | 'istanbul';
277
+
278
+ export type TestCoverageReporter = 'text' | 'json' | 'html' | 'lcov' | 'lcovonly' | 'coverage-final.json' | 'clover';
279
+
280
+ export interface TestCoverageOptions {
281
+ provider?: TestCoverageProvider;
282
+ reporter?: TestCoverageReporter[];
283
+ dir?: string;
284
+ include?: string[];
285
+ exclude?: string[];
286
+ thresholds?: {
287
+ lines?: number;
288
+ functions?: number;
289
+ branches?: number;
290
+ statements?: number;
291
+ };
292
+ all?: boolean;
293
+ }
294
+
295
+ export interface TestOptions {
296
+ environment?: TestEnvironment;
297
+ globals?: boolean;
298
+ setupFiles?: string[];
299
+ include?: string[];
300
+ exclude?: string[];
301
+ testTimeout?: number;
302
+ isolate?: boolean;
303
+ pool?: string;
304
+ poolOptions?: {
305
+ threads?: {
306
+ singleThread?: boolean;
307
+ minThreads?: number;
308
+ maxThreads?: number;
309
+ isolate?: boolean;
310
+ };
311
+ forks?: {
312
+ singleFork?: boolean;
313
+ minForks?: number;
314
+ maxForks?: number;
315
+ isolate?: boolean;
316
+ };
317
+ };
318
+ coverage?: TestCoverageOptions;
319
+ watch?: boolean;
320
+ ui?: boolean;
321
+ reporter?: 'verbose' | 'dot' | 'json' | 'tap';
322
+ bail?: number | boolean;
323
+ pattern?: string | RegExp;
324
+ colors?: boolean;
325
+ retry?: number;
326
+ includeSrc?: string[];
327
+ excludeSrc?: string[];
328
+ env?: Record<string, string>;
329
+ }