Files
rrweb/packages/rrdom-nodejs/src/polyfill.ts
2023-02-11 10:19:52 +01:00

93 lines
2.9 KiB
TypeScript

import { RRDocument, RRNode } from './document-nodejs';
/**
* Polyfill the performance for nodejs.
* Note: The performance api is available through the global object from nodejs v16.0.0.
* https://github.com/nodejs/node/pull/37970
*/
export function polyfillPerformance() {
if (typeof window !== 'undefined' || 'performance' in global) return;
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-var-requires
const performance = require('perf_hooks').performance;
((global as Window & typeof globalThis).performance as unknown) = performance;
}
/**
* Polyfill requestAnimationFrame and cancelAnimationFrame for nodejs.
*/
export function polyfillRAF() {
if (typeof window !== 'undefined' || 'requestAnimationFrame' in global)
return;
const FPS = 60,
INTERVAL = 1_000 / FPS;
let timeoutHandle: NodeJS.Timeout | null = null,
rafCount = 0,
requests = Object.create(null) as Record<string, (time: number) => void>;
function onFrameTimer() {
const currentRequests = requests;
requests = Object.create(null) as Record<string, (time: number) => void>;
timeoutHandle = null;
Object.keys(currentRequests).forEach(function (id) {
const request = currentRequests[id];
if (request) request(Date.now());
});
}
function requestAnimationFrame(callback: (timestamp: number) => void) {
const cbHandle = ++rafCount;
requests[cbHandle] = callback;
if (timeoutHandle === null)
timeoutHandle = setTimeout(onFrameTimer, INTERVAL);
return cbHandle;
}
function cancelAnimationFrame(handleId: number) {
delete requests[handleId];
if (Object.keys(requests).length === 0 && timeoutHandle !== null) {
clearTimeout(timeoutHandle);
timeoutHandle = null;
}
}
(global as Window & typeof globalThis).requestAnimationFrame =
requestAnimationFrame;
(global as Window & typeof globalThis).cancelAnimationFrame =
cancelAnimationFrame;
}
/**
* Try to polyfill Event type.
* The implementation of Event so far is empty because rrweb doesn't strongly depend on it in nodejs mode.
* Note: The Event class is available through the global object from nodejs v15.0.0.
*/
export function polyfillEvent() {
if (typeof Event !== 'undefined') return;
(global.Event as unknown) = function () {
//
};
}
/**
* Polyfill Node type with RRNode for nodejs.
*/
export function polyfillNode() {
if (typeof Node !== 'undefined') return;
(global.Node as unknown) = RRNode;
}
/**
* Polyfill document object with RRDocument for nodejs.
*/
export function polyfillDocument() {
if (typeof document !== 'undefined') return;
const rrdom = new RRDocument();
(() => {
rrdom.appendChild(rrdom.createElement('html'));
rrdom.documentElement!.appendChild(rrdom.createElement('head'));
rrdom.documentElement!.appendChild(rrdom.createElement('body'));
})();
global.document = rrdom as unknown as Document;
}