@rspack/test-tools 1.1.8 → 1.2.0-alpha.0

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 (38) hide show
  1. package/dist/case/cache.d.ts +2 -0
  2. package/dist/case/cache.js +31 -0
  3. package/dist/case/diff.js +2 -2
  4. package/dist/case/index.d.ts +1 -0
  5. package/dist/case/index.js +1 -0
  6. package/dist/compare/format-code.js +17 -7
  7. package/dist/helper/expect/placeholder.js +4 -1
  8. package/dist/helper/expect/to-match-file-snapshot.js +2 -3
  9. package/dist/helper/legacy/checkArrayExpectation.d.ts +1 -1
  10. package/dist/helper/legacy/checkArrayExpectation.js +5 -6
  11. package/dist/helper/legacy/copyDiff.js +2 -2
  12. package/dist/helper/loaders/hot-update.d.ts +1 -0
  13. package/dist/helper/loaders/hot-update.js +37 -0
  14. package/dist/helper/plugins/hot-update.d.ts +7 -0
  15. package/dist/helper/plugins/hot-update.js +33 -0
  16. package/dist/helper/plugins/index.d.ts +1 -0
  17. package/dist/helper/plugins/index.js +17 -0
  18. package/dist/helper/util/refreshModifyTime.d.ts +1 -0
  19. package/dist/helper/util/refreshModifyTime.js +8 -0
  20. package/dist/processor/basic.js +2 -8
  21. package/dist/processor/cache.d.ts +17 -0
  22. package/dist/processor/cache.js +140 -0
  23. package/dist/processor/hot-step.js +3 -1
  24. package/dist/processor/hot.js +3 -2
  25. package/dist/processor/index.d.ts +1 -0
  26. package/dist/processor/index.js +1 -0
  27. package/dist/processor/watch.js +2 -8
  28. package/dist/runner/cache.d.ts +5 -0
  29. package/dist/runner/cache.js +92 -0
  30. package/dist/runner/hot-step.js +37 -27
  31. package/dist/runner/hot.js +34 -19
  32. package/dist/runner/index.d.ts +1 -0
  33. package/dist/runner/index.js +1 -0
  34. package/dist/runner/runner/esm.js +17 -7
  35. package/dist/test/creator.js +2 -2
  36. package/package.json +9 -10
  37. package/dist/helper/legacy/fake-update-loader.d.ts +0 -2
  38. package/dist/helper/legacy/fake-update-loader.js +0 -18
@@ -0,0 +1,2 @@
1
+ import { ECompilerType, type TCompilerOptions } from "../type";
2
+ export declare function createCacheCase(name: string, src: string, dist: string, target: TCompilerOptions<ECompilerType.Rspack>["target"]): void;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createCacheCase = createCacheCase;
4
+ const cache_1 = require("../processor/cache");
5
+ const runner_1 = require("../runner");
6
+ const creator_1 = require("../test/creator");
7
+ const type_1 = require("../type");
8
+ const creators = new Map();
9
+ function getCreator(target) {
10
+ if (!creators.has(target)) {
11
+ creators.set(target, new creator_1.BasicCaseCreator({
12
+ clean: true,
13
+ describe: true,
14
+ target,
15
+ steps: ({ name, target }) => [
16
+ new cache_1.CacheProcessor({
17
+ name,
18
+ target: target,
19
+ compilerType: type_1.ECompilerType.Rspack,
20
+ configFiles: ["rspack.config.js", "webpack.config.js"]
21
+ })
22
+ ],
23
+ runner: runner_1.CacheRunnerFactory
24
+ }));
25
+ }
26
+ return creators.get(target);
27
+ }
28
+ function createCacheCase(name, src, dist, target) {
29
+ const creator = getCreator(target);
30
+ creator.create(name, src, dist);
31
+ }
package/dist/case/diff.js CHANGED
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.createDiffCase = createDiffCase;
7
7
  const node_path_1 = __importDefault(require("node:path"));
8
8
  const fs_extra_1 = __importDefault(require("fs-extra"));
9
- const rimraf_1 = __importDefault(require("rimraf"));
9
+ const rimraf_1 = require("rimraf");
10
10
  const createLazyTestEnv_1 = __importDefault(require("../helper/legacy/createLazyTestEnv"));
11
11
  const processor_1 = require("../processor");
12
12
  const tester_1 = require("../test/tester");
@@ -33,7 +33,7 @@ function createDiffCase(name, src, dist) {
33
33
  steps: [processor]
34
34
  });
35
35
  beforeAll(async () => {
36
- rimraf_1.default.sync(dist);
36
+ (0, rimraf_1.rimrafSync)(dist);
37
37
  await tester.prepare();
38
38
  });
39
39
  do {
@@ -15,3 +15,4 @@ export * from "./stats-output";
15
15
  export * from "./treeshaking";
16
16
  export * from "./watch";
17
17
  export * from "./new-incremental";
18
+ export * from "./cache";
@@ -31,3 +31,4 @@ __exportStar(require("./stats-output"), exports);
31
31
  __exportStar(require("./treeshaking"), exports);
32
32
  __exportStar(require("./watch"), exports);
33
33
  __exportStar(require("./new-incremental"), exports);
34
+ __exportStar(require("./cache"), exports);
@@ -15,13 +15,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
15
15
  }) : function(o, v) {
16
16
  o["default"] = v;
17
17
  });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
25
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
26
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
37
  };
@@ -10,7 +10,10 @@ const path_serializer_1 = require("path-serializer");
10
10
  // 2. replace <RSPACK_ROOT> etc
11
11
  // 3. transform win32 sep
12
12
  const placeholderSerializer = (0, path_serializer_1.createSnapshotSerializer)({
13
- root: node_path_1.default.resolve(__dirname, "../../../../../"),
13
+ root: __dirname.includes("node_modules")
14
+ ? // Use `process.cwd()` when using outside Rspack
15
+ process.cwd()
16
+ : node_path_1.default.resolve(__dirname, "../../../../../"),
14
17
  replace: [
15
18
  {
16
19
  match: node_path_1.default.resolve(__dirname, "../../../"),
@@ -11,7 +11,6 @@ const node_path_1 = __importDefault(require("node:path"));
11
11
  const chalk_1 = __importDefault(require("chalk"));
12
12
  const filenamify_1 = __importDefault(require("filenamify"));
13
13
  const jest_diff_1 = require("jest-diff");
14
- const mkdirp_1 = __importDefault(require("mkdirp"));
15
14
  /**
16
15
  * Check if 2 strings or buffer are equal
17
16
  */
@@ -62,7 +61,7 @@ function toMatchFileSnapshot(content, filepath, options = {}) {
62
61
  return { pass: true, message: () => "" };
63
62
  }
64
63
  if (snapshotState._updateSnapshot === "all") {
65
- mkdirp_1.default.sync(node_path_1.default.dirname(filename));
64
+ node_fs_1.default.mkdirSync(node_path_1.default.dirname(filename), { recursive: true });
66
65
  node_fs_1.default.writeFileSync(filename, content);
67
66
  snapshotState.updated++;
68
67
  return { pass: true, message: () => "" };
@@ -83,7 +82,7 @@ function toMatchFileSnapshot(content, filepath, options = {}) {
83
82
  if (!isNot &&
84
83
  (snapshotState._updateSnapshot === "new" ||
85
84
  snapshotState._updateSnapshot === "all")) {
86
- mkdirp_1.default.sync(node_path_1.default.dirname(filename));
85
+ node_fs_1.default.mkdirSync(node_path_1.default.dirname(filename), { recursive: true });
87
86
  node_fs_1.default.writeFileSync(filename, content);
88
87
  snapshotState.added++;
89
88
  return { pass: true, message: () => "" };
@@ -1,2 +1,2 @@
1
- declare function _exports(testDirectory: any, object: any, kind: any, filename: any, upperCaseKind: any, done: any): true | undefined;
1
+ declare function _exports(testDirectory: any, object: any, kind: any, filename: any, upperCaseKind: any, done: any): Promise<true | undefined>;
2
2
  export = _exports;
@@ -59,12 +59,11 @@ ${tooMuch.map(item => `${explain(item)}`).join("\n\n")}`);
59
59
  }
60
60
  return diff.join("\n\n");
61
61
  };
62
- module.exports = function checkArrayExpectation(testDirectory, object, kind, filename, upperCaseKind, done) {
63
- if (!done) {
64
- done = upperCaseKind;
65
- upperCaseKind = filename;
66
- filename = `${kind}s`;
67
- }
62
+ module.exports = async function checkArrayExpectation(testDirectory, object, kind, filename, upperCaseKind, done) {
63
+ const usePromise = typeof done === "function";
64
+ done = typeof done === "function" ? done : error => {
65
+ throw error;
66
+ };
68
67
  let array = object[`${kind}s`];
69
68
  if (Array.isArray(array)) {
70
69
  if (kind === "warning") {
@@ -2,7 +2,7 @@
2
2
  // @ts-nocheck
3
3
  const fs = require("node:fs");
4
4
  const path = require("node:path");
5
- const rimraf = require("rimraf");
5
+ const { rimrafSync } = require("rimraf");
6
6
  module.exports = function copyDiff(src, dest, initial) {
7
7
  fs.mkdirSync(dest, { recursive: true });
8
8
  const files = fs.readdirSync(src);
@@ -19,7 +19,7 @@ module.exports = function copyDiff(src, dest, initial) {
19
19
  fs.unlinkSync(destFile);
20
20
  }
21
21
  else if (/^DELETE_DIRECTORY\s*$/.test(content.toString("utf-8"))) {
22
- rimraf.sync(destFile);
22
+ rimrafSync(destFile);
23
23
  }
24
24
  else {
25
25
  fs.writeFileSync(destFile, content);
@@ -0,0 +1 @@
1
+ export default function (this: any, c: string): string;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = default_1;
4
+ function default_1(c) {
5
+ let content = c;
6
+ if (content.includes("NEXT_HMR")) {
7
+ content = `
8
+ ${content}
9
+ let __hmr_children__ = [...module.children];
10
+ let __hmr_used_exports__ = __hmr_children__.reduce((res, child) => {
11
+ res[child] = __webpack_module_cache__[child].exports;
12
+ return res;
13
+ }, {});
14
+ module.hot.accept(__hmr_children__, () => {
15
+ __hmr_children__.forEach((child) => {
16
+ const reexports = __webpack_require__(child);
17
+ for (let key in reexports) {
18
+ Object.defineProperty(__hmr_used_exports__[child], key, {
19
+ configurable: true,
20
+ enumerable: true,
21
+ get: () => reexports[key]
22
+ });
23
+ }
24
+ });
25
+ });
26
+ `;
27
+ }
28
+ content = content.replace(/NEXT_HMR/g, "NEXT_HMR.bind(null, module)");
29
+ const options = this.getOptions();
30
+ const items = content.split(/---+\r?\n/g);
31
+ if (items.length <= 1) {
32
+ return content;
33
+ }
34
+ options.totalUpdates = Math.max(options.totalUpdates, items.length);
35
+ options.changedFiles.push(this.resourcePath);
36
+ return items[options.updateIndex];
37
+ }
@@ -0,0 +1,7 @@
1
+ import type { Compiler } from "@rspack/core";
2
+ import type { TUpdateOptions } from "../../type";
3
+ export declare class TestHotUpdatePlugin {
4
+ private updateOptions;
5
+ constructor(updateOptions: TUpdateOptions);
6
+ apply(compiler: Compiler): void;
7
+ }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TestHotUpdatePlugin = void 0;
4
+ class TestHotUpdatePlugin {
5
+ constructor(updateOptions) {
6
+ this.updateOptions = updateOptions;
7
+ }
8
+ apply(compiler) {
9
+ compiler.hooks.beforeRun.tap("TestHotUpdatePlugin", () => {
10
+ compiler.modifiedFiles = new Set(this.updateOptions.changedFiles);
11
+ this.updateOptions.changedFiles = [];
12
+ });
13
+ compiler.hooks.compilation.tap("TestHotUpdatePlugin", compilation => {
14
+ compilation.hooks.additionalTreeRuntimeRequirements.tap("HMR_TEST_PLUGIN", (_chunk, set) => {
15
+ set.add(compiler.webpack.RuntimeGlobals.moduleCache);
16
+ });
17
+ compilation.hooks.runtimeModule.tap("HMR_TEST_PLUGIN", (module, _set) => {
18
+ if (module.constructorName === "DefinePropertyGettersRuntimeModule") {
19
+ module.source.source = Buffer.from(`
20
+ __webpack_require__.d = function (exports, definition) {
21
+ for (var key in definition) {
22
+ if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
23
+ Object.defineProperty(exports, key, { configurable: true, enumerable: true, get: definition[key] });
24
+ }
25
+ }
26
+ };
27
+ `, "utf-8");
28
+ }
29
+ });
30
+ });
31
+ }
32
+ }
33
+ exports.TestHotUpdatePlugin = TestHotUpdatePlugin;
@@ -0,0 +1 @@
1
+ export * from "./hot-update";
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./hot-update"), exports);
@@ -0,0 +1 @@
1
+ export declare function refreshModifyTime(file: string): Promise<void>;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.refreshModifyTime = refreshModifyTime;
4
+ const fs_extra_1 = require("fs-extra");
5
+ async function refreshModifyTime(file) {
6
+ const data = await (0, fs_extra_1.readFile)(file);
7
+ await (0, fs_extra_1.writeFile)(file, data);
8
+ }
@@ -110,14 +110,8 @@ class BasicProcessor {
110
110
  warnings.push(...jsonStats.warnings);
111
111
  }
112
112
  }
113
- await new Promise((resolve, reject) => {
114
- (0, checkArrayExpectation_1.default)(context.getSource(), { errors }, "error", "Error", reject);
115
- resolve();
116
- });
117
- await new Promise((resolve, reject) => {
118
- (0, checkArrayExpectation_1.default)(context.getSource(), { warnings }, "warning", "Warning", reject);
119
- resolve();
120
- });
113
+ await (0, checkArrayExpectation_1.default)(context.getSource(), { errors }, "error", "errors", "Error");
114
+ await (0, checkArrayExpectation_1.default)(context.getSource(), { warnings }, "warning", "warnings", "Warning");
121
115
  // clear error if checked
122
116
  if (node_fs_1.default.existsSync(context.getSource("errors.js"))) {
123
117
  context.clearError(this._options.name);
@@ -0,0 +1,17 @@
1
+ import { ECompilerType, type ITestContext, type ITestEnv, type ITestRunner, type TCompilerOptions, type TUpdateOptions } from "../type";
2
+ import { BasicProcessor, type IBasicProcessorOptions } from "./basic";
3
+ export interface ICacheProcessorOptions<T extends ECompilerType> extends Omit<IBasicProcessorOptions<T>, "runable"> {
4
+ target: TCompilerOptions<T>["target"];
5
+ }
6
+ export declare class CacheProcessor<T extends ECompilerType> extends BasicProcessor<T> {
7
+ protected _cacheOptions: ICacheProcessorOptions<T>;
8
+ protected updateOptions: TUpdateOptions;
9
+ protected runner: ITestRunner | null;
10
+ constructor(_cacheOptions: ICacheProcessorOptions<T>);
11
+ build(context: ITestContext): Promise<void>;
12
+ run(env: ITestEnv, context: ITestContext): Promise<void>;
13
+ static findBundle<T extends ECompilerType>(this: CacheProcessor<T>, context: ITestContext): string[];
14
+ afterAll(context: ITestContext): Promise<void>;
15
+ static defaultOptions<T extends ECompilerType>(this: CacheProcessor<T>, context: ITestContext): TCompilerOptions<T>;
16
+ static overrideOptions<T extends ECompilerType>(this: CacheProcessor<T>, context: ITestContext, options: TCompilerOptions<T>): void;
17
+ }
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CacheProcessor = void 0;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const core_1 = require("@rspack/core");
9
+ const fs_extra_1 = require("fs-extra");
10
+ const plugins_1 = require("../helper/plugins");
11
+ const type_1 = require("../type");
12
+ const basic_1 = require("./basic");
13
+ class CacheProcessor extends basic_1.BasicProcessor {
14
+ constructor(_cacheOptions) {
15
+ const fakeUpdateLoaderOptions = {
16
+ updateIndex: 0,
17
+ totalUpdates: 1,
18
+ changedFiles: []
19
+ };
20
+ super({
21
+ defaultOptions: CacheProcessor.defaultOptions,
22
+ overrideOptions: CacheProcessor.overrideOptions,
23
+ findBundle: CacheProcessor.findBundle,
24
+ runable: true,
25
+ ..._cacheOptions
26
+ });
27
+ this._cacheOptions = _cacheOptions;
28
+ this.runner = null;
29
+ this.updateOptions = fakeUpdateLoaderOptions;
30
+ }
31
+ async build(context) {
32
+ // clear cache directory first time.
33
+ const experiments = this.getCompiler(context).getOptions().experiments || {};
34
+ let directory = "";
35
+ if ("cache" in experiments &&
36
+ typeof experiments.cache === "object" &&
37
+ experiments.cache.type === "persistent") {
38
+ directory = experiments.cache.storage?.directory || directory;
39
+ }
40
+ (0, fs_extra_1.removeSync)(node_path_1.default.join(context.getSource(), directory || "node_modules/.cache"));
41
+ await super.build(context);
42
+ }
43
+ async run(env, context) {
44
+ context.setValue(this._options.name, "hotUpdateContext", this.updateOptions);
45
+ await super.run(env, context);
46
+ }
47
+ static findBundle(context) {
48
+ const files = [];
49
+ const prefiles = [];
50
+ const compiler = context.getCompiler(this._cacheOptions.name);
51
+ if (!compiler)
52
+ throw new Error("Compiler should exists when find bundle");
53
+ const stats = compiler.getStats();
54
+ if (!stats)
55
+ throw new Error("Stats should exists when find bundle");
56
+ const info = stats.toJson({ all: false, entrypoints: true });
57
+ if (this._cacheOptions.target === "web" ||
58
+ this._cacheOptions.target === "webworker") {
59
+ for (const file of info.entrypoints.main.assets) {
60
+ if (file.name.endsWith(".js")) {
61
+ files.push(file.name);
62
+ }
63
+ else {
64
+ prefiles.push(file.name);
65
+ }
66
+ }
67
+ }
68
+ else {
69
+ const assets = info.entrypoints.main.assets.filter(s => s.name.endsWith(".js"));
70
+ files.push(assets[assets.length - 1].name);
71
+ }
72
+ return [...prefiles, ...files];
73
+ }
74
+ async afterAll(context) {
75
+ await super.afterAll(context);
76
+ if (this.updateOptions.updateIndex + 1 !==
77
+ this.updateOptions.totalUpdates) {
78
+ throw new Error(`Should run all hot steps (${this.updateOptions.updateIndex + 1} / ${this.updateOptions.totalUpdates}): ${this._options.name}`);
79
+ }
80
+ }
81
+ static defaultOptions(context) {
82
+ const options = {
83
+ context: context.getSource(),
84
+ mode: "production",
85
+ cache: true,
86
+ devtool: false,
87
+ output: {
88
+ path: context.getDist(),
89
+ filename: "bundle.js",
90
+ chunkFilename: "[name].chunk.[fullhash].js",
91
+ publicPath: "https://test.cases/path/",
92
+ library: { type: "commonjs2" }
93
+ },
94
+ optimization: {
95
+ moduleIds: "named"
96
+ },
97
+ target: this._cacheOptions.target,
98
+ experiments: {
99
+ css: true,
100
+ rspackFuture: {
101
+ bundlerInfo: {
102
+ force: false
103
+ }
104
+ }
105
+ }
106
+ };
107
+ if (this._cacheOptions.compilerType === type_1.ECompilerType.Rspack) {
108
+ options.plugins ??= [];
109
+ options.plugins.push(new core_1.rspack.HotModuleReplacementPlugin());
110
+ }
111
+ return options;
112
+ }
113
+ static overrideOptions(context, options) {
114
+ if (!options.entry) {
115
+ options.entry = "./index.js";
116
+ }
117
+ options.module ??= {};
118
+ for (const cssModuleType of ["css/auto", "css/module", "css"]) {
119
+ options.module.generator ??= {};
120
+ options.module.generator[cssModuleType] ??= {};
121
+ options.module.generator[cssModuleType].exportsOnly ??=
122
+ this._cacheOptions.target === "async-node";
123
+ }
124
+ options.module.rules ??= [];
125
+ options.module.rules.push({
126
+ test: /\.(js|css|json)/,
127
+ use: [
128
+ {
129
+ loader: node_path_1.default.resolve(__dirname, "../helper/loaders/hot-update.js"),
130
+ options: this.updateOptions
131
+ }
132
+ ]
133
+ });
134
+ if (this._cacheOptions.compilerType === type_1.ECompilerType.Rspack) {
135
+ options.plugins ??= [];
136
+ options.plugins.push(new plugins_1.TestHotUpdatePlugin(this.updateOptions));
137
+ }
138
+ }
139
+ }
140
+ exports.CacheProcessor = CacheProcessor;
@@ -111,7 +111,9 @@ class HotSnapshotProcessor extends hot_1.HotProcessor {
111
111
  const title = `Case ${node_path_1.default.basename(this._options.name)}: Step ${step}`;
112
112
  const hotUpdateFile = [];
113
113
  const hotUpdateManifest = [];
114
- const changedFiles = this.updateOptions.changedFiles.map((i) => (0, win_1.escapeSep)(node_path_1.default.relative(context.getSource(), i)));
114
+ const changedFiles = this.updateOptions.updateIndex === 0
115
+ ? []
116
+ : this.updateOptions.changedFiles.map((i) => (0, win_1.escapeSep)(node_path_1.default.relative(context.getSource(), i)));
115
117
  changedFiles.sort();
116
118
  const hashes = {
117
119
  [lastHash || "LAST_HASH"]: "LAST_HASH",
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.HotProcessor = void 0;
7
7
  const node_path_1 = __importDefault(require("node:path"));
8
8
  const core_1 = require("@rspack/core");
9
+ const plugins_1 = require("../helper/plugins");
9
10
  const type_1 = require("../type");
10
11
  const basic_1 = require("./basic");
11
12
  class HotProcessor extends basic_1.BasicProcessor {
@@ -91,7 +92,7 @@ class HotProcessor extends basic_1.BasicProcessor {
91
92
  };
92
93
  if (this._hotOptions.compilerType === type_1.ECompilerType.Rspack) {
93
94
  options.plugins ??= [];
94
- options.plugins.push(new core_1.rspack.HotModuleReplacementPlugin());
95
+ options.plugins.push(new core_1.rspack.HotModuleReplacementPlugin(), new plugins_1.TestHotUpdatePlugin(this.updateOptions));
95
96
  }
96
97
  return options;
97
98
  }
@@ -111,7 +112,7 @@ class HotProcessor extends basic_1.BasicProcessor {
111
112
  test: /\.(js|css|json)/,
112
113
  use: [
113
114
  {
114
- loader: node_path_1.default.resolve(__dirname, "../helper/legacy/fake-update-loader.js"),
115
+ loader: node_path_1.default.resolve(__dirname, "../helper/loaders/hot-update.js"),
115
116
  options: this.updateOptions
116
117
  }
117
118
  ]
@@ -17,3 +17,4 @@ export * from "./snapshot";
17
17
  export * from "./stats";
18
18
  export * from "./stats-api";
19
19
  export * from "./watch";
20
+ export * from "./cache";
@@ -33,3 +33,4 @@ __exportStar(require("./snapshot"), exports);
33
33
  __exportStar(require("./stats"), exports);
34
34
  __exportStar(require("./stats-api"), exports);
35
35
  __exportStar(require("./watch"), exports);
36
+ __exportStar(require("./cache"), exports);
@@ -83,14 +83,8 @@ class WatchProcessor extends multi_1.MultiTaskProcessor {
83
83
  warnings.push(...jsonStats.warnings);
84
84
  }
85
85
  }
86
- await new Promise((resolve, reject) => {
87
- (0, checkArrayExpectation_1.default)(node_path_1.default.join(context.getSource(), this._watchOptions.stepName), { errors }, "error", "Error", reject);
88
- resolve();
89
- });
90
- await new Promise((resolve, reject) => {
91
- (0, checkArrayExpectation_1.default)(node_path_1.default.join(context.getSource(), this._watchOptions.stepName), { warnings }, "warning", "Warning", reject);
92
- resolve();
93
- });
86
+ await (0, checkArrayExpectation_1.default)(node_path_1.default.join(context.getSource(), this._watchOptions.stepName), { errors }, "error", "errors", "Error");
87
+ await (0, checkArrayExpectation_1.default)(node_path_1.default.join(context.getSource(), this._watchOptions.stepName), { warnings }, "warning", "warnings", "Warning");
94
88
  // clear error if checked
95
89
  if (node_fs_1.default.existsSync(context.getSource("errors.js"))) {
96
90
  context.clearError(this._options.name);
@@ -0,0 +1,5 @@
1
+ import { type ECompilerType, type ITestEnv, type ITestRunner, type TCompilerOptions, type TCompilerStatsCompilation } from "../type";
2
+ import { BasicRunnerFactory } from "./basic";
3
+ export declare class CacheRunnerFactory<T extends ECompilerType> extends BasicRunnerFactory<T> {
4
+ protected createRunner(file: string, stats: TCompilerStatsCompilation<T>, compilerOptions: TCompilerOptions<T>, env: ITestEnv): ITestRunner;
5
+ }
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CacheRunnerFactory = void 0;
7
+ const checkArrayExpectation_1 = __importDefault(require("../helper/legacy/checkArrayExpectation"));
8
+ const refreshModifyTime_1 = require("../helper/util/refreshModifyTime");
9
+ const type_1 = require("../type");
10
+ const basic_1 = require("./basic");
11
+ const web_1 = require("./runner/web");
12
+ const MAX_COMPILER_INDEX = 100;
13
+ class CacheRunnerFactory extends basic_1.BasicRunnerFactory {
14
+ createRunner(file, stats, compilerOptions, env) {
15
+ const compiler = this.context.getCompiler(this.name);
16
+ let compilerIndex = 0;
17
+ const testConfig = this.context.getTestConfig();
18
+ const source = this.context.getSource();
19
+ const dist = this.context.getDist();
20
+ const hotUpdateContext = this.context.getValue(this.name, "hotUpdateContext");
21
+ const getWebRunner = () => {
22
+ return new web_1.WebRunner({
23
+ dom: this.context.getValue(this.name, "documentType") ||
24
+ type_1.EDocumentType.JSDOM,
25
+ env,
26
+ stats,
27
+ name: this.name,
28
+ runInNewContext: false,
29
+ testConfig: {
30
+ ...testConfig,
31
+ moduleScope(ms, stats) {
32
+ const moduleScope = typeof testConfig.moduleScope === "function"
33
+ ? testConfig.moduleScope(ms, stats)
34
+ : ms;
35
+ moduleScope.COMPILER_INDEX = compilerIndex;
36
+ moduleScope.NEXT_HMR = nextHmr;
37
+ moduleScope.NEXT_START = nextStart;
38
+ return moduleScope;
39
+ }
40
+ },
41
+ source,
42
+ dist,
43
+ compilerOptions
44
+ });
45
+ };
46
+ const nextHmr = async (m, options) => {
47
+ hotUpdateContext.updateIndex++;
48
+ const stats = await compiler.build();
49
+ if (!stats) {
50
+ throw new Error("Should generate stats during build");
51
+ }
52
+ const jsonStats = stats.toJson({
53
+ // errorDetails: true
54
+ });
55
+ await (0, checkArrayExpectation_1.default)(source, jsonStats, "error", `errors${hotUpdateContext.updateIndex}`, "Error");
56
+ await (0, checkArrayExpectation_1.default)(source, jsonStats, "warning", `warnings${hotUpdateContext.updateIndex}`, "Warning");
57
+ const updatedModules = await m.hot.check(options || true);
58
+ if (!updatedModules) {
59
+ throw new Error("No update available");
60
+ }
61
+ return jsonStats;
62
+ };
63
+ const nextStart = async () => {
64
+ await compiler.close();
65
+ compiler.createCompiler();
66
+ await Promise.all(hotUpdateContext.changedFiles.map(async (file) => {
67
+ await (0, refreshModifyTime_1.refreshModifyTime)(file);
68
+ }));
69
+ hotUpdateContext.changedFiles = [];
70
+ hotUpdateContext.updateIndex++;
71
+ const stats = await compiler.build();
72
+ if (!stats) {
73
+ throw new Error("Should generate stats during build");
74
+ }
75
+ const jsonStats = stats.toJson({
76
+ // errorDetails: true
77
+ });
78
+ await (0, checkArrayExpectation_1.default)(source, jsonStats, "error", `errors${hotUpdateContext.updateIndex}`, "Error");
79
+ await (0, checkArrayExpectation_1.default)(source, jsonStats, "warning", `warnings${hotUpdateContext.updateIndex}`, "Warning");
80
+ env.it(`NEXT_START run with compilerIndex==${compilerIndex + 1}`, async () => {
81
+ if (compilerIndex > MAX_COMPILER_INDEX) {
82
+ throw new Error("NEXT_START has been called more than the maximum times");
83
+ }
84
+ compilerIndex++;
85
+ return getWebRunner().run(file);
86
+ });
87
+ return jsonStats;
88
+ };
89
+ return getWebRunner();
90
+ }
91
+ }
92
+ exports.CacheRunnerFactory = CacheRunnerFactory;
@@ -15,39 +15,48 @@ class HotStepRunnerFactory extends hot_1.HotRunnerFactory {
15
15
  const source = this.context.getSource();
16
16
  const dist = this.context.getDist();
17
17
  const hotUpdateContext = this.context.getValue(this.name, "hotUpdateContext");
18
- const next = (callback) => {
19
- hotUpdateContext.updateIndex++;
20
- // TODO: find a better way to collect changed files from fake-update-loader
21
- const changedFiles = new Map();
22
- global.__CHANGED_FILES__ = changedFiles;
23
- compiler
24
- .build()
25
- .then(stats => {
26
- if (!stats)
27
- return callback(new Error("Should generate stats during build"));
18
+ const next = async (callback) => {
19
+ const usePromise = typeof callback === "function";
20
+ try {
21
+ hotUpdateContext.updateIndex++;
22
+ const stats = await compiler.build();
23
+ if (!stats) {
24
+ throw new Error("Should generate stats during build");
25
+ }
28
26
  const jsonStats = stats.toJson({
29
- errorDetails: true
27
+ // errorDetails: true
30
28
  });
31
- hotUpdateContext.totalUpdates = Math.max(hotUpdateContext.totalUpdates, ...changedFiles.values());
32
- hotUpdateContext.changedFiles = [...changedFiles.keys()];
33
- try {
34
- const checker = this.context.getValue(this.name, jsonStats.errors?.length
35
- ? "hotUpdateStepErrorChecker"
36
- : "hotUpdateStepChecker");
37
- checker(hotUpdateContext, stats, runner.getGlobal("__HMR_UPDATED_RUNTIME__"));
38
- if ((0, checkArrayExpectation_1.default)(source, jsonStats, "error", `errors${hotUpdateContext.updateIndex}`, "Error", callback)) {
39
- return;
40
- }
41
- if ((0, checkArrayExpectation_1.default)(source, jsonStats, "warning", `warnings${hotUpdateContext.updateIndex}`, "Warning", callback)) {
42
- return;
43
- }
29
+ const checker = this.context.getValue(this.name, jsonStats.errors?.length
30
+ ? "hotUpdateStepErrorChecker"
31
+ : "hotUpdateStepChecker");
32
+ checker(hotUpdateContext, stats, runner.getGlobal("__HMR_UPDATED_RUNTIME__"));
33
+ await (0, checkArrayExpectation_1.default)(source, jsonStats, "error", `errors${hotUpdateContext.updateIndex}`, "Error");
34
+ await (0, checkArrayExpectation_1.default)(source, jsonStats, "warning", `warnings${hotUpdateContext.updateIndex}`, "Warning");
35
+ if (usePromise) {
36
+ // old callback style hmr cases
44
37
  callback(null, jsonStats);
45
38
  }
46
- catch (e) {
39
+ else {
40
+ // new promise style hmr cases
41
+ return jsonStats;
42
+ }
43
+ }
44
+ catch (e) {
45
+ if (usePromise) {
47
46
  callback(e);
48
47
  }
49
- })
50
- .catch(callback);
48
+ else {
49
+ throw e;
50
+ }
51
+ }
52
+ };
53
+ const nextHMR = async (m, options) => {
54
+ const jsonStats = await next();
55
+ const updatedModules = await m.hot.check(options || true);
56
+ if (!updatedModules) {
57
+ throw new Error("No update available");
58
+ }
59
+ return jsonStats;
51
60
  };
52
61
  const runner = new web_1.WebRunner({
53
62
  dom: this.context.getValue(this.name, "documentType") || type_1.EDocumentType.JSDOM,
@@ -62,6 +71,7 @@ class HotStepRunnerFactory extends hot_1.HotRunnerFactory {
62
71
  ? testConfig.moduleScope(ms, stats)
63
72
  : ms;
64
73
  moduleScope.NEXT = next;
74
+ moduleScope.NEXT_HMR = nextHMR;
65
75
  return moduleScope;
66
76
  }
67
77
  },
@@ -15,30 +15,44 @@ class HotRunnerFactory extends basic_1.BasicRunnerFactory {
15
15
  const source = this.context.getSource();
16
16
  const dist = this.context.getDist();
17
17
  const hotUpdateContext = this.context.getValue(this.name, "hotUpdateContext");
18
- const next = (callback) => {
19
- hotUpdateContext.updateIndex++;
20
- // TODO: find a better way to collect changed files from fake-update-loader
21
- const changedFiles = new Map();
22
- global.__CHANGED_FILES__ = changedFiles;
23
- compiler
24
- .build()
25
- .then(stats => {
26
- if (!stats)
27
- return callback(new Error("Should generate stats during build"));
18
+ const next = async (callback) => {
19
+ const usePromise = typeof callback === "function";
20
+ try {
21
+ hotUpdateContext.updateIndex++;
22
+ const stats = await compiler.build();
23
+ if (!stats) {
24
+ throw new Error("Should generate stats during build");
25
+ }
28
26
  const jsonStats = stats.toJson({
29
27
  // errorDetails: true
30
28
  });
31
- hotUpdateContext.totalUpdates = Math.max(hotUpdateContext.totalUpdates, ...changedFiles.values());
32
- hotUpdateContext.changedFiles = [...changedFiles.keys()];
33
- if ((0, checkArrayExpectation_1.default)(source, jsonStats, "error", `errors${hotUpdateContext.updateIndex}`, "Error", callback)) {
34
- return;
29
+ await (0, checkArrayExpectation_1.default)(source, jsonStats, "error", `errors${hotUpdateContext.updateIndex}`, "Error");
30
+ await (0, checkArrayExpectation_1.default)(source, jsonStats, "warning", `warnings${hotUpdateContext.updateIndex}`, "Warning");
31
+ if (usePromise) {
32
+ // old callback style hmr cases
33
+ callback(null, jsonStats);
34
+ }
35
+ else {
36
+ // new promise style hmr cases
37
+ return jsonStats;
35
38
  }
36
- if ((0, checkArrayExpectation_1.default)(source, jsonStats, "warning", `warnings${hotUpdateContext.updateIndex}`, "Warning", callback)) {
37
- return;
39
+ }
40
+ catch (e) {
41
+ if (usePromise) {
42
+ callback(e);
38
43
  }
39
- callback(null, jsonStats);
40
- })
41
- .catch(callback);
44
+ else {
45
+ throw e;
46
+ }
47
+ }
48
+ };
49
+ const nextHMR = async (m, options) => {
50
+ const jsonStats = await next();
51
+ const updatedModules = await m.hot.check(options || true);
52
+ if (!updatedModules) {
53
+ throw new Error("No update available");
54
+ }
55
+ return jsonStats;
42
56
  };
43
57
  return new web_1.WebRunner({
44
58
  dom: this.context.getValue(this.name, "documentType") || type_1.EDocumentType.JSDOM,
@@ -53,6 +67,7 @@ class HotRunnerFactory extends basic_1.BasicRunnerFactory {
53
67
  ? testConfig.moduleScope(ms, stats)
54
68
  : ms;
55
69
  moduleScope.NEXT = next;
70
+ moduleScope.NEXT_HMR = nextHMR;
56
71
  return moduleScope;
57
72
  }
58
73
  },
@@ -6,3 +6,4 @@ export * from "./normal";
6
6
  export * from "./runner";
7
7
  export * from "./type";
8
8
  export * from "./watch";
9
+ export * from "./cache";
@@ -22,3 +22,4 @@ __exportStar(require("./normal"), exports);
22
22
  __exportStar(require("./runner"), exports);
23
23
  __exportStar(require("./type"), exports);
24
24
  __exportStar(require("./watch"), exports);
25
+ __exportStar(require("./cache"), exports);
@@ -15,13 +15,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
15
15
  }) : function(o, v) {
16
16
  o["default"] = v;
17
17
  });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
25
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
26
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
37
  };
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.BasicCaseCreator = void 0;
7
7
  const node_fs_1 = __importDefault(require("node:fs"));
8
8
  const node_path_1 = __importDefault(require("node:path"));
9
- const rimraf_1 = __importDefault(require("rimraf"));
9
+ const rimraf_1 = require("rimraf");
10
10
  const createLazyTestEnv_1 = __importDefault(require("../helper/legacy/createLazyTestEnv"));
11
11
  const tester_1 = require("./tester");
12
12
  class BasicCaseCreator {
@@ -80,7 +80,7 @@ class BasicCaseCreator {
80
80
  }
81
81
  clean(folders) {
82
82
  for (const f of folders) {
83
- rimraf_1.default.sync(f);
83
+ (0, rimraf_1.rimrafSync)(f);
84
84
  node_fs_1.default.mkdirSync(f, { recursive: true });
85
85
  }
86
86
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rspack/test-tools",
3
- "version": "1.1.8",
3
+ "version": "1.2.0-alpha.0",
4
4
  "license": "MIT",
5
5
  "description": "Test tools for rspack",
6
6
  "main": "dist/index.js",
@@ -42,15 +42,14 @@
42
42
  "deepmerge": "^4.3.1",
43
43
  "filenamify": "4.3.0",
44
44
  "fs-extra": "^11.2.0",
45
- "glob": "^10.3.10",
45
+ "glob": "^11.0.0",
46
46
  "jest-diff": "^29.7.0",
47
47
  "jest-snapshot": "29.7.0",
48
48
  "jsdom": "^25.0.0",
49
49
  "memfs": "4.14.0",
50
- "mkdirp": "0.5.6",
51
50
  "path-serializer": "0.3.4",
52
51
  "pretty-format": "29.7.0",
53
- "rimraf": "3.0.2",
52
+ "rimraf": "^5.0.10",
54
53
  "webpack": "^5.94.0",
55
54
  "webpack-merge": "5.9.0",
56
55
  "webpack-sources": "3.2.3"
@@ -60,8 +59,8 @@
60
59
  "@monaco-editor/react": "^4.6.0",
61
60
  "@rspack/plugin-preact-refresh": "1.1.0",
62
61
  "@rspack/plugin-react-refresh": "1.0.0",
63
- "@swc/helpers": "0.5.13",
64
- "@swc/plugin-remove-console": "^5.0.0",
62
+ "@swc/helpers": "0.5.15",
63
+ "@swc/plugin-remove-console": "^6.0.2",
65
64
  "@types/babel__generator": "7.6.8",
66
65
  "@types/babel__traverse": "7.20.6",
67
66
  "@types/fs-extra": "11.0.4",
@@ -78,7 +77,7 @@
78
77
  "coffeescript": "^2.5.1",
79
78
  "copy-webpack-plugin": "5.1.2",
80
79
  "core-js": "3.38.1",
81
- "css-loader": "^6.11.0",
80
+ "css-loader": "^7.1.2",
82
81
  "file-loader": "^6.2.0",
83
82
  "html-loader": "^5.0.0",
84
83
  "html-webpack-plugin": "^5.5.0",
@@ -99,11 +98,11 @@
99
98
  "source-map-loader": "^5.0.0",
100
99
  "style-loader": "^4.0.0",
101
100
  "terser": "5.36.0",
102
- "typescript": "^5.6.3",
101
+ "typescript": "^5.7.2",
103
102
  "wast-loader": "^1.12.1",
104
103
  "worker-rspack-loader": "^3.1.2",
105
- "@rspack/cli": "1.1.8",
106
- "@rspack/core": "1.1.8"
104
+ "@rspack/cli": "1.2.0-alpha.0",
105
+ "@rspack/core": "1.2.0-alpha.0"
107
106
  },
108
107
  "peerDependencies": {
109
108
  "@rspack/core": ">=1.0.0"
@@ -1,2 +0,0 @@
1
- declare function _exports(content: any): void;
2
- export = _exports;
@@ -1,18 +0,0 @@
1
- "use strict";
2
- // @ts-nocheck
3
- const contentMap = {};
4
- module.exports = function (content) {
5
- // CHANGE:
6
- var idx = this.getOptions().updateIndex;
7
- var items = content.split(/---+\r?\n/g);
8
- var curIdx = items[idx] ? idx : items.length - 1;
9
- var oldIdx = contentMap[this.resourcePath];
10
- if (curIdx !== oldIdx && global.__CHANGED_FILES__) {
11
- global.__CHANGED_FILES__.set(this.resourcePath, items.length);
12
- }
13
- contentMap[this.resourcePath] = curIdx;
14
- if (items.length > 1) {
15
- this.cacheable(false);
16
- }
17
- this.callback(null, items[curIdx]);
18
- };