From ab97f8fb936295728ffa6e59610af3bddb27599a Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Fri, 27 Oct 2023 10:10:12 +0200 Subject: [PATCH] feat: Refactor module scope vars & export `mirror` & `takeFullSnapshot` directly (#113) Currently, we use these from `record.xx`, which is not ideal thinking about treeshaking and lazy loading. This moves these to be proper exports so we can use the in Replay without having to import `record` and everything that comes with it (=everything). --- packages/rrweb/src/index.ts | 13 ++------- packages/rrweb/src/record/index.ts | 47 +++++++++++++++++------------- packages/rrweb/test/record.test.ts | 10 +++---- 3 files changed, 33 insertions(+), 37 deletions(-) diff --git a/packages/rrweb/src/index.ts b/packages/rrweb/src/index.ts index 047fe83948..b30f7a1b4c 100644 --- a/packages/rrweb/src/index.ts +++ b/packages/rrweb/src/index.ts @@ -1,6 +1,5 @@ import record from './record'; import { Replayer } from './replay'; -import { _mirror } from './utils'; import * as utils from './utils'; export { @@ -19,14 +18,6 @@ export type { export type { recordOptions } from './types'; -const { addCustomEvent } = record; -const { freezePage } = record; +export { record, Replayer, utils }; -export { - record, - addCustomEvent, - freezePage, - Replayer, - _mirror as mirror, - utils, -}; +export { takeFullSnapshot, mirror, freezePage, addCustomEvent } from './record'; diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index 4a9b4064fc..c5fc3ed580 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -65,13 +65,14 @@ declare global { const __RRWEB_EXCLUDE_IFRAME__: boolean; } -let wrappedEmit!: (e: eventWithTime, isCheckout?: boolean) => void; +// These are stored in module scope because we access them in other exported methods +let _wrappedEmit: + | undefined + | ((e: eventWithTime, isCheckout?: boolean) => void); +let _takeFullSnapshot: undefined | ((isCheckout?: boolean) => void); -let takeFullSnapshot!: (isCheckout?: boolean) => void; -let canvasManager: CanvasManagerInterface; -let recording = false; +export const mirror = createMirror(); -const mirror = createMirror(); function record( options: recordOptions = {}, ): listenerHandler | undefined { @@ -206,7 +207,7 @@ function record( } return e as unknown as T; }; - wrappedEmit = (e: eventWithTime, isCheckout?: boolean) => { + const wrappedEmit = (e: eventWithTime, isCheckout?: boolean) => { if ( mutationBuffers[0]?.isFrozen() && e.type !== EventType.FullSnapshot && @@ -255,6 +256,7 @@ function record( } } }; + _wrappedEmit = wrappedEmit; const wrappedMutationEmit = (m: mutationCallbackParam) => { wrappedEmit( @@ -322,7 +324,7 @@ function record( const processedNodeManager = new ProcessedNodeManager(); - canvasManager = + const canvasManager: CanvasManagerInterface = typeof __RRWEB_EXCLUDE_CANVAS__ === 'boolean' && __RRWEB_EXCLUDE_CANVAS__ ? new CanvasManagerNoop() : new CanvasManager({ @@ -373,7 +375,7 @@ function record( mirror, }); - takeFullSnapshot = (isCheckout = false) => { + const takeFullSnapshot = (isCheckout = false) => { wrappedEmit( wrapEvent({ type: EventType.Meta, @@ -455,6 +457,7 @@ function record( mirror.getId(document), ); }; + _takeFullSnapshot = takeFullSnapshot; try { const handlers: listenerHandler[] = []; @@ -617,7 +620,6 @@ function record( const init = () => { takeFullSnapshot(); handlers.push(observe(document)); - recording = true; }; if ( document.readyState === 'interactive' || @@ -655,7 +657,7 @@ function record( return () => { handlers.forEach((h) => h()); processedNodeManager.destroy(); - recording = false; + _takeFullSnapshot = undefined; unregisterErrorHandler(); }; } catch (error) { @@ -664,11 +666,11 @@ function record( } } -record.addCustomEvent = (tag: string, payload: T) => { - if (!recording) { +export function addCustomEvent(tag: string, payload: T) { + if (!_wrappedEmit) { throw new Error('please add custom event after start recording'); } - wrappedEmit( + _wrappedEmit( wrapEvent({ type: EventType.Custom, data: { @@ -677,19 +679,24 @@ record.addCustomEvent = (tag: string, payload: T) => { }, }), ); -}; +} -record.freezePage = () => { +export function freezePage() { mutationBuffers.forEach((buf) => buf.freeze()); -}; +} -record.takeFullSnapshot = (isCheckout?: boolean) => { - if (!recording) { +export function takeFullSnapshot(isCheckout?: boolean) { + if (!_takeFullSnapshot) { throw new Error('please take full snapshot after start recording'); } - takeFullSnapshot(isCheckout); -}; + _takeFullSnapshot(isCheckout); +} + +// record.addCustomEvent is removed because Sentry Session Replay does not use it +// record.freezePage is removed because Sentry Session Replay does not use it +// For backwards compatibility - we can eventually remove this when we migrated to using the exported `mirror` & `takeFullSnapshot` record.mirror = mirror; +record.takeFullSnapshot = takeFullSnapshot; export default record; diff --git a/packages/rrweb/test/record.test.ts b/packages/rrweb/test/record.test.ts index ce3cf9e3b4..698fb2312c 100644 --- a/packages/rrweb/test/record.test.ts +++ b/packages/rrweb/test/record.test.ts @@ -29,12 +29,10 @@ interface ISuite { interface IWindow extends Window { rrweb: { - record: (( + record: ( options: recordOptions, - ) => listenerHandler | undefined) & { - takeFullSnapshot: (isCheckout?: boolean | undefined) => void; - }; - + ) => listenerHandler | undefined; + takeFullSnapshot: (isCheckout?: boolean | undefined) => void; freezePage(): void; addCustomEvent(tag: string, payload: T): void; }; @@ -611,7 +609,7 @@ describe('record', function (this: ISuite) { setTimeout(() => { // When a full snapshot is checked out manually, all adoptedStylesheets should also be captured. - rrweb.record.takeFullSnapshot(true); + rrweb.takeFullSnapshot(true); resolve(undefined); }, 10); });