@thednp/dommatrix 3.0.6 → 3.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/AGENTS.md +9 -0
- package/CHANGELOG.md +26 -0
- package/README.md +75 -0
- package/dist/dommatrix.cjs +262 -103
- package/dist/dommatrix.cjs.map +1 -1
- package/dist/dommatrix.d.ts +29 -1
- package/dist/dommatrix.js +262 -103
- package/dist/dommatrix.js.map +1 -1
- package/dist/dommatrix.mjs +262 -103
- package/dist/dommatrix.mjs.map +1 -1
- package/package.json +3 -1
package/AGENTS.md
CHANGED
|
@@ -29,6 +29,8 @@ pnpm lint:ts # deno lint src
|
|
|
29
29
|
pnpm check:ts # tsc --noEmit
|
|
30
30
|
pnpm fix:ts # deno lint src --fix
|
|
31
31
|
pnpm format # deno fmt src
|
|
32
|
+
pnpm bench # vitest browser bench vs test/fixtures/index-3.0.x.ts (no coverage)
|
|
33
|
+
pnpm bench:native # vitest browser bench vs native DOMMatrix (no coverage)
|
|
32
34
|
```
|
|
33
35
|
|
|
34
36
|
Always run `pnpm lint` and `pnpm test` after making changes.
|
|
@@ -38,8 +40,11 @@ Deno equivalents: `deno task test`, `deno task lint`, `deno task check`, `deno t
|
|
|
38
40
|
## Project Structure
|
|
39
41
|
|
|
40
42
|
- `src/index.ts` — the entire library (single file, ~1200 lines). The `CSSMatrix` class plus module-level helper functions (`Translate`, `Rotate`, `fromString`, etc.). Heavily documented with JSDoc.
|
|
43
|
+
- `test/fixtures/index-3.0.x.ts` — snapshot of the pre-3.1.0 implementation used as the benchmark baseline by `test/dommatrix.bench.test.ts`. Excluded from coverage (see `vitest.config.mts`) and from JSR publish (only the `publish.include` files ship). Included in the `tsconfig.json` `include` so `pnpm check:ts` covers it.
|
|
41
44
|
- `src/types.ts` — exported types (`JSONMatrix`, `Matrix`, `Matrix3d`, `PointTuple`)
|
|
42
45
|
- `test/dommatrix.test.ts` — the full test suite (Vitest, browser mode)
|
|
46
|
+
- `test/dommatrix.bench.test.ts` — benchmark vs the `test/fixtures/index-3.0.x.ts` snapshot: parity suite (every op must match the backup within `1e-9`, runs in `pnpm test` and gates CI) + interleaved perf suite (auto-skipped under coverage)
|
|
47
|
+
- `test/native.bench.test.ts` — benchmark vs native `DOMMatrix`: parity suite (21 ops at `1e-9`, runs in `pnpm test`) + interleaved perf suite
|
|
43
48
|
- `test/fixtures/` — test helpers and sample data
|
|
44
49
|
- `docs/` — GitHub Pages demo; `docs/dommatrix.js` is a build artifact copied from `dist/`
|
|
45
50
|
- `dist/` — build output, committed to the repo; regenerate with `pnpm build`, never edit by hand
|
|
@@ -83,5 +88,9 @@ Note: `prepublishOnly` runs `pnpm up --latest` (updates all deps), `pnpm format`
|
|
|
83
88
|
- The old `vite.config.ts` was replaced by `tsdown.config.mts` in 3.0.5 — `tsdown.config.mts` relies on: object-form `entry` for the chunk name (`dommatrix`), an `outputOptions` override for UMD (it must spread defaults, otherwise `sourcemap` is silently dropped), and `outExtensions` to keep the `.d.ts` filename. The `.mts` extension (not `.ts`) prevents Node's `MODULE_TYPELESS_PACKAGE_JSON` ESM-reparse warning — `package.json` deliberately has no `"type": "module"` because that would break Node `require()` of the UMD `dist/dommatrix.js`
|
|
84
89
|
- With `deno.json` present, `deno lint` enforces `verbatim-module-syntax` — type-only imports (like `import type CSSMatrix from "."` in `src/types.ts`) must use `import type`
|
|
85
90
|
- Vitest runs only in browser mode; any Node-only concern (globals, `require()`, `process`) needs the stubbed-global test pattern
|
|
91
|
+
- **Browser `console.table` is NOT forwarded to the terminal** — both bench suites accumulate rows on `globalThis` (`__CSSMATRIX_BENCH__`, `__CSSMATRIX_NATIVE_BENCH__`, inspectable in `pnpm test-ui` devtools) and print a pad-aligned `console.log` table in `afterAll`. `console.log` in `afterAll` does forward.
|
|
92
|
+
- **Bench measurements must run without coverage**: istanbul instruments `src/index.ts` but not `test/fixtures/index-3.0.x.ts`, so under coverage the current file measures 30-60% slower than an identical backup. The suites skip timing at runtime when `typeof globalThis["__VITEST_COVERAGE__"] === "object"` (the key only appears after instrumented code executes) — parity assertions still run in `pnpm test`.
|
|
93
|
+
- **Interleaved sampling**: `bench(a, b, iterations)` warms up both implementations, then takes 2 alternating samples each and reports medians. Sequential per-implementation measurement biases toward the first implementation (~20-30%) because the second's allocations raise GC pressure. Identical files must measure ~0.92-1.09x.
|
|
94
|
+
- Native DOMMatrix quirks that shaped `test/native.bench.test.ts`: Chrome lacks `DOMMatrix.fromArray` (use `DOMMatrix.fromMatrix(values)`); native has no `skew()`/`skewSelf()` (only `skewX`/`skewY`); `rotateAxisAngle(x, y, z, angle)` takes the angle last; `DOMPoint` exposes `x/y/z/w` as prototype getters so `Object.keys()` is empty — extract values into a plain object before comparing; the bench test needs a long timeout (120s) because 22 interleaved cases take ~15s.
|
|
86
95
|
- `rotateAxisAngle`/`rotateAxisAngleSelf` throw only when any of the 4 values is non-finite; zero-length vector returns a copy (or `this` for the `Self` variant)
|
|
87
96
|
- `deno lint` runs on `src/` only — the `tsconfig.json` `include` is `["src/*"]`, `noEmit: true`
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,32 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [3.1.0] - 2026-08-04
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- A benchmark suite in `test/dommatrix.bench.test.ts` comparing the current implementation against a `test/fixtures/index-3.0.x.ts` snapshot of the previous release: a parity suite (every operation must produce output identical to the backup, within `1e-9`) and a performance suite with interleaved, median-of-2 sampling per operation. Measurements are skipped automatically under coverage (istanbul instrumentation distorts timings); run `pnpm bench`.
|
|
13
|
+
- A native comparison suite in `test/native.bench.test.ts` comparing the current implementation against the browser's native `DOMMatrix`: parity at `1e-9` for 21 operations plus interleaved performance measurements; run `pnpm bench:native`.
|
|
14
|
+
- `pnpm bench` and `pnpm bench:native` scripts. Both suites store their result tables on `globalThis.__CSSMATRIX_BENCH__` / `__CSSMATRIX_NATIVE_BENCH__` for inspection in `pnpm test-ui` devtools, and print a combined table to the terminal (browser `console.table` output is not forwarded by Vitest).
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- Major performance rework of the internal hot paths, all parity-verified against the previous release and against native `DOMMatrix`:
|
|
19
|
+
- `new CSSMatrix(init)` now fast-paths string / array / typed-array / object initializers instead of round-tripping through `setMatrixValue()`, and construction is funneled through a new internal `fromValues()` factory that writes all 22 aliases in one pass (a single hidden class keeps the constructor monomorphic).
|
|
20
|
+
- `CSSMatrix.fromString()` parses transform lists with a single regex iteration instead of split/filter/forEach, with equivalent strict validation of non-transform garbage.
|
|
21
|
+
- `CSSMatrix.fromArray()` and `CSSMatrix.fromMatrix()` validate and destructure input directly, no intermediate `Array` copy.
|
|
22
|
+
- `multiply()`/`multiplySelf()` share a new `multiplyInto()` kernel with a single 64-multiplication pass; `multiplySelf()` writes into `this` instead of allocating and spreading a result object.
|
|
23
|
+
- `translate()`, `translateSelf()`, `scale()`, `scaleSelf()`, `skew()`, `skewSelf()` (plus `skewX()`/`skewY()` delegating to `skew()`) use specialized pre-multiply math instead of general matrix multiplication.
|
|
24
|
+
- `toJSON()` builds the result object with explicit property writes instead of spreading `this`; `toFloat32Array()`/`toFloat64Array()` build typed arrays directly, skipping the intermediate `toArray()` allocation.
|
|
25
|
+
- Measured performance deltas (vs `3.0.7` snapshot, Chromium, `pnpm bench`; ops/s ratios are noise-floor-corrected via interleaved sampling): `multiplySelf` ~17–18x, `scale` ~13x, `skew` ~6–14x, `translate` ~6.5–13x, `multiply` ~7x, `fromMatrix` ~7–7.6x, `rotate` ~5–5.6x, `rotateAxisAngle` ~4.1–4.5x, `toJSON` ~9.7–10.1x, `new CSSMatrix(transform list)` ~1.9–2.4x; unchanged code paths (`new CSSMatrix()`, `fromArray`, `transformPoint`, `toString`, `is2D`/`isIdentity`) measure ~0.96–1.1x.
|
|
26
|
+
- vs native `DOMMatrix` (`pnpm bench:native`, Chromium): the shim is faster on construction and mutation-heavy operations (e.g. `multiplySelf` ~48x, `translateSelf` ~28x, `scaleSelf` ~36x, `multiply` ~4x — largely native's Web IDL per-property access and `toJSON()` overhead), while native wins the pure-compute cases: `transformPoint` ~0.36x and `toString` ~0.84x (shim/native ratios); `toFloat32Array` is at parity.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- `CSSMatrix.toArray()` remained coverage-uncovered after the typed-array paths stopped using it; the `is2D` branch is now exercised by tests again (coverage back to 100%).
|
|
31
|
+
|
|
32
|
+
[3.1.0]: https://github.com/thednp/dommatrix/releases/tag/3.1.0
|
|
33
|
+
|
|
8
34
|
## [3.0.6] - 2026-08-04
|
|
9
35
|
- The `README.md` now documents installing from JSR (`jsr add @thednp/dommatrix`).
|
|
10
36
|
|
package/README.md
CHANGED
|
@@ -17,6 +17,7 @@ A TypeScript sourced [DOMMatrix](https://developer.mozilla.org/en-US/docs/Web/AP
|
|
|
17
17
|
- [Quick Start](#quick-start)
|
|
18
18
|
- [API Reference](#api-reference)
|
|
19
19
|
- [CSSMatrix vs native DOMMatrix](#cssmatrix-vs-native-dommatrix)
|
|
20
|
+
- [Benchmarks](#benchmarks)
|
|
20
21
|
- [Alternatives](#alternatives)
|
|
21
22
|
- [History](#history)
|
|
22
23
|
- [Thanks](#thanks)
|
|
@@ -194,6 +195,79 @@ The shim mirrors the native **DOMMatrix** API surface closely — the same `m11`
|
|
|
194
195
|
|
|
195
196
|
Methods of the `DOMMatrixReadOnly` prototype that are not part of this shim: `flipX()`, `flipY()`, `inverse()` and `rotateFromVector()` (`transpose()` is not part of the native interface either). Everything else — `translate*`, `rotate*`, `rotateAxisAngle*`, `scale*`, `skew*`, `multiply*`, `toString()`, `toFloat(32/64)Array()`, `transformPoint()` — is implemented with behavior verified against the native interface by the test suite.
|
|
196
197
|
|
|
198
|
+
## Benchmarks
|
|
199
|
+
|
|
200
|
+
Two benchmark suites run headless Chromium via Vitest and gate CI with **parity assertions** (output must be identical within `1e-9`), while the timings are reported only:
|
|
201
|
+
|
|
202
|
+
- `pnpm bench` — `test/dommatrix.bench.test.ts`, current implementation vs a `test/fixtures/index-3.0.x.ts` snapshot of the previous release (22 operations)
|
|
203
|
+
- `pnpm bench:native` — `test/native.bench.test.ts`, current implementation vs the browser's native `DOMMatrix` (22 operations, 21 parity-checked)
|
|
204
|
+
|
|
205
|
+
Measurements are skipped under coverage (istanbul instrumentation distorts timings) and use interleaved sampling — both implementations are alternated per sample and medians are reported, so the ratios are noise-floor-corrected (identical files measure ~0.92–1.09x). Results are also stored on `globalThis.__CSSMATRIX_BENCH__` / `__CSSMATRIX_NATIVE_BENCH__` for inspection in `pnpm test-ui` devtools, since browser `console.table` output is not forwarded to the terminal.
|
|
206
|
+
|
|
207
|
+
The tables below report the median of two interleaved runs on headless Chromium (Playwright 1217), 2026-08-04. Ratios above 1 mean the shim is faster.
|
|
208
|
+
|
|
209
|
+
### vs 3.0.x (previous release)
|
|
210
|
+
|
|
211
|
+
Ratio = 3.1.x ops/s ÷ 3.0.x ops/s.
|
|
212
|
+
|
|
213
|
+
**Large wins (6.9–104x)** — the hot paths that were specialized:
|
|
214
|
+
|
|
215
|
+
| Operation | Ratio | Operation | Ratio |
|
|
216
|
+
| --- | --- | --- | --- |
|
|
217
|
+
| `skewSelf` | ~104x | `scaleSelf` | ~61x |
|
|
218
|
+
| `translateSelf` | ~49x | `multiplySelf` | ~18x |
|
|
219
|
+
| `scale` | ~14x | `skew` | ~13x |
|
|
220
|
+
| `translate` | ~13x | `toJSON` | ~9.7x |
|
|
221
|
+
| `fromMatrix` | ~7.3x | `multiply` | ~6.9x |
|
|
222
|
+
|
|
223
|
+
**Moderate wins (2.2–5.2x)**:
|
|
224
|
+
|
|
225
|
+
| Operation | Ratio | Operation | Ratio |
|
|
226
|
+
| --- | --- | --- | --- |
|
|
227
|
+
| `rotate` | ~5.2x | `rotateSelf` | ~4.7x |
|
|
228
|
+
| `rotateAxisAngle` | ~4.1x | `new CSSMatrix(transform list)` | ~2.2x |
|
|
229
|
+
|
|
230
|
+
**Mild wins (1.2–1.4x)**:
|
|
231
|
+
|
|
232
|
+
| Operation | Ratio |
|
|
233
|
+
| --- | --- |
|
|
234
|
+
| `new CSSMatrix(matrix 2D)` | ~1.3x |
|
|
235
|
+
| `new CSSMatrix(matrix3d)` | ~1.2x |
|
|
236
|
+
|
|
237
|
+
**At parity (~1.0x)** — unchanged code paths, no regression:
|
|
238
|
+
|
|
239
|
+
`new CSSMatrix()`, `fromArray`, `transformPoint`, `toString`, `toFloat32Array`, `is2D` + `isIdentity` (~1.0–1.1x).
|
|
240
|
+
|
|
241
|
+
### vs native `DOMMatrix`
|
|
242
|
+
|
|
243
|
+
Ratio = shim ops/s ÷ native ops/s.
|
|
244
|
+
|
|
245
|
+
**Shim faster** (1.8–150x):
|
|
246
|
+
|
|
247
|
+
| Operation | Ratio | Operation | Ratio |
|
|
248
|
+
| --- | --- | --- | --- |
|
|
249
|
+
| `new Matrix()` | ~150x¹ | `multiplySelf` | ~48x |
|
|
250
|
+
| `scaleSelf` | ~35x | `skewXSelf` | ~35x |
|
|
251
|
+
| `translateSelf` | ~31x | `rotateAxisAngle` | ~10x |
|
|
252
|
+
| `rotateSelf` | ~8.3x | static from values | ~7.0x |
|
|
253
|
+
| `multiply` | ~4.0x | `translate` | ~4.0x |
|
|
254
|
+
| `scale` | ~4.0x | `fromMatrix` | ~3.8x |
|
|
255
|
+
| `skewX` | ~3.7x | `rotate` | ~3.4x |
|
|
256
|
+
| `new Matrix(matrix 2D)` | ~2.7x | `toJSON` | ~2.0x |
|
|
257
|
+
| `new Matrix(matrix3d)` | ~1.95x | `new Matrix(transform list)` | ~1.9x |
|
|
258
|
+
| `is2D` + `isIdentity` | ~1.8x | `toFloat32Array` | ~1.0x |
|
|
259
|
+
|
|
260
|
+
¹ measured as construct + `toJSON()`; native's `toJSON()` is a slow JS↔C++ bridge that dominates this row.
|
|
261
|
+
|
|
262
|
+
**Native faster** (the pure-compute cases):
|
|
263
|
+
|
|
264
|
+
| Operation | Ratio |
|
|
265
|
+
| --- | --- |
|
|
266
|
+
| `transformPoint` | ~0.36x (native ~2.8x faster) |
|
|
267
|
+
| `toString` | ~0.82x (native ~1.2x faster) |
|
|
268
|
+
|
|
269
|
+
The shim wins construction and mutation-heavy operations because V8 fully optimizes the plain-JS hot paths, while native `DOMMatrix` pays a C++ call per property access. Native C++ wins where the operation itself is the whole cost: point transforms and serialization.
|
|
270
|
+
|
|
197
271
|
## Alternatives
|
|
198
272
|
|
|
199
273
|
DOMMatrix shim is meant to be a light pocket tool for many things like [svg-path-commander](http://thednp.github.io/svg-path-commander). For a complete polyfill that fills in the missing `DOMMatrixReadOnly` methods (`inverse()`, `flipX()`, `flipY()`, ...), you might want to also consider [geometry-interfaces](https://github.com/trusktr/geometry-interfaces) and [geometry-polyfill](https://github.com/jarek-foksa/geometry-polyfill).
|
|
@@ -204,6 +278,7 @@ DOMMatrix shim is meant to be a light pocket tool for many things like [svg-path
|
|
|
204
278
|
|
|
205
279
|
- **changed** how the constructor determines if the matrix is 2D, based on a [more accurate method](https://github.com/jsidea/jsidea/blob/2b4486c131d5cca2334293936fa13454b34fcdef/ts/jsidea/geom/Matrix3D.ts#L788) which is actually checking the designated values of the 3D space; in contrast, the old *CSSMatrix* constructor sets the `afine` property at initialization only and based on the number of arguments or the type of the input CSS transform syntax;
|
|
206
280
|
- **fixed** the `translate()`, `scale()` and `rotate()` instance methods to work with one axis transformation, also inline with **DOMMatrix**;
|
|
281
|
+
- **added** the `*Self` instance methods — `translateSelf()`, `scaleSelf()`, `rotateSelf()`, `rotateAxisAngleSelf()`, `skewXSelf()`, `skewYSelf()`, `skewSelf()` and `multiplySelf()` — the mutating counterparts of the immutable methods above, inline with the native **DOMMatrix** API;
|
|
207
282
|
- **changed** `toString()` instance method to utilize the new method `toArray()` described below;
|
|
208
283
|
- **changed** `setMatrixValue()` instance method to do all the heavy duty work with parameters;
|
|
209
284
|
- **added** `is2D` (*getter*) property;
|