From ec8473b5e4a8a5f0f2b516317eeb3271d06ac1a6 Mon Sep 17 00:00:00 2001 From: Yanzhen Yu Date: Wed, 1 Apr 2026 12:00:00 +0800 Subject: [PATCH] ignore prefetch and preload script links related to: 1. https://github.com/rrweb-io/rrweb/issues/52 2. https://github.com/rrweb-io/rrweb/issues/297 3. https://github.com/rrweb-io/rrweb/issues/597 --- src/rebuild.ts | 23 +++++++++++++++++++---- src/snapshot.ts | 13 ++++++++++--- test/__snapshots__/integration.ts.snap | 11 +++++++++++ test/html/preload.html | 11 +++++++++++ 4 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 test/html/preload.html diff --git a/src/rebuild.ts b/src/rebuild.ts index c9322536..a0778a0c 100644 --- a/src/rebuild.ts +++ b/src/rebuild.ts @@ -58,8 +58,8 @@ function getTagName(n: elementNode): string { } // based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping -function escapeRegExp(string: string) { - return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +function escapeRegExp(str: string) { + return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string } const HOVER_SELECTOR = /([^\\]):hover/; @@ -84,7 +84,9 @@ export function addHoverClass(cssText: string): string { } }); - if (selectors.length === 0) return cssText; + if (selectors.length === 0) { + return cssText; + } const selectorMatcher = new RegExp( selectors @@ -170,12 +172,25 @@ function buildNode( } else if ( tagName === 'meta' && n.attributes['http-equiv'] === 'Content-Security-Policy' && - name == 'content' + name === 'content' ) { // If CSP contains style-src and inline-style is disabled, there will be an error "Refused to apply inline style because it violates the following Content Security Policy directive: style-src '*'". // And the function insertStyleRules in rrweb replayer will throw an error "Uncaught TypeError: Cannot read property 'insertRule' of null". node.setAttribute('csp-content', value); continue; + } else if ( + tagName === 'link' && + n.attributes.rel === 'preload' && + n.attributes.as === 'script' + ) { + // ignore + } else if ( + tagName === 'link' && + n.attributes.rel === 'prefetch' && + typeof n.attributes.href === 'string' && + n.attributes.href.endsWith('.js') + ) { + // ignore } else { node.setAttribute(name, value); } diff --git a/src/snapshot.ts b/src/snapshot.ts index f412cbfd..49c2e13c 100644 --- a/src/snapshot.ts +++ b/src/snapshot.ts @@ -369,8 +369,8 @@ function serializeNode( } = options; // Only record root id when document object is not the base document let rootId: number | undefined; - if ((doc as unknown as INode).__sn) { - const docId = (doc as unknown as INode).__sn.id; + if (((doc as unknown) as INode).__sn) { + const docId = ((doc as unknown) as INode).__sn.id; rootId = docId === 1 ? undefined : docId; } switch (n.nodeType) { @@ -568,10 +568,17 @@ function slimDOMExcluded( } else if (sn.type === NodeType.Element) { if ( slimDOMOptions.script && + // script tag (sn.tagName === 'script' || + // preload link (sn.tagName === 'link' && sn.attributes.rel === 'preload' && - sn.attributes.as === 'script')) + sn.attributes.as === 'script') || + // prefetch link + (sn.tagName === 'link' && + sn.attributes.rel === 'prefetch' && + typeof sn.attributes.href === 'string' && + sn.attributes.href.endsWith('.js'))) ) { return true; } else if ( diff --git a/test/__snapshots__/integration.ts.snap b/test/__snapshots__/integration.ts.snap index cd30b31c..e087f79b 100644 --- a/test/__snapshots__/integration.ts.snap +++ b/test/__snapshots__/integration.ts.snap @@ -226,6 +226,17 @@ exports[`[html file]: picture.html 1`] = ` " `; +exports[`[html file]: preload.html 1`] = ` +" + + + Document + + + + " +`; + exports[`[html file]: shadow-dom.html 1`] = ` " diff --git a/test/html/preload.html b/test/html/preload.html new file mode 100644 index 00000000..32e84a26 --- /dev/null +++ b/test/html/preload.html @@ -0,0 +1,11 @@ + + + + + + Document + + + + +