@zumer/snapdom 0.9.0 → 0.9.2
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/README.md +69 -61
- package/dist/snapdom.js +117 -57
- package/dist/snapdom.min.js +1 -1
- package/dist/snapdom.min.mjs +1 -1
- package/dist/snapdom.mjs +117 -57
- package/package.json +9 -2
- package/src/index.js +0 -6
package/README.md
CHANGED
|
@@ -1,33 +1,36 @@
|
|
|
1
1
|
# snapDOM
|
|
2
2
|
|
|
3
|
-
**snapDOM** is a high-fidelity DOM capture tool, developed as part of the animation engine for Zumly — a framework for creating smooth zoom-based view transitions.
|
|
3
|
+
**snapDOM** is a high-fidelity DOM capture tool, developed as part of the animation engine I'm building for Zumly — a framework for creating smooth zoom-based view transitions.
|
|
4
4
|
|
|
5
5
|
It converts any HTML element into a scalable SVG image, preserving styles, fonts, backgrounds, shadow DOM content, pseudo-elements, and more.
|
|
6
6
|
|
|
7
7
|
- 📸 Full DOM capture
|
|
8
8
|
- 🎨 Embedded styles, pseudo-elements, and fonts
|
|
9
9
|
- 🖼️ Export to SVG, PNG, JPG, WebP, or `canvas`
|
|
10
|
-
- ⚡
|
|
10
|
+
- ⚡ Ultra fast, no dependencies
|
|
11
11
|
- 📦 100% based on standard Web APIs
|
|
12
12
|
|
|
13
|
-
---
|
|
14
|
-
¡Obvio, Martín! 🔥
|
|
15
|
-
Acá te integro la parte de **Installation** agregando también la opción **CDN**, de manera prolija y siguiendo tu estilo:
|
|
16
|
-
|
|
17
|
-
---
|
|
18
13
|
|
|
19
14
|
## Installation
|
|
20
15
|
|
|
21
|
-
You can use **snapDOM**
|
|
16
|
+
You can use **snapDOM** via **NPM**, **CDN**, **script tag**, or by **importing as a module**.
|
|
17
|
+
|
|
18
|
+
### NPM / Yarn
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
npm i @zumer/snapdom
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
yarn add @zumer/snapdom
|
|
26
|
+
```
|
|
22
27
|
|
|
23
28
|
### CDN
|
|
24
29
|
|
|
25
30
|
```html
|
|
26
|
-
<script src="https://unpkg.com/@zumer/snapdom@latest/snapdom.min.js"></script>
|
|
31
|
+
<script src="https://unpkg.com/@zumer/snapdom@latest/dist/snapdom.min.js"></script>
|
|
27
32
|
```
|
|
28
33
|
|
|
29
|
-
---
|
|
30
|
-
|
|
31
34
|
### Script tag (local)
|
|
32
35
|
|
|
33
36
|
```html
|
|
@@ -36,8 +39,6 @@ You can use **snapDOM** by including it via **CDN**, **script tag**, or by **imp
|
|
|
36
39
|
|
|
37
40
|
The global object `snapdom` will be available.
|
|
38
41
|
|
|
39
|
-
---
|
|
40
|
-
|
|
41
42
|
### ES Module
|
|
42
43
|
|
|
43
44
|
```javascript
|
|
@@ -46,15 +47,14 @@ import { snapdom } from './snapdom.mjs';
|
|
|
46
47
|
|
|
47
48
|
### Script Tag (Type Module)
|
|
48
49
|
|
|
49
|
-
```
|
|
50
|
+
```javascript
|
|
50
51
|
<script type="module">
|
|
51
|
-
import { snapdom } from 'https://unpkg.com/@zumer/snapdom@latest/snapdom.mjs';
|
|
52
|
+
import { snapdom } from 'https://unpkg.com/@zumer/snapdom@latest/dist/snapdom.mjs';
|
|
52
53
|
</script>
|
|
53
54
|
```
|
|
54
55
|
|
|
55
56
|
Now you can call `snapdom(el)`, `snapdom.toPng(el)`, etc., directly in your JavaScript.
|
|
56
57
|
|
|
57
|
-
---
|
|
58
58
|
|
|
59
59
|
## Basic usage
|
|
60
60
|
|
|
@@ -68,26 +68,27 @@ img.src = svgDataUrl;
|
|
|
68
68
|
document.body.appendChild(img);
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
-
---
|
|
72
71
|
|
|
73
72
|
## API
|
|
74
73
|
|
|
74
|
+
### Note: API may evolve until v1.0.0!
|
|
75
|
+
|
|
75
76
|
The main API is exposed as `snapdom` and offers multiple capture methods:
|
|
76
77
|
|
|
77
|
-
| Method
|
|
78
|
-
|
|
79
|
-
| `snapdom(el,
|
|
80
|
-
| `snapdom.toImg(el,
|
|
81
|
-
| `snapdom.toCanvas(el,
|
|
82
|
-
| `snapdom.toPng(el,
|
|
83
|
-
| `snapdom.toJpg(el,
|
|
84
|
-
| `snapdom.toWebp(el,
|
|
85
|
-
| `snapdom.toBlob(el,
|
|
86
|
-
|
|
87
|
-
**
|
|
88
|
-
- `
|
|
89
|
-
- `
|
|
90
|
-
- `
|
|
78
|
+
| Method | Description | Returns |
|
|
79
|
+
|:----------------------|:---------------------------------------|:-------------------------|
|
|
80
|
+
| `snapdom(el, options?)` | Captures as SVG Data URL | `Promise<string>` |
|
|
81
|
+
| `snapdom.toImg(el, options?)` | Captures as `HTMLImageElement` (SVG) | `Promise<HTMLImageElement>` |
|
|
82
|
+
| `snapdom.toCanvas(el, options?)` | Captures as `HTMLCanvasElement` | `Promise<HTMLCanvasElement>` |
|
|
83
|
+
| `snapdom.toPng(el, options?)` | Captures as PNG image (`Image`) | `Promise<HTMLImageElement>` |
|
|
84
|
+
| `snapdom.toJpg(el, options?)` | Captures as JPG image (`Image`) | `Promise<HTMLImageElement>` |
|
|
85
|
+
| `snapdom.toWebp(el, options?)` | Captures as WebP image (`Image`) | `Promise<HTMLImageElement>` |
|
|
86
|
+
| `snapdom.toBlob(el, options?)` | Captures as SVG `Blob` | `Promise<Blob>` |
|
|
87
|
+
|
|
88
|
+
**Options:**
|
|
89
|
+
- `scale` *(number)*: Scale factor (default is `1`)
|
|
90
|
+
- `quality` *(number)*: Compression quality for JPG/WebP (0–1)
|
|
91
|
+
- `backgroundColor` *(string)*: Background fill for JPG/WebP exports
|
|
91
92
|
|
|
92
93
|
---
|
|
93
94
|
|
|
@@ -96,12 +97,13 @@ The main API is exposed as `snapdom` and offers multiple capture methods:
|
|
|
96
97
|
- **Shadow DOM**: Captures content inside Web Components and `shadowRoot`.
|
|
97
98
|
- **Pseudo-elements**: Captures `::before` and `::after`, including background images.
|
|
98
99
|
- **Backgrounds and images**: Inlines external images as Data URLs.
|
|
99
|
-
- **Fonts**: Replicates applied font families without
|
|
100
|
+
- **Fonts**: Replicates applied font families without requiring external font files.
|
|
101
|
+
- **Icon fonts**: Captures icon fonts like **Font Awesome** and **Material Icons**.
|
|
100
102
|
- **Placeholder and Exclusion**:
|
|
101
103
|
- `data-capture="exclude"`: Skips an element while preserving layout space.
|
|
102
|
-
- `data-capture="placeholder"` + `data-placeholder-text="Text"`: Replaces an element with
|
|
104
|
+
- `data-capture="placeholder"` + `data-placeholder-text="Text"`: Replaces an element with placeholder text.
|
|
103
105
|
|
|
104
|
-
|
|
106
|
+
**Now with improved fidelity and even faster performance.**
|
|
105
107
|
|
|
106
108
|
## Full example
|
|
107
109
|
|
|
@@ -111,20 +113,20 @@ The main API is exposed as `snapdom` and offers multiple capture methods:
|
|
|
111
113
|
<p>This content will be captured.</p>
|
|
112
114
|
</div>
|
|
113
115
|
|
|
116
|
+
<button id="captureBtn">Capture as img</button>
|
|
117
|
+
|
|
114
118
|
<script type="module">
|
|
115
|
-
import { snapdom } from './snapdom.
|
|
119
|
+
import { snapdom } from './snapdom.mjs';
|
|
116
120
|
|
|
117
|
-
const button = document.
|
|
118
|
-
button.
|
|
119
|
-
|
|
120
|
-
const img = await snapdom.
|
|
121
|
+
const button = document.getElementById('captureBtn');
|
|
122
|
+
button.addEventListener('click', async () => {
|
|
123
|
+
const target = document.getElementById('captureMe');
|
|
124
|
+
const img = await snapdom.toImg(target);
|
|
121
125
|
document.body.appendChild(img);
|
|
122
|
-
};
|
|
123
|
-
document.body.appendChild(button);
|
|
126
|
+
});
|
|
124
127
|
</script>
|
|
125
128
|
```
|
|
126
129
|
|
|
127
|
-
---
|
|
128
130
|
|
|
129
131
|
## Limitations
|
|
130
132
|
|
|
@@ -132,32 +134,38 @@ The main API is exposed as `snapdom` and offers multiple capture methods:
|
|
|
132
134
|
- Fonts must be fully loaded before capturing (`document.fonts.ready` is automatically awaited).
|
|
133
135
|
- Iframes are not captured.
|
|
134
136
|
|
|
135
|
-
---
|
|
136
137
|
|
|
137
|
-
##
|
|
138
|
+
## Benchmarks
|
|
138
139
|
|
|
139
|
-
`snapDOM` is not only highly accurate — it
|
|
140
|
+
`snapDOM` is not only highly accurate — it’s **extremely fast**, especially for large elements.
|
|
140
141
|
|
|
141
|
-
|
|
142
|
+
Latest benchmarks show **major speed improvements** over existing libraries:
|
|
142
143
|
|
|
143
|
-
| Element Size
|
|
144
|
-
|
|
145
|
-
| 200×100
|
|
146
|
-
| 400×300
|
|
147
|
-
| 1200×800
|
|
148
|
-
| 2000×1500
|
|
149
|
-
| 4000×2000
|
|
144
|
+
| Element Size | vs. `modern-screenshot` | vs. `html2canvas` |
|
|
145
|
+
|:------------------------:|:-----------------------:|:-----------------:|
|
|
146
|
+
| **Small (200×100)** | 9.84× faster | 33.19× faster |
|
|
147
|
+
| **Modal (400×300)** | 11.72× faster | 36.03× faster |
|
|
148
|
+
| **Page view (1200×800)** | 24.40× faster | 55.65× faster |
|
|
149
|
+
| **Large (2000×1500)** | 49.85× faster | 90.74× faster |
|
|
150
|
+
| **Very large (4000×2000)** | 138.93× faster | 148.41× faster |
|
|
150
151
|
|
|
151
|
-
✅ **
|
|
152
|
-
|
|
152
|
+
✅ **snapDOM** also improved fidelity, even on complex layouts:
|
|
153
|
+
- Up to **7.82× faster** for complex modal captures.
|
|
154
|
+
- Still faster even at very large sizes.
|
|
153
155
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
156
|
+
### Run the benchmarks
|
|
157
|
+
To run these benchmarks yourself:
|
|
158
|
+
|
|
159
|
+
```sh
|
|
160
|
+
git clone https://github.com/zumerlab/snapdom.git
|
|
161
|
+
cd snapdom
|
|
162
|
+
npm install
|
|
163
|
+
npm run test:benchmark
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
They execute in **headless Chromium** using real DOM nodes.
|
|
158
167
|
|
|
159
|
-
---
|
|
160
168
|
|
|
161
169
|
## License
|
|
162
170
|
|
|
163
|
-
MIT ©
|
|
171
|
+
MIT © Zumerlab
|
package/dist/snapdom.js
CHANGED
|
@@ -1,28 +1,17 @@
|
|
|
1
1
|
|
|
2
2
|
/*
|
|
3
3
|
* snapdom
|
|
4
|
-
* v.0.9.
|
|
4
|
+
* v.0.9.2
|
|
5
5
|
* Author Juan Martin Muda
|
|
6
6
|
* License MIT
|
|
7
7
|
**/
|
|
8
8
|
(() => {
|
|
9
9
|
// src/utils/cssTools.js
|
|
10
|
-
function
|
|
11
|
-
const defaultEl = document.createElement("div");
|
|
12
|
-
document.body.appendChild(defaultEl);
|
|
13
|
-
const styles = window.getComputedStyle(defaultEl);
|
|
14
|
-
const defaults = {};
|
|
15
|
-
for (let prop of styles) {
|
|
16
|
-
defaults[prop] = styles.getPropertyValue(prop);
|
|
17
|
-
}
|
|
18
|
-
document.body.removeChild(defaultEl);
|
|
19
|
-
return defaults;
|
|
20
|
-
}
|
|
21
|
-
function getStyleKey(style, defaults) {
|
|
10
|
+
function getStyleKey(style, defaults, mode) {
|
|
22
11
|
const entries = [];
|
|
23
|
-
for (let prop of
|
|
12
|
+
for (let prop of style) {
|
|
24
13
|
const value = style.getPropertyValue(prop);
|
|
25
|
-
if (value
|
|
14
|
+
if (value) {
|
|
26
15
|
entries.push(`${prop}:${value}`);
|
|
27
16
|
}
|
|
28
17
|
}
|
|
@@ -106,8 +95,6 @@
|
|
|
106
95
|
shadowContent.forEach((child) => shadowFrag.appendChild(child));
|
|
107
96
|
clone.appendChild(shadowFrag);
|
|
108
97
|
}
|
|
109
|
-
clone.querySelectorAll("style").forEach((tag) => tag.removeAttribute("style"));
|
|
110
|
-
if (clone.tagName === "STYLE") clone.removeAttribute("style");
|
|
111
98
|
return clone;
|
|
112
99
|
}
|
|
113
100
|
|
|
@@ -146,11 +133,6 @@
|
|
|
146
133
|
});
|
|
147
134
|
}
|
|
148
135
|
|
|
149
|
-
// src/utils/delay.js
|
|
150
|
-
function delay(ms = 0) {
|
|
151
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
152
|
-
}
|
|
153
|
-
|
|
154
136
|
// src/modules/images.js
|
|
155
137
|
async function inlineImages(clone) {
|
|
156
138
|
const imgs = Array.from(clone.querySelectorAll("img"));
|
|
@@ -171,7 +153,6 @@
|
|
|
171
153
|
for (let i = 0; i < imgs.length; i += 4) {
|
|
172
154
|
const group = imgs.slice(i, i + 4).map(processImg);
|
|
173
155
|
await Promise.allSettled(group);
|
|
174
|
-
await delay(1);
|
|
175
156
|
}
|
|
176
157
|
}
|
|
177
158
|
|
|
@@ -199,7 +180,6 @@
|
|
|
199
180
|
for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
|
|
200
181
|
queue.push([sChildren[i], cChildren[i]]);
|
|
201
182
|
}
|
|
202
|
-
await delay(1);
|
|
203
183
|
}
|
|
204
184
|
}
|
|
205
185
|
|
|
@@ -221,7 +201,29 @@
|
|
|
221
201
|
"margin",
|
|
222
202
|
"padding",
|
|
223
203
|
"border",
|
|
204
|
+
"border-width",
|
|
205
|
+
"border-style",
|
|
206
|
+
"border-color",
|
|
224
207
|
"border-radius",
|
|
208
|
+
"box-sizing",
|
|
209
|
+
"display",
|
|
210
|
+
"overflow",
|
|
211
|
+
"overflow-x",
|
|
212
|
+
"overflow-y",
|
|
213
|
+
"flex",
|
|
214
|
+
"flex-grow",
|
|
215
|
+
"flex-shrink",
|
|
216
|
+
"flex-basis",
|
|
217
|
+
"flex-direction",
|
|
218
|
+
"justify-content",
|
|
219
|
+
"align-items",
|
|
220
|
+
"align-content",
|
|
221
|
+
"gap",
|
|
222
|
+
"row-gap",
|
|
223
|
+
"column-gap",
|
|
224
|
+
"grid",
|
|
225
|
+
"grid-template-columns",
|
|
226
|
+
"grid-template-rows",
|
|
225
227
|
"background",
|
|
226
228
|
"background-color",
|
|
227
229
|
"background-image",
|
|
@@ -230,29 +232,44 @@
|
|
|
230
232
|
"font-family",
|
|
231
233
|
"font-size",
|
|
232
234
|
"font-weight",
|
|
235
|
+
"font-style",
|
|
236
|
+
"font-stretch",
|
|
233
237
|
"text-align",
|
|
238
|
+
"vertical-align",
|
|
234
239
|
"text-decoration",
|
|
235
240
|
"line-height",
|
|
236
241
|
"letter-spacing",
|
|
237
|
-
"
|
|
242
|
+
"white-space",
|
|
243
|
+
"word-break",
|
|
244
|
+
"text-overflow",
|
|
238
245
|
"visibility",
|
|
239
246
|
"opacity",
|
|
240
|
-
"overflow",
|
|
241
247
|
"box-shadow",
|
|
242
248
|
"transform",
|
|
243
249
|
"transition",
|
|
244
|
-
"
|
|
245
|
-
"
|
|
250
|
+
"clip-path",
|
|
251
|
+
"-webkit-text-fill-color"
|
|
246
252
|
];
|
|
247
253
|
for (const pseudo of ["::before", "::after"]) {
|
|
248
254
|
try {
|
|
249
255
|
const style = window.getComputedStyle(source, pseudo);
|
|
250
256
|
const content = style.getPropertyValue("content");
|
|
251
|
-
|
|
257
|
+
const bg = style.getPropertyValue("background-image");
|
|
258
|
+
if (content && content !== "none" && content !== '""' && content !== "''" || bg && bg.startsWith("url(")) {
|
|
259
|
+
const fontFamily = style.getPropertyValue("font-family");
|
|
252
260
|
const pseudoEl = document.createElement("span");
|
|
253
261
|
pseudoEl.className = `snapdom-pseudo-${pseudo.replace("::", "")}`;
|
|
254
|
-
let cleanContent = content
|
|
255
|
-
|
|
262
|
+
let cleanContent = parseContent(content);
|
|
263
|
+
const isIconFont = fontFamily && /font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(fontFamily);
|
|
264
|
+
const fontSize = parseInt(style.getPropertyValue("font-size")) || 32;
|
|
265
|
+
const fontWeight = parseInt(style.getPropertyValue("font-weight")) || false;
|
|
266
|
+
const color = style.getPropertyValue("color") || "#000";
|
|
267
|
+
if (isIconFont && cleanContent.length === 1) {
|
|
268
|
+
const imgEl = document.createElement("img");
|
|
269
|
+
imgEl.src = await iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color);
|
|
270
|
+
imgEl.style = "display:block;width:100%;height:100%;object-fit:contain;";
|
|
271
|
+
pseudoEl.appendChild(imgEl);
|
|
272
|
+
} else if (cleanContent.startsWith("url(")) {
|
|
256
273
|
const match = cleanContent.match(/url\(["']?([^"')]+)["']?\)/);
|
|
257
274
|
if (match?.[1]) {
|
|
258
275
|
try {
|
|
@@ -262,6 +279,7 @@
|
|
|
262
279
|
imgEl.style = "display:block;width:100%;height:100%;object-fit:contain;";
|
|
263
280
|
pseudoEl.appendChild(imgEl);
|
|
264
281
|
} catch (e) {
|
|
282
|
+
console.error(`[snapdom] Error in pseudo ${pseudo} for`, source, e);
|
|
265
283
|
}
|
|
266
284
|
}
|
|
267
285
|
} else {
|
|
@@ -289,9 +307,34 @@
|
|
|
289
307
|
);
|
|
290
308
|
for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
|
|
291
309
|
await inlinePseudoElements(sChildren[i], cChildren[i]);
|
|
292
|
-
if (i % 5 === 0) await delay(1);
|
|
293
310
|
}
|
|
294
311
|
}
|
|
312
|
+
function parseContent(content) {
|
|
313
|
+
let clean = content.replace(/^['"]|['"]$/g, "");
|
|
314
|
+
if (clean.startsWith("\\")) {
|
|
315
|
+
try {
|
|
316
|
+
return String.fromCharCode(parseInt(clean.replace("\\", ""), 16));
|
|
317
|
+
} catch {
|
|
318
|
+
return clean;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return clean;
|
|
322
|
+
}
|
|
323
|
+
async function iconToImage(unicodeChar, fontFamily, fontWeight, fontSize = 32, color = "#000") {
|
|
324
|
+
fontFamily = fontFamily.replace(/^['"]+|['"]+$/g, "");
|
|
325
|
+
await document.fonts.ready;
|
|
326
|
+
await document.fonts.load(`${fontSize}px "${fontFamily}"`);
|
|
327
|
+
const canvas = document.createElement("canvas");
|
|
328
|
+
canvas.width = fontSize;
|
|
329
|
+
canvas.height = fontSize;
|
|
330
|
+
const ctx = canvas.getContext("2d");
|
|
331
|
+
ctx.font = fontWeight ? `${fontWeight} ${fontSize}px "${fontFamily}"` : ` ${fontSize}px "${fontFamily}"`;
|
|
332
|
+
ctx.textAlign = "center";
|
|
333
|
+
ctx.textBaseline = "middle";
|
|
334
|
+
ctx.fillStyle = color;
|
|
335
|
+
ctx.fillText(unicodeChar, canvas.width / 2, canvas.height / 2);
|
|
336
|
+
return canvas.toDataURL();
|
|
337
|
+
}
|
|
295
338
|
|
|
296
339
|
// src/utils/normalize.js
|
|
297
340
|
function normalizeFontName(str) {
|
|
@@ -381,7 +424,7 @@
|
|
|
381
424
|
const nodeMap = /* @__PURE__ */ new WeakMap();
|
|
382
425
|
let defaults;
|
|
383
426
|
try {
|
|
384
|
-
defaults =
|
|
427
|
+
defaults = {};
|
|
385
428
|
} catch (e) {
|
|
386
429
|
console.warn("getDefaultStyles failed:", e);
|
|
387
430
|
defaults = {};
|
|
@@ -434,7 +477,8 @@
|
|
|
434
477
|
}
|
|
435
478
|
|
|
436
479
|
// src/core/capture.js
|
|
437
|
-
async function capture(element,
|
|
480
|
+
async function capture(element, options = {}) {
|
|
481
|
+
const { scale = 1 } = options;
|
|
438
482
|
let clone, classCSS, styleCache;
|
|
439
483
|
try {
|
|
440
484
|
({ clone, classCSS, styleCache } = await prepareClone(element));
|
|
@@ -442,8 +486,9 @@
|
|
|
442
486
|
console.error("prepareClone failed:", e);
|
|
443
487
|
throw e;
|
|
444
488
|
}
|
|
445
|
-
const
|
|
446
|
-
const
|
|
489
|
+
const rect = element.getBoundingClientRect();
|
|
490
|
+
const w = rect.width * scale;
|
|
491
|
+
const h = rect.height * scale;
|
|
447
492
|
let fonts = "";
|
|
448
493
|
try {
|
|
449
494
|
fonts = await inlineFonts(styleCache);
|
|
@@ -476,49 +521,64 @@
|
|
|
476
521
|
}
|
|
477
522
|
|
|
478
523
|
// src/api/snapdom.js
|
|
479
|
-
async function snapdom(
|
|
480
|
-
return await capture(
|
|
524
|
+
async function snapdom(element, options = {}) {
|
|
525
|
+
return await capture(element, options);
|
|
481
526
|
}
|
|
482
|
-
snapdom.toImg = async
|
|
483
|
-
const
|
|
527
|
+
snapdom.toImg = async (el, opt = {}) => {
|
|
528
|
+
const url = await snapdom(el, opt);
|
|
484
529
|
const img = new Image();
|
|
485
|
-
img.src =
|
|
530
|
+
img.src = url;
|
|
486
531
|
await img.decode();
|
|
487
532
|
return img;
|
|
488
533
|
};
|
|
489
|
-
snapdom.toCanvas = async
|
|
490
|
-
const img = await snapdom.toImg(el,
|
|
534
|
+
snapdom.toCanvas = async (el, opt = {}) => {
|
|
535
|
+
const img = await snapdom.toImg(el, opt);
|
|
491
536
|
const canvas = document.createElement("canvas");
|
|
492
537
|
canvas.width = img.width;
|
|
493
538
|
canvas.height = img.height;
|
|
494
|
-
|
|
495
|
-
ctx.drawImage(img, 0, 0);
|
|
539
|
+
canvas.getContext("2d").drawImage(img, 0, 0);
|
|
496
540
|
return canvas;
|
|
497
541
|
};
|
|
498
|
-
snapdom.toPng = async
|
|
499
|
-
const canvas = await snapdom.toCanvas(el,
|
|
542
|
+
snapdom.toPng = async (el, opt = {}) => {
|
|
543
|
+
const canvas = await snapdom.toCanvas(el, opt);
|
|
500
544
|
const img = new Image();
|
|
501
545
|
img.src = canvas.toDataURL("image/png");
|
|
502
546
|
await img.decode();
|
|
503
547
|
return img;
|
|
504
548
|
};
|
|
505
|
-
snapdom.toJpg = async
|
|
506
|
-
const
|
|
549
|
+
snapdom.toJpg = async (el, opt = {}) => {
|
|
550
|
+
const { backgroundColor = "#fff", quality = 0.92 } = opt;
|
|
551
|
+
const canvas = await snapdom.toCanvas(el, opt);
|
|
552
|
+
const temp = document.createElement("canvas");
|
|
553
|
+
temp.width = canvas.width;
|
|
554
|
+
temp.height = canvas.height;
|
|
555
|
+
const ctx = temp.getContext("2d");
|
|
556
|
+
ctx.fillStyle = backgroundColor;
|
|
557
|
+
ctx.fillRect(0, 0, temp.width, temp.height);
|
|
558
|
+
ctx.drawImage(canvas, 0, 0);
|
|
507
559
|
const img = new Image();
|
|
508
|
-
img.src =
|
|
560
|
+
img.src = temp.toDataURL("image/jpeg", quality);
|
|
509
561
|
await img.decode();
|
|
510
562
|
return img;
|
|
511
563
|
};
|
|
512
|
-
snapdom.toWebp = async
|
|
513
|
-
const
|
|
564
|
+
snapdom.toWebp = async (el, opt = {}) => {
|
|
565
|
+
const { backgroundColor = "#fff", quality = 0.92 } = opt;
|
|
566
|
+
const canvas = await snapdom.toCanvas(el, opt);
|
|
567
|
+
const temp = document.createElement("canvas");
|
|
568
|
+
temp.width = canvas.width;
|
|
569
|
+
temp.height = canvas.height;
|
|
570
|
+
const ctx = temp.getContext("2d");
|
|
571
|
+
ctx.fillStyle = backgroundColor;
|
|
572
|
+
ctx.fillRect(0, 0, temp.width, temp.height);
|
|
573
|
+
ctx.drawImage(canvas, 0, 0);
|
|
514
574
|
const img = new Image();
|
|
515
|
-
img.src =
|
|
575
|
+
img.src = temp.toDataURL("image/webp", quality);
|
|
516
576
|
await img.decode();
|
|
517
577
|
return img;
|
|
518
578
|
};
|
|
519
|
-
snapdom.toBlob = async
|
|
520
|
-
const
|
|
521
|
-
const
|
|
522
|
-
return new Blob([
|
|
579
|
+
snapdom.toBlob = async (el, opt = {}) => {
|
|
580
|
+
const dataUrl = await snapdom(el, opt);
|
|
581
|
+
const svgText = decodeURIComponent(dataUrl.split(",")[1]);
|
|
582
|
+
return new Blob([svgText], { type: "image/svg+xml" });
|
|
523
583
|
};
|
|
524
584
|
})();
|
package/dist/snapdom.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{function b(){let t=document.createElement("div");document.body.appendChild(t);let a=window.getComputedStyle(t),r={};for(let e of a)r[e]=a.getPropertyValue(e);return document.body.removeChild(t),r}function S(t,a){let r=[];for(let e of Array.from(t)){let i=t.getPropertyValue(e);i&&i!==a[e]&&r.push(`${e}:${i}`)}return r.sort().join(";")}function E(t){let a=new Set(t.values()),r=new Map,e=1;for(let i of a)r.set(i,`c${e++}`);return r}function w(t,a,r,e,i){if(t.tagName==="STYLE")return;i.has(t)||i.set(t,window.getComputedStyle(t));let o=i.get(t),c=S(o,e);r.set(a,c)}function y(t,a,r,e,i){if(t.nodeType===Node.ELEMENT_NODE&&t.getAttribute("data-capture")==="exclude"){let s=document.createElement("div"),n=t.getBoundingClientRect();return s.style.cssText=`display: inline-block; width: ${n.width}px; height: ${n.height}px; visibility: hidden;`,s}if(t.tagName==="IFRAME"){let s=document.createElement("div");return s.textContent="",s.style.cssText=`width: ${t.offsetWidth}px; height: ${t.offsetHeight}px; background: repeating-linear-gradient(45deg, #ddd, #ddd 5px, #f9f9f9 5px, #f9f9f9 10px);display: flex;align-items: center;justify-content: center;font-size: 12px;color: #555; border: 1px solid #aaa;`,s}if(t.nodeType===Node.ELEMENT_NODE&&t.getAttribute("data-capture")==="placeholder"){let s=t.cloneNode(!1);i.set(s,t),w(t,s,a,r,e);let n=document.createElement("div");return n.textContent=t.getAttribute("data-placeholder-text")||"",n.style.cssText="color: #666;font-size: 12px;text-align: center;line-height: 1.4;padding: 0.5em;box-sizing: border-box;",s.appendChild(n),s}if(t.tagName==="CANVAS"){let s=t.toDataURL(),n=document.createElement("img");return n.src=s,n.width=t.width,n.height=t.height,n}if(t.nodeType===Node.TEXT_NODE){if(!t.textContent.trim())return null;if(t.parentElement?.shadowRoot){let n=t.parentElement.tagName.toLowerCase();if(customElements.get(n))return null}return t.cloneNode(!0)}if(t.nodeType!==Node.ELEMENT_NODE)return t.cloneNode(!0);let o=t.cloneNode(!1);i.set(o,t),w(t,o,a,r,e);let c=document.createDocumentFragment();if(t.childNodes.forEach(s=>{let n=y(s,a,r,e,i);n&&c.appendChild(n)}),o.appendChild(c),t.shadowRoot){let s=Array.from(t.shadowRoot.children).filter(d=>d.tagName!=="STYLE").map(d=>y(d,a,r,e,i)).filter(Boolean),n=document.createDocumentFragment();s.forEach(d=>n.appendChild(d)),o.appendChild(n)}return o.querySelectorAll("style").forEach(s=>s.removeAttribute("style")),o.tagName==="STYLE"&&o.removeAttribute("style"),o}function h(t,a=3e3){return new Promise((r,e)=>{let i=setTimeout(()=>{e(new Error("Image load timed out"))},a),o=new Image;o.crossOrigin="anonymous",o.onload=async()=>{clearTimeout(i);try{await o.decode();let c=document.createElement("canvas");c.width=o.width,c.height=o.height,c.getContext("2d").drawImage(o,0,0);try{let n=c.toDataURL("image/png");r(n)}catch{e(new Error("CORS restrictions prevented image capture"))}}catch(c){e(c)}},o.onerror=c=>{clearTimeout(i),e(new Error("Failed to load image: "+(c.message||"Unknown error")))},o.src=t})}function u(t=0){return new Promise(a=>setTimeout(a,t))}async function v(t){let a=Array.from(t.querySelectorAll("img")),r=async e=>{let i=e.src;try{let o=await h(i);e.src=o,e.width||(e.width=e.naturalWidth||100),e.height||(e.height=e.naturalHeight||100)}catch{let o=document.createElement("div");o.style=`width: ${e.width||100}px; height: ${e.height||100}px; background: #ccc; display: inline-block; text-align: center; line-height: ${e.height||100}px; color: #666; font-size: 12px;`,o.innerText="img",e.replaceWith(o)}};for(let e=0;e<a.length;e+=4){let i=a.slice(e,e+4).map(r);await Promise.allSettled(i),await u(1)}}async function A(t,a,r){let e=[[t,a]];for(;e.length;){let[i,o]=e.shift(),c=r.get(i)||window.getComputedStyle(i);r.has(i)||r.set(i,c);let s=c.getPropertyValue("background-image");if(s&&s.includes("url(")){let l=s.match(/url\(["']?([^"')]+)["']?\)/);if(l?.[1])try{let m=await h(l[1]);o.style.backgroundImage=`url(${m})`}catch{o.style.backgroundImage="none"}}let n=Array.from(i.children),d=Array.from(o.children);for(let l=0;l<Math.min(n.length,d.length);l++)e.push([n[l],d[l]]);await u(1)}}async function x(t,a){let r=["position","top","right","bottom","left","z-index","width","height","min-width","max-width","min-height","max-height","margin","padding","border","border-radius","background","background-color","background-image","color","font","font-family","font-size","font-weight","text-align","text-decoration","line-height","letter-spacing","display","visibility","opacity","overflow","box-shadow","transform","transition","flex","grid"];for(let o of["::before","::after"])try{let c=window.getComputedStyle(t,o),s=c.getPropertyValue("content");if(s&&s!=="none"&&s!=='""'&&s!=="''"){let n=document.createElement("span");n.className=`snapdom-pseudo-${o.replace("::","")}`;let d=s.replace(/^\"|\"$/g,"");if(d.startsWith("url(")){let l=d.match(/url\(["']?([^"')]+)["']?\)/);if(l?.[1])try{let m=document.createElement("img"),f=await h(l[1]);m.src=f,m.style="display:block;width:100%;height:100%;object-fit:contain;",n.appendChild(m)}catch{}}else n.textContent=d;for(let l of r){let m=c.getPropertyValue(l);m&&(n.style[l]=m)}n.style.position||(n.style.position="absolute"),o==="::before"?a.insertBefore(n,a.firstChild):a.appendChild(n)}}catch{}let e=Array.from(t.children),i=Array.from(a.children).filter(o=>!o.classList.contains("snapdom-pseudo-before")&&!o.classList.contains("snapdom-pseudo-after"));for(let o=0;o<Math.min(e.length,i.length);o++)await x(e[o],i[o]),o%5===0&&await u(1)}function C(t){return t.toLowerCase().replace(/\s+/g,"-").replace(/[^a-z0-9-]/g,"").replace(/-+/g,"-").replace(/^-|-$/g,"")}function g(t){return{normal:"400",bold:"700",italic:"italic",oblique:"italic","ultra-condensed":"50","extra-condensed":"62",condensed:"75","semi-condensed":"87",expanded:"125","semi-expanded":"112","extra-expanded":"150","ultra-expanded":"200"}[t]||t.replace(/%/g,"")}async function $(t=new WeakMap){await document.fonts.ready;let a=[];Array.from(document.styleSheets).forEach(o=>{try{Array.from(o.cssRules).forEach(c=>{c instanceof CSSFontFaceRule&&a.push(c.cssText)})}catch(c){console.warn("Could not access stylesheet rules:",c)}});let r=new Map;document.querySelectorAll("*").forEach(o=>{let c=t.get(o)||window.getComputedStyle(o);t.has(o)||t.set(o,c);let s=c.fontFamily?.split(",")[0]?.trim().replace(/["']/g,"");if(!s)return;let n=g(c.fontWeight||"400"),d=g(c.fontStyle||"normal"),l=g(c.fontStretch||"normal");r.has(s)||r.set(s,new Set),r.get(s).add(`${n}_${d}_${l}`)});let i=[...a];for(let[o,c]of r){let s=C(o);for(let n of c){let[d,l,m]=n.split("_"),f=`fg-f-${s}-w${d}-${l}-s${m}`,R=[`font-family:"${o}",sans-serif`,`font-weight:${d}`,`font-style:${l}`,`font-stretch:${m}%`].filter(Boolean).join(";");i.push(`.${f}{${R}}`)}}return i.push("*{font-family:inherit;font-weight:inherit;font-style:inherit;}"),i.join("")}function N(t,a,r){let e=r.get(a)||window.getComputedStyle(a),i=e.fontFamily?.split(",")[0]?.trim().replace(/["']/g,"");if(!i)return;let o=g(e.fontWeight||"400"),c=g(e.fontStyle||"normal"),s=g(e.fontStretch||"normal"),n=C(i);t.classList.add(`fg-f-${n}-w${o}-${c}-s${s}`)}async function I(t){let a=new Map,r=new WeakMap,e=new WeakMap,i;try{i=b()}catch(n){console.warn("getDefaultStyles failed:",n),i={}}let o;try{o=y(t,a,i,r,e)}catch(n){throw console.warn("deepCloneWithShadow failed:",n),n}try{await v(o)}catch(n){console.warn("inlineImages failed:",n)}try{await A(t,o,r)}catch(n){console.warn("inlineBackgroundImages failed:",n)}try{await x(t,o)}catch(n){console.warn("inlinePseudoElements failed:",n)}let c=E(a),s=Array.from(c.entries()).map(([n,d])=>`.${d}{${n}}`).join("");for(let[n,d]of a.entries()){if(n.tagName==="STYLE")continue;let l=c.get(d);if(l){n.classList.add(l);try{N(n,e.get(n),r)}catch(f){console.warn("applyFontClasses failed for node:",n,f)}}let m=n.style?.backgroundImage;n.removeAttribute("style"),m&&m!=="none"&&(n.style.backgroundImage=m)}return{clone:o,classCSS:s,styleCache:r}}function k(t){return t.replace(/>\s+</g,"><").replace(/\s{2,}/g," ").replace(/\n/g,"").trim()}async function L(t,a=1){let r,e,i;try{({clone:r,classCSS:e,styleCache:i}=await I(t))}catch(f){throw console.error("prepareClone failed:",f),f}let o=t.offsetWidth*a,c=t.offsetHeight*a,s="";try{s=await $(i)}catch(f){console.warn("inlineFonts failed:",f)}let n=document.createElementNS("http://www.w3.org/2000/svg","svg");n.setAttribute("width",o),n.setAttribute("height",c),n.setAttribute("viewBox",`0 0 ${o} ${c}`);let d=document.createElementNS("http://www.w3.org/2000/svg","foreignObject");d.setAttribute("width","100%"),d.setAttribute("height","100%");let l=document.createElement("div");l.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),l.setAttribute("style",`width:${o}px;height:${c}px;`);let m=document.createElement("style");m.textContent=s+"svg{overflow:visible}"+e,l.appendChild(m),l.appendChild(r),d.appendChild(l),n.appendChild(d);try{let f=new XMLSerializer().serializeToString(n);return`data:image/svg+xml;charset=utf-8,${encodeURIComponent(k(f))}`}catch(f){throw console.error("SVG serialization failed:",f),f}}async function p(t,a=1){return await L(t,a)}p.toImg=async function(t,a=1){let r=await p(t,a),e=new Image;return e.src=r,await e.decode(),e};p.toCanvas=async function(t,a=1){let r=await p.toImg(t,a),e=document.createElement("canvas");return e.width=r.width,e.height=r.height,e.getContext("2d").drawImage(r,0,0),e};p.toPng=async function(t,a=1){let r=await p.toCanvas(t,a),e=new Image;return e.src=r.toDataURL("image/png"),await e.decode(),e};p.toJpg=async function(t,a=1,r=.92){let e=await p.toCanvas(t,a),i=new Image;return i.src=e.toDataURL("image/jpeg",r),await i.decode(),i};p.toWebp=async function(t,a=1,r=.92){let e=await p.toCanvas(t,a),i=new Image;return i.src=e.toDataURL("image/webp",r),await i.decode(),i};p.toBlob=async function(t,a=1){let r=await p(t,a),e=decodeURIComponent(r.split(",")[1]);return new Blob([e],{type:"image/svg+xml"})};})();
|
|
1
|
+
(()=>{function E(t,n,s){let o=[];for(let i of t){let e=t.getPropertyValue(i);e&&o.push(`${i}:${e}`)}return o.sort().join(";")}function S(t){let n=new Set(t.values()),s=new Map,o=1;for(let i of n)s.set(i,`c${o++}`);return s}function v(t,n,s,o,i){if(t.tagName==="STYLE")return;i.has(t)||i.set(t,window.getComputedStyle(t));let e=i.get(t),r=E(e,o);s.set(n,r)}function b(t,n,s,o,i){if(t.nodeType===Node.ELEMENT_NODE&&t.getAttribute("data-capture")==="exclude"){let l=document.createElement("div"),a=t.getBoundingClientRect();return l.style.cssText=`display: inline-block; width: ${a.width}px; height: ${a.height}px; visibility: hidden;`,l}if(t.tagName==="IFRAME"){let l=document.createElement("div");return l.textContent="",l.style.cssText=`width: ${t.offsetWidth}px; height: ${t.offsetHeight}px; background: repeating-linear-gradient(45deg, #ddd, #ddd 5px, #f9f9f9 5px, #f9f9f9 10px);display: flex;align-items: center;justify-content: center;font-size: 12px;color: #555; border: 1px solid #aaa;`,l}if(t.nodeType===Node.ELEMENT_NODE&&t.getAttribute("data-capture")==="placeholder"){let l=t.cloneNode(!1);i.set(l,t),v(t,l,n,s,o);let a=document.createElement("div");return a.textContent=t.getAttribute("data-placeholder-text")||"",a.style.cssText="color: #666;font-size: 12px;text-align: center;line-height: 1.4;padding: 0.5em;box-sizing: border-box;",l.appendChild(a),l}if(t.tagName==="CANVAS"){let l=t.toDataURL(),a=document.createElement("img");return a.src=l,a.width=t.width,a.height=t.height,a}if(t.nodeType===Node.TEXT_NODE){if(!t.textContent.trim())return null;if(t.parentElement?.shadowRoot){let a=t.parentElement.tagName.toLowerCase();if(customElements.get(a))return null}return t.cloneNode(!0)}if(t.nodeType!==Node.ELEMENT_NODE)return t.cloneNode(!0);let e=t.cloneNode(!1);i.set(e,t),v(t,e,n,s,o);let r=document.createDocumentFragment();if(t.childNodes.forEach(l=>{let a=b(l,n,s,o,i);a&&r.appendChild(a)}),e.appendChild(r),t.shadowRoot){let l=Array.from(t.shadowRoot.children).filter(d=>d.tagName!=="STYLE").map(d=>b(d,n,s,o,i)).filter(Boolean),a=document.createDocumentFragment();l.forEach(d=>a.appendChild(d)),e.appendChild(a)}return e}function x(t,n=3e3){return new Promise((s,o)=>{let i=setTimeout(()=>{o(new Error("Image load timed out"))},n),e=new Image;e.crossOrigin="anonymous",e.onload=async()=>{clearTimeout(i);try{await e.decode();let r=document.createElement("canvas");r.width=e.width,r.height=e.height,r.getContext("2d").drawImage(e,0,0);try{let a=r.toDataURL("image/png");s(a)}catch{o(new Error("CORS restrictions prevented image capture"))}}catch(r){o(r)}},e.onerror=r=>{clearTimeout(i),o(new Error("Failed to load image: "+(r.message||"Unknown error")))},e.src=t})}async function $(t){let n=Array.from(t.querySelectorAll("img")),s=async o=>{let i=o.src;try{let e=await x(i);o.src=e,o.width||(o.width=o.naturalWidth||100),o.height||(o.height=o.naturalHeight||100)}catch{let e=document.createElement("div");e.style=`width: ${o.width||100}px; height: ${o.height||100}px; background: #ccc; display: inline-block; text-align: center; line-height: ${o.height||100}px; color: #666; font-size: 12px;`,e.innerText="img",o.replaceWith(e)}};for(let o=0;o<n.length;o+=4){let i=n.slice(o,o+4).map(s);await Promise.allSettled(i)}}async function I(t,n,s){let o=[[t,n]];for(;o.length;){let[i,e]=o.shift(),r=s.get(i)||window.getComputedStyle(i);s.has(i)||s.set(i,r);let l=r.getPropertyValue("background-image");if(l&&l.includes("url(")){let c=l.match(/url\(["']?([^"')]+)["']?\)/);if(c?.[1])try{let m=await x(c[1]);e.style.backgroundImage=`url(${m})`}catch{e.style.backgroundImage="none"}}let a=Array.from(i.children),d=Array.from(e.children);for(let c=0;c<Math.min(a.length,d.length);c++)o.push([a[c],d[c]])}}async function C(t,n){let s=["position","top","right","bottom","left","z-index","width","height","min-width","max-width","min-height","max-height","margin","padding","border","border-width","border-style","border-color","border-radius","box-sizing","display","overflow","overflow-x","overflow-y","flex","flex-grow","flex-shrink","flex-basis","flex-direction","justify-content","align-items","align-content","gap","row-gap","column-gap","grid","grid-template-columns","grid-template-rows","background","background-color","background-image","color","font","font-family","font-size","font-weight","font-style","font-stretch","text-align","vertical-align","text-decoration","line-height","letter-spacing","white-space","word-break","text-overflow","visibility","opacity","box-shadow","transform","transition","clip-path","-webkit-text-fill-color"];for(let e of["::before","::after"])try{let r=window.getComputedStyle(t,e),l=r.getPropertyValue("content"),a=r.getPropertyValue("background-image");if(l&&l!=="none"&&l!=='""'&&l!=="''"||a&&a.startsWith("url(")){let d=r.getPropertyValue("font-family"),c=document.createElement("span");c.className=`snapdom-pseudo-${e.replace("::","")}`;let m=P(l),g=d&&/font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(d),y=parseInt(r.getPropertyValue("font-size"))||32,u=parseInt(r.getPropertyValue("font-weight"))||!1,L=r.getPropertyValue("color")||"#000";if(g&&m.length===1){let f=document.createElement("img");f.src=await U(m,d,u,y,L),f.style="display:block;width:100%;height:100%;object-fit:contain;",c.appendChild(f)}else if(m.startsWith("url(")){let f=m.match(/url\(["']?([^"')]+)["']?\)/);if(f?.[1])try{let h=document.createElement("img"),D=await x(f[1]);h.src=D,h.style="display:block;width:100%;height:100%;object-fit:contain;",c.appendChild(h)}catch(h){console.error(`[snapdom] Error in pseudo ${e} for`,t,h)}}else c.textContent=m;for(let f of s){let h=r.getPropertyValue(f);h&&(c.style[f]=h)}c.style.position||(c.style.position="absolute"),e==="::before"?n.insertBefore(c,n.firstChild):n.appendChild(c)}}catch{}let o=Array.from(t.children),i=Array.from(n.children).filter(e=>!e.classList.contains("snapdom-pseudo-before")&&!e.classList.contains("snapdom-pseudo-after"));for(let e=0;e<Math.min(o.length,i.length);e++)await C(o[e],i[e])}function P(t){let n=t.replace(/^['"]|['"]$/g,"");if(n.startsWith("\\"))try{return String.fromCharCode(parseInt(n.replace("\\",""),16))}catch{return n}return n}async function U(t,n,s,o=32,i="#000"){n=n.replace(/^['"]+|['"]+$/g,""),await document.fonts.ready,await document.fonts.load(`${o}px "${n}"`);let e=document.createElement("canvas");e.width=o,e.height=o;let r=e.getContext("2d");return r.font=s?`${s} ${o}px "${n}"`:` ${o}px "${n}"`,r.textAlign="center",r.textBaseline="middle",r.fillStyle=i,r.fillText(t,e.width/2,e.height/2),e.toDataURL()}function k(t){return t.toLowerCase().replace(/\s+/g,"-").replace(/[^a-z0-9-]/g,"").replace(/-+/g,"-").replace(/^-|-$/g,"")}function w(t){return{normal:"400",bold:"700",italic:"italic",oblique:"italic","ultra-condensed":"50","extra-condensed":"62",condensed:"75","semi-condensed":"87",expanded:"125","semi-expanded":"112","extra-expanded":"150","ultra-expanded":"200"}[t]||t.replace(/%/g,"")}async function A(t=new WeakMap){await document.fonts.ready;let n=[];Array.from(document.styleSheets).forEach(e=>{try{Array.from(e.cssRules).forEach(r=>{r instanceof CSSFontFaceRule&&n.push(r.cssText)})}catch(r){console.warn("Could not access stylesheet rules:",r)}});let s=new Map;document.querySelectorAll("*").forEach(e=>{let r=t.get(e)||window.getComputedStyle(e);t.has(e)||t.set(e,r);let l=r.fontFamily?.split(",")[0]?.trim().replace(/["']/g,"");if(!l)return;let a=w(r.fontWeight||"400"),d=w(r.fontStyle||"normal"),c=w(r.fontStretch||"normal");s.has(l)||s.set(l,new Set),s.get(l).add(`${a}_${d}_${c}`)});let i=[...n];for(let[e,r]of s){let l=k(e);for(let a of r){let[d,c,m]=a.split("_"),g=`fg-f-${l}-w${d}-${c}-s${m}`,y=[`font-family:"${e}",sans-serif`,`font-weight:${d}`,`font-style:${c}`,`font-stretch:${m}%`].filter(Boolean).join(";");i.push(`.${g}{${y}}`)}}return i.push("*{font-family:inherit;font-weight:inherit;font-style:inherit;}"),i.join("")}function N(t,n,s){let o=s.get(n)||window.getComputedStyle(n),i=o.fontFamily?.split(",")[0]?.trim().replace(/["']/g,"");if(!i)return;let e=w(o.fontWeight||"400"),r=w(o.fontStyle||"normal"),l=w(o.fontStretch||"normal"),a=k(i);t.classList.add(`fg-f-${a}-w${e}-${r}-s${l}`)}async function z(t){let n=new Map,s=new WeakMap,o=new WeakMap,i;try{i={}}catch(a){console.warn("getDefaultStyles failed:",a),i={}}let e;try{e=b(t,n,i,s,o)}catch(a){throw console.warn("deepCloneWithShadow failed:",a),a}try{await $(e)}catch(a){console.warn("inlineImages failed:",a)}try{await I(t,e,s)}catch(a){console.warn("inlineBackgroundImages failed:",a)}try{await C(t,e)}catch(a){console.warn("inlinePseudoElements failed:",a)}let r=S(n),l=Array.from(r.entries()).map(([a,d])=>`.${d}{${a}}`).join("");for(let[a,d]of n.entries()){if(a.tagName==="STYLE")continue;let c=r.get(d);if(c){a.classList.add(c);try{N(a,o.get(a),s)}catch(g){console.warn("applyFontClasses failed for node:",a,g)}}let m=a.style?.backgroundImage;a.removeAttribute("style"),m&&m!=="none"&&(a.style.backgroundImage=m)}return{clone:e,classCSS:l,styleCache:s}}function R(t){return t.replace(/>\s+</g,"><").replace(/\s{2,}/g," ").replace(/\n/g,"").trim()}async function T(t,n={}){let{scale:s=1}=n,o,i,e;try{({clone:o,classCSS:i,styleCache:e}=await z(t))}catch(u){throw console.error("prepareClone failed:",u),u}let r=t.getBoundingClientRect(),l=r.width*s,a=r.height*s,d="";try{d=await A(e)}catch(u){console.warn("inlineFonts failed:",u)}let c=document.createElementNS("http://www.w3.org/2000/svg","svg");c.setAttribute("width",l),c.setAttribute("height",a),c.setAttribute("viewBox",`0 0 ${l} ${a}`);let m=document.createElementNS("http://www.w3.org/2000/svg","foreignObject");m.setAttribute("width","100%"),m.setAttribute("height","100%");let g=document.createElement("div");g.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),g.setAttribute("style",`width:${l}px;height:${a}px;`);let y=document.createElement("style");y.textContent=d+"svg{overflow:visible}"+i,g.appendChild(y),g.appendChild(o),m.appendChild(g),c.appendChild(m);try{let u=new XMLSerializer().serializeToString(c);return`data:image/svg+xml;charset=utf-8,${encodeURIComponent(R(u))}`}catch(u){throw console.error("SVG serialization failed:",u),u}}async function p(t,n={}){return await T(t,n)}p.toImg=async(t,n={})=>{let s=await p(t,n),o=new Image;return o.src=s,await o.decode(),o};p.toCanvas=async(t,n={})=>{let s=await p.toImg(t,n),o=document.createElement("canvas");return o.width=s.width,o.height=s.height,o.getContext("2d").drawImage(s,0,0),o};p.toPng=async(t,n={})=>{let s=await p.toCanvas(t,n),o=new Image;return o.src=s.toDataURL("image/png"),await o.decode(),o};p.toJpg=async(t,n={})=>{let{backgroundColor:s="#fff",quality:o=.92}=n,i=await p.toCanvas(t,n),e=document.createElement("canvas");e.width=i.width,e.height=i.height;let r=e.getContext("2d");r.fillStyle=s,r.fillRect(0,0,e.width,e.height),r.drawImage(i,0,0);let l=new Image;return l.src=e.toDataURL("image/jpeg",o),await l.decode(),l};p.toWebp=async(t,n={})=>{let{backgroundColor:s="#fff",quality:o=.92}=n,i=await p.toCanvas(t,n),e=document.createElement("canvas");e.width=i.width,e.height=i.height;let r=e.getContext("2d");r.fillStyle=s,r.fillRect(0,0,e.width,e.height),r.drawImage(i,0,0);let l=new Image;return l.src=e.toDataURL("image/webp",o),await l.decode(),l};p.toBlob=async(t,n={})=>{let s=await p(t,n),o=decodeURIComponent(s.split(",")[1]);return new Blob([o],{type:"image/svg+xml"})};})();
|
package/dist/snapdom.min.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function b(){let t=document.createElement("div");document.body.appendChild(t);let a=window.getComputedStyle(t),r={};for(let e of a)r[e]=a.getPropertyValue(e);return document.body.removeChild(t),r}function S(t,a){let r=[];for(let e of Array.from(t)){let i=t.getPropertyValue(e);i&&i!==a[e]&&r.push(`${e}:${i}`)}return r.sort().join(";")}function E(t){let a=new Set(t.values()),r=new Map,e=1;for(let i of a)r.set(i,`c${e++}`);return r}function w(t,a,r,e,i){if(t.tagName==="STYLE")return;i.has(t)||i.set(t,window.getComputedStyle(t));let o=i.get(t),c=S(o,e);r.set(a,c)}function y(t,a,r,e,i){if(t.nodeType===Node.ELEMENT_NODE&&t.getAttribute("data-capture")==="exclude"){let s=document.createElement("div"),n=t.getBoundingClientRect();return s.style.cssText=`display: inline-block; width: ${n.width}px; height: ${n.height}px; visibility: hidden;`,s}if(t.tagName==="IFRAME"){let s=document.createElement("div");return s.textContent="",s.style.cssText=`width: ${t.offsetWidth}px; height: ${t.offsetHeight}px; background: repeating-linear-gradient(45deg, #ddd, #ddd 5px, #f9f9f9 5px, #f9f9f9 10px);display: flex;align-items: center;justify-content: center;font-size: 12px;color: #555; border: 1px solid #aaa;`,s}if(t.nodeType===Node.ELEMENT_NODE&&t.getAttribute("data-capture")==="placeholder"){let s=t.cloneNode(!1);i.set(s,t),w(t,s,a,r,e);let n=document.createElement("div");return n.textContent=t.getAttribute("data-placeholder-text")||"",n.style.cssText="color: #666;font-size: 12px;text-align: center;line-height: 1.4;padding: 0.5em;box-sizing: border-box;",s.appendChild(n),s}if(t.tagName==="CANVAS"){let s=t.toDataURL(),n=document.createElement("img");return n.src=s,n.width=t.width,n.height=t.height,n}if(t.nodeType===Node.TEXT_NODE){if(!t.textContent.trim())return null;if(t.parentElement?.shadowRoot){let n=t.parentElement.tagName.toLowerCase();if(customElements.get(n))return null}return t.cloneNode(!0)}if(t.nodeType!==Node.ELEMENT_NODE)return t.cloneNode(!0);let o=t.cloneNode(!1);i.set(o,t),w(t,o,a,r,e);let c=document.createDocumentFragment();if(t.childNodes.forEach(s=>{let n=y(s,a,r,e,i);n&&c.appendChild(n)}),o.appendChild(c),t.shadowRoot){let s=Array.from(t.shadowRoot.children).filter(d=>d.tagName!=="STYLE").map(d=>y(d,a,r,e,i)).filter(Boolean),n=document.createDocumentFragment();s.forEach(d=>n.appendChild(d)),o.appendChild(n)}return o.querySelectorAll("style").forEach(s=>s.removeAttribute("style")),o.tagName==="STYLE"&&o.removeAttribute("style"),o}function h(t,a=3e3){return new Promise((r,e)=>{let i=setTimeout(()=>{e(new Error("Image load timed out"))},a),o=new Image;o.crossOrigin="anonymous",o.onload=async()=>{clearTimeout(i);try{await o.decode();let c=document.createElement("canvas");c.width=o.width,c.height=o.height,c.getContext("2d").drawImage(o,0,0);try{let n=c.toDataURL("image/png");r(n)}catch{e(new Error("CORS restrictions prevented image capture"))}}catch(c){e(c)}},o.onerror=c=>{clearTimeout(i),e(new Error("Failed to load image: "+(c.message||"Unknown error")))},o.src=t})}function u(t=0){return new Promise(a=>setTimeout(a,t))}async function v(t){let a=Array.from(t.querySelectorAll("img")),r=async e=>{let i=e.src;try{let o=await h(i);e.src=o,e.width||(e.width=e.naturalWidth||100),e.height||(e.height=e.naturalHeight||100)}catch{let o=document.createElement("div");o.style=`width: ${e.width||100}px; height: ${e.height||100}px; background: #ccc; display: inline-block; text-align: center; line-height: ${e.height||100}px; color: #666; font-size: 12px;`,o.innerText="img",e.replaceWith(o)}};for(let e=0;e<a.length;e+=4){let i=a.slice(e,e+4).map(r);await Promise.allSettled(i),await u(1)}}async function A(t,a,r){let e=[[t,a]];for(;e.length;){let[i,o]=e.shift(),c=r.get(i)||window.getComputedStyle(i);r.has(i)||r.set(i,c);let s=c.getPropertyValue("background-image");if(s&&s.includes("url(")){let l=s.match(/url\(["']?([^"')]+)["']?\)/);if(l?.[1])try{let m=await h(l[1]);o.style.backgroundImage=`url(${m})`}catch{o.style.backgroundImage="none"}}let n=Array.from(i.children),d=Array.from(o.children);for(let l=0;l<Math.min(n.length,d.length);l++)e.push([n[l],d[l]]);await u(1)}}async function x(t,a){let r=["position","top","right","bottom","left","z-index","width","height","min-width","max-width","min-height","max-height","margin","padding","border","border-radius","background","background-color","background-image","color","font","font-family","font-size","font-weight","text-align","text-decoration","line-height","letter-spacing","display","visibility","opacity","overflow","box-shadow","transform","transition","flex","grid"];for(let o of["::before","::after"])try{let c=window.getComputedStyle(t,o),s=c.getPropertyValue("content");if(s&&s!=="none"&&s!=='""'&&s!=="''"){let n=document.createElement("span");n.className=`snapdom-pseudo-${o.replace("::","")}`;let d=s.replace(/^\"|\"$/g,"");if(d.startsWith("url(")){let l=d.match(/url\(["']?([^"')]+)["']?\)/);if(l?.[1])try{let m=document.createElement("img"),f=await h(l[1]);m.src=f,m.style="display:block;width:100%;height:100%;object-fit:contain;",n.appendChild(m)}catch{}}else n.textContent=d;for(let l of r){let m=c.getPropertyValue(l);m&&(n.style[l]=m)}n.style.position||(n.style.position="absolute"),o==="::before"?a.insertBefore(n,a.firstChild):a.appendChild(n)}}catch{}let e=Array.from(t.children),i=Array.from(a.children).filter(o=>!o.classList.contains("snapdom-pseudo-before")&&!o.classList.contains("snapdom-pseudo-after"));for(let o=0;o<Math.min(e.length,i.length);o++)await x(e[o],i[o]),o%5===0&&await u(1)}function C(t){return t.toLowerCase().replace(/\s+/g,"-").replace(/[^a-z0-9-]/g,"").replace(/-+/g,"-").replace(/^-|-$/g,"")}function g(t){return{normal:"400",bold:"700",italic:"italic",oblique:"italic","ultra-condensed":"50","extra-condensed":"62",condensed:"75","semi-condensed":"87",expanded:"125","semi-expanded":"112","extra-expanded":"150","ultra-expanded":"200"}[t]||t.replace(/%/g,"")}async function $(t=new WeakMap){await document.fonts.ready;let a=[];Array.from(document.styleSheets).forEach(o=>{try{Array.from(o.cssRules).forEach(c=>{c instanceof CSSFontFaceRule&&a.push(c.cssText)})}catch(c){console.warn("Could not access stylesheet rules:",c)}});let r=new Map;document.querySelectorAll("*").forEach(o=>{let c=t.get(o)||window.getComputedStyle(o);t.has(o)||t.set(o,c);let s=c.fontFamily?.split(",")[0]?.trim().replace(/["']/g,"");if(!s)return;let n=g(c.fontWeight||"400"),d=g(c.fontStyle||"normal"),l=g(c.fontStretch||"normal");r.has(s)||r.set(s,new Set),r.get(s).add(`${n}_${d}_${l}`)});let i=[...a];for(let[o,c]of r){let s=C(o);for(let n of c){let[d,l,m]=n.split("_"),f=`fg-f-${s}-w${d}-${l}-s${m}`,R=[`font-family:"${o}",sans-serif`,`font-weight:${d}`,`font-style:${l}`,`font-stretch:${m}%`].filter(Boolean).join(";");i.push(`.${f}{${R}}`)}}return i.push("*{font-family:inherit;font-weight:inherit;font-style:inherit;}"),i.join("")}function N(t,a,r){let e=r.get(a)||window.getComputedStyle(a),i=e.fontFamily?.split(",")[0]?.trim().replace(/["']/g,"");if(!i)return;let o=g(e.fontWeight||"400"),c=g(e.fontStyle||"normal"),s=g(e.fontStretch||"normal"),n=C(i);t.classList.add(`fg-f-${n}-w${o}-${c}-s${s}`)}async function I(t){let a=new Map,r=new WeakMap,e=new WeakMap,i;try{i=b()}catch(n){console.warn("getDefaultStyles failed:",n),i={}}let o;try{o=y(t,a,i,r,e)}catch(n){throw console.warn("deepCloneWithShadow failed:",n),n}try{await v(o)}catch(n){console.warn("inlineImages failed:",n)}try{await A(t,o,r)}catch(n){console.warn("inlineBackgroundImages failed:",n)}try{await x(t,o)}catch(n){console.warn("inlinePseudoElements failed:",n)}let c=E(a),s=Array.from(c.entries()).map(([n,d])=>`.${d}{${n}}`).join("");for(let[n,d]of a.entries()){if(n.tagName==="STYLE")continue;let l=c.get(d);if(l){n.classList.add(l);try{N(n,e.get(n),r)}catch(f){console.warn("applyFontClasses failed for node:",n,f)}}let m=n.style?.backgroundImage;n.removeAttribute("style"),m&&m!=="none"&&(n.style.backgroundImage=m)}return{clone:o,classCSS:s,styleCache:r}}function k(t){return t.replace(/>\s+</g,"><").replace(/\s{2,}/g," ").replace(/\n/g,"").trim()}async function L(t,a=1){let r,e,i;try{({clone:r,classCSS:e,styleCache:i}=await I(t))}catch(f){throw console.error("prepareClone failed:",f),f}let o=t.offsetWidth*a,c=t.offsetHeight*a,s="";try{s=await $(i)}catch(f){console.warn("inlineFonts failed:",f)}let n=document.createElementNS("http://www.w3.org/2000/svg","svg");n.setAttribute("width",o),n.setAttribute("height",c),n.setAttribute("viewBox",`0 0 ${o} ${c}`);let d=document.createElementNS("http://www.w3.org/2000/svg","foreignObject");d.setAttribute("width","100%"),d.setAttribute("height","100%");let l=document.createElement("div");l.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),l.setAttribute("style",`width:${o}px;height:${c}px;`);let m=document.createElement("style");m.textContent=s+"svg{overflow:visible}"+e,l.appendChild(m),l.appendChild(r),d.appendChild(l),n.appendChild(d);try{let f=new XMLSerializer().serializeToString(n);return`data:image/svg+xml;charset=utf-8,${encodeURIComponent(k(f))}`}catch(f){throw console.error("SVG serialization failed:",f),f}}async function p(t,a=1){return await L(t,a)}p.toImg=async function(t,a=1){let r=await p(t,a),e=new Image;return e.src=r,await e.decode(),e};p.toCanvas=async function(t,a=1){let r=await p.toImg(t,a),e=document.createElement("canvas");return e.width=r.width,e.height=r.height,e.getContext("2d").drawImage(r,0,0),e};p.toPng=async function(t,a=1){let r=await p.toCanvas(t,a),e=new Image;return e.src=r.toDataURL("image/png"),await e.decode(),e};p.toJpg=async function(t,a=1,r=.92){let e=await p.toCanvas(t,a),i=new Image;return i.src=e.toDataURL("image/jpeg",r),await i.decode(),i};p.toWebp=async function(t,a=1,r=.92){let e=await p.toCanvas(t,a),i=new Image;return i.src=e.toDataURL("image/webp",r),await i.decode(),i};p.toBlob=async function(t,a=1){let r=await p(t,a),e=decodeURIComponent(r.split(",")[1]);return new Blob([e],{type:"image/svg+xml"})};export{p as snapdom};
|
|
1
|
+
function E(t,n,s){let o=[];for(let i of t){let e=t.getPropertyValue(i);e&&o.push(`${i}:${e}`)}return o.sort().join(";")}function S(t){let n=new Set(t.values()),s=new Map,o=1;for(let i of n)s.set(i,`c${o++}`);return s}function v(t,n,s,o,i){if(t.tagName==="STYLE")return;i.has(t)||i.set(t,window.getComputedStyle(t));let e=i.get(t),r=E(e,o);s.set(n,r)}function b(t,n,s,o,i){if(t.nodeType===Node.ELEMENT_NODE&&t.getAttribute("data-capture")==="exclude"){let l=document.createElement("div"),a=t.getBoundingClientRect();return l.style.cssText=`display: inline-block; width: ${a.width}px; height: ${a.height}px; visibility: hidden;`,l}if(t.tagName==="IFRAME"){let l=document.createElement("div");return l.textContent="",l.style.cssText=`width: ${t.offsetWidth}px; height: ${t.offsetHeight}px; background: repeating-linear-gradient(45deg, #ddd, #ddd 5px, #f9f9f9 5px, #f9f9f9 10px);display: flex;align-items: center;justify-content: center;font-size: 12px;color: #555; border: 1px solid #aaa;`,l}if(t.nodeType===Node.ELEMENT_NODE&&t.getAttribute("data-capture")==="placeholder"){let l=t.cloneNode(!1);i.set(l,t),v(t,l,n,s,o);let a=document.createElement("div");return a.textContent=t.getAttribute("data-placeholder-text")||"",a.style.cssText="color: #666;font-size: 12px;text-align: center;line-height: 1.4;padding: 0.5em;box-sizing: border-box;",l.appendChild(a),l}if(t.tagName==="CANVAS"){let l=t.toDataURL(),a=document.createElement("img");return a.src=l,a.width=t.width,a.height=t.height,a}if(t.nodeType===Node.TEXT_NODE){if(!t.textContent.trim())return null;if(t.parentElement?.shadowRoot){let a=t.parentElement.tagName.toLowerCase();if(customElements.get(a))return null}return t.cloneNode(!0)}if(t.nodeType!==Node.ELEMENT_NODE)return t.cloneNode(!0);let e=t.cloneNode(!1);i.set(e,t),v(t,e,n,s,o);let r=document.createDocumentFragment();if(t.childNodes.forEach(l=>{let a=b(l,n,s,o,i);a&&r.appendChild(a)}),e.appendChild(r),t.shadowRoot){let l=Array.from(t.shadowRoot.children).filter(d=>d.tagName!=="STYLE").map(d=>b(d,n,s,o,i)).filter(Boolean),a=document.createDocumentFragment();l.forEach(d=>a.appendChild(d)),e.appendChild(a)}return e}function x(t,n=3e3){return new Promise((s,o)=>{let i=setTimeout(()=>{o(new Error("Image load timed out"))},n),e=new Image;e.crossOrigin="anonymous",e.onload=async()=>{clearTimeout(i);try{await e.decode();let r=document.createElement("canvas");r.width=e.width,r.height=e.height,r.getContext("2d").drawImage(e,0,0);try{let a=r.toDataURL("image/png");s(a)}catch{o(new Error("CORS restrictions prevented image capture"))}}catch(r){o(r)}},e.onerror=r=>{clearTimeout(i),o(new Error("Failed to load image: "+(r.message||"Unknown error")))},e.src=t})}async function $(t){let n=Array.from(t.querySelectorAll("img")),s=async o=>{let i=o.src;try{let e=await x(i);o.src=e,o.width||(o.width=o.naturalWidth||100),o.height||(o.height=o.naturalHeight||100)}catch{let e=document.createElement("div");e.style=`width: ${o.width||100}px; height: ${o.height||100}px; background: #ccc; display: inline-block; text-align: center; line-height: ${o.height||100}px; color: #666; font-size: 12px;`,e.innerText="img",o.replaceWith(e)}};for(let o=0;o<n.length;o+=4){let i=n.slice(o,o+4).map(s);await Promise.allSettled(i)}}async function I(t,n,s){let o=[[t,n]];for(;o.length;){let[i,e]=o.shift(),r=s.get(i)||window.getComputedStyle(i);s.has(i)||s.set(i,r);let l=r.getPropertyValue("background-image");if(l&&l.includes("url(")){let c=l.match(/url\(["']?([^"')]+)["']?\)/);if(c?.[1])try{let m=await x(c[1]);e.style.backgroundImage=`url(${m})`}catch{e.style.backgroundImage="none"}}let a=Array.from(i.children),d=Array.from(e.children);for(let c=0;c<Math.min(a.length,d.length);c++)o.push([a[c],d[c]])}}async function C(t,n){let s=["position","top","right","bottom","left","z-index","width","height","min-width","max-width","min-height","max-height","margin","padding","border","border-width","border-style","border-color","border-radius","box-sizing","display","overflow","overflow-x","overflow-y","flex","flex-grow","flex-shrink","flex-basis","flex-direction","justify-content","align-items","align-content","gap","row-gap","column-gap","grid","grid-template-columns","grid-template-rows","background","background-color","background-image","color","font","font-family","font-size","font-weight","font-style","font-stretch","text-align","vertical-align","text-decoration","line-height","letter-spacing","white-space","word-break","text-overflow","visibility","opacity","box-shadow","transform","transition","clip-path","-webkit-text-fill-color"];for(let e of["::before","::after"])try{let r=window.getComputedStyle(t,e),l=r.getPropertyValue("content"),a=r.getPropertyValue("background-image");if(l&&l!=="none"&&l!=='""'&&l!=="''"||a&&a.startsWith("url(")){let d=r.getPropertyValue("font-family"),c=document.createElement("span");c.className=`snapdom-pseudo-${e.replace("::","")}`;let m=P(l),g=d&&/font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(d),y=parseInt(r.getPropertyValue("font-size"))||32,u=parseInt(r.getPropertyValue("font-weight"))||!1,L=r.getPropertyValue("color")||"#000";if(g&&m.length===1){let f=document.createElement("img");f.src=await U(m,d,u,y,L),f.style="display:block;width:100%;height:100%;object-fit:contain;",c.appendChild(f)}else if(m.startsWith("url(")){let f=m.match(/url\(["']?([^"')]+)["']?\)/);if(f?.[1])try{let h=document.createElement("img"),D=await x(f[1]);h.src=D,h.style="display:block;width:100%;height:100%;object-fit:contain;",c.appendChild(h)}catch(h){console.error(`[snapdom] Error in pseudo ${e} for`,t,h)}}else c.textContent=m;for(let f of s){let h=r.getPropertyValue(f);h&&(c.style[f]=h)}c.style.position||(c.style.position="absolute"),e==="::before"?n.insertBefore(c,n.firstChild):n.appendChild(c)}}catch{}let o=Array.from(t.children),i=Array.from(n.children).filter(e=>!e.classList.contains("snapdom-pseudo-before")&&!e.classList.contains("snapdom-pseudo-after"));for(let e=0;e<Math.min(o.length,i.length);e++)await C(o[e],i[e])}function P(t){let n=t.replace(/^['"]|['"]$/g,"");if(n.startsWith("\\"))try{return String.fromCharCode(parseInt(n.replace("\\",""),16))}catch{return n}return n}async function U(t,n,s,o=32,i="#000"){n=n.replace(/^['"]+|['"]+$/g,""),await document.fonts.ready,await document.fonts.load(`${o}px "${n}"`);let e=document.createElement("canvas");e.width=o,e.height=o;let r=e.getContext("2d");return r.font=s?`${s} ${o}px "${n}"`:` ${o}px "${n}"`,r.textAlign="center",r.textBaseline="middle",r.fillStyle=i,r.fillText(t,e.width/2,e.height/2),e.toDataURL()}function k(t){return t.toLowerCase().replace(/\s+/g,"-").replace(/[^a-z0-9-]/g,"").replace(/-+/g,"-").replace(/^-|-$/g,"")}function w(t){return{normal:"400",bold:"700",italic:"italic",oblique:"italic","ultra-condensed":"50","extra-condensed":"62",condensed:"75","semi-condensed":"87",expanded:"125","semi-expanded":"112","extra-expanded":"150","ultra-expanded":"200"}[t]||t.replace(/%/g,"")}async function A(t=new WeakMap){await document.fonts.ready;let n=[];Array.from(document.styleSheets).forEach(e=>{try{Array.from(e.cssRules).forEach(r=>{r instanceof CSSFontFaceRule&&n.push(r.cssText)})}catch(r){console.warn("Could not access stylesheet rules:",r)}});let s=new Map;document.querySelectorAll("*").forEach(e=>{let r=t.get(e)||window.getComputedStyle(e);t.has(e)||t.set(e,r);let l=r.fontFamily?.split(",")[0]?.trim().replace(/["']/g,"");if(!l)return;let a=w(r.fontWeight||"400"),d=w(r.fontStyle||"normal"),c=w(r.fontStretch||"normal");s.has(l)||s.set(l,new Set),s.get(l).add(`${a}_${d}_${c}`)});let i=[...n];for(let[e,r]of s){let l=k(e);for(let a of r){let[d,c,m]=a.split("_"),g=`fg-f-${l}-w${d}-${c}-s${m}`,y=[`font-family:"${e}",sans-serif`,`font-weight:${d}`,`font-style:${c}`,`font-stretch:${m}%`].filter(Boolean).join(";");i.push(`.${g}{${y}}`)}}return i.push("*{font-family:inherit;font-weight:inherit;font-style:inherit;}"),i.join("")}function N(t,n,s){let o=s.get(n)||window.getComputedStyle(n),i=o.fontFamily?.split(",")[0]?.trim().replace(/["']/g,"");if(!i)return;let e=w(o.fontWeight||"400"),r=w(o.fontStyle||"normal"),l=w(o.fontStretch||"normal"),a=k(i);t.classList.add(`fg-f-${a}-w${e}-${r}-s${l}`)}async function z(t){let n=new Map,s=new WeakMap,o=new WeakMap,i;try{i={}}catch(a){console.warn("getDefaultStyles failed:",a),i={}}let e;try{e=b(t,n,i,s,o)}catch(a){throw console.warn("deepCloneWithShadow failed:",a),a}try{await $(e)}catch(a){console.warn("inlineImages failed:",a)}try{await I(t,e,s)}catch(a){console.warn("inlineBackgroundImages failed:",a)}try{await C(t,e)}catch(a){console.warn("inlinePseudoElements failed:",a)}let r=S(n),l=Array.from(r.entries()).map(([a,d])=>`.${d}{${a}}`).join("");for(let[a,d]of n.entries()){if(a.tagName==="STYLE")continue;let c=r.get(d);if(c){a.classList.add(c);try{N(a,o.get(a),s)}catch(g){console.warn("applyFontClasses failed for node:",a,g)}}let m=a.style?.backgroundImage;a.removeAttribute("style"),m&&m!=="none"&&(a.style.backgroundImage=m)}return{clone:e,classCSS:l,styleCache:s}}function R(t){return t.replace(/>\s+</g,"><").replace(/\s{2,}/g," ").replace(/\n/g,"").trim()}async function T(t,n={}){let{scale:s=1}=n,o,i,e;try{({clone:o,classCSS:i,styleCache:e}=await z(t))}catch(u){throw console.error("prepareClone failed:",u),u}let r=t.getBoundingClientRect(),l=r.width*s,a=r.height*s,d="";try{d=await A(e)}catch(u){console.warn("inlineFonts failed:",u)}let c=document.createElementNS("http://www.w3.org/2000/svg","svg");c.setAttribute("width",l),c.setAttribute("height",a),c.setAttribute("viewBox",`0 0 ${l} ${a}`);let m=document.createElementNS("http://www.w3.org/2000/svg","foreignObject");m.setAttribute("width","100%"),m.setAttribute("height","100%");let g=document.createElement("div");g.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),g.setAttribute("style",`width:${l}px;height:${a}px;`);let y=document.createElement("style");y.textContent=d+"svg{overflow:visible}"+i,g.appendChild(y),g.appendChild(o),m.appendChild(g),c.appendChild(m);try{let u=new XMLSerializer().serializeToString(c);return`data:image/svg+xml;charset=utf-8,${encodeURIComponent(R(u))}`}catch(u){throw console.error("SVG serialization failed:",u),u}}async function p(t,n={}){return await T(t,n)}p.toImg=async(t,n={})=>{let s=await p(t,n),o=new Image;return o.src=s,await o.decode(),o};p.toCanvas=async(t,n={})=>{let s=await p.toImg(t,n),o=document.createElement("canvas");return o.width=s.width,o.height=s.height,o.getContext("2d").drawImage(s,0,0),o};p.toPng=async(t,n={})=>{let s=await p.toCanvas(t,n),o=new Image;return o.src=s.toDataURL("image/png"),await o.decode(),o};p.toJpg=async(t,n={})=>{let{backgroundColor:s="#fff",quality:o=.92}=n,i=await p.toCanvas(t,n),e=document.createElement("canvas");e.width=i.width,e.height=i.height;let r=e.getContext("2d");r.fillStyle=s,r.fillRect(0,0,e.width,e.height),r.drawImage(i,0,0);let l=new Image;return l.src=e.toDataURL("image/jpeg",o),await l.decode(),l};p.toWebp=async(t,n={})=>{let{backgroundColor:s="#fff",quality:o=.92}=n,i=await p.toCanvas(t,n),e=document.createElement("canvas");e.width=i.width,e.height=i.height;let r=e.getContext("2d");r.fillStyle=s,r.fillRect(0,0,e.width,e.height),r.drawImage(i,0,0);let l=new Image;return l.src=e.toDataURL("image/webp",o),await l.decode(),l};p.toBlob=async(t,n={})=>{let s=await p(t,n),o=decodeURIComponent(s.split(",")[1]);return new Blob([o],{type:"image/svg+xml"})};export{p as snapdom};
|
package/dist/snapdom.mjs
CHANGED
|
@@ -1,28 +1,17 @@
|
|
|
1
1
|
|
|
2
2
|
/*
|
|
3
3
|
* snapdom
|
|
4
|
-
* v.0.9.
|
|
4
|
+
* v.0.9.2
|
|
5
5
|
* Author Juan Martin Muda
|
|
6
6
|
* License MIT
|
|
7
7
|
**/
|
|
8
8
|
|
|
9
9
|
// src/utils/cssTools.js
|
|
10
|
-
function
|
|
11
|
-
const defaultEl = document.createElement("div");
|
|
12
|
-
document.body.appendChild(defaultEl);
|
|
13
|
-
const styles = window.getComputedStyle(defaultEl);
|
|
14
|
-
const defaults = {};
|
|
15
|
-
for (let prop of styles) {
|
|
16
|
-
defaults[prop] = styles.getPropertyValue(prop);
|
|
17
|
-
}
|
|
18
|
-
document.body.removeChild(defaultEl);
|
|
19
|
-
return defaults;
|
|
20
|
-
}
|
|
21
|
-
function getStyleKey(style, defaults) {
|
|
10
|
+
function getStyleKey(style, defaults, mode) {
|
|
22
11
|
const entries = [];
|
|
23
|
-
for (let prop of
|
|
12
|
+
for (let prop of style) {
|
|
24
13
|
const value = style.getPropertyValue(prop);
|
|
25
|
-
if (value
|
|
14
|
+
if (value) {
|
|
26
15
|
entries.push(`${prop}:${value}`);
|
|
27
16
|
}
|
|
28
17
|
}
|
|
@@ -106,8 +95,6 @@ function deepCloneWithShadow(node, styleMap, defaults, styleCache, nodeMap) {
|
|
|
106
95
|
shadowContent.forEach((child) => shadowFrag.appendChild(child));
|
|
107
96
|
clone.appendChild(shadowFrag);
|
|
108
97
|
}
|
|
109
|
-
clone.querySelectorAll("style").forEach((tag) => tag.removeAttribute("style"));
|
|
110
|
-
if (clone.tagName === "STYLE") clone.removeAttribute("style");
|
|
111
98
|
return clone;
|
|
112
99
|
}
|
|
113
100
|
|
|
@@ -146,11 +133,6 @@ function fetchImageAsDataURL(src, timeout = 3e3) {
|
|
|
146
133
|
});
|
|
147
134
|
}
|
|
148
135
|
|
|
149
|
-
// src/utils/delay.js
|
|
150
|
-
function delay(ms = 0) {
|
|
151
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
152
|
-
}
|
|
153
|
-
|
|
154
136
|
// src/modules/images.js
|
|
155
137
|
async function inlineImages(clone) {
|
|
156
138
|
const imgs = Array.from(clone.querySelectorAll("img"));
|
|
@@ -171,7 +153,6 @@ async function inlineImages(clone) {
|
|
|
171
153
|
for (let i = 0; i < imgs.length; i += 4) {
|
|
172
154
|
const group = imgs.slice(i, i + 4).map(processImg);
|
|
173
155
|
await Promise.allSettled(group);
|
|
174
|
-
await delay(1);
|
|
175
156
|
}
|
|
176
157
|
}
|
|
177
158
|
|
|
@@ -199,7 +180,6 @@ async function inlineBackgroundImages(source, clone, styleCache) {
|
|
|
199
180
|
for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
|
|
200
181
|
queue.push([sChildren[i], cChildren[i]]);
|
|
201
182
|
}
|
|
202
|
-
await delay(1);
|
|
203
183
|
}
|
|
204
184
|
}
|
|
205
185
|
|
|
@@ -221,7 +201,29 @@ async function inlinePseudoElements(source, clone) {
|
|
|
221
201
|
"margin",
|
|
222
202
|
"padding",
|
|
223
203
|
"border",
|
|
204
|
+
"border-width",
|
|
205
|
+
"border-style",
|
|
206
|
+
"border-color",
|
|
224
207
|
"border-radius",
|
|
208
|
+
"box-sizing",
|
|
209
|
+
"display",
|
|
210
|
+
"overflow",
|
|
211
|
+
"overflow-x",
|
|
212
|
+
"overflow-y",
|
|
213
|
+
"flex",
|
|
214
|
+
"flex-grow",
|
|
215
|
+
"flex-shrink",
|
|
216
|
+
"flex-basis",
|
|
217
|
+
"flex-direction",
|
|
218
|
+
"justify-content",
|
|
219
|
+
"align-items",
|
|
220
|
+
"align-content",
|
|
221
|
+
"gap",
|
|
222
|
+
"row-gap",
|
|
223
|
+
"column-gap",
|
|
224
|
+
"grid",
|
|
225
|
+
"grid-template-columns",
|
|
226
|
+
"grid-template-rows",
|
|
225
227
|
"background",
|
|
226
228
|
"background-color",
|
|
227
229
|
"background-image",
|
|
@@ -230,29 +232,44 @@ async function inlinePseudoElements(source, clone) {
|
|
|
230
232
|
"font-family",
|
|
231
233
|
"font-size",
|
|
232
234
|
"font-weight",
|
|
235
|
+
"font-style",
|
|
236
|
+
"font-stretch",
|
|
233
237
|
"text-align",
|
|
238
|
+
"vertical-align",
|
|
234
239
|
"text-decoration",
|
|
235
240
|
"line-height",
|
|
236
241
|
"letter-spacing",
|
|
237
|
-
"
|
|
242
|
+
"white-space",
|
|
243
|
+
"word-break",
|
|
244
|
+
"text-overflow",
|
|
238
245
|
"visibility",
|
|
239
246
|
"opacity",
|
|
240
|
-
"overflow",
|
|
241
247
|
"box-shadow",
|
|
242
248
|
"transform",
|
|
243
249
|
"transition",
|
|
244
|
-
"
|
|
245
|
-
"
|
|
250
|
+
"clip-path",
|
|
251
|
+
"-webkit-text-fill-color"
|
|
246
252
|
];
|
|
247
253
|
for (const pseudo of ["::before", "::after"]) {
|
|
248
254
|
try {
|
|
249
255
|
const style = window.getComputedStyle(source, pseudo);
|
|
250
256
|
const content = style.getPropertyValue("content");
|
|
251
|
-
|
|
257
|
+
const bg = style.getPropertyValue("background-image");
|
|
258
|
+
if (content && content !== "none" && content !== '""' && content !== "''" || bg && bg.startsWith("url(")) {
|
|
259
|
+
const fontFamily = style.getPropertyValue("font-family");
|
|
252
260
|
const pseudoEl = document.createElement("span");
|
|
253
261
|
pseudoEl.className = `snapdom-pseudo-${pseudo.replace("::", "")}`;
|
|
254
|
-
let cleanContent = content
|
|
255
|
-
|
|
262
|
+
let cleanContent = parseContent(content);
|
|
263
|
+
const isIconFont = fontFamily && /font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(fontFamily);
|
|
264
|
+
const fontSize = parseInt(style.getPropertyValue("font-size")) || 32;
|
|
265
|
+
const fontWeight = parseInt(style.getPropertyValue("font-weight")) || false;
|
|
266
|
+
const color = style.getPropertyValue("color") || "#000";
|
|
267
|
+
if (isIconFont && cleanContent.length === 1) {
|
|
268
|
+
const imgEl = document.createElement("img");
|
|
269
|
+
imgEl.src = await iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color);
|
|
270
|
+
imgEl.style = "display:block;width:100%;height:100%;object-fit:contain;";
|
|
271
|
+
pseudoEl.appendChild(imgEl);
|
|
272
|
+
} else if (cleanContent.startsWith("url(")) {
|
|
256
273
|
const match = cleanContent.match(/url\(["']?([^"')]+)["']?\)/);
|
|
257
274
|
if (match?.[1]) {
|
|
258
275
|
try {
|
|
@@ -262,6 +279,7 @@ async function inlinePseudoElements(source, clone) {
|
|
|
262
279
|
imgEl.style = "display:block;width:100%;height:100%;object-fit:contain;";
|
|
263
280
|
pseudoEl.appendChild(imgEl);
|
|
264
281
|
} catch (e) {
|
|
282
|
+
console.error(`[snapdom] Error in pseudo ${pseudo} for`, source, e);
|
|
265
283
|
}
|
|
266
284
|
}
|
|
267
285
|
} else {
|
|
@@ -289,9 +307,34 @@ async function inlinePseudoElements(source, clone) {
|
|
|
289
307
|
);
|
|
290
308
|
for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
|
|
291
309
|
await inlinePseudoElements(sChildren[i], cChildren[i]);
|
|
292
|
-
if (i % 5 === 0) await delay(1);
|
|
293
310
|
}
|
|
294
311
|
}
|
|
312
|
+
function parseContent(content) {
|
|
313
|
+
let clean = content.replace(/^['"]|['"]$/g, "");
|
|
314
|
+
if (clean.startsWith("\\")) {
|
|
315
|
+
try {
|
|
316
|
+
return String.fromCharCode(parseInt(clean.replace("\\", ""), 16));
|
|
317
|
+
} catch {
|
|
318
|
+
return clean;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return clean;
|
|
322
|
+
}
|
|
323
|
+
async function iconToImage(unicodeChar, fontFamily, fontWeight, fontSize = 32, color = "#000") {
|
|
324
|
+
fontFamily = fontFamily.replace(/^['"]+|['"]+$/g, "");
|
|
325
|
+
await document.fonts.ready;
|
|
326
|
+
await document.fonts.load(`${fontSize}px "${fontFamily}"`);
|
|
327
|
+
const canvas = document.createElement("canvas");
|
|
328
|
+
canvas.width = fontSize;
|
|
329
|
+
canvas.height = fontSize;
|
|
330
|
+
const ctx = canvas.getContext("2d");
|
|
331
|
+
ctx.font = fontWeight ? `${fontWeight} ${fontSize}px "${fontFamily}"` : ` ${fontSize}px "${fontFamily}"`;
|
|
332
|
+
ctx.textAlign = "center";
|
|
333
|
+
ctx.textBaseline = "middle";
|
|
334
|
+
ctx.fillStyle = color;
|
|
335
|
+
ctx.fillText(unicodeChar, canvas.width / 2, canvas.height / 2);
|
|
336
|
+
return canvas.toDataURL();
|
|
337
|
+
}
|
|
295
338
|
|
|
296
339
|
// src/utils/normalize.js
|
|
297
340
|
function normalizeFontName(str) {
|
|
@@ -381,7 +424,7 @@ async function prepareClone(element) {
|
|
|
381
424
|
const nodeMap = /* @__PURE__ */ new WeakMap();
|
|
382
425
|
let defaults;
|
|
383
426
|
try {
|
|
384
|
-
defaults =
|
|
427
|
+
defaults = {};
|
|
385
428
|
} catch (e) {
|
|
386
429
|
console.warn("getDefaultStyles failed:", e);
|
|
387
430
|
defaults = {};
|
|
@@ -434,7 +477,8 @@ function minifySVG(svgString) {
|
|
|
434
477
|
}
|
|
435
478
|
|
|
436
479
|
// src/core/capture.js
|
|
437
|
-
async function capture(element,
|
|
480
|
+
async function capture(element, options = {}) {
|
|
481
|
+
const { scale = 1 } = options;
|
|
438
482
|
let clone, classCSS, styleCache;
|
|
439
483
|
try {
|
|
440
484
|
({ clone, classCSS, styleCache } = await prepareClone(element));
|
|
@@ -442,8 +486,9 @@ async function capture(element, scale = 1) {
|
|
|
442
486
|
console.error("prepareClone failed:", e);
|
|
443
487
|
throw e;
|
|
444
488
|
}
|
|
445
|
-
const
|
|
446
|
-
const
|
|
489
|
+
const rect = element.getBoundingClientRect();
|
|
490
|
+
const w = rect.width * scale;
|
|
491
|
+
const h = rect.height * scale;
|
|
447
492
|
let fonts = "";
|
|
448
493
|
try {
|
|
449
494
|
fonts = await inlineFonts(styleCache);
|
|
@@ -476,50 +521,65 @@ async function capture(element, scale = 1) {
|
|
|
476
521
|
}
|
|
477
522
|
|
|
478
523
|
// src/api/snapdom.js
|
|
479
|
-
async function snapdom(
|
|
480
|
-
return await capture(
|
|
524
|
+
async function snapdom(element, options = {}) {
|
|
525
|
+
return await capture(element, options);
|
|
481
526
|
}
|
|
482
|
-
snapdom.toImg = async
|
|
483
|
-
const
|
|
527
|
+
snapdom.toImg = async (el, opt = {}) => {
|
|
528
|
+
const url = await snapdom(el, opt);
|
|
484
529
|
const img = new Image();
|
|
485
|
-
img.src =
|
|
530
|
+
img.src = url;
|
|
486
531
|
await img.decode();
|
|
487
532
|
return img;
|
|
488
533
|
};
|
|
489
|
-
snapdom.toCanvas = async
|
|
490
|
-
const img = await snapdom.toImg(el,
|
|
534
|
+
snapdom.toCanvas = async (el, opt = {}) => {
|
|
535
|
+
const img = await snapdom.toImg(el, opt);
|
|
491
536
|
const canvas = document.createElement("canvas");
|
|
492
537
|
canvas.width = img.width;
|
|
493
538
|
canvas.height = img.height;
|
|
494
|
-
|
|
495
|
-
ctx.drawImage(img, 0, 0);
|
|
539
|
+
canvas.getContext("2d").drawImage(img, 0, 0);
|
|
496
540
|
return canvas;
|
|
497
541
|
};
|
|
498
|
-
snapdom.toPng = async
|
|
499
|
-
const canvas = await snapdom.toCanvas(el,
|
|
542
|
+
snapdom.toPng = async (el, opt = {}) => {
|
|
543
|
+
const canvas = await snapdom.toCanvas(el, opt);
|
|
500
544
|
const img = new Image();
|
|
501
545
|
img.src = canvas.toDataURL("image/png");
|
|
502
546
|
await img.decode();
|
|
503
547
|
return img;
|
|
504
548
|
};
|
|
505
|
-
snapdom.toJpg = async
|
|
506
|
-
const
|
|
549
|
+
snapdom.toJpg = async (el, opt = {}) => {
|
|
550
|
+
const { backgroundColor = "#fff", quality = 0.92 } = opt;
|
|
551
|
+
const canvas = await snapdom.toCanvas(el, opt);
|
|
552
|
+
const temp = document.createElement("canvas");
|
|
553
|
+
temp.width = canvas.width;
|
|
554
|
+
temp.height = canvas.height;
|
|
555
|
+
const ctx = temp.getContext("2d");
|
|
556
|
+
ctx.fillStyle = backgroundColor;
|
|
557
|
+
ctx.fillRect(0, 0, temp.width, temp.height);
|
|
558
|
+
ctx.drawImage(canvas, 0, 0);
|
|
507
559
|
const img = new Image();
|
|
508
|
-
img.src =
|
|
560
|
+
img.src = temp.toDataURL("image/jpeg", quality);
|
|
509
561
|
await img.decode();
|
|
510
562
|
return img;
|
|
511
563
|
};
|
|
512
|
-
snapdom.toWebp = async
|
|
513
|
-
const
|
|
564
|
+
snapdom.toWebp = async (el, opt = {}) => {
|
|
565
|
+
const { backgroundColor = "#fff", quality = 0.92 } = opt;
|
|
566
|
+
const canvas = await snapdom.toCanvas(el, opt);
|
|
567
|
+
const temp = document.createElement("canvas");
|
|
568
|
+
temp.width = canvas.width;
|
|
569
|
+
temp.height = canvas.height;
|
|
570
|
+
const ctx = temp.getContext("2d");
|
|
571
|
+
ctx.fillStyle = backgroundColor;
|
|
572
|
+
ctx.fillRect(0, 0, temp.width, temp.height);
|
|
573
|
+
ctx.drawImage(canvas, 0, 0);
|
|
514
574
|
const img = new Image();
|
|
515
|
-
img.src =
|
|
575
|
+
img.src = temp.toDataURL("image/webp", quality);
|
|
516
576
|
await img.decode();
|
|
517
577
|
return img;
|
|
518
578
|
};
|
|
519
|
-
snapdom.toBlob = async
|
|
520
|
-
const
|
|
521
|
-
const
|
|
522
|
-
return new Blob([
|
|
579
|
+
snapdom.toBlob = async (el, opt = {}) => {
|
|
580
|
+
const dataUrl = await snapdom(el, opt);
|
|
581
|
+
const svgText = decodeURIComponent(dataUrl.split(",")[1]);
|
|
582
|
+
return new Blob([svgText], { type: "image/svg+xml" });
|
|
523
583
|
};
|
|
524
584
|
export {
|
|
525
585
|
snapdom
|
package/package.json
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zumer/snapdom",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"description": "snapDOM captures DOM elements as images with exceptional speed and minimal overhead. Designed to avoid bottlenecks and long tasks.",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "dist/snapdom.js",
|
|
6
|
+
"module": "dist/snapdom.mjs",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/snapdom.mjs",
|
|
10
|
+
"require": "./dist/snapdom.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
6
13
|
"scripts": {
|
|
7
14
|
"test": "npx vitest run --browser.headless --reporter=verbose",
|
|
8
15
|
"test:benchmark": "npx vitest bench --browser.headless --watch=false",
|