@rstest/core 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/992.js ADDED
@@ -0,0 +1,485 @@
1
+ export const __webpack_ids__ = [
2
+ "992"
3
+ ];
4
+ export const __webpack_modules__ = {
5
+ "./src/core/rsbuild.ts": function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
6
+ __webpack_require__.d(__webpack_exports__, {
7
+ r: ()=>createRsbuildServer,
8
+ z: ()=>prepareRsbuild
9
+ });
10
+ var external_node_fs_ = __webpack_require__("node:fs");
11
+ var core_ = __webpack_require__("@rsbuild/core");
12
+ var external_pathe_ = __webpack_require__("pathe");
13
+ var utils = __webpack_require__("./src/utils/index.ts");
14
+ class TestFileWatchPlugin {
15
+ contextToWatch = null;
16
+ constructor(contextToWatch){
17
+ this.contextToWatch = contextToWatch;
18
+ }
19
+ apply(compiler) {
20
+ compiler.hooks.afterCompile.tap('Rstest:TestFileWatchPlugin', (compilation)=>{
21
+ if (null === this.contextToWatch) return;
22
+ const contextDep = compilation.contextDependencies;
23
+ if (!contextDep.has(this.contextToWatch)) contextDep.add(this.contextToWatch);
24
+ });
25
+ }
26
+ }
27
+ const pluginEntryWatch = ({ isWatch, globTestSourceEntries, setupFiles })=>({
28
+ name: 'rstest:entry-watch',
29
+ setup: (api)=>{
30
+ api.modifyRspackConfig(async (config)=>{
31
+ if (isWatch) {
32
+ config.plugins.push(new TestFileWatchPlugin(api.context.rootPath));
33
+ config.entry = async ()=>{
34
+ const sourceEntries = await globTestSourceEntries();
35
+ return {
36
+ ...sourceEntries,
37
+ ...setupFiles
38
+ };
39
+ };
40
+ config.watchOptions ??= {};
41
+ config.watchOptions.ignored = (0, utils.XQ)(config.watchOptions.ignored || []);
42
+ if (0 === config.watchOptions.ignored.length) config.watchOptions.ignored.push('**/.git', '**/node_modules');
43
+ config.watchOptions.ignored.push(utils.f1);
44
+ } else {
45
+ const sourceEntries = await globTestSourceEntries();
46
+ config.entry = {
47
+ ...sourceEntries,
48
+ ...setupFiles
49
+ };
50
+ }
51
+ });
52
+ }
53
+ });
54
+ class IgnoreModuleNotFoundErrorPlugin {
55
+ apply(compiler) {
56
+ compiler.hooks.done.tap('Rstest:IgnoreModuleNotFoundPlugin', (stats)=>{
57
+ stats.compilation.errors = stats.compilation.errors.filter((error)=>{
58
+ if (/Module not found/.test(error.message)) return false;
59
+ return true;
60
+ });
61
+ });
62
+ }
63
+ }
64
+ const pluginIgnoreResolveError = {
65
+ name: 'rstest:ignore-resolve-error',
66
+ setup: (api)=>{
67
+ api.modifyRspackConfig(async (config)=>{
68
+ config.plugins.push(new IgnoreModuleNotFoundErrorPlugin());
69
+ config.optimization ??= {};
70
+ config.optimization.emitOnErrors = true;
71
+ config.ignoreWarnings ??= [];
72
+ config.ignoreWarnings.push(/Module not found/);
73
+ });
74
+ }
75
+ };
76
+ const isMultiCompiler = (compiler)=>'compilers' in compiler && Array.isArray(compiler.compilers);
77
+ const autoExternalNodeModules = ({ context, request, dependencyType, getResolve }, callback)=>{
78
+ if (!request || request.startsWith('node:')) return callback();
79
+ if (request.startsWith('@swc/helpers/')) return callback();
80
+ const doExternal = (externalPath = request)=>{
81
+ callback(void 0, externalPath, 'commonjs' === dependencyType ? 'commonjs' : 'module-import');
82
+ };
83
+ if (/node_modules/.test(request)) return doExternal();
84
+ const resolver = getResolve?.();
85
+ if (!resolver) return callback();
86
+ resolver(context, request, (err, resolvePath)=>{
87
+ if (err) return callback();
88
+ if (resolvePath && /node_modules/.test(resolvePath)) return doExternal(resolvePath);
89
+ return callback();
90
+ });
91
+ };
92
+ const prepareRsbuild = async (context, globTestSourceEntries, setupFiles)=>{
93
+ const { command, normalizedConfig: { name, plugins, resolve, source, output, tools } } = context;
94
+ core_.logger.level = (0, utils.L1)() ? 'verbose' : 'error';
95
+ const writeToDisk = 'true' === process.env.DEBUG_RSTEST_OUTPUTS;
96
+ const rsbuildInstance = await (0, core_.createRsbuild)({
97
+ rsbuildConfig: {
98
+ tools,
99
+ plugins,
100
+ resolve,
101
+ source,
102
+ output,
103
+ server: {
104
+ printUrls: false,
105
+ strictPort: false,
106
+ middlewareMode: true
107
+ },
108
+ environments: {
109
+ [name]: {
110
+ dev: {
111
+ writeToDisk
112
+ },
113
+ source: {
114
+ define: {
115
+ 'import.meta.rstest': "global['@rstest/core']",
116
+ 'import.meta.dirname': '__dirname',
117
+ 'import.meta.filename': '__filename'
118
+ }
119
+ },
120
+ output: {
121
+ manifest: true,
122
+ sourceMap: {
123
+ js: 'source-map'
124
+ },
125
+ distPath: {
126
+ root: utils.Nk
127
+ },
128
+ externals: [
129
+ {
130
+ '@rstest/core': 'global @rstest/core'
131
+ },
132
+ autoExternalNodeModules
133
+ ],
134
+ target: 'node'
135
+ },
136
+ tools: {
137
+ rspack: (config)=>{
138
+ config.output ??= {};
139
+ config.output.iife = false;
140
+ config.externalsPresets = {
141
+ node: true
142
+ };
143
+ config.output.devtoolModuleFilenameTemplate = '[absolute-resource-path]';
144
+ config.module.parser ??= {};
145
+ config.module.parser.javascript = {
146
+ importDynamic: false,
147
+ requireDynamic: false,
148
+ requireAsExpression: false,
149
+ ...config.module.parser.javascript || {}
150
+ };
151
+ config.optimization = {
152
+ ...config.optimization || {},
153
+ moduleIds: 'named',
154
+ chunkIds: 'named',
155
+ splitChunks: {
156
+ chunks: 'all',
157
+ minSize: 0,
158
+ maxInitialRequests: Number.POSITIVE_INFINITY
159
+ }
160
+ };
161
+ }
162
+ },
163
+ plugins: [
164
+ pluginIgnoreResolveError,
165
+ pluginEntryWatch({
166
+ globTestSourceEntries,
167
+ setupFiles,
168
+ isWatch: 'watch' === command
169
+ })
170
+ ]
171
+ }
172
+ }
173
+ }
174
+ });
175
+ return rsbuildInstance;
176
+ };
177
+ const createRsbuildServer = async ({ name, globTestSourceEntries, setupFiles, rsbuildInstance })=>{
178
+ let rspackCompiler;
179
+ const rstestCompilerPlugin = {
180
+ name: 'rstest:compiler',
181
+ setup: (api)=>{
182
+ api.onAfterCreateCompiler(({ compiler })=>{
183
+ rspackCompiler = compiler;
184
+ });
185
+ }
186
+ };
187
+ rsbuildInstance.addPlugins([
188
+ rstestCompilerPlugin
189
+ ]);
190
+ const devServer = await rsbuildInstance.createDevServer({
191
+ getPortSilently: true
192
+ });
193
+ if ((0, utils.L1)()) await rsbuildInstance.inspectConfig({
194
+ writeToDisk: true
195
+ });
196
+ const outputFileSystem = (isMultiCompiler(rspackCompiler) ? rspackCompiler.compilers[0].outputFileSystem : rspackCompiler.outputFileSystem) || external_node_fs_["default"];
197
+ const getRsbuildStats = async ()=>{
198
+ const stats = await devServer.environments[name].getStats();
199
+ const manifest = devServer.environments[name].context.manifest;
200
+ const { entrypoints, outputPath, assets, time: buildTime } = stats.toJson({
201
+ all: false,
202
+ entrypoints: true,
203
+ outputPath: true,
204
+ assets: true,
205
+ relatedAssets: true,
206
+ cachedAssets: true,
207
+ timings: true
208
+ });
209
+ const readFile = async (fileName)=>new Promise((resolve, reject)=>{
210
+ outputFileSystem.readFile(fileName, (err, data)=>{
211
+ if (err) reject(err);
212
+ resolve('string' == typeof data ? data : data.toString());
213
+ });
214
+ });
215
+ const getEntryFiles = async ()=>{
216
+ const entryFiles = {};
217
+ const entries = Object.keys(manifest.entries);
218
+ for (const entry of entries){
219
+ const data = manifest.entries[entry];
220
+ entryFiles[entry] = ((data?.initial?.js || []).concat(data?.async?.js || []) || []).map((file)=>external_pathe_["default"].join(outputPath, file));
221
+ }
222
+ return entryFiles;
223
+ };
224
+ const entryFiles = await getEntryFiles();
225
+ const entries = [];
226
+ const setupEntries = [];
227
+ const sourceEntries = await globTestSourceEntries();
228
+ for (const entry of Object.keys(entrypoints)){
229
+ const e = entrypoints[entry];
230
+ const distPath = external_pathe_["default"].join(outputPath, e.assets[e.assets.length - 1].name);
231
+ if (setupFiles[entry]) setupEntries.push({
232
+ distPath,
233
+ testPath: setupFiles[entry],
234
+ files: entryFiles[entry]
235
+ });
236
+ else if (sourceEntries[entry]) entries.push({
237
+ distPath,
238
+ testPath: sourceEntries[entry],
239
+ files: entryFiles[entry]
240
+ });
241
+ }
242
+ const sourceMaps = Object.fromEntries((await Promise.all(assets.map(async (asset)=>{
243
+ const sourceMapPath = asset?.info.related?.sourceMap?.[0];
244
+ const assetFilePath = external_pathe_["default"].join(outputPath, asset.name);
245
+ if (sourceMapPath) {
246
+ const filePath = external_pathe_["default"].join(outputPath, sourceMapPath);
247
+ const sourceMap = await readFile(filePath);
248
+ return [
249
+ assetFilePath,
250
+ JSON.parse(sourceMap)
251
+ ];
252
+ }
253
+ return [
254
+ assetFilePath,
255
+ null
256
+ ];
257
+ }))).filter((asset)=>null !== asset[1]));
258
+ return {
259
+ entries,
260
+ setupEntries,
261
+ buildTime: buildTime,
262
+ assetFiles: Object.fromEntries(await Promise.all(assets.map(async (a)=>{
263
+ const filePath = external_pathe_["default"].join(outputPath, a.name);
264
+ return [
265
+ filePath,
266
+ await readFile(filePath)
267
+ ];
268
+ }))),
269
+ sourceMaps,
270
+ getSourcemap: (sourcePath)=>sourceMaps[sourcePath] || null,
271
+ close: devServer.close
272
+ };
273
+ };
274
+ return getRsbuildStats;
275
+ };
276
+ },
277
+ "./src/pool/index.ts": function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
278
+ __webpack_require__.d(__webpack_exports__, {
279
+ K: ()=>createPool
280
+ });
281
+ var external_node_os_ = __webpack_require__("node:os");
282
+ var utils = __webpack_require__("./src/utils/index.ts");
283
+ var external_node_events_ = __webpack_require__("node:events");
284
+ var external_node_url_ = __webpack_require__("node:url");
285
+ var external_node_v8_ = __webpack_require__("node:v8");
286
+ var external_birpc_ = __webpack_require__("birpc");
287
+ var external_pathe_ = __webpack_require__("pathe");
288
+ var external_tinypool_ = __webpack_require__("tinypool");
289
+ const forks_filename = (0, external_node_url_.fileURLToPath)(import.meta.url);
290
+ const forks_dirname = (0, external_pathe_.dirname)(forks_filename);
291
+ function createChannel(rpcMethods) {
292
+ const emitter = new external_node_events_["default"]();
293
+ const cleanup = ()=>emitter.removeAllListeners();
294
+ const events = {
295
+ message: 'message',
296
+ response: 'response'
297
+ };
298
+ const channel = {
299
+ onMessage: (callback)=>{
300
+ emitter.on(events.message, callback);
301
+ },
302
+ postMessage: (message)=>{
303
+ emitter.emit(events.response, message);
304
+ }
305
+ };
306
+ (0, external_birpc_.createBirpc)(rpcMethods, {
307
+ serialize: external_node_v8_["default"].serialize,
308
+ deserialize: (v)=>external_node_v8_["default"].deserialize(Buffer.from(v)),
309
+ post (v) {
310
+ emitter.emit(events.message, v);
311
+ },
312
+ on (fn) {
313
+ emitter.on(events.response, fn);
314
+ }
315
+ });
316
+ return {
317
+ channel,
318
+ cleanup
319
+ };
320
+ }
321
+ const createForksPool = (poolOptions)=>{
322
+ const { maxWorkers: maxThreads, minWorkers: minThreads, env, execArgv = [], isolate = true } = poolOptions;
323
+ const options = {
324
+ runtime: 'child_process',
325
+ filename: (0, external_pathe_.resolve)(forks_dirname, './worker.js'),
326
+ env,
327
+ execArgv,
328
+ maxThreads,
329
+ minThreads,
330
+ concurrentTasksPerWorker: 1,
331
+ isolateWorkers: isolate
332
+ };
333
+ const pool = new external_tinypool_.Tinypool(options);
334
+ return {
335
+ name: 'forks',
336
+ runTest: async ({ options, rpcMethods })=>{
337
+ const { channel, cleanup } = createChannel(rpcMethods);
338
+ try {
339
+ return await pool.run(options, {
340
+ channel
341
+ });
342
+ } finally{
343
+ cleanup();
344
+ }
345
+ },
346
+ collectTests: async ({ options, rpcMethods })=>{
347
+ const { channel, cleanup } = createChannel(rpcMethods);
348
+ try {
349
+ return await pool.run(options, {
350
+ channel
351
+ });
352
+ } finally{
353
+ cleanup();
354
+ }
355
+ },
356
+ close: ()=>pool.destroy()
357
+ };
358
+ };
359
+ const getNumCpus = ()=>external_node_os_["default"].availableParallelism?.() ?? external_node_os_["default"].cpus().length;
360
+ const parseWorkers = (maxWorkers)=>{
361
+ const parsed = Number.parseInt(maxWorkers.toString(), 10);
362
+ if ('string' == typeof maxWorkers && maxWorkers.trim().endsWith('%')) {
363
+ const numCpus = getNumCpus();
364
+ const workers = Math.floor(parsed / 100 * numCpus);
365
+ return Math.max(workers, 1);
366
+ }
367
+ return parsed > 0 ? parsed : 1;
368
+ };
369
+ const createPool = async ({ entries, context, assetFiles, setupEntries, sourceMaps })=>{
370
+ const execArgv = process.execArgv.filter((execArg)=>execArg.startsWith('--perf') || execArg.startsWith('--cpu-prof') || execArg.startsWith('--heap-prof') || execArg.startsWith('--diagnostic-dir'));
371
+ const numCpus = getNumCpus();
372
+ const { normalizedConfig: { pool: poolOptions, isolate }, reporters } = context;
373
+ const threadsCount = 'watch' === context.command ? Math.max(Math.floor(numCpus / 2), 1) : Math.max(numCpus - 1, 1);
374
+ const maxWorkers = poolOptions.maxWorkers ? parseWorkers(poolOptions.maxWorkers) : threadsCount;
375
+ const minWorkers = poolOptions.minWorkers ? parseWorkers(poolOptions.minWorkers) : maxWorkers < threadsCount ? maxWorkers : threadsCount;
376
+ if (maxWorkers < minWorkers) throw `Invalid pool configuration: maxWorkers(${maxWorkers}) cannot be less than minWorkers(${minWorkers}).`;
377
+ const pool = createForksPool({
378
+ ...poolOptions,
379
+ isolate,
380
+ maxWorkers,
381
+ minWorkers,
382
+ execArgv: [
383
+ ...poolOptions?.execArgv ?? [],
384
+ ...execArgv,
385
+ '--experimental-vm-modules',
386
+ '--experimental-import-meta-resolve'
387
+ ],
388
+ env: {
389
+ NODE_ENV: 'test',
390
+ FORCE_COLOR: '1',
391
+ ...process.env
392
+ }
393
+ });
394
+ const { updateSnapshot } = context.snapshotManager.options;
395
+ const { testNamePattern, testTimeout, passWithNoTests, retry, globals, clearMocks, resetMocks, restoreMocks, unstubEnvs, unstubGlobals, maxConcurrency, printConsoleTrace, disableConsoleIntercept, testEnvironment } = context.normalizedConfig;
396
+ const runtimeConfig = {
397
+ testNamePattern,
398
+ testTimeout,
399
+ passWithNoTests,
400
+ retry,
401
+ globals,
402
+ clearMocks,
403
+ resetMocks,
404
+ restoreMocks,
405
+ unstubEnvs,
406
+ unstubGlobals,
407
+ maxConcurrency,
408
+ printConsoleTrace,
409
+ disableConsoleIntercept,
410
+ testEnvironment
411
+ };
412
+ const rpcMethods = {
413
+ onTestCaseResult: async (result)=>{
414
+ await Promise.all(reporters.map((reporter)=>reporter.onTestCaseResult?.(result)));
415
+ },
416
+ onConsoleLog: async (log)=>{
417
+ await Promise.all(reporters.map((reporter)=>reporter.onUserConsoleLog?.(log)));
418
+ },
419
+ onTestFileStart: async (test)=>{
420
+ await Promise.all(reporters.map((reporter)=>reporter.onTestFileStart?.(test)));
421
+ },
422
+ onTestFileResult: async (test)=>{
423
+ await Promise.all(reporters.map((reporter)=>reporter.onTestFileResult?.(test)));
424
+ }
425
+ };
426
+ const setupAssets = setupEntries.flatMap((entry)=>entry.files);
427
+ const entryLength = Object.keys(entries).length;
428
+ const filterAssetsByEntry = (entryInfo)=>{
429
+ const neededFiles = entryLength > 1 && entryInfo.files ? Object.fromEntries(Object.entries(assetFiles).filter(([key])=>entryInfo.files.includes(key) || setupAssets.includes(key))) : assetFiles;
430
+ const neededSourceMaps = entryLength > 1 ? Object.fromEntries(Object.entries(sourceMaps).filter(([key])=>neededFiles[key])) : sourceMaps;
431
+ return {
432
+ assetFiles: neededFiles,
433
+ sourceMaps: neededSourceMaps
434
+ };
435
+ };
436
+ return {
437
+ runTests: async ()=>{
438
+ const results = await Promise.all(entries.map((entryInfo)=>{
439
+ const { assetFiles, sourceMaps } = filterAssetsByEntry(entryInfo);
440
+ return pool.runTest({
441
+ options: {
442
+ entryInfo,
443
+ assetFiles,
444
+ context: {
445
+ rootPath: context.rootPath,
446
+ runtimeConfig: (0, utils.v8)(runtimeConfig)
447
+ },
448
+ type: 'run',
449
+ sourceMaps,
450
+ setupEntries,
451
+ updateSnapshot
452
+ },
453
+ rpcMethods
454
+ });
455
+ }));
456
+ for (const result of results)if (result.snapshotResult) context.snapshotManager.add(result.snapshotResult);
457
+ const testResults = results.flatMap((r)=>r.results);
458
+ return {
459
+ results,
460
+ testResults
461
+ };
462
+ },
463
+ collectTests: async ()=>Promise.all(entries.map((entryInfo)=>{
464
+ const { assetFiles, sourceMaps } = filterAssetsByEntry(entryInfo);
465
+ return pool.collectTests({
466
+ options: {
467
+ entryInfo,
468
+ assetFiles,
469
+ context: {
470
+ rootPath: context.rootPath,
471
+ runtimeConfig: (0, utils.v8)(runtimeConfig)
472
+ },
473
+ type: 'collect',
474
+ sourceMaps,
475
+ setupEntries,
476
+ updateSnapshot
477
+ },
478
+ rpcMethods
479
+ });
480
+ })),
481
+ close: ()=>pool.close()
482
+ };
483
+ };
484
+ }
485
+ };