Skip to content

Commit

Permalink
chore: add jsdom web runner (#6286)
Browse files Browse the repository at this point in the history
* chore: add jsdom web runner

* chore: protected
  • Loading branch information
LingyuCoder authored Apr 19, 2024
1 parent 45ec169 commit 0f0ecdf
Show file tree
Hide file tree
Showing 22 changed files with 1,369 additions and 419 deletions.
25 changes: 5 additions & 20 deletions packages/rspack-test-tools/etc/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,26 +280,6 @@ export interface IBasicProcessorOptions<T extends ECompilerType = ECompilerType.
runable: boolean;
}

// @public (undocumented)
export interface IBasicRunnerOptions<T extends ECompilerType> {
// (undocumented)
compilerOptions: TCompilerOptions<T>;
// (undocumented)
dist: string;
// (undocumented)
env: ITestEnv;
// (undocumented)
name: string;
// (undocumented)
runInNewContext?: boolean;
// (undocumented)
source: string;
// (undocumented)
stats?: TCompilerStatsCompilation<T>;
// (undocumented)
testConfig: TTestConfig<T>;
}

// @public (undocumented)
export interface ICompareOptions {
// (undocumented)
Expand Down Expand Up @@ -1094,6 +1074,11 @@ export type TModuleCompareResult = TCompareResult & {
name: string;
};

// @public (undocumented)
export type TModuleObject = {
exports: unknown;
};

// @public (undocumented)
export type TModuleTypeId = "normal" | "runtime";

Expand Down
6 changes: 5 additions & 1 deletion packages/rspack-test-tools/src/runner/basic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@ export class BasicRunnerFactory<T extends ECompilerType>
compilerOptions.target === "web" ||
compilerOptions.target === "webworker"
) {
return new WebRunner<T>(runnerOptions);
return new WebRunner<T>({
...runnerOptions,
runInNewContext: true,
dom: "fake"
});
} else {
return new EsmRunner<T>(runnerOptions);
}
Expand Down
23 changes: 17 additions & 6 deletions packages/rspack-test-tools/src/runner/hot-step.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { StatsCompilation } from "@rspack/core";
import checkArrayExpectation from "../helper/legacy/checkArrayExpectation";
import {
ECompilerType,
ITestEnv,
Expand All @@ -8,8 +7,8 @@ import {
TCompilerStats,
TCompilerStatsCompilation
} from "../type";
import { HotRunner } from "./runner/hot";
import { HotRunnerFactory } from "./hot";
import { WebRunner } from "./runner/web";

export class HotStepRunnerFactory<
T extends ECompilerType
Expand All @@ -30,7 +29,10 @@ export class HotStepRunnerFactory<
) as { updateIndex: number };

const next = (
callback: (error: Error | null, stats?: StatsCompilation) => void
callback: (
error: Error | null,
stats?: TCompilerStatsCompilation<T>
) => void
) => {
hotUpdateContext.updateIndex++;
compiler
Expand Down Expand Up @@ -60,15 +62,24 @@ export class HotStepRunnerFactory<
.catch(callback);
};

return new HotRunner({
return new WebRunner({
dom: "jsdom",
env,
stats,
name: this.name,
runInNewContext: false,
testConfig,
testConfig: {
...testConfig,
moduleScope(ms) {
if (typeof testConfig.moduleScope === "function") {
ms = testConfig.moduleScope(ms);
}
ms["NEXT"] = next;
return ms;
}
},
source,
dist,
next,
compilerOptions
});
}
Expand Down
22 changes: 17 additions & 5 deletions packages/rspack-test-tools/src/runner/hot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
TCompilerStatsCompilation
} from "../type";
import { BasicRunnerFactory } from "./basic";
import { HotRunner } from "./runner/hot";
import { WebRunner } from "./runner/web";

export class HotRunnerFactory<
T extends ECompilerType
Expand All @@ -29,7 +29,10 @@ export class HotRunnerFactory<
) as { updateIndex: number };

const next = (
callback: (error: Error | null, stats?: StatsCompilation) => void
callback: (
error: Error | null,
stats?: TCompilerStatsCompilation<T>
) => void
) => {
hotUpdateContext.updateIndex++;
compiler
Expand Down Expand Up @@ -69,15 +72,24 @@ export class HotRunnerFactory<
.catch(callback);
};

return new HotRunner({
return new WebRunner({
dom: "fake",
env,
stats,
name: this.name,
runInNewContext: false,
testConfig,
testConfig: {
...testConfig,
moduleScope(ms) {
if (typeof testConfig.moduleScope === "function") {
ms = testConfig.moduleScope(ms);
}
ms["NEXT"] = next;
return ms;
}
},
source,
dist,
next,
compilerOptions
});
}
Expand Down
168 changes: 34 additions & 134 deletions packages/rspack-test-tools/src/runner/runner/basic.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { ECompilerType, ITestRunner } from "../../type";
import vm from "vm";
import {
ECompilerType,
ITestEnv,
ITestRunner,
TCompilerOptions,
TCompilerStatsCompilation,
TTestConfig
} from "../../type";
import path from "path";
import fs from "fs";
import {
IBasicGlobalContext,
IBasicModuleScope,
IBasicRunnerOptions,
TBasicRunnerFile,
TModuleObject,
TRunnerRequirer
} from "../type";

const define = function (...args: unknown[]) {
const factory = args.pop() as () => {};
factory();
};
const isRelativePath = (p: string) => /^\.\.?\//.test(p);
const getSubPath = (p: string) => {
const lastSlash = p.lastIndexOf("/");
Expand All @@ -36,8 +38,20 @@ const getSubPath = (p: string) => {
return "";
};

export class BasicRunner<T extends ECompilerType = ECompilerType.Rspack>
implements ITestRunner
export interface IBasicRunnerOptions<T extends ECompilerType> {
env: ITestEnv;
stats?: TCompilerStatsCompilation<T>;
name: string;
runInNewContext?: boolean;
testConfig: TTestConfig<T>;
source: string;
dist: string;
compilerOptions: TCompilerOptions<T>;
}

export abstract class BasicRunner<
T extends ECompilerType = ECompilerType.Rspack
> implements ITestRunner
{
protected globalContext: IBasicGlobalContext | null = null;
protected baseModuleScope: IBasicModuleScope | null = null;
Expand Down Expand Up @@ -66,62 +80,13 @@ export class BasicRunner<T extends ECompilerType = ECompilerType.Rspack>
return this.requirers.get("entry")!;
}

protected createGlobalContext(): IBasicGlobalContext {
return {
console: console,
expect: expect,
setTimeout: ((
cb: (...args: any[]) => void,
ms: number | undefined,
...args: any
) => {
let timeout = setTimeout(cb, ms, ...args);
timeout.unref();
return timeout;
}) as typeof setTimeout,
clearTimeout: clearTimeout
};
}

protected createBaseModuleScope(): IBasicModuleScope {
const baseModuleScope: IBasicModuleScope = {
console: this.globalContext!.console,
expect: this.globalContext!.expect,
setTimeout: this.globalContext!.setTimeout,
clearTimeout: this.globalContext!.clearTimeout,
it: this._options.env.it,
beforeEach: this._options.env.beforeEach,
afterEach: this._options.env.afterEach,
jest,
nsObj: (m: Object) => {
Object.defineProperty(m, Symbol.toStringTag, {
value: "Module"
});
return m;
}
};
if (this._options.stats) {
baseModuleScope["__STATS__"] = this._options.stats;
}
return baseModuleScope;
}

protected createModuleScope(
protected abstract createGlobalContext(): IBasicGlobalContext;
protected abstract createBaseModuleScope(): IBasicModuleScope;
protected abstract createModuleScope(
requireFn: TRunnerRequirer,
m: { exports: unknown },
m: TModuleObject,
file: TBasicRunnerFile
): IBasicModuleScope {
return {
...this.baseModuleScope!,
require: requireFn.bind(null, path.dirname(file.path)),
module: m,
exports: m.exports,
__dirname: path.dirname(file.path),
__filename: file.path,
_globalAssign: { expect },
define
};
}
): IBasicModuleScope;

protected getFile(
modulePath: string[] | string,
Expand Down Expand Up @@ -149,80 +114,15 @@ export class BasicRunner<T extends ECompilerType = ECompilerType.Rspack>
}
}

protected createMissRequirer(): TRunnerRequirer {
return (currentDirectory, modulePath, context = {}) => {
const modulePathStr = modulePath as string;
const modules = this._options.testConfig.modules;
if (modules && modulePathStr in modules) {
return modules[modulePathStr];
} else {
return require(
modulePathStr.startsWith("node:")
? modulePathStr.slice(5)
: modulePathStr
);
}
};
}

protected createCjsRequirer(): TRunnerRequirer {
const requireCache = Object.create(null);

return (currentDirectory, modulePath, context = {}) => {
let file = context["file"] || this.getFile(modulePath, currentDirectory);
if (!file) {
return this.requirers.get("miss")!(currentDirectory, modulePath);
}

if (file.path in requireCache) {
return requireCache[file.path].exports;
}

const m = {
exports: {}
};
requireCache[file.path] = m;
const currentModuleScope = this.createModuleScope(
this.getRequire(),
m,
file
);

if (this._options.testConfig.moduleScope) {
this._options.testConfig.moduleScope(currentModuleScope);
}

if (!this._options.runInNewContext) {
file.content = `Object.assign(global, _globalAssign);\n ${file.content}`;
}
const args = Object.keys(currentModuleScope);
const argValues = args.map(arg => currentModuleScope[arg]);
const code = `(function(${args.join(", ")}) {
${file.content}
})`;

this.preExecute(code, file);
const fn = this._options.runInNewContext
? vm.runInNewContext(code, this.globalContext!, file.path)
: vm.runInThisContext(code, file.path);

fn.call(
this._options.testConfig.nonEsmThis
? this._options.testConfig.nonEsmThis(modulePath)
: m.exports,
...argValues
);

this.postExecute(m, file);
return m.exports;
};
}

protected preExecute(code: string, file: TBasicRunnerFile) {}
protected postExecute(m: Object, file: TBasicRunnerFile) {}

protected createRunner() {
this.requirers.set("miss", this.createMissRequirer());
this.requirers.set("entry", this.createCjsRequirer());
this.requirers.set(
"entry",
(currentDirectory, modulePath, context = {}) => {
throw new Error("Not implement");
}
);
}
}
Loading

2 comments on commit 0f0ecdf

@rspack-bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Benchmark detail: Open

Name Base (2024-04-19 25c7594) Current Change
10000_development-mode + exec 2.66 s ± 20 ms 2.68 s ± 31 ms +0.48 %
10000_development-mode_hmr + exec 681 ms ± 6.4 ms 699 ms ± 20 ms +2.58 %
10000_production-mode + exec 2.47 s ± 19 ms 2.5 s ± 53 ms +1.56 %
arco-pro_development-mode + exec 2.51 s ± 55 ms 2.49 s ± 104 ms -0.91 %
arco-pro_development-mode_hmr + exec 430 ms ± 2.1 ms 430 ms ± 2.9 ms +0.20 %
arco-pro_development-mode_hmr_intercept-plugin + exec 440 ms ± 3.4 ms 441 ms ± 3.2 ms +0.23 %
arco-pro_development-mode_intercept-plugin + exec 3.2 s ± 64 ms 3.2 s ± 48 ms +0.02 %
arco-pro_production-mode + exec 3.94 s ± 84 ms 4.01 s ± 71 ms +1.66 %
arco-pro_production-mode_intercept-plugin + exec 4.7 s ± 79 ms 4.76 s ± 80 ms +1.30 %
threejs_development-mode_10x + exec 2.05 s ± 15 ms 2.05 s ± 21 ms +0.12 %
threejs_development-mode_10x_hmr + exec 746 ms ± 16 ms 753 ms ± 15 ms +0.96 %
threejs_production-mode_10x + exec 5.16 s ± 43 ms 5.13 s ± 28 ms -0.56 %

@rspack-bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Ran ecosystem CI: Open

suite result
modernjs, self-hosted, Linux, ci ❌ failure
_selftest, ubuntu-latest ✅ success
nx, ubuntu-latest ✅ success
rspress, ubuntu-latest ✅ success
rsbuild, ubuntu-latest ✅ success
compat, ubuntu-latest ✅ success
examples, ubuntu-latest ✅ success

Please sign in to comment.