72 lines
2.3 KiB
TypeScript
72 lines
2.3 KiB
TypeScript
import { readFileSync } from "node:fs";
|
|
import { createRequire } from "node:module";
|
|
import path from "node:path";
|
|
import vm from "node:vm";
|
|
import * as crypto from "node:crypto";
|
|
import ts from "typescript";
|
|
const root = path.resolve(__dirname, "../..");
|
|
export function serverLoader(mocks: Record<string, unknown>, env: Record<string, string> = {}) {
|
|
const modules = new Map<string, { exports: any }>();
|
|
const runtimeGlobal = {};
|
|
const load = (relative: string): any => {
|
|
const filename = path.resolve(root, relative);
|
|
if (modules.has(filename)) return modules.get(filename)!.exports;
|
|
const module = { exports: {} };
|
|
modules.set(filename, module);
|
|
const source = ts.transpileModule(readFileSync(filename, "utf8"), {
|
|
fileName: filename,
|
|
compilerOptions: {
|
|
jsx: ts.JsxEmit.ReactJSX,
|
|
module: ts.ModuleKind.CommonJS,
|
|
target: ts.ScriptTarget.ES2022,
|
|
esModuleInterop: true,
|
|
},
|
|
}).outputText;
|
|
const nativeRequire = createRequire(filename);
|
|
const requireMock = (name: string) => {
|
|
if (name === "server-only") return {};
|
|
if (Object.prototype.hasOwnProperty.call(mocks, name)) return mocks[name];
|
|
if (name.startsWith("@/")) return load(`${name.slice(2)}.ts`);
|
|
if (name.startsWith("."))
|
|
return load(path.relative(root, path.resolve(path.dirname(filename), `${name}.ts`)));
|
|
return nativeRequire(name);
|
|
};
|
|
const context = vm.createContext({
|
|
module,
|
|
exports: module.exports,
|
|
require: requireMock,
|
|
globalThis: runtimeGlobal,
|
|
process: { env, pid: process.pid, kill: process.kill },
|
|
Buffer,
|
|
URL,
|
|
URLSearchParams,
|
|
DOMException,
|
|
Response,
|
|
crypto,
|
|
setTimeout: mocks.setTimeout || setTimeout,
|
|
clearTimeout,
|
|
TextEncoder,
|
|
sessionStorage: mocks.sessionStorage,
|
|
setImmediate,
|
|
AbortSignal,
|
|
AbortController,
|
|
ReadableStream,
|
|
Headers,
|
|
Blob,
|
|
Request,
|
|
Date: mocks.Date || Date,
|
|
document: mocks.document,
|
|
window: mocks.window,
|
|
console: { log() {}, warn() {}, error() {}, info() {} },
|
|
fetch:
|
|
mocks.fetch ||
|
|
(() => {
|
|
throw new Error("Unexpected network call");
|
|
}),
|
|
});
|
|
new vm.Script(source, { filename }).runInContext(context);
|
|
return module.exports;
|
|
};
|
|
return load;
|
|
}
|