defuss-morph 0.1.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/LICENSE +21 -0
- package/README.md +369 -0
- package/dist/all.js +1312 -0
- package/dist/all.js.map +17 -0
- package/dist/all.min.js +2 -0
- package/dist/all.min.js.map +1 -0
- package/dist/index.cjs +1242 -0
- package/dist/index.d.ts +233 -0
- package/dist/index.mjs +1204 -0
- package/dist/stats.json +22 -0
- package/package.json +89 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aron Homberg
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
# defuss-morph
|
|
2
|
+
|
|
3
|
+
The [defuss](https://github.com/kyr0/defuss) DOM morphing engine as a standalone, dependency-free package.
|
|
4
|
+
|
|
5
|
+
The defuss DOM morphing algorithm is a standalone package: morph an HTML string or JSX object into an existing DOM tree using native APIs (`DOMParser`) and a smart DOM diffing algorithm.
|
|
6
|
+
|
|
7
|
+
## TL;DR
|
|
8
|
+
|
|
9
|
+
You don't need any complex framework like React, Vue, Solid, Angular, Svelte etc. to do DOM morphing. You don't even need a virtual DOM. You can simply get hold of a DOM element reference, and pass it new HTML or JSX to morph it into, and it works isomorphically everywhere - even without any complex transpilation or build step. Just include one `<script>` tag and you are ready to go.
|
|
10
|
+
|
|
11
|
+
Features:
|
|
12
|
+
- ๐ Patch-rendering via DOM-diff; HTML + JSX supported
|
|
13
|
+
- ๐ค Works with any other framework or library, and without any
|
|
14
|
+
- ๐ฏ Runs isomorphic in the browser and on the server
|
|
15
|
+
- ๐๏ธ Fast! Uses native DOM APIs
|
|
16
|
+
- ๐ฏ Stable key/id-aware node matching
|
|
17
|
+
- ๐ฆ CDN-served and packaged with ESM + CJS
|
|
18
|
+
- ๐งน Event handler preservation w/ supports delegated event listeners
|
|
19
|
+
- ๐ Form-state preservation (see one limitation below w/ solution)
|
|
20
|
+
- โก No compile/transpile step required (but optionally available)
|
|
21
|
+
- ๐ชถ Extremely lightweight โ just ~6 KiB minified and gzipped
|
|
22
|
+
- ๐
Includes support for beautiful transitions (fade, slide, shake, custom styles)
|
|
23
|
+
- ๐ฆ Written in TypeScript
|
|
24
|
+
|
|
25
|
+
## Quick, traditional CDN-based setup
|
|
26
|
+
|
|
27
|
+
The traditional way using `window.df$` globally exported API:
|
|
28
|
+
|
|
29
|
+
```html
|
|
30
|
+
<body>
|
|
31
|
+
<div id="app">Old content</div>
|
|
32
|
+
|
|
33
|
+
<!-- right before the closing </body> tag, include the CDN bundle -->
|
|
34
|
+
<script type="module" src="https://cdn.jsdelivr.net/npm/defuss-morph@latest/dist/all.min.js"></script>
|
|
35
|
+
</body>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Then run:
|
|
39
|
+
|
|
40
|
+
```html
|
|
41
|
+
<script>
|
|
42
|
+
const { morph } = window.df$;
|
|
43
|
+
|
|
44
|
+
morph(document.getElementById("app"), "<p>Hello from the CDN</p>", {
|
|
45
|
+
transition: { type: "fade", duration: 200 }
|
|
46
|
+
});
|
|
47
|
+
</script>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
- **Good for:** Quick, powerful AI-prototyping, demos, and testing in the browser.
|
|
51
|
+
- **Drawbacks:** Doesn't work offline, single-point-of-failure, no bundler optimizations.
|
|
52
|
+
|
|
53
|
+
## Quick, modern CDN-based setup using ESM imports
|
|
54
|
+
|
|
55
|
+
The modern way, using direct ESM imports from the CDN โ no globals required:
|
|
56
|
+
|
|
57
|
+
```html
|
|
58
|
+
<body>
|
|
59
|
+
<div id="app">Old content</div>
|
|
60
|
+
|
|
61
|
+
<!-- right before the closing </body> tag, import via ESM -->
|
|
62
|
+
<script type="module">
|
|
63
|
+
import { morph } from "https://cdn.jsdelivr.net/npm/defuss-morph@latest/dist/all.min.js";
|
|
64
|
+
|
|
65
|
+
morph(document.getElementById("app"), "<p>Hello from the CDN</p>", {
|
|
66
|
+
transition: { type: "fade", duration: 200 }
|
|
67
|
+
});
|
|
68
|
+
</script>
|
|
69
|
+
</body>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
- **Good for:** Quick, powerful AI-prototyping, demos, and testing in the browser, with explicit imports and no global namespace pollution.
|
|
73
|
+
- **Drawbacks:** Also doesn't work offline, still single-point-of-failure, still no bundler optimizations.
|
|
74
|
+
|
|
75
|
+
## Quick, non-CDN setup
|
|
76
|
+
|
|
77
|
+
Download this package as a ZIP-file (either a versioned release or the latest `main` branch): [Download latest `defuss-morph` as ZIP](https://github.com/kyr0/defuss-morph/archive/refs/heads/main.zip). Extract it and copy the `dist` folder to your project assets files.
|
|
78
|
+
|
|
79
|
+
Example:
|
|
80
|
+
```text
|
|
81
|
+
project/
|
|
82
|
+
โโโ assets/
|
|
83
|
+
โ โโโ js/
|
|
84
|
+
โ โโโ defuss-morph/
|
|
85
|
+
โ โโโ all.min.js
|
|
86
|
+
โโโ index.html
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Then include the local bundle in your HTML, using either the modern ESM import:
|
|
90
|
+
|
|
91
|
+
```html
|
|
92
|
+
<body>
|
|
93
|
+
<div id="app">Old content</div>
|
|
94
|
+
|
|
95
|
+
<script type="module">
|
|
96
|
+
import { morph } from "./assets/js/defuss-morph/all.min.js";
|
|
97
|
+
|
|
98
|
+
morph(document.getElementById("app"), "<p>Hello from local assets</p>", {
|
|
99
|
+
transition: { type: "fade", duration: 200 }
|
|
100
|
+
});
|
|
101
|
+
</script>
|
|
102
|
+
</body>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
This also works with the traditional `window.df$` global (see above).
|
|
106
|
+
|
|
107
|
+
- **Good for:** Offline-support, air-gapped environments, and locking the exact version you ship.
|
|
108
|
+
- **Drawbacks:** Manual updates โ you need to re-download the ZIP to get a new version, and still no bundler optimizations.
|
|
109
|
+
|
|
110
|
+
## For production: Install via package manager
|
|
111
|
+
|
|
112
|
+
The most professional approach is to use a package manager: This allows for version-pinning, offline-support, and allows for optimized production builds using tree-shaking and minification.
|
|
113
|
+
|
|
114
|
+
We recommend [`bun`](https://bun.sh) only as a package manager and simple script runner.
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
bun add defuss-morph
|
|
118
|
+
# or: npm install defuss-morph
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Then, in `.ts(x)` or `.js(x)` files:
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
import { morph } from "defuss-morph";
|
|
125
|
+
|
|
126
|
+
const appEl = document.getElementById("app")!;
|
|
127
|
+
|
|
128
|
+
// only what actually changed is patched; untouched nodes keep
|
|
129
|
+
// their identity, focus, selection, scroll position and event state
|
|
130
|
+
morph(appEl, `<ul>
|
|
131
|
+
<li key="a">Alpha</li>
|
|
132
|
+
<li key="b">Beta</li>
|
|
133
|
+
</ul>`, { transition: { type: "fade", duration: 200 } });
|
|
134
|
+
|
|
135
|
+
morph(appEl, `<ul>
|
|
136
|
+
<li key="b">Beta (updated)</li>
|
|
137
|
+
<li key="a">Alpha</li>
|
|
138
|
+
</ul>`, { transition: { type: "slide-left", duration: 200 } });
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
If you have `bun` installed, you can serve your project directory statically with a single command:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
bunx serve .
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## API
|
|
148
|
+
|
|
149
|
+
### `morph(el: Element, newContent: string | VNode | VNode[]): void`
|
|
150
|
+
|
|
151
|
+
Morphs the children of `el` to match the given HTML string or JSX/VNode content.
|
|
152
|
+
|
|
153
|
+
- HTML strings are parsed with the element's own document (`el.ownerDocument`), so it works isomorphically (browser, happy-dom, ...) and across multiple documents/windows. VNode/JSX input is used directly.
|
|
154
|
+
- Matching is **key-aware** (`key` attribute) and **id-aware**; matched nodes are *moved*, never replaced, preserving DOM identity and element state.
|
|
155
|
+
- Same-tag elements are patched in place (attributes and children).
|
|
156
|
+
- **Event listeners survive**: because node identity is preserved, native `addEventListener` listeners keep working. Handlers registered via `registerDelegatedEvent` (or defuss JSX `onClick` props) are preserved too โ an HTML string cannot declare handlers, so existing ones are treated like uncontrolled form state. Handlers of *removed* elements are cleaned up.
|
|
157
|
+
- Uncontrolled form state survives: an `<input>`'s live value is kept unless the new HTML explicitly sets `value`.
|
|
158
|
+
- Plain text input morphs as a text node.
|
|
159
|
+
- Morphs triggered from within an active morph (e.g. from lifecycle callbacks) on the same or an ancestor element are queued latest-wins and replayed after the active morph finishes.
|
|
160
|
+
|
|
161
|
+
### Morphing JSX (VNodes)
|
|
162
|
+
|
|
163
|
+
`morph()` accepts **either an HTML string or JSX / VNodes** โ same API, same
|
|
164
|
+
algorithm. Strings are parsed with the native `DOMParser`; VNodes
|
|
165
|
+
(`{ type, attributes, children }` objects, as produced by any JSX factory)
|
|
166
|
+
are morphed directly:
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
import { morph } from "defuss-morph";
|
|
170
|
+
|
|
171
|
+
// HTML string:
|
|
172
|
+
morph(document.getElementById("app")!, `<p>Hi</p>`);
|
|
173
|
+
|
|
174
|
+
// JSX / VNodes (any factory producing { type, attributes, children } works โ
|
|
175
|
+
// e.g. defuss: import { jsx } from "defuss"; const vdom = <p>Hi</p>;)
|
|
176
|
+
morph(
|
|
177
|
+
document.getElementById("app")!,
|
|
178
|
+
<ul>
|
|
179
|
+
<li key="a">Alpha</li>
|
|
180
|
+
<li key="b">Beta</li>
|
|
181
|
+
</ul>,
|
|
182
|
+
);
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Unlike an HTML string, a VNode can carry real JavaScript values: explicit
|
|
186
|
+
booleans (`checked: false`), live values (`value: ""`), event handlers
|
|
187
|
+
(`onClick={...}`, registered as delegated events) and refs. In other words โ
|
|
188
|
+
**controlled state and JSX event props only work through VNodes**, never
|
|
189
|
+
through HTML strings.
|
|
190
|
+
|
|
191
|
+
Under the hood, `morph()` delegates to `updateDomWithVdom` (the entry point
|
|
192
|
+
`defuss` itself uses), which is exported for direct/framework use.
|
|
193
|
+
|
|
194
|
+
### Partial updates (diff mode)
|
|
195
|
+
|
|
196
|
+
By default `morph()` reconciles the **complete** next state: anything you don't
|
|
197
|
+
describe, is removed. With `{ diff: true }` you send only a **change-set** โ a
|
|
198
|
+
partial update addressed by `key` (preferred) or `id`:
|
|
199
|
+
|
|
200
|
+
Current DOM:
|
|
201
|
+
```html
|
|
202
|
+
<ul id="listEl">
|
|
203
|
+
<li key="a" data-x="1">A</li>
|
|
204
|
+
<li key="b">B</li>
|
|
205
|
+
<li key="c">C</li>
|
|
206
|
+
</ul>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
The change-set: only the nodes that change, each addressed by `key`/`id`:
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
const listEl = document.getElementById("listEl")!;
|
|
213
|
+
const changeSetHtml = `
|
|
214
|
+
<li key="b">B (updated)</li>
|
|
215
|
+
<li key="d">D (new)</li>
|
|
216
|
+
`;
|
|
217
|
+
|
|
218
|
+
morph(listEl, changeSetHtml, { diff: true });
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
New DOM:
|
|
222
|
+
|
|
223
|
+
```html
|
|
224
|
+
<ul id="listEl">
|
|
225
|
+
<li key="a" data-x="1">A</li> <!-- untouched -->
|
|
226
|
+
<li key="b">B (updated)</li> <!-- patched in place, attributes MERGED -->
|
|
227
|
+
<li key="c">C</li> <!-- untouched -->
|
|
228
|
+
<li key="d">D (new)</li> <!-- appended -->
|
|
229
|
+
</ul>
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Result: `a` and `c` are **untouched** (identity, `data-x`, order, focus,
|
|
233
|
+
listeners all kept) even though the change-set never mentioned them; `b` is
|
|
234
|
+
patched โ its text updates and declared attributes merge while undeclared ones
|
|
235
|
+
survive โ and `d` is appended. Diff mode **never removes and never moves**
|
|
236
|
+
anything, so addressing stays unambiguous:
|
|
237
|
+
|
|
238
|
+
- Patch items must be addressed by `key`/`id` โ plain text or key-less
|
|
239
|
+
elements throw (there is no honest way to address "some `<div>`").
|
|
240
|
+
- A tag is only required for *new* (unmatched) items.
|
|
241
|
+
- Deleting is not expressible in a diff change-set โ use a regular full
|
|
242
|
+
`morph()` for that.
|
|
243
|
+
- A declared `children` replaces the node's children; if children are not
|
|
244
|
+
declared, they stay untouched (via VNodes, `children: []` clears explicitly).
|
|
245
|
+
- A tag change on an addressed node applies as an in-place replacement.
|
|
246
|
+
|
|
247
|
+
Great for streaming/AI-driven UIs: patch one table row or status badge without
|
|
248
|
+
re-sending (or risking) the rest of the subtree. Runnable demo:
|
|
249
|
+
[`examples/diff-mode.html`](examples/diff-mode.html).
|
|
250
|
+
|
|
251
|
+
### Transitions
|
|
252
|
+
|
|
253
|
+
`morph()` accepts an optional transition, turning it into an async operation
|
|
254
|
+
(mirroring defuss' `updateDom` transition semantics):
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
await morph(appEl, "<p>Faded in</p>", {
|
|
258
|
+
transition: { type: "fade", duration: 200 }, // slide-left | slide-right | shake | custom styles
|
|
259
|
+
});
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Without a `transition` option, `morph()` is fully synchronous.
|
|
263
|
+
|
|
264
|
+
Overlapping updates are **latest-wins**: a plain `morph()` issued while a transition is in flight applies immediately and also rewrites the transition's pending content, so the transition completes with the latest HTML โ never stale content. `await` the returned promise (or chain morphs into a queue) when you want the *animations* to play sequentially โ see [`examples/transition-await.html`](examples/transition-await.html).
|
|
265
|
+
|
|
266
|
+
### Low-level primitives
|
|
267
|
+
|
|
268
|
+
The building blocks are exported for framework integration (used by `defuss` itself):
|
|
269
|
+
|
|
270
|
+
```ts
|
|
271
|
+
import {
|
|
272
|
+
updateDomWithVdom, // guarded VNode -> DOM morph entry point (globals optional,
|
|
273
|
+
// 4th arg mode: "replace" | "diff")
|
|
274
|
+
replaceDomWithVdom, // full replace (no patching)
|
|
275
|
+
htmlStringToVNodes, // HTML string -> VNode[]
|
|
276
|
+
domNodeToVNode, // DOM Node -> VNode
|
|
277
|
+
getRenderer, // VNode -> DOM renderer factory
|
|
278
|
+
registerDelegatedEvent, removeDelegatedEvent, clearDelegatedEventsDeep, // ...
|
|
279
|
+
} from "defuss-morph";
|
|
280
|
+
|
|
281
|
+
// the third `globals` argument is optional everywhere โ it is derived
|
|
282
|
+
// from the element's own document when omitted:
|
|
283
|
+
updateDomWithVdom(el, [{ type: "p", attributes: {}, children: ["hi"] }]);
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Bundle size
|
|
287
|
+
|
|
288
|
+
Direct jsDelivr ESM loading (`<script type="module">`), no build step needed:
|
|
289
|
+
|
|
290
|
+
```html
|
|
291
|
+
<script type="module">
|
|
292
|
+
import { morph } from "https://cdn.jsdelivr.net/npm/defuss-morph@latest/dist/all.min.js";
|
|
293
|
+
|
|
294
|
+
morph(document.getElementById("app"), "<p>Hello from the CDN</p>");
|
|
295
|
+
</script>
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Or via the `df$` global โ declared by the CDN bundle on load, no import statement required. `df$` carries the **full public API** (`morph`, `updateDomWithVdom`, `registerDelegatedEvent`, transitions, โฆ); a pre-existing `df$` object is preserved and extended:
|
|
299
|
+
|
|
300
|
+
```html
|
|
301
|
+
<script type="module" src="https://cdn.jsdelivr.net/npm/defuss-morph@latest/dist/all.min.js"></script>
|
|
302
|
+
<script type="module">
|
|
303
|
+
const { morph, updateDomWithVdom } = window.df$;
|
|
304
|
+
|
|
305
|
+
morph(document.getElementById("app"), "<p>Hello from the CDN</p>");
|
|
306
|
+
|
|
307
|
+
// explicit (controlled) form state โ HTML can't express "unchecked":
|
|
308
|
+
updateDomWithVdom(document.getElementById("app"), [
|
|
309
|
+
{ type: "input", attributes: { type: "checkbox", checked: false }, children: [] },
|
|
310
|
+
]); // globals optional: derived from the element's own document
|
|
311
|
+
</script>
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
A complete, runnable page lives at [`examples/cdn-global.html`](examples/cdn-global.html).
|
|
315
|
+
|
|
316
|
+
The `df$` global is only registered by the CDN bundle (`all.js` / `all.min.js`). The library entries (`index.mjs` / `index.cjs`, used by bundlers, Node and SSR) are side-effect-free.
|
|
317
|
+
|
|
318
|
+
## Examples
|
|
319
|
+
|
|
320
|
+
Runnable zero-build pages (serve the package directory statically, e.g. `bunx serve .`):
|
|
321
|
+
|
|
322
|
+
| Example | What it shows |
|
|
323
|
+
| --- | --- |
|
|
324
|
+
| [`examples/cdn-global.html`](examples/cdn-global.html) | The `df$` global end-to-end: morphing, keyed lists, controlled form state, delegated events, transitions |
|
|
325
|
+
| [`examples/controlled-form-state.html`](examples/controlled-form-state.html) | The "HTML can't uncheck" limitation and its fix via `df$.updateDomWithVdom` |
|
|
326
|
+
| [`examples/event-listener-preservation.html`](examples/event-listener-preservation.html) | Native + delegated listeners surviving morphs; handler cleanup on removal |
|
|
327
|
+
| [`examples/transition-await.html`](examples/transition-await.html) | Transitions: latest-wins content, `await`/queue patterns for sequencing animations |
|
|
328
|
+
| [`examples/diff-mode.html`](examples/diff-mode.html) | Diff mode: partial change-sets by key/id โ merge-patch, append, untouched siblings |
|
|
329
|
+
|
|
330
|
+
## One limitation when morphing from HTML strings
|
|
331
|
+
|
|
332
|
+
**HTML cannot unset `checked`/live form values**: since an HTML string cannot declare "explicitly unchecked", absent form attributes are treated as uncontrolled and the live state is preserved. This is deliberate โ it's what makes form state survive unrelated morphs.
|
|
333
|
+
|
|
334
|
+
When you *do* need explicit control, use [diff mode](#partial-updates-diff-mode) with a VNode patch item: address the node by `id` (or `key`) and send **only the new value** โ a VNode can express an explicit `false`, and since the addressed node already exists, not even a tag is needed (it is borrowed from the match):
|
|
335
|
+
|
|
336
|
+
```ts
|
|
337
|
+
import { morph } from "defuss-morph";
|
|
338
|
+
|
|
339
|
+
const appEl = document.getElementById("app")!;
|
|
340
|
+
|
|
341
|
+
// โ stays checked โ "no checked attribute" means "don't touch"
|
|
342
|
+
morph(appEl, `<input type="checkbox" id="c" checked>`); // checked in HTML
|
|
343
|
+
morph(appEl, `<input type="checkbox" id="c">`); // still checked!
|
|
344
|
+
|
|
345
|
+
// โ
diff mode: address by id, set the new value โ done
|
|
346
|
+
morph(appEl, [{ attributes: { id: "c", checked: false } }], { diff: true });
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
The same rule applies to an `<input>`'s live `value`. See the runnable demos [`examples/controlled-form-state.html`](examples/controlled-form-state.html) and [`examples/diff-mode.html`](examples/diff-mode.html).
|
|
350
|
+
|
|
351
|
+
## Size
|
|
352
|
+
|
|
353
|
+
<!-- bundle-size:start -->
|
|
354
|
+
| File | Size | Gzipped | Purpose |
|
|
355
|
+
| --- | ---: | ---: | --- |
|
|
356
|
+
| `index.mjs` | 40.2 kB | 9.5 kB | ESM/library build; used when installing via npm/bun |
|
|
357
|
+
| `index.cjs` | 41.2 kB | 9.7 kB | CommonJS build |
|
|
358
|
+
| `all.js` | 41.6 kB | 9.8 kB | UMD build; for CDN-based usage with debugging |
|
|
359
|
+
| `all.min.js` | 18.9 kB | **6.8 kB** | Minified UMD build; for CDN-based usage without debugging (Pareto-optimal when no bundler is used) |
|
|
360
|
+
<!-- bundle-size:end -->
|
|
361
|
+
|
|
362
|
+
`index.cjs` (CommonJS) is kept for `require()` compatibility on older tool
|
|
363
|
+
chains (CJS jest configs, bundlers resolving `main`). On modern Node
|
|
364
|
+
(โฅ 20.19 / 22.12, which can `require()` ESM directly) it is redundant โ but at
|
|
365
|
+
~1 kB it is cheap insurance.
|
|
366
|
+
|
|
367
|
+
## License
|
|
368
|
+
|
|
369
|
+
[MIT](LICENSE)
|