@zumer/snapdom 1.9.5 → 1.9.6-dev.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/snapdom.js +114 -109
- package/dist/snapdom.min.js +7 -7
- package/dist/snapdom.min.mjs +12 -12
- package/dist/snapdom.mjs +113 -108
- package/package.json +1 -1
package/dist/snapdom.mjs
CHANGED
|
@@ -1,20 +1,36 @@
|
|
|
1
1
|
|
|
2
2
|
/*
|
|
3
3
|
* snapdom
|
|
4
|
-
* v.1.9.
|
|
4
|
+
* v.1.9.6-dev.0
|
|
5
5
|
* Author Juan Martin Muda
|
|
6
6
|
* License MIT
|
|
7
7
|
**/
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
// src/core/cache.js
|
|
11
|
-
var
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
var cache = {
|
|
12
|
+
image: /* @__PURE__ */ new Map(),
|
|
13
|
+
background: /* @__PURE__ */ new Map(),
|
|
14
|
+
resource: /* @__PURE__ */ new Map(),
|
|
15
|
+
defaultStyle: /* @__PURE__ */ new Map(),
|
|
16
|
+
baseStyle: /* @__PURE__ */ new Map(),
|
|
17
|
+
computedStyle: /* @__PURE__ */ new WeakMap(),
|
|
18
|
+
font: /* @__PURE__ */ new Set(),
|
|
19
|
+
snapshot: /* @__PURE__ */ new WeakMap(),
|
|
20
|
+
snapshotKey: /* @__PURE__ */ new Map(),
|
|
21
|
+
preStyleMap: /* @__PURE__ */ new Map(),
|
|
22
|
+
preStyle: /* @__PURE__ */ new WeakMap(),
|
|
23
|
+
preNodeMap: /* @__PURE__ */ new Map(),
|
|
24
|
+
reset: resetCache
|
|
25
|
+
};
|
|
26
|
+
function resetCache() {
|
|
27
|
+
cache.computedStyle = /* @__PURE__ */ new WeakMap();
|
|
28
|
+
cache.snapshot = /* @__PURE__ */ new WeakMap();
|
|
29
|
+
cache.snapshotKey.clear();
|
|
30
|
+
cache.preStyleMap.clear();
|
|
31
|
+
cache.preStyle = /* @__PURE__ */ new WeakMap();
|
|
32
|
+
cache.preNodeMap.clear();
|
|
33
|
+
}
|
|
18
34
|
|
|
19
35
|
// src/utils/cssTools.js
|
|
20
36
|
var commonTags = [
|
|
@@ -62,13 +78,13 @@ function precacheCommonTags() {
|
|
|
62
78
|
}
|
|
63
79
|
}
|
|
64
80
|
function getDefaultStyleForTag(tagName) {
|
|
65
|
-
if (
|
|
66
|
-
return
|
|
81
|
+
if (cache.defaultStyle.has(tagName)) {
|
|
82
|
+
return cache.defaultStyle.get(tagName);
|
|
67
83
|
}
|
|
68
84
|
const skipTags = /* @__PURE__ */ new Set(["script", "style", "meta", "link", "noscript", "template"]);
|
|
69
85
|
if (skipTags.has(tagName)) {
|
|
70
86
|
const empty = {};
|
|
71
|
-
|
|
87
|
+
cache.defaultStyle.set(tagName, empty);
|
|
72
88
|
return empty;
|
|
73
89
|
}
|
|
74
90
|
let sandbox = document.getElementById("snapdom-sandbox");
|
|
@@ -92,7 +108,7 @@ function getDefaultStyleForTag(tagName) {
|
|
|
92
108
|
defaults[prop] = styles.getPropertyValue(prop);
|
|
93
109
|
}
|
|
94
110
|
sandbox.removeChild(el);
|
|
95
|
-
|
|
111
|
+
cache.defaultStyle.set(tagName, defaults);
|
|
96
112
|
return defaults;
|
|
97
113
|
}
|
|
98
114
|
function getStyleKey(snapshot, tagName, compress = false) {
|
|
@@ -128,7 +144,7 @@ function collectUsedTagNames(root) {
|
|
|
128
144
|
function generateDedupedBaseCSS(usedTagNames) {
|
|
129
145
|
const groups = /* @__PURE__ */ new Map();
|
|
130
146
|
for (let tagName of usedTagNames) {
|
|
131
|
-
const styles =
|
|
147
|
+
const styles = cache.defaultStyle.get(tagName);
|
|
132
148
|
if (!styles) continue;
|
|
133
149
|
const key = Object.entries(styles).map(([k, v]) => `${k}:${v};`).sort().join("");
|
|
134
150
|
if (!groups.has(key)) {
|
|
@@ -143,8 +159,8 @@ function generateDedupedBaseCSS(usedTagNames) {
|
|
|
143
159
|
}
|
|
144
160
|
return css;
|
|
145
161
|
}
|
|
146
|
-
function generateCSSClasses(
|
|
147
|
-
const keySet = new Set(
|
|
162
|
+
function generateCSSClasses() {
|
|
163
|
+
const keySet = new Set(cache.preStyleMap.values());
|
|
148
164
|
const classMap = /* @__PURE__ */ new Map();
|
|
149
165
|
let counter = 1;
|
|
150
166
|
for (const key of keySet) {
|
|
@@ -155,16 +171,15 @@ function generateCSSClasses(styleMap) {
|
|
|
155
171
|
|
|
156
172
|
// src/utils/helpers.js
|
|
157
173
|
async function inlineSingleBackgroundEntry(entry, options = {}) {
|
|
158
|
-
const
|
|
159
|
-
const rawUrl = match?.[1];
|
|
174
|
+
const rawUrl = extractURL(entry);
|
|
160
175
|
const isGradient = /^((repeating-)?(linear|radial|conic)-gradient)\(/i.test(entry);
|
|
161
176
|
if (rawUrl) {
|
|
162
177
|
const encodedUrl = safeEncodeURI(rawUrl);
|
|
163
|
-
if (
|
|
164
|
-
return options.skipInline ? void 0 : `url(${
|
|
178
|
+
if (cache.background.has(encodedUrl)) {
|
|
179
|
+
return options.skipInline ? void 0 : `url(${cache.background.get(encodedUrl)})`;
|
|
165
180
|
} else {
|
|
166
181
|
const dataUrl = await fetchImage(encodedUrl, { useProxy: options.useProxy });
|
|
167
|
-
|
|
182
|
+
cache.background.set(encodedUrl, dataUrl);
|
|
168
183
|
return options.skipInline ? void 0 : `url("${dataUrl}")`;
|
|
169
184
|
}
|
|
170
185
|
}
|
|
@@ -185,10 +200,10 @@ function getStyle(el, pseudo = null) {
|
|
|
185
200
|
if (!(el instanceof Element)) {
|
|
186
201
|
return window.getComputedStyle(el, pseudo);
|
|
187
202
|
}
|
|
188
|
-
let map =
|
|
203
|
+
let map = cache.computedStyle.get(el);
|
|
189
204
|
if (!map) {
|
|
190
205
|
map = /* @__PURE__ */ new Map();
|
|
191
|
-
|
|
206
|
+
cache.computedStyle.set(el, map);
|
|
192
207
|
}
|
|
193
208
|
if (!map.has(pseudo)) {
|
|
194
209
|
const st = window.getComputedStyle(el, pseudo);
|
|
@@ -262,13 +277,13 @@ function fetchImage(src, { timeout = 3e3, useProxy = "" } = {}) {
|
|
|
262
277
|
}
|
|
263
278
|
const crossOriginValue = getCrossOriginMode(src);
|
|
264
279
|
console.log(`[SnapDOM - fetchImage] Start loading image: ${src} with crossOrigin=${crossOriginValue}`);
|
|
265
|
-
if (
|
|
280
|
+
if (cache.image.has(src)) {
|
|
266
281
|
console.log(`[SnapDOM - fetchImage] Cache hit for: ${src}`);
|
|
267
|
-
return Promise.resolve(
|
|
282
|
+
return Promise.resolve(cache.image.get(src));
|
|
268
283
|
}
|
|
269
284
|
const isDataURI = src.startsWith("data:image/");
|
|
270
285
|
if (isDataURI) {
|
|
271
|
-
|
|
286
|
+
cache.image.set(src, src);
|
|
272
287
|
return Promise.resolve(src);
|
|
273
288
|
}
|
|
274
289
|
const isSVG = /\.svg(\?.*)?$/i.test(src);
|
|
@@ -281,7 +296,7 @@ function fetchImage(src, { timeout = 3e3, useProxy = "" } = {}) {
|
|
|
281
296
|
});
|
|
282
297
|
const svgText = await response.text();
|
|
283
298
|
const encoded = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgText)}`;
|
|
284
|
-
|
|
299
|
+
cache.image.set(src, encoded);
|
|
285
300
|
return encoded;
|
|
286
301
|
} catch {
|
|
287
302
|
return fetchWithFallback(src);
|
|
@@ -305,12 +320,12 @@ function fetchImage(src, { timeout = 3e3, useProxy = "" } = {}) {
|
|
|
305
320
|
const ctx = canvas.getContext("2d");
|
|
306
321
|
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
307
322
|
const dataURL = canvas.toDataURL("image/png");
|
|
308
|
-
|
|
323
|
+
cache.image.set(src, dataURL);
|
|
309
324
|
resolve(dataURL);
|
|
310
325
|
} catch {
|
|
311
326
|
try {
|
|
312
327
|
const fallbackDataURL = await fetchWithFallback(src);
|
|
313
|
-
|
|
328
|
+
cache.image.set(src, fallbackDataURL);
|
|
314
329
|
resolve(fallbackDataURL);
|
|
315
330
|
} catch (e) {
|
|
316
331
|
reject(e);
|
|
@@ -322,7 +337,7 @@ function fetchImage(src, { timeout = 3e3, useProxy = "" } = {}) {
|
|
|
322
337
|
console.error(`[SnapDOM - fetchImage] Image failed to load: ${src}`);
|
|
323
338
|
try {
|
|
324
339
|
const fallbackDataURL = await fetchWithFallback(src);
|
|
325
|
-
|
|
340
|
+
cache.image.set(src, fallbackDataURL);
|
|
326
341
|
resolve(fallbackDataURL);
|
|
327
342
|
} catch (e) {
|
|
328
343
|
reject(e);
|
|
@@ -386,8 +401,6 @@ function splitBackgroundImage(bg) {
|
|
|
386
401
|
}
|
|
387
402
|
|
|
388
403
|
// src/modules/styles.js
|
|
389
|
-
var snapshotCache = /* @__PURE__ */ new WeakMap();
|
|
390
|
-
var snapshotKeyCache = /* @__PURE__ */ new Map();
|
|
391
404
|
function snapshotComputedStyleFull(style) {
|
|
392
405
|
const result = {};
|
|
393
406
|
for (let i = 0; i < style.length; i++) {
|
|
@@ -400,33 +413,33 @@ function snapshotComputedStyleFull(style) {
|
|
|
400
413
|
}
|
|
401
414
|
return result;
|
|
402
415
|
}
|
|
403
|
-
function inlineAllStyles(source, clone,
|
|
416
|
+
function inlineAllStyles(source, clone, compress) {
|
|
404
417
|
if (source.tagName === "STYLE") return;
|
|
405
|
-
if (!cache.has(source)) {
|
|
406
|
-
cache.set(source, getStyle(source));
|
|
418
|
+
if (!cache.preStyle.has(source)) {
|
|
419
|
+
cache.preStyle.set(source, getStyle(source));
|
|
407
420
|
}
|
|
408
|
-
const style = cache.get(source);
|
|
409
|
-
if (!
|
|
421
|
+
const style = cache.preStyle.get(source);
|
|
422
|
+
if (!cache.snapshot.has(source)) {
|
|
410
423
|
const snapshot2 = snapshotComputedStyleFull(style);
|
|
411
|
-
|
|
424
|
+
cache.snapshot.set(source, snapshot2);
|
|
412
425
|
}
|
|
413
|
-
const snapshot =
|
|
426
|
+
const snapshot = cache.snapshot.get(source);
|
|
414
427
|
const hash = Object.entries(snapshot).sort(([a], [b]) => a.localeCompare(b)).map(([prop, val]) => `${prop}:${val}`).join(";");
|
|
415
|
-
if (
|
|
416
|
-
|
|
428
|
+
if (cache.snapshotKey.has(hash)) {
|
|
429
|
+
cache.preStyleMap.set(clone, cache.snapshotKey.get(hash));
|
|
417
430
|
return;
|
|
418
431
|
}
|
|
419
432
|
const tagName = source.tagName?.toLowerCase() || "div";
|
|
420
433
|
const key = getStyleKey(snapshot, tagName, compress);
|
|
421
|
-
|
|
422
|
-
|
|
434
|
+
cache.snapshotKey.set(hash, key);
|
|
435
|
+
cache.preStyleMap.set(clone, key);
|
|
423
436
|
}
|
|
424
437
|
|
|
425
438
|
// src/core/clone.js
|
|
426
439
|
function isSlotElement(node) {
|
|
427
440
|
return node.nodeType === Node.ELEMENT_NODE && node.tagName === "SLOT";
|
|
428
441
|
}
|
|
429
|
-
function deepClone(node,
|
|
442
|
+
function deepClone(node, compress, options = {}, originalRoot) {
|
|
430
443
|
if (node.nodeType === Node.TEXT_NODE) {
|
|
431
444
|
return node.cloneNode(true);
|
|
432
445
|
}
|
|
@@ -471,8 +484,8 @@ function deepClone(node, styleMap, styleCache, nodeMap, compress, options = {},
|
|
|
471
484
|
}
|
|
472
485
|
if (node.getAttribute("data-capture") === "placeholder") {
|
|
473
486
|
const clone2 = node.cloneNode(false);
|
|
474
|
-
|
|
475
|
-
inlineAllStyles(node, clone2,
|
|
487
|
+
cache.preNodeMap.set(clone2, node);
|
|
488
|
+
inlineAllStyles(node, clone2, compress);
|
|
476
489
|
const placeholder = document.createElement("div");
|
|
477
490
|
placeholder.textContent = node.getAttribute("data-placeholder-text") || "";
|
|
478
491
|
placeholder.style.cssText = `color: #666;font-size: 12px;text-align: center;line-height: 1.4;padding: 0.5em;box-sizing: border-box;`;
|
|
@@ -491,7 +504,7 @@ function deepClone(node, styleMap, styleCache, nodeMap, compress, options = {},
|
|
|
491
504
|
return img;
|
|
492
505
|
}
|
|
493
506
|
const clone = node.cloneNode(false);
|
|
494
|
-
|
|
507
|
+
cache.preNodeMap.set(clone, node);
|
|
495
508
|
if (node instanceof HTMLInputElement) {
|
|
496
509
|
clone.value = node.value;
|
|
497
510
|
clone.setAttribute("value", node.value);
|
|
@@ -514,13 +527,13 @@ function deepClone(node, styleMap, styleCache, nodeMap, compress, options = {},
|
|
|
514
527
|
}
|
|
515
528
|
});
|
|
516
529
|
}
|
|
517
|
-
inlineAllStyles(node, clone,
|
|
530
|
+
inlineAllStyles(node, clone, compress);
|
|
518
531
|
if (isSlotElement(node)) {
|
|
519
532
|
const assigned = node.assignedNodes?.({ flatten: true }) || [];
|
|
520
533
|
const nodesToClone = assigned.length > 0 ? assigned : Array.from(node.childNodes);
|
|
521
534
|
const fragment = document.createDocumentFragment();
|
|
522
535
|
for (const child of nodesToClone) {
|
|
523
|
-
const clonedChild = deepClone(child,
|
|
536
|
+
const clonedChild = deepClone(child, compress, options, originalRoot || node);
|
|
524
537
|
if (clonedChild) fragment.appendChild(clonedChild);
|
|
525
538
|
}
|
|
526
539
|
return fragment;
|
|
@@ -528,13 +541,13 @@ function deepClone(node, styleMap, styleCache, nodeMap, compress, options = {},
|
|
|
528
541
|
} else {
|
|
529
542
|
const baseChildren = node.shadowRoot ? node.shadowRoot.childNodes : node.childNodes;
|
|
530
543
|
for (const child of baseChildren) {
|
|
531
|
-
const clonedChild = deepClone(child,
|
|
544
|
+
const clonedChild = deepClone(child, compress, options, originalRoot || node);
|
|
532
545
|
if (clonedChild) clone.appendChild(clonedChild);
|
|
533
546
|
}
|
|
534
547
|
if (node.shadowRoot && node.childNodes.length > 0 && !node.shadowRoot.querySelector("slot")) {
|
|
535
548
|
const lightDomContent = document.createDocumentFragment();
|
|
536
549
|
for (const child of node.childNodes) {
|
|
537
|
-
const clonedChild = deepClone(child,
|
|
550
|
+
const clonedChild = deepClone(child, compress, options, originalRoot || node);
|
|
538
551
|
if (clonedChild) lightDomContent.appendChild(clonedChild);
|
|
539
552
|
}
|
|
540
553
|
clone.appendChild(lightDomContent);
|
|
@@ -620,14 +633,14 @@ function injectLinkIfMissing(href) {
|
|
|
620
633
|
});
|
|
621
634
|
}
|
|
622
635
|
async function embedCustomFonts({ preCached = false } = {}) {
|
|
623
|
-
if (
|
|
636
|
+
if (cache.resource.has("fonts-embed-css")) {
|
|
624
637
|
if (preCached) {
|
|
625
638
|
const style = document.createElement("style");
|
|
626
639
|
style.setAttribute("data-snapdom", "embedFonts");
|
|
627
|
-
style.textContent =
|
|
640
|
+
style.textContent = cache.resource.get("fonts-embed-css");
|
|
628
641
|
document.head.appendChild(style);
|
|
629
642
|
}
|
|
630
|
-
return
|
|
643
|
+
return cache.resource.get("fonts-embed-css");
|
|
631
644
|
}
|
|
632
645
|
const importRegex = /@import\s+url\(["']?([^"')]+)["']?\)/g;
|
|
633
646
|
const styleImports = [];
|
|
@@ -660,11 +673,11 @@ async function embedCustomFonts({ preCached = false } = {}) {
|
|
|
660
673
|
url = new URL(url, link.href).href;
|
|
661
674
|
}
|
|
662
675
|
if (isIconFont(url)) return null;
|
|
663
|
-
if (
|
|
664
|
-
|
|
665
|
-
return { original: match[0], inlined: `url(${
|
|
676
|
+
if (cache.resource.has(url)) {
|
|
677
|
+
cache.font.add(url);
|
|
678
|
+
return { original: match[0], inlined: `url(${cache.resource.get(url)})` };
|
|
666
679
|
}
|
|
667
|
-
if (
|
|
680
|
+
if (cache.font.has(url)) return null;
|
|
668
681
|
try {
|
|
669
682
|
const fontRes = await fetch(url);
|
|
670
683
|
const blob = await fontRes.blob();
|
|
@@ -673,8 +686,8 @@ async function embedCustomFonts({ preCached = false } = {}) {
|
|
|
673
686
|
reader.onload = () => resolve(reader.result);
|
|
674
687
|
reader.readAsDataURL(blob);
|
|
675
688
|
});
|
|
676
|
-
|
|
677
|
-
|
|
689
|
+
cache.resource.set(url, b64);
|
|
690
|
+
cache.font.add(url);
|
|
678
691
|
return { original: match[0], inlined: `url(${b64})` };
|
|
679
692
|
} catch (e) {
|
|
680
693
|
console.warn("[snapdom] Failed to fetch font resource:", url);
|
|
@@ -710,12 +723,12 @@ async function embedCustomFonts({ preCached = false } = {}) {
|
|
|
710
723
|
url = new URL(url, sheet.href || location.href).href;
|
|
711
724
|
}
|
|
712
725
|
if (isIconFont(url)) continue;
|
|
713
|
-
if (
|
|
714
|
-
|
|
715
|
-
inlinedSrc = inlinedSrc.replace(match[0], `url(${
|
|
726
|
+
if (cache.resource.has(url)) {
|
|
727
|
+
cache.font.add(url);
|
|
728
|
+
inlinedSrc = inlinedSrc.replace(match[0], `url(${cache.resource.get(url)})`);
|
|
716
729
|
continue;
|
|
717
730
|
}
|
|
718
|
-
if (
|
|
731
|
+
if (cache.font.has(url)) continue;
|
|
719
732
|
try {
|
|
720
733
|
const res = await fetch(url);
|
|
721
734
|
const blob = await res.blob();
|
|
@@ -724,8 +737,8 @@ async function embedCustomFonts({ preCached = false } = {}) {
|
|
|
724
737
|
reader.onload = () => resolve(reader.result);
|
|
725
738
|
reader.readAsDataURL(blob);
|
|
726
739
|
});
|
|
727
|
-
|
|
728
|
-
|
|
740
|
+
cache.resource.set(url, b64);
|
|
741
|
+
cache.font.add(url);
|
|
729
742
|
inlinedSrc = inlinedSrc.replace(match[0], `url(${b64})`);
|
|
730
743
|
} catch (e) {
|
|
731
744
|
console.warn("[snapdom] Failed to fetch font URL:", url);
|
|
@@ -750,10 +763,10 @@ async function embedCustomFonts({ preCached = false } = {}) {
|
|
|
750
763
|
if (isIconFont(font.family)) continue;
|
|
751
764
|
let b64 = font._snapdomSrc;
|
|
752
765
|
if (!b64.startsWith("data:")) {
|
|
753
|
-
if (
|
|
754
|
-
b64 =
|
|
755
|
-
|
|
756
|
-
} else if (!
|
|
766
|
+
if (cache.resource.has(font._snapdomSrc)) {
|
|
767
|
+
b64 = cache.resource.get(font._snapdomSrc);
|
|
768
|
+
cache.font.add(font._snapdomSrc);
|
|
769
|
+
} else if (!cache.font.has(font._snapdomSrc)) {
|
|
757
770
|
try {
|
|
758
771
|
const res = await fetch(font._snapdomSrc);
|
|
759
772
|
const blob = await res.blob();
|
|
@@ -762,8 +775,8 @@ async function embedCustomFonts({ preCached = false } = {}) {
|
|
|
762
775
|
reader.onload = () => resolve(reader.result);
|
|
763
776
|
reader.readAsDataURL(blob);
|
|
764
777
|
});
|
|
765
|
-
|
|
766
|
-
|
|
778
|
+
cache.resource.set(font._snapdomSrc, b64);
|
|
779
|
+
cache.font.add(font._snapdomSrc);
|
|
767
780
|
} catch (e) {
|
|
768
781
|
console.warn("[snapdom] Failed to fetch dynamic font src:", font._snapdomSrc);
|
|
769
782
|
continue;
|
|
@@ -780,7 +793,7 @@ async function embedCustomFonts({ preCached = false } = {}) {
|
|
|
780
793
|
}
|
|
781
794
|
}
|
|
782
795
|
if (finalCSS) {
|
|
783
|
-
|
|
796
|
+
cache.resource.set("fonts-embed-css", finalCSS);
|
|
784
797
|
if (preCached) {
|
|
785
798
|
const style = document.createElement("style");
|
|
786
799
|
style.setAttribute("data-snapdom", "embedFonts");
|
|
@@ -792,7 +805,7 @@ async function embedCustomFonts({ preCached = false } = {}) {
|
|
|
792
805
|
}
|
|
793
806
|
|
|
794
807
|
// src/modules/pseudo.js
|
|
795
|
-
async function inlinePseudoElements(source, clone,
|
|
808
|
+
async function inlinePseudoElements(source, clone, compress, embedFonts = false, useProxy) {
|
|
796
809
|
if (!(source instanceof Element) || !(clone instanceof Element)) return;
|
|
797
810
|
for (const pseudo of ["::before", "::after", "::first-letter"]) {
|
|
798
811
|
try {
|
|
@@ -816,7 +829,7 @@ async function inlinePseudoElements(source, clone, styleMap, styleCache, compres
|
|
|
816
829
|
span.dataset.snapdomPseudo = "::first-letter";
|
|
817
830
|
const snapshot = snapshotComputedStyle(style);
|
|
818
831
|
const key = getStyleKey(snapshot, "span", compress);
|
|
819
|
-
|
|
832
|
+
cache.preStyleMap.set(span, key);
|
|
820
833
|
const restNode = document.createTextNode(rest);
|
|
821
834
|
clone.replaceChild(restNode, textNode);
|
|
822
835
|
clone.insertBefore(span, restNode);
|
|
@@ -837,7 +850,7 @@ async function inlinePseudoElements(source, clone, styleMap, styleCache, compres
|
|
|
837
850
|
pseudoEl.dataset.snapdomPseudo = pseudo;
|
|
838
851
|
const snapshot = snapshotComputedStyle(style);
|
|
839
852
|
const key = getStyleKey(snapshot, "span", compress);
|
|
840
|
-
|
|
853
|
+
cache.preStyleMap.set(pseudoEl, key);
|
|
841
854
|
const isIconFont2 = isIconFont(fontFamily);
|
|
842
855
|
const cleanContent = parseContent(content);
|
|
843
856
|
if (isIconFont2 && cleanContent.length === 1) {
|
|
@@ -888,8 +901,6 @@ async function inlinePseudoElements(source, clone, styleMap, styleCache, compres
|
|
|
888
901
|
await inlinePseudoElements(
|
|
889
902
|
sChildren[i],
|
|
890
903
|
cChildren[i],
|
|
891
|
-
styleMap,
|
|
892
|
-
styleCache,
|
|
893
904
|
compress,
|
|
894
905
|
embedFonts,
|
|
895
906
|
useProxy
|
|
@@ -929,18 +940,15 @@ function inlineExternalDef(root) {
|
|
|
929
940
|
|
|
930
941
|
// src/core/prepare.js
|
|
931
942
|
async function prepareClone(element, compress = false, embedFonts = false, options = {}) {
|
|
932
|
-
|
|
933
|
-
const styleCache = /* @__PURE__ */ new WeakMap();
|
|
934
|
-
const nodeMap = /* @__PURE__ */ new Map();
|
|
935
|
-
let clone;
|
|
943
|
+
let clone, classCSS;
|
|
936
944
|
try {
|
|
937
|
-
clone = deepClone(element,
|
|
945
|
+
clone = deepClone(element, compress, options, element);
|
|
938
946
|
} catch (e) {
|
|
939
947
|
console.warn("deepClone failed:", e);
|
|
940
948
|
throw e;
|
|
941
949
|
}
|
|
942
950
|
try {
|
|
943
|
-
await inlinePseudoElements(element, clone,
|
|
951
|
+
await inlinePseudoElements(element, clone, compress, embedFonts, options.useProxy);
|
|
944
952
|
} catch (e) {
|
|
945
953
|
console.warn("inlinePseudoElements failed:", e);
|
|
946
954
|
}
|
|
@@ -949,11 +957,10 @@ async function prepareClone(element, compress = false, embedFonts = false, optio
|
|
|
949
957
|
} catch (e) {
|
|
950
958
|
console.warn("inlineExternalDef failed:", e);
|
|
951
959
|
}
|
|
952
|
-
let classCSS = "";
|
|
953
960
|
if (compress) {
|
|
954
|
-
const keyToClass = generateCSSClasses(
|
|
961
|
+
const keyToClass = generateCSSClasses();
|
|
955
962
|
classCSS = Array.from(keyToClass.entries()).map(([key, className]) => `.${className}{${key}}`).join("");
|
|
956
|
-
for (const [node, key] of
|
|
963
|
+
for (const [node, key] of cache.preStyleMap.entries()) {
|
|
957
964
|
if (node.tagName === "STYLE") continue;
|
|
958
965
|
const className = keyToClass.get(key);
|
|
959
966
|
if (className) node.classList.add(className);
|
|
@@ -962,12 +969,12 @@ async function prepareClone(element, compress = false, embedFonts = false, optio
|
|
|
962
969
|
if (bgImage && bgImage !== "none") node.style.backgroundImage = bgImage;
|
|
963
970
|
}
|
|
964
971
|
} else {
|
|
965
|
-
for (const [node, key] of
|
|
972
|
+
for (const [node, key] of cache.preStyleMap.entries()) {
|
|
966
973
|
if (node.tagName === "STYLE") continue;
|
|
967
974
|
node.setAttribute("style", key.replace(/;/g, "; "));
|
|
968
975
|
}
|
|
969
976
|
}
|
|
970
|
-
for (const [cloneNode, originalNode] of
|
|
977
|
+
for (const [cloneNode, originalNode] of cache.preNodeMap.entries()) {
|
|
971
978
|
const scrollX = originalNode.scrollLeft;
|
|
972
979
|
const scrollY = originalNode.scrollTop;
|
|
973
980
|
const hasScroll = scrollX || scrollY;
|
|
@@ -986,9 +993,9 @@ async function prepareClone(element, compress = false, embedFonts = false, optio
|
|
|
986
993
|
cloneNode.appendChild(inner);
|
|
987
994
|
}
|
|
988
995
|
}
|
|
989
|
-
if (element ===
|
|
990
|
-
const computed =
|
|
991
|
-
|
|
996
|
+
if (element === cache.preNodeMap.get(clone)) {
|
|
997
|
+
const computed = cache.preStyle.get(element) || window.getComputedStyle(element);
|
|
998
|
+
cache.preStyle.set(element, computed);
|
|
992
999
|
const transform = stripTranslate(computed.transform);
|
|
993
1000
|
clone.style.margin = "0";
|
|
994
1001
|
clone.style.position = "static";
|
|
@@ -1001,13 +1008,13 @@ async function prepareClone(element, compress = false, embedFonts = false, optio
|
|
|
1001
1008
|
clone.style.clear = "none";
|
|
1002
1009
|
clone.style.transform = transform || "";
|
|
1003
1010
|
}
|
|
1004
|
-
for (const [cloneNode, originalNode] of
|
|
1011
|
+
for (const [cloneNode, originalNode] of cache.preNodeMap.entries()) {
|
|
1005
1012
|
if (originalNode.tagName === "PRE") {
|
|
1006
1013
|
cloneNode.style.marginTop = "0";
|
|
1007
1014
|
cloneNode.style.marginBlockStart = "0";
|
|
1008
1015
|
}
|
|
1009
1016
|
}
|
|
1010
|
-
return { clone, classCSS
|
|
1017
|
+
return { clone, classCSS };
|
|
1011
1018
|
}
|
|
1012
1019
|
|
|
1013
1020
|
// src/modules/images.js
|
|
@@ -1034,7 +1041,7 @@ async function inlineImages(clone, options = {}) {
|
|
|
1034
1041
|
}
|
|
1035
1042
|
|
|
1036
1043
|
// src/modules/background.js
|
|
1037
|
-
async function inlineBackgroundImages(source, clone,
|
|
1044
|
+
async function inlineBackgroundImages(source, clone, options = {}) {
|
|
1038
1045
|
const queue = [[source, clone]];
|
|
1039
1046
|
const imageProps = [
|
|
1040
1047
|
"background-image",
|
|
@@ -1048,8 +1055,8 @@ async function inlineBackgroundImages(source, clone, styleCache, options = {}) {
|
|
|
1048
1055
|
];
|
|
1049
1056
|
while (queue.length) {
|
|
1050
1057
|
const [srcNode, cloneNode] = queue.shift();
|
|
1051
|
-
const style =
|
|
1052
|
-
if (!
|
|
1058
|
+
const style = cache.preStyle.get(srcNode) || getStyle(srcNode);
|
|
1059
|
+
if (!cache.preStyle.has(srcNode)) cache.preStyle.set(srcNode, style);
|
|
1053
1060
|
for (const prop of imageProps) {
|
|
1054
1061
|
const val = style.getPropertyValue(prop);
|
|
1055
1062
|
if (!val || val === "none") continue;
|
|
@@ -1076,13 +1083,14 @@ async function inlineBackgroundImages(source, clone, styleCache, options = {}) {
|
|
|
1076
1083
|
// src/core/capture.js
|
|
1077
1084
|
async function captureDOM(element, options = {}) {
|
|
1078
1085
|
if (!element) throw new Error("Element cannot be null or undefined");
|
|
1086
|
+
cache.reset();
|
|
1079
1087
|
const { compress = true, embedFonts = false, fast = true, scale = 1, useProxy = "" } = options;
|
|
1080
|
-
let clone, classCSS
|
|
1088
|
+
let clone, classCSS;
|
|
1081
1089
|
let fontsCSS = "";
|
|
1082
1090
|
let baseCSS = "";
|
|
1083
1091
|
let dataURL;
|
|
1084
1092
|
let svgString;
|
|
1085
|
-
({ clone, classCSS
|
|
1093
|
+
({ clone, classCSS } = await prepareClone(element, compress, embedFonts, options));
|
|
1086
1094
|
await new Promise((resolve) => {
|
|
1087
1095
|
idle(async () => {
|
|
1088
1096
|
await inlineImages(clone, options);
|
|
@@ -1091,7 +1099,7 @@ async function captureDOM(element, options = {}) {
|
|
|
1091
1099
|
});
|
|
1092
1100
|
await new Promise((resolve) => {
|
|
1093
1101
|
idle(async () => {
|
|
1094
|
-
await inlineBackgroundImages(element, clone,
|
|
1102
|
+
await inlineBackgroundImages(element, clone, options);
|
|
1095
1103
|
resolve();
|
|
1096
1104
|
}, { fast });
|
|
1097
1105
|
});
|
|
@@ -1106,13 +1114,13 @@ async function captureDOM(element, options = {}) {
|
|
|
1106
1114
|
if (compress) {
|
|
1107
1115
|
const usedTags = collectUsedTagNames(clone).sort();
|
|
1108
1116
|
const tagKey = usedTags.join(",");
|
|
1109
|
-
if (
|
|
1110
|
-
baseCSS =
|
|
1117
|
+
if (cache.baseStyle.has(tagKey)) {
|
|
1118
|
+
baseCSS = cache.baseStyle.get(tagKey);
|
|
1111
1119
|
} else {
|
|
1112
1120
|
await new Promise((resolve) => {
|
|
1113
1121
|
idle(() => {
|
|
1114
1122
|
baseCSS = generateDedupedBaseCSS(usedTags);
|
|
1115
|
-
|
|
1123
|
+
cache.baseStyle.set(tagKey, baseCSS);
|
|
1116
1124
|
resolve();
|
|
1117
1125
|
}, { fast });
|
|
1118
1126
|
});
|
|
@@ -1321,10 +1329,7 @@ snapdom.download = async (el, options = {}) => {
|
|
|
1321
1329
|
async function preCache(root = document, options = {}) {
|
|
1322
1330
|
const { embedFonts = true, reset = false } = options;
|
|
1323
1331
|
if (reset) {
|
|
1324
|
-
|
|
1325
|
-
bgCache.clear();
|
|
1326
|
-
resourceCache.clear();
|
|
1327
|
-
baseCSSCache.clear();
|
|
1332
|
+
cache.reset();
|
|
1328
1333
|
return;
|
|
1329
1334
|
}
|
|
1330
1335
|
await document.fonts.ready;
|
|
@@ -1337,9 +1342,9 @@ async function preCache(root = document, options = {}) {
|
|
|
1337
1342
|
const promises = [];
|
|
1338
1343
|
for (const img of imgEls) {
|
|
1339
1344
|
const src = img.src;
|
|
1340
|
-
if (!
|
|
1345
|
+
if (!cache.image.has(src)) {
|
|
1341
1346
|
promises.push(
|
|
1342
|
-
fetchImage(src, { useProxy: options.useProxy }).then((dataURL) =>
|
|
1347
|
+
fetchImage(src, { useProxy: options.useProxy }).then((dataURL) => cache.image.set(src, dataURL)).catch(() => {
|
|
1343
1348
|
})
|
|
1344
1349
|
);
|
|
1345
1350
|
}
|