jsdom-context-require
Version:
Allows you to require files in a jsdom window context.
135 lines (134 loc) • 4.49 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.createBrowser = void 0;
const jsdom_1 = require("jsdom");
const context_require_1 = require("context-require");
const resolve_1 = require("./resolve");
/**
* Creates a custom Module object which runs all required scripts
* in a new jsdom instance.
*/
function createBrowser({ dir, html, extensions, beforeParse, ...jsdomOptions }) {
const browser = new jsdom_1.JSDOM("", {
runScripts: "dangerously",
pretendToBeVisual: true,
...jsdomOptions,
});
const { window } = browser;
const resolveExtensions = extensions
? [
...new Set([
...Object.keys(extensions),
...Object.keys(require.extensions).reverse(),
]),
]
: Object.keys(require.extensions).reverse();
// Pass through istanbul coverage.
if ("__coverage__" in globalThis) {
window.__coverage__ = globalThis.__coverage__;
}
// Expose some globals commonly shimmed by bundlers.
window.global = window;
window.Buffer = Buffer;
window.process = { ...process, browser: true };
window.setImmediate || (window.setImmediate = (() => {
const msg = `${Math.random()}`;
let queue = [];
let offset = 0;
window.addEventListener("message", (ev) => {
if (ev.data === msg) {
const cbs = queue;
offset += cbs.length;
queue = [];
for (const cb of cbs) {
if (cb) {
cb();
}
}
}
});
window.clearImmediate = function clearImmediate(id) {
const index = id - offset;
if (index >= 0) {
queue[index] = undefined;
}
};
return function setImmediate(cb, ...args) {
const index = queue.push(args.length ? () => cb(...args) : cb) - 1;
if (!index) {
window.postMessage(msg, "*");
}
return index + offset;
};
})());
browser.require = (0, context_require_1.default)({
dir,
context: browser,
extensions,
resolve(basedir, req, { filename }) {
const resolved = (0, resolve_1.resolve)(req, basedir, resolveExtensions);
if (resolved)
return resolved;
throw new Error(`Cannot find module '${req}' from '${filename}'`);
},
});
browser.yield = () => new Promise((resolve) => window.setImmediate(resolve));
browser.act = async (fn) => {
let errors;
let errorStage = 0;
window.addEventListener("error", handleError);
window.addEventListener("unhandledrejection", handleError);
try {
const result = await fn?.();
await browser.yield();
return result;
}
catch (err) {
trackError(err);
}
finally {
window.removeEventListener("error", handleError);
window.removeEventListener("unhandledrejection", handleError);
switch (errorStage) {
case 0:
break;
case 1:
throw errors;
default:
throw new window.AggregateError(errors);
}
}
function handleError(ev) {
if (!ev.defaultPrevented) {
let error = "error" in ev ? ev.error : ev.reason || ev;
if ("detail" in error)
error = error.detail;
ev.preventDefault();
}
}
function trackError(err) {
switch (errorStage) {
case 0:
errors = err;
errorStage = 1;
break;
case 1:
if (err !== errors) {
errors = new Set([errors, err]);
errorStage = 2;
}
break;
default:
errors.add(err);
break;
}
}
};
if (beforeParse) {
beforeParse(window, browser);
}
window.document.open();
window.document.write(html || "<!DOCTYPE html><html><head></head><body></body></html>");
return browser;
}
exports.createBrowser = createBrowser;