@rspack-canary/test-tools 1.6.0-canary-e28e40e9-20251022173516 → 1.6.0-canary-941e2203-20251024173714

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.
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createStatsAPICase = createStatsAPICase;
4
4
  const memfs_1 = require("memfs");
5
5
  const creator_1 = require("../test/creator");
6
- const common_1 = require("./common");
7
6
  let addedSerializer = false;
8
7
  const creator = new creator_1.BasicCaseCreator({
9
8
  clean: true,
@@ -13,16 +12,16 @@ const creator = new creator_1.BasicCaseCreator({
13
12
  return [
14
13
  {
15
14
  config: async (context) => {
16
- const compiler = (0, common_1.getCompiler)(context, name);
15
+ const compiler = context.getCompiler();
17
16
  compiler.setOptions(options(context, config.options));
18
17
  },
19
18
  compiler: async (context) => {
20
- const compilerManager = (0, common_1.getCompiler)(context, name);
19
+ const compilerManager = context.getCompiler();
21
20
  compilerManager.createCompiler();
22
21
  compiler(context, compilerManager.getCompiler(), config.compiler);
23
22
  },
24
23
  build: async (context) => {
25
- const compiler = (0, common_1.getCompiler)(context, name);
24
+ const compiler = context.getCompiler();
26
25
  if (typeof config.build === "function") {
27
26
  await config.build(context, compiler.getCompiler());
28
27
  }
@@ -74,7 +73,7 @@ async function compiler(context, compiler, custom) {
74
73
  }
75
74
  }
76
75
  async function check(env, context, name, custom) {
77
- const manager = (0, common_1.getCompiler)(context, name);
76
+ const manager = context.getCompiler();
78
77
  const stats = manager.getStats();
79
78
  env.expect(typeof stats).toBe("object");
80
79
  await custom?.(stats, manager.getCompiler());
@@ -114,7 +114,7 @@ class RspackStats {
114
114
  }
115
115
  }
116
116
  async function check(env, context, name, writeStatsOuptut, snapshot, stderr) {
117
- const compiler = (0, common_1.getCompiler)(context, name);
117
+ const compiler = context.getCompiler();
118
118
  const options = compiler.getOptions();
119
119
  const stats = compiler.getStats();
120
120
  if (!stats || !compiler)
@@ -13,7 +13,7 @@ const creator = new creator_1.BasicCaseCreator({
13
13
  steps: ({ name }) => [
14
14
  {
15
15
  config: async (context) => {
16
- const compiler = (0, common_1.getCompiler)(context, name);
16
+ const compiler = context.getCompiler();
17
17
  const options = (0, builtin_1.defaultOptions)(context);
18
18
  overrideOptions(context, options);
19
19
  compiler.setOptions(options);
@@ -29,7 +29,7 @@ function createWatchInitialProcessor(name, tempDir, step, watchState, { incremen
29
29
  };
30
30
  return {
31
31
  before: async (context) => {
32
- context.setValue(name, "watchContext", watchContext);
32
+ context.setValue("watchContext", watchContext);
33
33
  },
34
34
  config: async (context) => {
35
35
  const testConfig = context.getTestConfig();
@@ -46,9 +46,9 @@ function createWatchInitialProcessor(name, tempDir, step, watchState, { incremen
46
46
  const compilerOptions = multiCompilerOptions.length === 1
47
47
  ? multiCompilerOptions[0]
48
48
  : multiCompilerOptions;
49
- const compiler = (0, common_1.getCompiler)(context, name);
49
+ const compiler = context.getCompiler();
50
50
  compiler.setOptions(compilerOptions);
51
- context.setValue(name, "multiCompilerOptions", multiCompilerOptions);
51
+ context.setValue("multiCompilerOptions", multiCompilerOptions);
52
52
  },
53
53
  compiler: async (context) => {
54
54
  const c = await (0, common_1.compiler)(context, name);
@@ -57,7 +57,7 @@ function createWatchInitialProcessor(name, tempDir, step, watchState, { incremen
57
57
  });
58
58
  },
59
59
  build: async (context) => {
60
- const compiler = (0, common_1.getCompiler)(context, name);
60
+ const compiler = context.getCompiler();
61
61
  node_fs_1.default.mkdirSync(watchContext.tempDir, { recursive: true });
62
62
  (0, copyDiff_1.default)(node_path_1.default.join(context.getSource(), watchContext.step), watchContext.tempDir, true);
63
63
  const task = new Promise((resolve, reject) => {
@@ -77,12 +77,12 @@ function createWatchInitialProcessor(name, tempDir, step, watchState, { incremen
77
77
  const testConfig = context.getTestConfig();
78
78
  if (testConfig.noTests)
79
79
  return;
80
- const errors = (context.getError(name) || []).map(e => ({
80
+ const errors = (context.getError() || []).map(e => ({
81
81
  message: e.message,
82
82
  stack: e.stack
83
83
  }));
84
84
  const warnings = [];
85
- const compiler = (0, common_1.getCompiler)(context, name);
85
+ const compiler = context.getCompiler();
86
86
  const stats = compiler.getStats();
87
87
  const options = compiler.getOptions();
88
88
  const checkStats = testConfig.checkStats || (() => true);
@@ -148,7 +148,7 @@ function createWatchInitialProcessor(name, tempDir, step, watchState, { incremen
148
148
  await (0, checkArrayExpectation_1.default)(node_path_1.default.join(context.getSource(), watchContext.step), { warnings }, "warning", "warnings", "Warning", options);
149
149
  // clear error if checked
150
150
  if (node_fs_1.default.existsSync(context.getSource("errors.js"))) {
151
- context.clearError(name);
151
+ context.clearError();
152
152
  }
153
153
  // check hash
154
154
  if (testConfig.writeStatsOuptut) {
@@ -169,7 +169,7 @@ function createWatchStepProcessor(name, tempDir, step, watchState, { incremental
169
169
  // do nothing
170
170
  };
171
171
  processor.build = async (context) => {
172
- const compiler = (0, common_1.getCompiler)(context, name);
172
+ const compiler = context.getCompiler();
173
173
  const task = new Promise((resolve, reject) => {
174
174
  compiler.getEmitter().once(compiler_1.ECompilerEvent.Build, (e, stats) => {
175
175
  if (e)
@@ -283,13 +283,13 @@ function defaultOptions({ incremental = false, ignoreNotFriendlyForIncrementalWa
283
283
  return {};
284
284
  }
285
285
  function getWatchRunnerKey(context, name, file) {
286
- const watchContext = context.getValue(name, "watchContext");
286
+ const watchContext = context.getValue("watchContext");
287
287
  const stepName = watchContext?.step;
288
288
  return `${name}-${stepName}`;
289
289
  }
290
290
  function cachedWatchStats(context, name) {
291
- const compiler = context.getCompiler(name);
292
- const watchContext = context.getValue(name, "watchContext");
291
+ const compiler = context.getCompiler();
292
+ const watchContext = context.getValue("watchContext");
293
293
  const stepName = watchContext?.step;
294
294
  const statsGetter = (() => {
295
295
  const cached = {};
@@ -306,9 +306,9 @@ function cachedWatchStats(context, name) {
306
306
  return statsGetter;
307
307
  }
308
308
  function createWatchRunner(context, name, file, env) {
309
- const compiler = context.getCompiler(name);
309
+ const compiler = context.getCompiler();
310
310
  const compilerOptions = compiler.getOptions();
311
- const watchContext = context.getValue(name, "watchContext");
311
+ const watchContext = context.getValue("watchContext");
312
312
  const stepName = watchContext?.step;
313
313
  if (!stepName) {
314
314
  throw new Error("Can not get watch step name from context");
@@ -1,6 +1,6 @@
1
1
  import EventEmitter from "node:events";
2
- import type { Compiler, RspackOptions, Stats } from "@rspack/core";
3
- import type { ITestCompilerManager } from "./type";
2
+ import { Compiler, type RspackOptions, type Stats } from "@rspack/core";
3
+ import type { ITestCompilerManager, ITestContext } from "./type";
4
4
  export declare enum ECompilerEvent {
5
5
  Build = "build",
6
6
  Option = "option",
@@ -8,11 +8,12 @@ export declare enum ECompilerEvent {
8
8
  Close = "close"
9
9
  }
10
10
  export declare class TestCompilerManager implements ITestCompilerManager {
11
+ protected context: ITestContext;
11
12
  protected compilerOptions: RspackOptions;
12
13
  protected compilerInstance: Compiler | null;
13
14
  protected compilerStats: Stats | null;
14
15
  protected emitter: EventEmitter;
15
- constructor();
16
+ constructor(context: ITestContext);
16
17
  getOptions(): RspackOptions;
17
18
  setOptions(newOptions: RspackOptions): RspackOptions;
18
19
  mergeOptions(newOptions: RspackOptions): RspackOptions;
package/dist/compiler.js CHANGED
@@ -5,7 +5,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.TestCompilerManager = exports.ECompilerEvent = void 0;
7
7
  const node_events_1 = __importDefault(require("node:events"));
8
+ const core_1 = require("@rspack/core");
8
9
  const webpack_merge_1 = __importDefault(require("webpack-merge"));
10
+ const debug_1 = require("./test/debug");
9
11
  var ECompilerEvent;
10
12
  (function (ECompilerEvent) {
11
13
  ECompilerEvent["Build"] = "build";
@@ -14,7 +16,8 @@ var ECompilerEvent;
14
16
  ECompilerEvent["Close"] = "close";
15
17
  })(ECompilerEvent || (exports.ECompilerEvent = ECompilerEvent = {}));
16
18
  class TestCompilerManager {
17
- constructor() {
19
+ constructor(context) {
20
+ this.context = context;
18
21
  this.compilerOptions = {};
19
22
  this.compilerInstance = null;
20
23
  this.compilerStats = null;
@@ -38,11 +41,57 @@ class TestCompilerManager {
38
41
  }
39
42
  createCompiler() {
40
43
  this.compilerInstance = require("@rspack/core")(this.compilerOptions);
44
+ if (__DEBUG__) {
45
+ const context = this.context;
46
+ this.compilerInstance = new Proxy(this.compilerInstance, {
47
+ get(target, p, receiver) {
48
+ const value = Reflect.get(target, p, receiver);
49
+ if (typeof value === "function" &&
50
+ core_1.Compiler.prototype.hasOwnProperty(p)) {
51
+ return value.bind(target);
52
+ }
53
+ return value;
54
+ },
55
+ set(target, p, value, receiver) {
56
+ const debugSetProperties = context.getValue(debug_1.DEBUG_SCOPES.CreateCompilerSetProperties) || [];
57
+ debugSetProperties.push(`${p} ${new Error().stack?.split("\n")[2]?.trim()}`);
58
+ context.setValue(debug_1.DEBUG_SCOPES.CreateCompilerSetProperties, debugSetProperties);
59
+ return Reflect.set(target, p, value, receiver);
60
+ }
61
+ });
62
+ this.context.setValue(debug_1.DEBUG_SCOPES.CreateCompilerInstance, {
63
+ path: require.resolve("@rspack/core"),
64
+ mode: "no-callback"
65
+ });
66
+ }
41
67
  this.emitter.emit(ECompilerEvent.Create, this.compilerInstance);
42
68
  return this.compilerInstance;
43
69
  }
44
70
  createCompilerWithCallback(callback) {
45
71
  this.compilerInstance = require("@rspack/core")(this.compilerOptions, callback);
72
+ if (__DEBUG__) {
73
+ const context = this.context;
74
+ this.compilerInstance = new Proxy(this.compilerInstance, {
75
+ get(target, p, receiver) {
76
+ const value = Reflect.get(target, p, receiver);
77
+ if (typeof value === "function" &&
78
+ core_1.Compiler.prototype.hasOwnProperty(p)) {
79
+ return value.bind(target);
80
+ }
81
+ return value;
82
+ },
83
+ set(target, p, value, receiver) {
84
+ const debugSetProperties = context.getValue(debug_1.DEBUG_SCOPES.CreateCompilerSetProperties) || [];
85
+ debugSetProperties.push(`${p} ${new Error().stack?.split("\n")[2]?.trim()}`);
86
+ context.setValue(debug_1.DEBUG_SCOPES.CreateCompilerSetProperties, debugSetProperties);
87
+ return Reflect.set(target, p, value, receiver);
88
+ }
89
+ });
90
+ this.context.setValue(debug_1.DEBUG_SCOPES.CreateCompilerInstance, {
91
+ path: require.resolve("@rspack/core"),
92
+ mode: "callback"
93
+ });
94
+ }
46
95
  this.emitter.emit(ECompilerEvent.Create, this.compilerInstance);
47
96
  return this.compilerInstance;
48
97
  }
@@ -51,11 +100,39 @@ class TestCompilerManager {
51
100
  throw new Error("Compiler should be created before build");
52
101
  return new Promise((resolve, reject) => {
53
102
  try {
103
+ const context = this.context;
104
+ if (__DEBUG__) {
105
+ context.setValue(debug_1.DEBUG_SCOPES.BuildMethod, {
106
+ method: "run"
107
+ });
108
+ }
54
109
  this.compilerInstance.run((error, newStats) => {
55
110
  this.emitter.emit(ECompilerEvent.Build, error, newStats);
56
- if (error)
111
+ if (error) {
112
+ if (__DEBUG__) {
113
+ context.setValue(debug_1.DEBUG_SCOPES.BuildError, {
114
+ type: "fatal",
115
+ errors: [error]
116
+ });
117
+ }
57
118
  return reject(error);
119
+ }
58
120
  this.compilerStats = newStats;
121
+ if (__DEBUG__) {
122
+ if (newStats?.hasErrors()) {
123
+ context.setValue(debug_1.DEBUG_SCOPES.BuildError, {
124
+ type: "stats",
125
+ errors: newStats.toJson({
126
+ errors: true
127
+ }).errors || []
128
+ });
129
+ }
130
+ if (newStats?.hasWarnings()) {
131
+ context.setValue(debug_1.DEBUG_SCOPES.BuildWarning, newStats.toJson({
132
+ warnings: true
133
+ }).warnings || []);
134
+ }
135
+ }
59
136
  resolve(newStats);
60
137
  });
61
138
  }
@@ -67,7 +144,8 @@ class TestCompilerManager {
67
144
  watch(timeout = 1000) {
68
145
  if (!this.compilerInstance)
69
146
  throw new Error("Compiler should be created before watch");
70
- this.compilerInstance.watch({
147
+ const context = this.context;
148
+ const watchOptions = {
71
149
  // IMPORTANT:
72
150
  // This is a workaround for the issue that watchpack cannot detect the file change in time
73
151
  // so we set the poll to 300ms to make it more sensitive to the file change
@@ -76,11 +154,40 @@ class TestCompilerManager {
76
154
  // want to watch all files, which aligns with webpack's default behavior
77
155
  ignored: [],
78
156
  aggregateTimeout: timeout
79
- }, (error, newStats) => {
157
+ };
158
+ if (__DEBUG__) {
159
+ context.setValue(debug_1.DEBUG_SCOPES.BuildMethod, {
160
+ method: "watch",
161
+ options: watchOptions
162
+ });
163
+ }
164
+ this.compilerInstance.watch(watchOptions, (error, newStats) => {
80
165
  this.emitter.emit(ECompilerEvent.Build, error, newStats);
81
- if (error)
82
- return error;
166
+ if (__DEBUG__) {
167
+ if (error) {
168
+ context.setValue(debug_1.DEBUG_SCOPES.BuildError, {
169
+ type: "fatal",
170
+ errors: [error]
171
+ });
172
+ return error;
173
+ }
174
+ }
83
175
  if (newStats) {
176
+ if (__DEBUG__) {
177
+ if (newStats.hasErrors()) {
178
+ context.setValue(debug_1.DEBUG_SCOPES.BuildError, {
179
+ type: "stats",
180
+ errors: newStats.toJson({
181
+ errors: true
182
+ }).errors || []
183
+ });
184
+ }
185
+ if (newStats.hasWarnings()) {
186
+ context.setValue(debug_1.DEBUG_SCOPES.BuildWarning, newStats.toJson({
187
+ warnings: true
188
+ }).warnings || []);
189
+ }
190
+ }
84
191
  this.compilerStats = newStats;
85
192
  }
86
193
  return newStats;
@@ -2,6 +2,7 @@ export * from "./directory";
2
2
  export * from "./is";
3
3
  export * from "./parse-modules";
4
4
  export * from "./read-config-file";
5
+ export * from "./stringify-config";
5
6
  export * from "./update-snapshot";
6
7
  export * from "./util/checkStats";
7
8
  export * from "./win";
@@ -18,6 +18,7 @@ __exportStar(require("./directory"), exports);
18
18
  __exportStar(require("./is"), exports);
19
19
  __exportStar(require("./parse-modules"), exports);
20
20
  __exportStar(require("./read-config-file"), exports);
21
+ __exportStar(require("./stringify-config"), exports);
21
22
  __exportStar(require("./update-snapshot"), exports);
22
23
  __exportStar(require("./util/checkStats"), exports);
23
24
  __exportStar(require("./win"), exports);
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.readConfigFile = readConfigFile;
7
7
  const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const debug_1 = require("../test/debug");
8
9
  function readConfigFile(files, context, prevOption, functionApply) {
9
10
  const existsFile = files.find(i => fs_extra_1.default.existsSync(i));
10
11
  let fileConfig = existsFile ? require(existsFile) : {};
@@ -12,5 +13,11 @@ function readConfigFile(files, context, prevOption, functionApply) {
12
13
  fileConfig = fileConfig({ config: prevOption }, { testPath: context.getDist(), tempPath: context.getTemp() });
13
14
  }
14
15
  const configArr = Array.isArray(fileConfig) ? fileConfig : [fileConfig];
16
+ if (existsFile) {
17
+ context.setValue(debug_1.DEBUG_SCOPES.CompilerOptionsReadConfigFile, {
18
+ file: existsFile,
19
+ config: fileConfig
20
+ });
21
+ }
15
22
  return functionApply ? functionApply(configArr) : configArr;
16
23
  }
@@ -16,6 +16,7 @@ if (process.env.RSTEST) {
16
16
  global.__ROOT_PATH__ ??= process.env.__ROOT_PATH__;
17
17
  global.__RSPACK_PATH__ ??= process.env.__RSPACK_PATH__;
18
18
  global.__RSPACK_TEST_TOOLS_PATH__ ??= process.env.__RSPACK_TEST_TOOLS_PATH__;
19
+ global.__DEBUG__ ??= process.env.DEBUG === "test";
19
20
  }
20
21
  else {
21
22
  // Compatible with wasm tests (lazyTestEnv)
@@ -0,0 +1,2 @@
1
+ import type { RspackOptions } from "@rspack/core";
2
+ export default function stringifyConfig(config: RspackOptions, verbose?: boolean): string | undefined;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = stringifyConfig;
4
+ const javascript_stringify_1 = require("javascript-stringify");
5
+ function stringifyConfig(config, verbose = false) {
6
+ return (0, javascript_stringify_1.stringify)(config, (value, indent, stringify) => {
7
+ // improve plugin output
8
+ if (value?.__pluginName) {
9
+ const prefix = `/* config.${value.__pluginType}('${value.__pluginName}') */\n`;
10
+ const constructorExpression = value.__pluginPath
11
+ ? // The path is stringified to ensure special characters are escaped
12
+ // (such as the backslashes in Windows-style paths).
13
+ `(require(${stringify(value.__pluginPath)}))`
14
+ : value.__pluginConstructorName;
15
+ if (constructorExpression) {
16
+ // get correct indentation for args by stringifying the args array and
17
+ // discarding the square brackets.
18
+ const args = stringify(value.__pluginArgs)?.slice(1, -1);
19
+ return `${prefix}new ${constructorExpression}(${args})`;
20
+ }
21
+ return (prefix +
22
+ stringify(value.__pluginArgs?.length ? { args: value.__pluginArgs } : {}));
23
+ }
24
+ // improve rule/use output
25
+ if (value?.__ruleNames) {
26
+ const ruleTypes = value.__ruleTypes;
27
+ const prefix = `/* config.module${value.__ruleNames
28
+ .map((r, index) => `.${ruleTypes ? ruleTypes[index] : "rule"}('${r}')`)
29
+ .join("")}${value.__useName ? `.use('${value.__useName}')` : ``} */\n`;
30
+ return prefix + stringify(value);
31
+ }
32
+ if (value?.__expression) {
33
+ return value.__expression;
34
+ }
35
+ // shorten long functions
36
+ if (typeof value === "function") {
37
+ if (!verbose && value.toString().length > 100) {
38
+ return `function () { /* omitted long function */ }`;
39
+ }
40
+ }
41
+ return stringify(value);
42
+ }, 2);
43
+ }
@@ -13,6 +13,8 @@ export interface INodeRunnerOptions {
13
13
  dist: string;
14
14
  compilerOptions: RspackOptions;
15
15
  cachable?: boolean;
16
+ logs?: string[];
17
+ errors?: Error[];
16
18
  }
17
19
  export declare class NodeRunner implements ITestRunner {
18
20
  protected _options: INodeRunnerOptions;
@@ -21,6 +23,7 @@ export declare class NodeRunner implements ITestRunner {
21
23
  protected baseModuleScope: IModuleScope | null;
22
24
  protected requirers: Map<string, TRunnerRequirer>;
23
25
  constructor(_options: INodeRunnerOptions);
26
+ protected log(message: string): void;
24
27
  run(file: string): Promise<unknown>;
25
28
  getRequire(): TRunnerRequirer;
26
29
  getGlobal(name: string): unknown;
@@ -36,3 +39,4 @@ export declare class NodeRunner implements ITestRunner {
36
39
  protected createCjsRequirer(): TRunnerRequirer;
37
40
  protected createEsmRequirer(): TRunnerRequirer;
38
41
  }
42
+ export declare const createLocatedError: (collectedErrors: Error[], offset: number) => (e: Error, file: TRunnerFile) => Error;
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.NodeRunner = void 0;
39
+ exports.createLocatedError = exports.NodeRunner = void 0;
40
40
  const node_fs_1 = __importDefault(require("node:fs"));
41
41
  const node_path_1 = __importDefault(require("node:path"));
42
42
  const node_url_1 = require("node:url");
@@ -45,6 +45,7 @@ const asModule_1 = __importDefault(require("../../helper/legacy/asModule"));
45
45
  const createFakeWorker_1 = __importDefault(require("../../helper/legacy/createFakeWorker"));
46
46
  const urlToRelativePath_1 = __importDefault(require("../../helper/legacy/urlToRelativePath"));
47
47
  const type_1 = require("../../type");
48
+ const EVAL_LOCATION_REGEX = /<anonymous>:(\d+)/;
48
49
  const isRelativePath = (p) => /^\.\.?\//.test(p);
49
50
  const getSubPath = (p) => {
50
51
  const lastSlash = p.lastIndexOf("/");
@@ -72,6 +73,9 @@ class NodeRunner {
72
73
  this.baseModuleScope = null;
73
74
  this.requirers = new Map();
74
75
  }
76
+ log(message) {
77
+ this._options.logs?.push(`[NodeRunner] ${message}`);
78
+ }
75
79
  run(file) {
76
80
  if (!this.globalContext) {
77
81
  this.globalContext = this.createGlobalContext();
@@ -93,7 +97,15 @@ class NodeRunner {
93
97
  }
94
98
  getRequire() {
95
99
  const entryRequire = this.requirers.get("entry");
96
- return (currentDirectory, modulePath, context = {}) => {
100
+ const runner = this;
101
+ return function (currentDirectory, modulePath, context = {}) {
102
+ const from = this?.from;
103
+ if (from) {
104
+ runner.log(`require: ${modulePath} from ${from}`);
105
+ }
106
+ else {
107
+ runner.log(`require: ${modulePath}`);
108
+ }
97
109
  const p = Array.isArray(modulePath)
98
110
  ? modulePath
99
111
  : modulePath.split("?")[0];
@@ -177,7 +189,10 @@ class NodeRunner {
177
189
  return baseModuleScope;
178
190
  }
179
191
  createModuleScope(requireFn, m, file) {
180
- const requirer = requireFn.bind(null, node_path_1.default.dirname(file.path));
192
+ const requirer = requireFn.bind({
193
+ from: file.path,
194
+ module: m
195
+ }, node_path_1.default.dirname(file.path));
181
196
  requirer.webpackTestSuiteRequire = true;
182
197
  return {
183
198
  ...this.baseModuleScope,
@@ -246,6 +261,7 @@ class NodeRunner {
246
261
  this.requirers.set("json", this.createJsonRequirer());
247
262
  this.requirers.set("entry", (currentDirectory, modulePath, context) => {
248
263
  const file = this.getFile(modulePath, currentDirectory);
264
+ this.log(`entry: ${modulePath} -> ${file?.path}`);
249
265
  if (!file) {
250
266
  return this.requirers.get("miss")(currentDirectory, modulePath);
251
267
  }
@@ -270,11 +286,14 @@ class NodeRunner {
270
286
  }
271
287
  createMissRequirer() {
272
288
  return (currentDirectory, modulePath, context = {}) => {
289
+ this.log(`missing: ${modulePath}`);
273
290
  const modulePathStr = modulePath;
274
291
  const modules = this._options.testConfig.modules;
275
292
  if (modules && modulePathStr in modules) {
293
+ this.log(`mock module: ${modulePathStr}`);
276
294
  return modules[modulePathStr];
277
295
  }
296
+ this.log(`native require: ${modulePathStr}`);
278
297
  return require(modulePathStr.startsWith("node:")
279
298
  ? modulePathStr.slice(5)
280
299
  : modulePathStr);
@@ -286,6 +305,7 @@ class NodeRunner {
286
305
  throw new Error("Array module path is not supported in hot cases");
287
306
  }
288
307
  const file = context.file || this.getFile(modulePath, currentDirectory);
308
+ this.log(`json: ${modulePath} -> ${file?.path}`);
289
309
  if (!file) {
290
310
  return this.requirers.get("miss")(currentDirectory, modulePath);
291
311
  }
@@ -298,10 +318,12 @@ class NodeRunner {
298
318
  return require("@rspack/test-tools");
299
319
  }
300
320
  const file = context.file || this.getFile(modulePath, currentDirectory);
321
+ this.log(`cjs: ${modulePath} -> ${file?.path}`);
301
322
  if (!file) {
302
323
  return this.requirers.get("miss")(currentDirectory, modulePath);
303
324
  }
304
325
  if (file.path in this.requireCache) {
326
+ this.log(`cjs cache hit: ${file.path}`);
305
327
  return this.requireCache[file.path].exports;
306
328
  }
307
329
  const m = {
@@ -325,19 +347,45 @@ class NodeRunner {
325
347
  currentModuleScope.__STATS_I__ = statsIndex;
326
348
  }
327
349
  }
350
+ const createNodeLocatedError = (0, exports.createLocatedError)(this._options.errors || [], 2);
351
+ const originIt = currentModuleScope.it;
352
+ currentModuleScope.it = (description, fn) => {
353
+ originIt(description, async () => {
354
+ try {
355
+ await fn();
356
+ }
357
+ catch (err) {
358
+ throw createNodeLocatedError(err, file);
359
+ }
360
+ });
361
+ };
362
+ currentModuleScope.__CREATE_LOCATED_ERROR__ = createNodeLocatedError;
363
+ currentModuleScope.__FILE__ = file;
328
364
  const args = Object.keys(currentModuleScope);
329
365
  const argValues = args.map(arg => currentModuleScope[arg]);
330
366
  const code = `(function(${args.join(", ")}) {
331
- ${file.content}
367
+ try {
368
+ ${file.content}
369
+ } catch(err) {
370
+ throw __CREATE_LOCATED_ERROR__(err, __FILE__);
371
+ }
332
372
  })`;
333
373
  this.preExecute(code, file);
374
+ this.log(`run mode: ${this._options.runInNewContext ? "new context" : "this context"}`);
334
375
  const fn = this._options.runInNewContext
335
- ? node_vm_1.default.runInNewContext(code, this.globalContext, file.path)
336
- : node_vm_1.default.runInThisContext(code, file.path);
376
+ ? node_vm_1.default.runInNewContext(code, this.globalContext, {
377
+ filename: file.path,
378
+ lineOffset: 1
379
+ })
380
+ : node_vm_1.default.runInThisContext(code, {
381
+ filename: file.path,
382
+ lineOffset: 1
383
+ });
337
384
  fn.call(this._options.testConfig.nonEsmThis
338
385
  ? this._options.testConfig.nonEsmThis(modulePath)
339
386
  : m.exports, ...argValues);
340
387
  this.postExecute(m, file);
388
+ this.log(`end cjs: ${modulePath}`);
341
389
  return m.exports;
342
390
  };
343
391
  }
@@ -353,6 +401,7 @@ class NodeRunner {
353
401
  }
354
402
  const _require = this.getRequire();
355
403
  const file = context.file || this.getFile(modulePath, currentDirectory);
404
+ this.log(`esm: ${modulePath} -> ${file?.path}`);
356
405
  if (!file) {
357
406
  return this.requirers.get("miss")(currentDirectory, modulePath);
358
407
  }
@@ -378,6 +427,7 @@ class NodeRunner {
378
427
  meta.filename = file.path;
379
428
  },
380
429
  importModuleDynamically: async (specifier, module) => {
430
+ this.log(`import: ${specifier} from ${file?.path}`);
381
431
  const result = await _require(node_path_1.default.dirname(file.path), specifier, {
382
432
  esmMode: type_1.EEsmMode.Evaluated
383
433
  });
@@ -403,9 +453,36 @@ class NodeRunner {
403
453
  return esm;
404
454
  }
405
455
  const ns = esm.namespace;
456
+ this.log(`end esm: ${modulePath}`);
406
457
  return ns.default && ns.default instanceof Promise ? ns.default : ns;
407
458
  })();
408
459
  };
409
460
  }
410
461
  }
411
462
  exports.NodeRunner = NodeRunner;
463
+ const createLocatedError = (collectedErrors, offset) => {
464
+ return (e, file) => {
465
+ const match = (e.stack || e.message).match(EVAL_LOCATION_REGEX);
466
+ if (match) {
467
+ const [, line] = match;
468
+ const realLine = Number(line) - offset;
469
+ const codeLines = file.content.split("\n");
470
+ const lineContents = [
471
+ ...codeLines
472
+ .slice(Math.max(0, realLine - 3), Math.max(0, realLine - 1))
473
+ .map(line => `│ ${line}`),
474
+ `│> ${codeLines[realLine - 1]}`,
475
+ ...codeLines.slice(realLine, realLine + 2).map(line => `│ ${line}`)
476
+ ];
477
+ const message = `Error in JSDOM when running file '${file.path}' at line ${realLine}: ${e.message}\n${lineContents.join("\n")}`;
478
+ const finalError = new Error(message);
479
+ finalError.stack = undefined;
480
+ collectedErrors.push(finalError);
481
+ return finalError;
482
+ }
483
+ else {
484
+ return e;
485
+ }
486
+ };
487
+ };
488
+ exports.createLocatedError = createLocatedError;
@@ -9,6 +9,7 @@ export declare class WebRunner extends NodeRunner {
9
9
  constructor(_webOptions: IWebRunnerOptions);
10
10
  run(file: string): Promise<unknown>;
11
11
  getGlobal(name: string): unknown;
12
+ protected log(message: string): void;
12
13
  protected createResourceLoader(): {
13
14
  fetch(url: string, _: {
14
15
  element: HTMLScriptElement;