Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: cache nested loops #1677

Merged
merged 1 commit into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 52 additions & 16 deletions patches/@[email protected]
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
diff --git a/dist/record.js b/dist/record.js
index d9c57625633fb87da27fc5948c1b15a7c4e4caa5..472c9f1359baf685279614c47426648102479efb 100644
index d9c57625633fb87da27fc5948c1b15a7c4e4caa5..ff1783439be7fdd6ad947b805324e4c7f8f70851 100644
--- a/dist/record.js
+++ b/dist/record.js
@@ -68,10 +68,10 @@ function getUntaintedPrototype$1(key) {
Expand All @@ -25,12 +25,25 @@ index d9c57625633fb87da27fc5948c1b15a7c4e4caa5..472c9f1359baf685279614c474266481
class Mirror {
constructor() {
__publicField$1(this, "idNodeMap", /* @__PURE__ */ new Map());
@@ -426,23 +429,81 @@ function absolutifyURLs(cssText, href) {
function normalizeCssString(cssText) {
return cssText.replace(/(\/\*[^*]*\*\/)|[\s;]/g, "");
@@ -423,26 +426,96 @@ function absolutifyURLs(cssText, href) {
}
);
}
+// these operations can be slow, let's cache them
+const normalizationCache = new Map();
+const splitCache = new Map();
function normalizeCssString(cssText) {
- return cssText.replace(/(\/\*[^*]*\*\/)|[\s;]/g, "");
-}
-function splitCssText(cssText, style) {
- const childNodes2 = Array.from(style.childNodes);
+ if (!normalizationCache.has(cssText)) {
+ const normalized = cssText.replace(/(\/\*[^*]*\*\/)|[\s;]/g, '');
+ normalizationCache.set(cssText, normalized);
+ }
+ // we know that the value is in the cache now
+ return normalizationCache.get(cssText);
+}
+/**
+ * Maps the output of stringifyStylesheet to individual text nodes of a <style> element
+ * which occurs when javascript is used to append to the style element
Expand All @@ -51,6 +64,10 @@ index d9c57625633fb87da27fc5948c1b15a7c4e4caa5..472c9f1359baf685279614c474266481
- for (let j = 3; j < textContentNorm.length; j++) {
+ let iterLimit = 0;
+ if (childNodes.length > 1 && cssText && typeof cssText === 'string') {
+ if (splitCache.has(cssText)) {
+ // we know there's a result
+ return splitCache.get(cssText)
+ }
+ let cssTextNorm = normalizeCssString(cssText);
+ const normFactor = cssTextNorm.length / cssText.length;
+ for (let i = 1; i < childNodes.length; i++) {
Expand Down Expand Up @@ -119,16 +136,17 @@ index d9c57625633fb87da27fc5948c1b15a7c4e4caa5..472c9f1359baf685279614c474266481
}
}
break;
@@ -451,7 +512,7 @@ function splitCssText(cssText, style) {
@@ -451,7 +524,8 @@ function splitCssText(cssText, style) {
}
}
}
- splits.push(cssText);
+ splits.push(cssText); // either the full thing if no splits were found, or the last split
+ splitCache.set(cssText, splits);
return splits;
}
function markCssSplits(cssText, style) {
@@ -841,9 +902,14 @@ function serializeElementNode(n2, options) {
@@ -841,9 +915,14 @@ function serializeElementNode(n2, options) {
}
}
if (tagName === "link" && inlineStylesheet) {
Expand All @@ -146,7 +164,7 @@ index d9c57625633fb87da27fc5948c1b15a7c4e4caa5..472c9f1359baf685279614c474266481
let cssText = null;
if (stylesheet) {
cssText = stringifyStylesheet(stylesheet);
@@ -889,7 +955,15 @@ function serializeElementNode(n2, options) {
@@ -889,7 +968,15 @@ function serializeElementNode(n2, options) {
}
}
if (tagName === "dialog" && n2.open) {
Expand All @@ -163,7 +181,7 @@ index d9c57625633fb87da27fc5948c1b15a7c4e4caa5..472c9f1359baf685279614c474266481
}
if (tagName === "canvas" && recordCanvas) {
if (n2.__context === "2d") {
@@ -1112,7344 +1186,249 @@ function serializeNodeWithId(n2, options) {
@@ -1112,7344 +1199,249 @@ function serializeNodeWithId(n2, options) {
return null;
}
if (onSerialize) {
Expand Down Expand Up @@ -7736,7 +7754,7 @@ index d9c57625633fb87da27fc5948c1b15a7c4e4caa5..472c9f1359baf685279614c474266481
class BaseRRNode {
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
constructor(..._args) {
@@ -11451,11 +4430,19 @@ class CanvasManager {
@@ -11451,11 +4443,19 @@ class CanvasManager {
let rafId;
const getCanvas = () => {
const matchedCanvas = [];
Expand All @@ -7761,7 +7779,7 @@ index d9c57625633fb87da27fc5948c1b15a7c4e4caa5..472c9f1359baf685279614c474266481
return matchedCanvas;
};
const takeCanvasSnapshots = (timestamp) => {
@@ -11476,13 +4463,20 @@ class CanvasManager {
@@ -11476,13 +4476,20 @@ class CanvasManager {
context.clear(context.COLOR_BUFFER_BIT);
}
}
Expand All @@ -7786,22 +7804,35 @@ index d9c57625633fb87da27fc5948c1b15a7c4e4caa5..472c9f1359baf685279614c474266481
},
[bitmap]
diff --git a/dist/record.umd.cjs b/dist/record.umd.cjs
index 902c5eca13b2c3e69af25afa682d2e7300372bfc..4da3d8a172b53199f91880229ec5d665bb7d0fd4 100644
index 902c5eca13b2c3e69af25afa682d2e7300372bfc..23fc9fffa6b815da448311dd31cec52c3abeb44f 100644
--- a/dist/record.umd.cjs
+++ b/dist/record.umd.cjs
@@ -473,23 +473,81 @@ function absolutifyURLs(cssText, href) {
function normalizeCssString(cssText) {
return cssText.replace(/(\/\*[^*]*\*\/)|[\s;]/g, "");
@@ -470,26 +470,96 @@ function absolutifyURLs(cssText, href) {
}
);
}
+// these operations can be slow, let's cache them
+const normalizationCache = new Map();
+const splitCache = new Map();
function normalizeCssString(cssText) {
- return cssText.replace(/(\/\*[^*]*\*\/)|[\s;]/g, "");
-}
-function splitCssText(cssText, style) {
- const childNodes2 = Array.from(style.childNodes);
+ if (!normalizationCache.has(cssText)) {
+ const normalized = cssText.replace(/(\/\*[^*]*\*\/)|[\s;]/g, '');
+ normalizationCache.set(cssText, normalized);
+ }
+ // we know that the value is in the cache now
+ return normalizationCache.get(cssText);
+}
+/**
+ * Maps the output of stringifyStylesheet to individual text nodes of a <style> element
+ * which occurs when javascript is used to append to the style element
+ * and may also occur when browsers opt to break up large text nodes
+ * performance needs to be considered, see e.g. #1603
+ */
+export function splitCssText(
+function splitCssText(
+ cssText,
+ style,
+) {
Expand All @@ -7815,6 +7846,10 @@ index 902c5eca13b2c3e69af25afa682d2e7300372bfc..4da3d8a172b53199f91880229ec5d665
- for (let j = 3; j < textContentNorm.length; j++) {
+ let iterLimit = 0;
+ if (childNodes.length > 1 && cssText && typeof cssText === 'string') {
+ if (splitCache.has(cssText)) {
+ // we know there's a result
+ return splitCache.get(cssText)
+ }
+ let cssTextNorm = normalizeCssString(cssText);
+ const normFactor = cssTextNorm.length / cssText.length;
+ for (let i = 1; i < childNodes.length; i++) {
Expand Down Expand Up @@ -7883,12 +7918,13 @@ index 902c5eca13b2c3e69af25afa682d2e7300372bfc..4da3d8a172b53199f91880229ec5d665
}
}
break;
@@ -498,7 +556,7 @@ function splitCssText(cssText, style) {
@@ -498,7 +568,8 @@ function splitCssText(cssText, style) {
}
}
}
- splits.push(cssText);
+ splits.push(cssText); // either the full thing if no splits were found, or the last split
+ splitCache.set(cssText, splits);
return splits;
}
function markCssSplits(cssText, style) {
6 changes: 3 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading