@miaskiewicz/turbo-dom 0.6.0 → 0.7.1

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/Cargo.lock CHANGED
@@ -113,6 +113,15 @@ version = "0.22.1"
113
113
  source = "registry+https://github.com/rust-lang/crates.io-index"
114
114
  checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
115
115
 
116
+ [[package]]
117
+ name = "bincode"
118
+ version = "1.3.3"
119
+ source = "registry+https://github.com/rust-lang/crates.io-index"
120
+ checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
121
+ dependencies = [
122
+ "serde",
123
+ ]
124
+
116
125
  [[package]]
117
126
  name = "bindgen"
118
127
  version = "0.72.1"
@@ -717,6 +726,12 @@ dependencies = [
717
726
  "winapi",
718
727
  ]
719
728
 
729
+ [[package]]
730
+ name = "fst"
731
+ version = "0.4.7"
732
+ source = "registry+https://github.com/rust-lang/crates.io-index"
733
+ checksum = "7ab85b9b05e3978cc9a9cf8fea7f01b494e1a09ed3037e16ba39edc7a29eb61a"
734
+
720
735
  [[package]]
721
736
  name = "futures-core"
722
737
  version = "0.3.32"
@@ -950,6 +965,29 @@ dependencies = [
950
965
  "markup5ever",
951
966
  ]
952
967
 
968
+ [[package]]
969
+ name = "hyphenation"
970
+ version = "0.8.4"
971
+ source = "registry+https://github.com/rust-lang/crates.io-index"
972
+ checksum = "bcf4dd4c44ae85155502a52c48739c8a48185d1449fff1963cffee63c28a50f0"
973
+ dependencies = [
974
+ "bincode",
975
+ "fst",
976
+ "hyphenation_commons",
977
+ "pocket-resources",
978
+ "serde",
979
+ ]
980
+
981
+ [[package]]
982
+ name = "hyphenation_commons"
983
+ version = "0.8.4"
984
+ source = "registry+https://github.com/rust-lang/crates.io-index"
985
+ checksum = "5febe7a2ade5c7d98eb8b75f946c046b335324b06a14ea0998271504134c05bf"
986
+ dependencies = [
987
+ "fst",
988
+ "serde",
989
+ ]
990
+
953
991
  [[package]]
954
992
  name = "icu_calendar"
955
993
  version = "2.2.1"
@@ -1979,6 +2017,12 @@ dependencies = [
1979
2017
  "miniz_oxide",
1980
2018
  ]
1981
2019
 
2020
+ [[package]]
2021
+ name = "pocket-resources"
2022
+ version = "0.3.2"
2023
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2024
+ checksum = "c135f38778ad324d9e9ee68690bac2c1a51f340fdf96ca13e2ab3914eb2e51d8"
2025
+
1982
2026
  [[package]]
1983
2027
  name = "pollster"
1984
2028
  version = "0.3.0"
@@ -2747,7 +2791,7 @@ dependencies = [
2747
2791
 
2748
2792
  [[package]]
2749
2793
  name = "turbo-dom"
2750
- version = "0.6.0"
2794
+ version = "0.7.1"
2751
2795
  dependencies = [
2752
2796
  "cssparser",
2753
2797
  "html5ever",
@@ -2763,26 +2807,30 @@ dependencies = [
2763
2807
 
2764
2808
  [[package]]
2765
2809
  name = "turbo-dom-browser-env"
2766
- version = "0.6.0"
2810
+ version = "0.7.1"
2767
2811
  dependencies = [
2768
2812
  "cssparser",
2769
2813
  "lightningcss",
2770
2814
  "psl",
2815
+ "rustc-hash 2.1.2",
2771
2816
  "serde",
2772
2817
  "serde_json",
2773
2818
  "sha2",
2774
2819
  "turbo-dom",
2820
+ "turbo-dom-layout",
2775
2821
  "turbo-dom-v8",
2776
2822
  "url",
2823
+ "xml",
2777
2824
  ]
2778
2825
 
2779
2826
  [[package]]
2780
2827
  name = "turbo-dom-layout"
2781
- version = "0.6.0"
2828
+ version = "0.7.1"
2782
2829
  dependencies = [
2783
2830
  "core-foundation",
2784
2831
  "core-text",
2785
2832
  "encoding_rs",
2833
+ "hyphenation",
2786
2834
  "jpeg-decoder",
2787
2835
  "png 0.18.1",
2788
2836
  "resvg",
@@ -2795,12 +2843,13 @@ dependencies = [
2795
2843
  "thiserror 2.0.18",
2796
2844
  "turbo-dom",
2797
2845
  "turbo-dom-layout",
2846
+ "unicode-bidi",
2798
2847
  "unicode-linebreak",
2799
2848
  ]
2800
2849
 
2801
2850
  [[package]]
2802
2851
  name = "turbo-dom-parser"
2803
- version = "0.6.0"
2852
+ version = "0.7.1"
2804
2853
  dependencies = [
2805
2854
  "html5ever",
2806
2855
  "markup5ever_rcdom",
@@ -2816,7 +2865,7 @@ dependencies = [
2816
2865
 
2817
2866
  [[package]]
2818
2867
  name = "turbo-dom-raster"
2819
- version = "0.6.0"
2868
+ version = "0.7.1"
2820
2869
  dependencies = [
2821
2870
  "base64",
2822
2871
  "core-foundation",
@@ -3343,6 +3392,12 @@ version = "0.6.3"
3343
3392
  source = "registry+https://github.com/rust-lang/crates.io-index"
3344
3393
  checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
3345
3394
 
3395
+ [[package]]
3396
+ name = "xml"
3397
+ version = "1.4.0"
3398
+ source = "registry+https://github.com/rust-lang/crates.io-index"
3399
+ checksum = "2f45bb2c13fec6a6cb4c0f76a7e94839e110a14ec803ec2940777a94c347bc52"
3400
+
3346
3401
  [[package]]
3347
3402
  name = "xml-rs"
3348
3403
  version = "0.8.29"
package/Cargo.toml CHANGED
@@ -43,7 +43,7 @@ manual_midpoint = "allow"
43
43
 
44
44
  [package]
45
45
  name = "turbo-dom-parser"
46
- version = "0.6.0"
46
+ version = "0.7.1"
47
47
  edition = "2021"
48
48
  description = "html5ever-backed native parser for turbo-dom (Layer 1)"
49
49
  # Ships to npm only (napi/wasm addon); never published to crates.io.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # turbo-dom
2
2
 
3
- Release **0.6.0**. turbo-dom is the shared browser-engine base of three projects:
3
+ Release **0.7.1**. turbo-dom is the shared browser-engine base of three projects:
4
4
 
5
5
  - **[turbo-test](https://github.com/miaskiewicz/turbo-test)**, a vitest-compatible test runner. It runs tests in V8 over this repository's DOM.
6
6
  - **[turbo-html2pdf](https://github.com/miaskiewicz/turbo-html2pdf)**, which renders HTML to PDF and uses this repository's layout engine.
@@ -9,7 +9,7 @@ Release **0.6.0**. turbo-dom is the shared browser-engine base of three projects
9
9
  turbo-dom is also a standalone npm package: a fast, spec-correct DOM environment for
10
10
  **vitest** and **jest**, usable in place of jsdom or happy-dom.
11
11
 
12
- Up to 0.5.x, each consumer kept its own copy of these pieces. In 0.6.0 each piece lives
12
+ Up to 0.5.x, each consumer kept its own copy of these pieces. Since 0.6.0 each piece lives
13
13
  in this repository once and is published from here. The consumers depend on the
14
14
  published crates and keep no copies.
15
15
  [docs/CONSOLIDATION.md](docs/CONSOLIDATION.md) has the architecture, the dependency graph
@@ -19,11 +19,11 @@ and the publish order.
19
19
 
20
20
  | package | registry | version | what it is | used by |
21
21
  |---|---|---|---|---|
22
- | [`@miaskiewicz/turbo-dom`](https://www.npmjs.com/package/@miaskiewicz/turbo-dom) | npm | 0.6.0 | lazy JS DOM runtime + native html5ever parser (N-API, WASM fallback) | vitest / jest users |
23
- | [`turbo-dom`](crates/turbo-dom) | crates.io | 0.6.0 | pure-Rust DOM (`rtdom`), the one selector engine, CSSOM, partial cascade | all three consumers |
24
- | [`turbo-dom-layout`](crates/turbo-dom-layout) | crates.io | 0.6.0 | HTML/CSS layout (block / inline / flex / grid / table), fonts, images, CSSOM-View geometry | turbo-html2pdf, turbo-surf |
25
- | [`turbo-dom-raster`](crates/turbo-dom-raster) | crates.io | 0.6.0 | synthetic screenshots (PNG / SVG), canvas op-log replay, WebGL readback | turbo-surf |
26
- | [`turbo-dom-browser-env`](crates/turbo-dom-browser-env) | crates.io | 0.6.0 | the `rtdom` <-> V8 browser environment (window, document, CSSOM, forms, events) | turbo-test, turbo-surf |
22
+ | [`@miaskiewicz/turbo-dom`](https://www.npmjs.com/package/@miaskiewicz/turbo-dom) | npm | 0.7.0 | lazy JS DOM runtime + native html5ever parser (N-API, WASM fallback) | vitest / jest users |
23
+ | [`turbo-dom`](crates/turbo-dom) | crates.io | 0.7.0 | pure-Rust DOM (`rtdom`), the one selector engine, CSSOM, partial cascade | all three consumers |
24
+ | [`turbo-dom-layout`](crates/turbo-dom-layout) | crates.io | 0.7.0 | HTML/CSS layout (block / inline / flex / grid / table), fonts, images, CSSOM-View geometry | turbo-html2pdf, turbo-surf |
25
+ | [`turbo-dom-raster`](crates/turbo-dom-raster) | crates.io | 0.7.0 | synthetic screenshots (PNG / SVG), canvas op-log replay, WebGL readback | turbo-surf |
26
+ | [`turbo-dom-browser-env`](crates/turbo-dom-browser-env) | crates.io | 0.7.0 | the `rtdom` <-> V8 browser environment (window, document, CSSOM, forms, events) | turbo-test, turbo-surf |
27
27
  | [`turbo-dom-v8`](crates/turbo-dom-v8) | crates.io | 150.4.0 | turbo-dom's rusty_v8 fork, generated from the upstream crate plus reviewed patches | turbo-dom-browser-env, turbo-surf |
28
28
 
29
29
  The root `turbo-dom-parser` crate is `publish = false`. It ships inside the npm package.
@@ -105,10 +105,10 @@ parseBuffer, parseFragment })` or set it as `globalThis.__TURBO_DOM_PARSER__`. T
105
105
 
106
106
  ```toml
107
107
  [dependencies]
108
- turbo-dom = "0.6"
109
- turbo-dom-layout = "0.6" # bundled fonts on by default
110
- turbo-dom-raster = "0.6"
111
- turbo-dom-browser-env = "0.6" # needs a V8 archive, see its README
108
+ turbo-dom = "0.7"
109
+ turbo-dom-layout = "0.7" # bundled fonts on by default
110
+ turbo-dom-raster = "0.7"
111
+ turbo-dom-browser-env = "0.7" # needs a V8 archive, see its README
112
112
  ```
113
113
 
114
114
  ```rust
@@ -133,6 +133,7 @@ Each crate's README covers its API: [turbo-dom](crates/turbo-dom/README.md),
133
133
  | crate | feature | default | effect |
134
134
  |---|---|---|---|
135
135
  | `turbo-dom-layout` | `bundled-fonts` | on | embeds OFL faces (Inter/Roboto, Liberation Serif/PT Serif, Fira Code/IBM Plex Mono) so a document lays out with no caller fonts |
136
+ | | `hyphenation` | on | `hyphens: auto` for English (`en` / `en-*`, not en-GB) with the embedded en-US TeX patterns (~90 KB); off, `auto` acts as `manual` |
136
137
  | | `svg` | off | routes `image/svg+xml` through the SVG rasterizer in the image pipeline (implies `svg-rasterize`) |
137
138
  | | `svg-rasterize` | off | the shared resvg rasterizer only (`svg::rasterize`) |
138
139
  | | `serde` | off | `Serialize` for the CSSOM-View metrics records |
@@ -157,35 +158,47 @@ upstream rusty_v8's features (for example `simdutf`).
157
158
  upstream html5ever has not adopted yet. The Rust `rtdom` tree passes its direct
158
159
  html5lib-tests gate at 100% (`npm run conformance:rtdom`).
159
160
  - **Chrome parity:** behaviour fixes are backed by Chrome 156 controls. These are
160
- fixtures recorded from headless Chrome and asserted in tests. At the 0.6.0 parity
161
- remeasure (2026-10-29), turbo-surf's API inventory against Chrome 156 has 2 missing
162
- members, down from 142. Every corpus output change is declared in
163
- [`tools/parity/expected/`](tools/parity/expected) (checkpoint `b-0.6.0`).
161
+ fixtures recorded from headless Chrome and asserted in tests. Every corpus output change
162
+ is declared in [`tools/parity/expected/`](tools/parity/expected) (checkpoint `b-0.7.0`).
163
+ 0.7.0 adds vertical writing modes, UAX #9 bidi, Chrome's line box model on the PDF path,
164
+ the full `border-radius` model with `overflow` clipping, background tiling and gradient
165
+ length stops, native form-control appearance, size / layout containment, replaced media
166
+ boxes, XML documents (with DTD ATTLIST defaults), processing instructions, CSS
167
+ animations in `getAnimations()`, a browser-env cascade with specificity, `@layer`,
168
+ `@supports` and `@scope`, real canvas `measureText` and an opt-in V8 startup snapshot
169
+ install (see [CHANGELOG.md](CHANGELOG.md)).
164
170
  - **Open gaps (tracked as issues):**
165
- - XPath fragment contexts (turbo-dom#185)
166
- - XML parsing (#186)
167
- - CSS animations in `getAnimations` (#187)
168
- - focus / dialog details (#188)
169
- - FileList and selector error text (#189)
170
- - RTL scroll and vertical writing (#190)
171
- - shaping and font metrics (#191)
172
- - inset `box-shadow` (#192)
173
- - block and table layout residuals (#53–#102, #128, #178)
174
- - **The npm runtime has no layout:** `getBoundingClientRect()` returns zeros, and
175
- `getComputedStyle` is a partial cascade. It resolves real `<style>` rules and inline
176
- styles, applies inheritance, and serializes colours as `rgb()`. It never invents a
177
- value. `@media`, state pseudo-classes, pseudo-elements and unit conversion return `''`.
178
- Shadow DOM is supported: slots, retargeting, `:host` / `::slotted`, and declarative
179
- shadow roots. Layout belongs to `turbo-dom-layout`, and through it to turbo-html2pdf and
180
- turbo-surf.
171
+ - glyph stems in raster screenshots lack Chrome's stem darkening, so glyph edges are
172
+ up to ~0.5px thinner per side (#266).
173
+ - Known difference, not tracked: `hyphens: auto` covers English only, and Chrome on
174
+ macOS hyphenates with the system dictionary, whose points differ for some words.
175
+ - **The npm runtime has no layout.** Geometry is synthetic: `getBoundingClientRect()`,
176
+ `offsetWidth/Height`, `clientWidth/Height` and `scrollWidth/Height` return a non-zero
177
+ size that is stable for a given DOM state (blocks fill the parent's width, the root
178
+ takes `innerWidth` or 1024; heights stack children or 18px per line), while every
179
+ position (`top` / `left`, `offsetTop/Left`, `scrollTop/Left`) is 0.
180
+ `getComputedStyle` is a partial cascade: top-level `<style>` rules (and rules injected
181
+ with `insertRule`) plus inline styles, ordered by Selectors 4 specificity and source
182
+ order, with a fixed set of inherited properties. Colours read back as `rgb()` /
183
+ `rgba()` and a bare `0` as `0px`; a property no rule sets reads `''` (never an initial
184
+ value). Not resolved: rules inside any at-rule block (`@media`, `@supports`, `@layer`,
185
+ `@container`, `@scope`) are skipped, rules with state pseudo-classes (`:hover`, ...)
186
+ never match, a pseudo-element argument is ignored (the element's own style comes
187
+ back) and `::before`-style rules never apply. Values come back as written: no unit
188
+ conversion (`2em` stays `2em`), and `var(...)` and `inherit` / `initial` / `unset` are
189
+ returned verbatim, not resolved. Shadow DOM is supported: `attachShadow`, declarative
190
+ shadow roots, slots and `assignedNodes()`, event retargeting, scoped styles with
191
+ `:host` / `::slotted()`, and inheritance across the host boundary. Layout belongs to
192
+ `turbo-dom-layout`, and through it to turbo-html2pdf and turbo-surf.
181
193
 
182
194
  ## Benchmarks — pending re-run
183
195
 
184
- 0.6.0 has not been benchmarked yet. All four repositories will be re-run together on a
185
- quiet machine after the release. Until then, the last measured numbers (v0.1.55,
186
- 2026-06-07, darwin-arm64, JS runtime vs happy-dom and jsdom) are kept unchanged and
187
- labelled in [docs/BENCHMARKS-history.md](docs/BENCHMARKS-history.md). They predate the
188
- consolidation and say nothing about 0.6.0.
196
+ 0.6.0 and 0.7.0 have not been benchmarked against jsdom / happy-dom yet. All four
197
+ repositories will be re-run together on a quiet machine. Until then, the last measured
198
+ numbers (v0.1.55, 2026-06-07, darwin-arm64, JS runtime vs happy-dom and jsdom) are kept
199
+ unchanged and labelled in [docs/BENCHMARKS-history.md](docs/BENCHMARKS-history.md). They
200
+ predate the consolidation. 0.7.0's browser-env performance work (measured on a real
201
+ React/MUI suite) is listed in [CHANGELOG.md](CHANGELOG.md).
189
202
 
190
203
  ## Development
191
204
 
@@ -230,7 +243,11 @@ the host-font policy itself read system fonts.
230
243
  **CI runners.** Pull requests and pushes to `main` run on Linux (x64 and arm64) and on
231
244
  Windows x64: the napi build, and the V8 / browser-env matrix with the default and
232
245
  simdutf archives. macOS arm64 builds and jobs run only in the release workflow.
233
- `publish.yml` calls `ci.yml` with `darwin: true`.
246
+ `publish.yml` calls `ci.yml` with `darwin: true`. Linux binaries build on ubuntu-22.04 and a
247
+ guard (`scripts/check-glibc-ceiling.sh`) rejects any addon that needs glibc above 2.35. The
248
+ guard also fails when an expected addon is missing or unreadable. A newer push to a pull
249
+ request cancels its superseded CI run. `main`, tag and publish runs each get their own
250
+ concurrency group, so they are never cancelled or dropped while pending.
234
251
 
235
252
  ## Releasing
236
253
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miaskiewicz/turbo-dom",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "description": "Faster, more spec-correct DOM for test runners \u2014 native html5ever (Rust/WASM) parser + lazy copy-on-write DOM. A drop-in-style alternative to jsdom/happy-dom for vitest & jest.",
5
5
  "license": "MIT",
6
6
  "main": "index.js",
package/pkg/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "turbo-dom-parser",
3
3
  "description": "html5ever-backed native parser for turbo-dom (Layer 1)",
4
- "version": "0.6.0",
4
+ "version": "0.7.1",
5
5
  "files": [
6
6
  "turbo_dom_parser_bg.wasm",
7
7
  "turbo_dom_parser.js",
Binary file
@@ -2,7 +2,7 @@
2
2
  "name": "turbo-dom-parser",
3
3
  "type": "module",
4
4
  "description": "html5ever-backed native parser for turbo-dom (Layer 1)",
5
- "version": "0.6.0",
5
+ "version": "0.7.1",
6
6
  "files": [
7
7
  "turbo_dom_parser_bg.wasm",
8
8
  "turbo_dom_parser.js",
Binary file
@@ -14,6 +14,8 @@ import { SHORTHAND_TABLE, LONGHAND_ALIASES } from './css-shorthand-table.mjs';
14
14
 
15
15
  // ---------------------------------------------------------------- numbers ----
16
16
  const f32 = Math.fround;
17
+ /** The largest finite f32. */
18
+ const F32_MAX = 3.4028234663852886e38;
17
19
  /** Blink's CSS number serialization: six significant digits (printf `%g`). */
18
20
  export function cssNumber(value) {
19
21
  const v = f32(value);
@@ -583,7 +585,14 @@ function read(tokens, state, close) {
583
585
  out.push({ kind: 'percentage', value, text: `${cssNumber(Number(token.raw))}%` });
584
586
  break;
585
587
  }
586
- case 'dimension': out.push({ kind: 'dimension', value: f32(token.value), unit: token.unit.toLowerCase(), text: `${cssNumber(token.value)}${token.unit.toLowerCase()}` }); break;
588
+ case 'dimension': {
589
+ // A length past f32's range (`1e39px`) is the largest float, as Chrome clamps it
590
+ // (`3.40282e+38px`), never infinite (#262; the Rust port's tokenizer likewise).
591
+ const number = Math.max(-F32_MAX, Math.min(F32_MAX, token.value));
592
+ const unit = token.unit.toLowerCase();
593
+ out.push({ kind: 'dimension', value: f32(number), unit, text: `${cssNumber(number)}${unit}` });
594
+ break;
595
+ }
587
596
  case 'hash': out.push({ kind: 'hash', value: token.value, text: `#${token.value}` }); break;
588
597
  case 'string': out.push({ kind: 'string', value: token.value, text: quoted(token.value) }); break;
589
598
  case 'url': out.push({ kind: 'url', value: token.value, text: `url(${quoted(token.value)})` }); break;
Binary file
Binary file