css_inline 0.22.0 → 0.22.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 57120ea8fca07671de2fb675037e580ba6a8dc70898e3114d482d369ad8484ce
4
- data.tar.gz: b511fedab4f557823f3dc8377cc41648af679dbe9792393feac3a1c3388c7660
3
+ metadata.gz: 7bdb9e863d0c4b1d333dbab94beb014c8884308dcc0d78731ed5706f8468a300
4
+ data.tar.gz: ce1097ee0ac4313ad964607c17fb31ead1abf79d082a8afc1e95a65fe25a100d
5
5
  SHA512:
6
- metadata.gz: 8866b2c9edb026aa31558aef96b5a06e5a6d43b45208f7d04bced0f2d0b174137ce7833bfee8fc6fecab65e81a16ce4f9e388713311f9eae51067033f903813e
7
- data.tar.gz: ac3eef055019024f78778fbe8b6b62cd8552101720babc5974f86b702a7369db6889af1f2f7b6323a014df15196553c134be9071ff7ce8f3701d8fd04ba0bc6d
6
+ metadata.gz: 172a49c2daa9b63e608e2919124e00edc48d3419cd9a96eece513c9990dc187f638764c08520bddb226e6a54068a4ef4792209be4fb32d245a89505936ea4a41
7
+ data.tar.gz: 70058af033f406d517bb14c064f55d79886824ab417fc7dc6cd93a75edeb8f6c5faeeb55af1ecdca8f7efac86098d04358c4d6fe90ae0fba0ef66c98fbdcca9b
data/Cargo.lock CHANGED
@@ -154,9 +154,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
154
154
 
155
155
  [[package]]
156
156
  name = "css-inline"
157
- version = "0.21.2"
157
+ version = "0.22.1"
158
158
  source = "registry+https://github.com/rust-lang/crates.io-index"
159
- checksum = "7712a34dea84441c915bf8fbd94c78ce024f0f902dc333a4fa43510e047479f0"
159
+ checksum = "e50363b383b35fc4681129a12f93a653a58033cff464126a61c831c1563476ab"
160
160
  dependencies = [
161
161
  "cssparser",
162
162
  "html5ever",
@@ -172,7 +172,7 @@ dependencies = [
172
172
 
173
173
  [[package]]
174
174
  name = "css-inline-ruby"
175
- version = "0.22.0"
175
+ version = "0.22.1"
176
176
  dependencies = [
177
177
  "css-inline",
178
178
  "magnus",
@@ -181,9 +181,9 @@ dependencies = [
181
181
 
182
182
  [[package]]
183
183
  name = "cssparser"
184
- version = "0.37.0"
184
+ version = "0.38.0"
185
185
  source = "registry+https://github.com/rust-lang/crates.io-index"
186
- checksum = "8c9cdaae01d5ed7882b04d795e7f752f46ff52d2fa3b50a20d28c464510bba98"
186
+ checksum = "11119743ad110e8c1bdccd930d7f5c30c99e5fc76a7b63ec9807e84eef0c5f59"
187
187
  dependencies = [
188
188
  "cssparser-macros",
189
189
  "dtoa-short",
@@ -383,12 +383,13 @@ dependencies = [
383
383
 
384
384
  [[package]]
385
385
  name = "html5ever"
386
- version = "0.39.0"
386
+ version = "0.40.1"
387
387
  source = "registry+https://github.com/rust-lang/crates.io-index"
388
- checksum = "46a1761807faccc9a19e86944bbf40610014066306f96edcdedc2fb714bcb7b8"
388
+ checksum = "456a1a377e608e555d22ddab27ac0114bc7a7b4199078108e34c2aeae6c9b130"
389
389
  dependencies = [
390
390
  "log",
391
391
  "markup5ever",
392
+ "memchr",
392
393
  ]
393
394
 
394
395
  [[package]]
@@ -718,9 +719,9 @@ dependencies = [
718
719
 
719
720
  [[package]]
720
721
  name = "markup5ever"
721
- version = "0.39.0"
722
+ version = "0.40.0"
722
723
  source = "registry+https://github.com/rust-lang/crates.io-index"
723
- checksum = "7122d987ec5f704ee56f6e5b41a7d93722e9aae27ae07cafa4036c4d3f9757de"
724
+ checksum = "0ab3dc68ac4a0f5719e560136778c1ee716e296030d75dbd4484e37e39e3a842"
724
725
  dependencies = [
725
726
  "log",
726
727
  "tendril",
@@ -803,9 +804,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
803
804
 
804
805
  [[package]]
805
806
  name = "phf"
806
- version = "0.13.1"
807
+ version = "0.14.0"
807
808
  source = "registry+https://github.com/rust-lang/crates.io-index"
808
- checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf"
809
+ checksum = "010378780309880b08997fae13be7834dba947d36393bd372f2b1556deb2a2f6"
809
810
  dependencies = [
810
811
  "phf_macros",
811
812
  "phf_shared",
@@ -814,9 +815,9 @@ dependencies = [
814
815
 
815
816
  [[package]]
816
817
  name = "phf_codegen"
817
- version = "0.13.1"
818
+ version = "0.14.0"
818
819
  source = "registry+https://github.com/rust-lang/crates.io-index"
819
- checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1"
820
+ checksum = "41b585a510fb76fdebead6897982ef2a03a21d8e6cbcca904999742a4afc6ffe"
820
821
  dependencies = [
821
822
  "phf_generator",
822
823
  "phf_shared",
@@ -824,9 +825,9 @@ dependencies = [
824
825
 
825
826
  [[package]]
826
827
  name = "phf_generator"
827
- version = "0.13.1"
828
+ version = "0.14.0"
828
829
  source = "registry+https://github.com/rust-lang/crates.io-index"
829
- checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737"
830
+ checksum = "aeb62e0959d5a1bebc965f4d15d9e2b7cea002b6b0f5ba8cde6cc26738467100"
830
831
  dependencies = [
831
832
  "fastrand",
832
833
  "phf_shared",
@@ -834,9 +835,9 @@ dependencies = [
834
835
 
835
836
  [[package]]
836
837
  name = "phf_macros"
837
- version = "0.13.1"
838
+ version = "0.14.0"
838
839
  source = "registry+https://github.com/rust-lang/crates.io-index"
839
- checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef"
840
+ checksum = "5fa8d0ca26d424d27630da600c6624696e7dec8bf7b3b492b383c5dc49e5e085"
840
841
  dependencies = [
841
842
  "phf_generator",
842
843
  "phf_shared",
@@ -847,9 +848,9 @@ dependencies = [
847
848
 
848
849
  [[package]]
849
850
  name = "phf_shared"
850
- version = "0.13.1"
851
+ version = "0.14.0"
851
852
  source = "registry+https://github.com/rust-lang/crates.io-index"
852
- checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266"
853
+ checksum = "c6fd9027e2d9319be6349febd1db4e8d02aa544921200c9b777720ac34a3aa89"
853
854
  dependencies = [
854
855
  "siphasher",
855
856
  ]
@@ -1193,17 +1194,15 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
1193
1194
 
1194
1195
  [[package]]
1195
1196
  name = "selectors"
1196
- version = "0.40.0"
1197
+ version = "0.41.0"
1197
1198
  source = "registry+https://github.com/rust-lang/crates.io-index"
1198
- checksum = "eeee001a77567bb05e71652718d3ff76d8697c151ac523e68e9513194eb2b75a"
1199
+ checksum = "8322aebea94a6cc288ff884e8a49bdbceef60206c400c6c46d22e62fc4fd91a6"
1199
1200
  dependencies = [
1200
1201
  "bitflags",
1201
1202
  "cssparser",
1202
1203
  "derive_more",
1203
1204
  "log",
1204
1205
  "new_debug_unreachable",
1205
- "phf",
1206
- "phf_codegen",
1207
1206
  "precomputed-hash",
1208
1207
  "rustc-hash",
1209
1208
  "servo_arc",
@@ -1279,9 +1278,9 @@ dependencies = [
1279
1278
 
1280
1279
  [[package]]
1281
1280
  name = "servo_arc"
1282
- version = "0.4.3"
1281
+ version = "0.5.0"
1283
1282
  source = "registry+https://github.com/rust-lang/crates.io-index"
1284
- checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930"
1283
+ checksum = "b4eceb519ec34d8290aaf6e46f435102f2bfa63f22b05a144fb16ab266d3b198"
1285
1284
  dependencies = [
1286
1285
  "stable_deref_trait",
1287
1286
  ]
@@ -1334,9 +1333,9 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
1334
1333
 
1335
1334
  [[package]]
1336
1335
  name = "string_cache"
1337
- version = "0.9.0"
1336
+ version = "0.11.0"
1338
1337
  source = "registry+https://github.com/rust-lang/crates.io-index"
1339
- checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901"
1338
+ checksum = "ffa8a5dbe8b3f0bbe29d4c3225daafaeead63afdc1b65fc4c01a1384166038e6"
1340
1339
  dependencies = [
1341
1340
  "new_debug_unreachable",
1342
1341
  "parking_lot",
@@ -1346,9 +1345,9 @@ dependencies = [
1346
1345
 
1347
1346
  [[package]]
1348
1347
  name = "string_cache_codegen"
1349
- version = "0.6.1"
1348
+ version = "0.11.2"
1350
1349
  source = "registry+https://github.com/rust-lang/crates.io-index"
1351
- checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69"
1350
+ checksum = "928dcdf75e47626b3617a976ec205d9f057584c371c1f23b782129268d0e6edc"
1352
1351
  dependencies = [
1353
1352
  "phf_generator",
1354
1353
  "phf_shared",
@@ -1670,9 +1669,9 @@ dependencies = [
1670
1669
 
1671
1670
  [[package]]
1672
1671
  name = "web_atoms"
1673
- version = "0.2.3"
1672
+ version = "0.3.0"
1674
1673
  source = "registry+https://github.com/rust-lang/crates.io-index"
1675
- checksum = "57a9779e9f04d2ac1ce317aee707aa2f6b773afba7b931222bff6983843b1576"
1674
+ checksum = "7572660c8890448ba236b7376f27e389c6a7e1c70195622faced601f855c0ada"
1676
1675
  dependencies = [
1677
1676
  "phf",
1678
1677
  "phf_codegen",
data/README.md CHANGED
@@ -5,11 +5,10 @@
5
5
  [<img alt="codecov.io" src="https://img.shields.io/codecov/c/gh/Stranger6667/css-inline?logo=codecov&style=flat-square&token=tOzvV4kDY0" height="20">](https://app.codecov.io/github/Stranger6667/css-inline)
6
6
  [<img alt="gitter" src="https://img.shields.io/gitter/room/Stranger6667/css-inline?style=flat-square" height="20">](https://gitter.im/Stranger6667/css-inline)
7
7
 
8
- `css_inline` is a high-performance library for inlining CSS into HTML 'style' attributes.
8
+ `css_inline` moves CSS from `<style>` and `<link>` tags into HTML `style` attributes.
9
+ Use it to prepare HTML emails or to embed HTML into third-party web pages.
9
10
 
10
- This library is designed for scenarios such as preparing HTML emails or embedding HTML into third-party web pages.
11
-
12
- For instance, the library transforms HTML like this:
11
+ It turns this HTML:
13
12
 
14
13
  ```html
15
14
  <html>
@@ -28,36 +27,45 @@ into:
28
27
  <html>
29
28
  <head></head>
30
29
  <body>
31
- <h1 style="color:blue;">Big Text</h1>
30
+ <h1 style="color: blue;">Big Text</h1>
32
31
  </body>
33
32
  </html>
34
33
  ```
35
34
 
36
- - Uses reliable components from Mozilla's Servo project
35
+ - Parses and matches CSS with components from Mozilla's Servo project
37
36
  - Inlines CSS from `style` and `link` tags
38
37
  - Removes `style` and `link` tags
39
38
  - Resolves external stylesheets (including local files)
40
- - Optionally caches external stylesheets
39
+ - Caches external stylesheets (opt-in)
41
40
  - Can process multiple documents in parallel
42
41
  - Works on Linux, Windows, and macOS
43
42
  - Supports HTML5 & CSS3
44
- - Tested on Ruby 3.2, 3.3, and 3.4.
43
+ - Tested on Ruby 3.2, 3.3, 3.4, and 4.0
45
44
 
46
45
  ## Playground
47
46
 
48
- If you'd like to try `css-inline`, you can check the WebAssembly-powered [playground](https://css-inline.org/) to see the results instantly.
47
+ Try `css-inline` in the WebAssembly-powered [playground](https://css-inline.org/).
49
48
 
50
49
  ## Installation
51
50
 
52
- Add this line to your application's `Gemfile`:
51
+ Add this line to your application's `Gemfile` and run `bundle install`:
53
52
 
54
- ```
53
+ ```ruby
55
54
  gem 'css_inline'
56
55
  ```
57
56
 
57
+ Or install it directly:
58
+
59
+ ```sh
60
+ gem install css_inline
61
+ ```
62
+
63
+ RubyGems ships precompiled native gems for Ruby 3.2 to 4.0 on Linux (x86_64, aarch64, glibc and musl), macOS (x86_64, arm64) and Windows (x64 UCRT).
64
+ On other platforms, `gem install` builds the extension from source and needs a Rust toolchain.
65
+
58
66
  ## Usage
59
67
 
60
- To inline CSS in an HTML document:
68
+ Inline CSS in an HTML document:
61
69
 
62
70
  ```ruby
63
71
  require 'css_inline'
@@ -66,39 +74,39 @@ html = "<html><head><style>h1 { color:blue; }</style></head><body><h1>Big Text</
66
74
  inlined = CSSInline.inline(html)
67
75
 
68
76
  puts inlined
69
- # Outputs: "<html><head></head><body><h1 style=\"color:blue;\">Big Text</h1></body></html>"
77
+ # <html><head></head><body><h1 style="color: blue;">Big Text</h1></body></html>
70
78
  ```
71
79
 
72
- Note that `css-inline` automatically adds missing `html` and `body` tags, so the output is a valid HTML document.
80
+ `css-inline` adds missing `html`, `head` and `body` tags, so the output is a valid HTML document.
73
81
 
74
- Alternatively, you can inline CSS into an HTML fragment. Structural tags (`<html>`, `<head>`, `<body>`) are stripped from the output; only their contents are preserved. Use `CSSInline.inline` if you need to keep the full document structure:
82
+ To inline CSS into an HTML fragment, use `inline_fragment`. It strips structural tags (`<html>`, `<head>`, `<body>`) from the output and keeps their contents. Use `CSSInline.inline` to keep the full document structure:
75
83
 
76
84
  ```ruby
77
85
  require 'css_inline'
78
86
 
79
- fragment = """
80
- <main>
81
- <h1>Hello</h1>
82
- <section>
83
- <p>who am i</p>
84
- </section>
85
- </main>
86
- """
87
-
88
- css = """
89
- p {
90
- color: red;
91
- }
92
-
93
- h1 {
94
- color: blue;
95
- }
96
- """
87
+ fragment = <<~HTML
88
+ <main>
89
+ <h1>Hello</h1>
90
+ <section>
91
+ <p>who am i</p>
92
+ </section>
93
+ </main>
94
+ HTML
95
+
96
+ css = <<~CSS
97
+ p {
98
+ color: red;
99
+ }
100
+
101
+ h1 {
102
+ color: blue;
103
+ }
104
+ CSS
97
105
 
98
106
  inlined = CSSInline.inline_fragment(fragment, css)
99
107
 
100
108
  puts inlined
101
- # HTML becomes this:
109
+ # Output:
102
110
  # <main>
103
111
  # <h1 style="color: blue;">Hello</h1>
104
112
  # <section>
@@ -107,23 +115,27 @@ puts inlined
107
115
  # </main>
108
116
  ```
109
117
 
110
- When there is a need to inline multiple HTML documents simultaneously, `css_inline` offers `inline_many` and `inline_many_fragments` functions.
111
- This feature allows for concurrent processing of several inputs, significantly improving performance when dealing with a large number of documents.
118
+ To inline many documents in parallel, use `inline_many` and `inline_many_fragments`:
112
119
 
113
120
  ```ruby
114
121
  require 'css_inline'
115
122
 
116
123
  inlined = CSSInline.inline_many(["...", "..."])
124
+ inlined = CSSInline.inline_many_fragments(["<h1>...</h1>", "<p>...</p>"], ["h1 { color: blue; }", "p { color: red; }"])
117
125
  ```
118
126
 
119
- Under the hood, `inline_many`, spawns threads at the Rust layer to handle the parallel processing of inputs.
120
- This results in faster execution times compared to employing parallel processing techniques at the Ruby level.
127
+ `inline_many_fragments` pairs HTML and CSS by index.
128
+
129
+ **Warning**: pass arrays of equal length. If they differ, `inline_many_fragments` drops the extra items of the longer array without an error.
121
130
 
122
- **Note**: To fully benefit from `inline_many`, you should run your application on a multicore machine.
131
+ If one input fails, the whole call raises and returns no results.
132
+
133
+ Both functions process inputs on a Rust thread pool ([rayon](https://github.com/rayon-rs/rayon)), one thread per CPU core.
134
+ Ruby threads can't run this CPU-bound work in parallel because of the Global VM Lock, so `inline_many` gains over a Ruby loop only on a multicore machine.
123
135
 
124
136
  ## Configuration
125
137
 
126
- For customization options use the `CSSInliner` class:
138
+ Pass options as keyword arguments to `CSSInline::CSSInliner.new`:
127
139
 
128
140
  ```ruby
129
141
  require 'css_inline'
@@ -132,21 +144,25 @@ inliner = CSSInline::CSSInliner.new(keep_style_tags: true)
132
144
  inliner.inline("...")
133
145
  ```
134
146
 
135
- - `inline_style_tags`. Specifies whether to inline CSS from "style" tags. Default: `true`
136
- - `keep_style_tags`. Specifies whether to keep "style" tags after inlining. Default: `false`
137
- - `keep_link_tags`. Specifies whether to keep "link" tags after inlining. Default: `false`
138
- - `keep_at_rules`. Specifies whether to keep "at-rules" (starting with `@`) after inlining. Default: `false`
139
- - `minify_css`. Specifies whether to remove trailing semicolons and spaces between properties and values. Default: `false`
140
- - `base_url`. The base URL used to resolve relative URLs. If you'd like to load stylesheets from your filesystem, use the `file://` scheme. Default: `nil`
141
- - `load_remote_stylesheets`. Specifies whether remote stylesheets should be loaded. Default: `true`
142
- - `cache`. Specifies caching options for external stylesheets (for example, `StylesheetCache(size: 5)`). Default: `nil`
143
- - `extra_css`. Extra CSS to be inlined. Default: `nil`
144
- - `preallocate_node_capacity`. **Advanced**. Preallocates capacity for HTML nodes during parsing. This can improve performance when you have an estimate of the number of nodes in your HTML document. Default: `32`
145
- - `remove_inlined_selectors`. Specifies whether to remove selectors that were successfully inlined from `<style>` blocks. Default: `false`
146
- - `apply_width_attributes`. Specifies whether to add `width` HTML attributes from CSS `width` properties on supported elements (`table`, `td`, `th`, `img`). Default: `false`
147
- - `apply_height_attributes`. Specifies whether to add `height` HTML attributes from CSS `height` properties on supported elements (`table`, `td`, `th`, `img`). Default: `false`
148
-
149
- You can also skip CSS inlining for an HTML tag by adding the `data-css-inline="ignore"` attribute to it:
147
+ A `CSSInliner` instance has the same four methods as the module: `inline`, `inline_fragment`, `inline_many` and `inline_many_fragments`.
148
+ Reuse one instance to share its stylesheet cache across calls.
149
+ The module functions accept the same keyword arguments, for example `CSSInline.inline(html, keep_style_tags: true)`.
150
+
151
+ - `inline_style_tags`. Inline CSS from `style` tags. Default: `true`
152
+ - `keep_style_tags`. Keep `style` tags after inlining. Default: `false`
153
+ - `keep_link_tags`. Keep `link` tags after inlining. Default: `false`
154
+ - `keep_at_rules`. Keep at-rules (starting with `@`) after inlining. Default: `false`
155
+ - `minify_css`. Remove trailing semicolons and spaces between properties and values. Default: `false`
156
+ - `base_url`. Base URL for resolving relative URLs. Use the `file://` scheme to load stylesheets from the filesystem. Default: `nil`
157
+ - `load_remote_stylesheets`. Load stylesheets from `link` tags, over the network or from `file://` paths. Default: `true`
158
+ - `cache`. Cache for external stylesheets, for example `CSSInline::StylesheetCache.new(size: 5)`. Default: `nil`
159
+ - `extra_css`. Extra CSS to inline. Default: `nil`
160
+ - `preallocate_node_capacity`. **Advanced**. Number of HTML nodes to preallocate during parsing. Set it to your expected node count to avoid reallocations. Default: `32`
161
+ - `remove_inlined_selectors`. Remove inlined selectors from `<style>` blocks. Default: `false`
162
+ - `apply_width_attributes`. Add `width` HTML attributes from CSS `width` properties on `table`, `td`, `th` and `img`. Default: `false`
163
+ - `apply_height_attributes`. Add `height` HTML attributes from CSS `height` properties on `table`, `td`, `th` and `img`. Default: `false`
164
+
165
+ To skip CSS inlining for an HTML tag, add the `data-css-inline="ignore"` attribute to it:
150
166
 
151
167
  ```html
152
168
  <head>
@@ -158,7 +174,7 @@ You can also skip CSS inlining for an HTML tag by adding the `data-css-inline="i
158
174
  </body>
159
175
  ```
160
176
 
161
- The `data-css-inline="ignore"` attribute also allows you to skip `link` and `style` tags:
177
+ The same attribute on a `link` or `style` tag skips that stylesheet:
162
178
 
163
179
  ```html
164
180
  <head>
@@ -170,9 +186,9 @@ The `data-css-inline="ignore"` attribute also allows you to skip `link` and `sty
170
186
  </body>
171
187
  ```
172
188
 
173
- Alternatively, you may keep `style` from being removed by using the `data-css-inline="keep"` attribute.
174
- This is useful if you want to keep `@media` queries for responsive emails in separate `style` tags.
175
- Such tags will be kept in the resulting HTML even if the `keep_style_tags` option is set to `false`.
189
+ To keep a `style` tag in the output, add the `data-css-inline="keep"` attribute.
190
+ Use it to keep `@media` queries for responsive emails in separate `style` tags.
191
+ `css-inline` keeps such tags even when `keep_style_tags` is `false`.
176
192
 
177
193
  ```html
178
194
  <head>
@@ -184,14 +200,13 @@ Such tags will be kept in the resulting HTML even if the `keep_style_tags` optio
184
200
  </body>
185
201
  ```
186
202
 
187
- Another possibility is to set `keep_at_rules` option to `true`. At-rules cannot be inlined into HTML therefore they
188
- get removed by default. This is useful if you want to keep at-rules, e.g. `@media` queries for responsive emails in
189
- separate `style` tags but inline any styles which can be inlined.
190
- Such tags will be kept in the resulting HTML even if the `keep_style_tags` option is explicitly set to `false`.
203
+ Another way is the `keep_at_rules` option. At-rules can't go into `style` attributes, so `css-inline` removes them by default.
204
+ With `keep_at_rules: true`, it inlines regular rules and keeps at-rules, such as `@media` queries, in `style` tags.
205
+ It keeps these tags even when `keep_style_tags` is `false`.
191
206
 
192
207
  ```html
193
208
  <head>
194
- <!-- With keep_at_rules=true "color:blue" will get inlined into <h1> but @media will be kept in <style> -->
209
+ <!-- With keep_at_rules: true, "color:blue" will get inlined into <h1> but @media will be kept in <style> -->
195
210
  <style>h1 { color: blue; } @media (max-width: 600px) { h1 { font-size: 18px; } }</style>
196
211
  </head>
197
212
  <body>
@@ -199,12 +214,11 @@ Such tags will be kept in the resulting HTML even if the `keep_style_tags` optio
199
214
  </body>
200
215
  ```
201
216
 
202
- If you set the the `minify_css` option to `true`, the inlined styles will be minified by removing trailing semicolons
203
- and spaces between properties and values.
217
+ With `minify_css: true`, `css-inline` removes trailing semicolons and spaces between properties and values in inlined styles.
204
218
 
205
219
  ```html
206
220
  <head>
207
- <!-- With minify_css=true, the <h1> will have `style="color:blue;font-weight:bold"` -->
221
+ <!-- With minify_css: true, the <h1> will have `style="color:blue;font-weight:bold"` -->
208
222
  <style>h1 { color: blue; font-weight: bold; }</style>
209
223
  </head>
210
224
  <body>
@@ -212,17 +226,20 @@ and spaces between properties and values.
212
226
  </body>
213
227
  ```
214
228
 
215
- If you'd like to load stylesheets from your filesystem, use the `file://` scheme:
229
+ To load stylesheets from the filesystem, use the `file://` scheme.
230
+ `css-inline` treats a `file://` URL without a third slash, such as `file://styles/email/`, as a path relative to the current working directory.
231
+ Standard URL parsers read `styles` there as a host, so this form is specific to `css-inline`.
232
+ For an absolute path, use three slashes: `file:///path/to/styles/`.
216
233
 
217
234
  ```ruby
218
235
  require 'css_inline'
219
236
 
220
- # styles/email is relative to the current directory
237
+ # Loads stylesheets from ./styles/email/
221
238
  inliner = CSSInline::CSSInliner.new(base_url: "file://styles/email/")
222
239
  inliner.inline("...")
223
240
  ```
224
241
 
225
- You can also cache external stylesheets to avoid excessive network requests:
242
+ To avoid repeated network requests, cache external stylesheets:
226
243
 
227
244
  ```ruby
228
245
  require 'css_inline'
@@ -233,32 +250,45 @@ inliner = CSSInline::CSSInliner.new(
233
250
  inliner.inline("...")
234
251
  ```
235
252
 
236
- Caching is disabled by default.
253
+ The cache covers every `link` stylesheet, `file://` included. `size` is the number of stylesheets to keep (default: `8`). When the cache is full, it evicts the least recently used stylesheet.
254
+ Caching is off by default.
237
255
 
238
- ## Performance
256
+ ## Errors
239
257
 
240
- This library uses components from Mozilla's Servo project for CSS parsing and matching.
241
- Performance benchmarks show 50-100x faster execution than `roadie` and `premailer`.
258
+ All functions raise `ArgumentError` when they can't load a stylesheet (missing file, network error), can't parse one, or get an invalid `base_url`:
242
259
 
243
- The table below shows benchmark results comparing `css_inline`, `roadie`, and `premailer` on typical HTML documents:
260
+ ```ruby
261
+ require 'css_inline'
244
262
 
245
- | | Size | `css_inline 0.19.0` | `roadie 5.2.1` | `premailer 1.21.0` |
263
+ begin
264
+ CSSInline.inline('<link rel="stylesheet" href="missing.css"><h1>Hi</h1>')
265
+ rescue ArgumentError => e
266
+ puts e.message
267
+ # Missing stylesheet file: missing.css
268
+ end
269
+ ```
270
+
271
+ ## Performance
272
+
273
+ `css_inline` runs 8.1x to 354x faster than `roadie` and 57x to 123x faster than `premailer` on these documents:
274
+
275
+ | | Size | `css_inline 0.22.0` | `roadie 5.2.1` | `premailer 1.27.0` |
246
276
  |-------------------|---------|---------------------|-------------------------|-------------------------|
247
- | Basic usage | 230 B | 6.07 µs | 173.50 µs (**28.60x**) | 345.30 µs (**56.91x**) |
248
- | Realistic email 1 | 8.58 KB | 91.23 µs | 713.40 µs (**7.82x**) | 6.80 ms (**74.53x**) |
249
- | Realistic email 2 | 4.3 KB | 57.56 µs | 1.99 ms (**34.56x**) | ERROR |
250
- | GitHub Page | 1.81 MB | 21.61 ms | 8.20 s (**379.45x**) | 2.40 s (**111.06x**) |
277
+ | Basic usage | 230 B | 5.84 µs | 168.88 µs (**28.92x**) | 331.07 µs (**56.69x**) |
278
+ | Realistic email 1 | 8.58 KB | 87.66 µs | 713.78 µs (**8.14x**) | 6.95 ms (**79.28x**) |
279
+ | Realistic email 2 | 4.30 KB | 50.45 µs | 1.98 ms (**39.25x**) | ERROR |
280
+ | GitHub Page | 1.81 MB | 19.01 ms | 6.73 s (**354.02x**) | 2.34 s (**123.09x**) |
251
281
 
252
- Please refer to the `test/bench.rb` file to review the benchmark code.
253
- The results displayed above were measured using stable `rustc 1.91` on Ruby `3.4.7`.
282
+ `premailer` fails on "Realistic email 2" with `ArgumentError: Cannot parse 0 calc((100% - 500px) / 2)`.
254
283
 
255
- ## Further reading
284
+ Inputs come from [`benchmarks/benchmarks.json`](https://github.com/Stranger6667/css-inline/blob/master/benchmarks/benchmarks.json): Basic usage is `simple`, Realistic email 1 and 2 are `big_email_1` and `big_email_2`, GitHub Page is `big_page`.
285
+ The benchmark code is in [`test/bench.rb`](https://github.com/Stranger6667/css-inline/blob/master/bindings/ruby/test/bench.rb) and uses [`benchmark-ips`](https://github.com/evanphx/benchmark-ips) (5 s per case; `roadie` completes one run on the GitHub page in that time).
286
+ We measured these results with stable `rustc 1.99` on Ruby `3.4.8`, Ryzen 9 9950X.
256
287
 
257
- If you want to know how this library was created & how it works internally, you could take a look at these articles:
288
+ ## Further reading
258
289
 
259
- - [Rust crate](https://dygalo.dev/blog/rust-for-a-pythonista-2/)
260
- - [Python bindings](https://dygalo.dev/blog/rust-for-a-pythonista-3/)
290
+ - [How the Rust crate works](https://dygalo.dev/blog/rust-for-a-pythonista-2/)
261
291
 
262
292
  ## License
263
293
 
264
- This project is licensed under the terms of the [MIT license](https://opensource.org/licenses/MIT).
294
+ [MIT license](https://opensource.org/licenses/MIT).
@@ -154,9 +154,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
154
154
 
155
155
  [[package]]
156
156
  name = "css-inline"
157
- version = "0.21.2"
157
+ version = "0.22.1"
158
158
  source = "registry+https://github.com/rust-lang/crates.io-index"
159
- checksum = "7712a34dea84441c915bf8fbd94c78ce024f0f902dc333a4fa43510e047479f0"
159
+ checksum = "e50363b383b35fc4681129a12f93a653a58033cff464126a61c831c1563476ab"
160
160
  dependencies = [
161
161
  "cssparser",
162
162
  "html5ever",
@@ -172,7 +172,7 @@ dependencies = [
172
172
 
173
173
  [[package]]
174
174
  name = "css-inline-ruby"
175
- version = "0.22.0"
175
+ version = "0.22.1"
176
176
  dependencies = [
177
177
  "css-inline",
178
178
  "magnus",
@@ -181,9 +181,9 @@ dependencies = [
181
181
 
182
182
  [[package]]
183
183
  name = "cssparser"
184
- version = "0.37.0"
184
+ version = "0.38.0"
185
185
  source = "registry+https://github.com/rust-lang/crates.io-index"
186
- checksum = "8c9cdaae01d5ed7882b04d795e7f752f46ff52d2fa3b50a20d28c464510bba98"
186
+ checksum = "11119743ad110e8c1bdccd930d7f5c30c99e5fc76a7b63ec9807e84eef0c5f59"
187
187
  dependencies = [
188
188
  "cssparser-macros",
189
189
  "dtoa-short",
@@ -383,12 +383,13 @@ dependencies = [
383
383
 
384
384
  [[package]]
385
385
  name = "html5ever"
386
- version = "0.39.0"
386
+ version = "0.40.1"
387
387
  source = "registry+https://github.com/rust-lang/crates.io-index"
388
- checksum = "46a1761807faccc9a19e86944bbf40610014066306f96edcdedc2fb714bcb7b8"
388
+ checksum = "456a1a377e608e555d22ddab27ac0114bc7a7b4199078108e34c2aeae6c9b130"
389
389
  dependencies = [
390
390
  "log",
391
391
  "markup5ever",
392
+ "memchr",
392
393
  ]
393
394
 
394
395
  [[package]]
@@ -718,9 +719,9 @@ dependencies = [
718
719
 
719
720
  [[package]]
720
721
  name = "markup5ever"
721
- version = "0.39.0"
722
+ version = "0.40.0"
722
723
  source = "registry+https://github.com/rust-lang/crates.io-index"
723
- checksum = "7122d987ec5f704ee56f6e5b41a7d93722e9aae27ae07cafa4036c4d3f9757de"
724
+ checksum = "0ab3dc68ac4a0f5719e560136778c1ee716e296030d75dbd4484e37e39e3a842"
724
725
  dependencies = [
725
726
  "log",
726
727
  "tendril",
@@ -803,9 +804,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
803
804
 
804
805
  [[package]]
805
806
  name = "phf"
806
- version = "0.13.1"
807
+ version = "0.14.0"
807
808
  source = "registry+https://github.com/rust-lang/crates.io-index"
808
- checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf"
809
+ checksum = "010378780309880b08997fae13be7834dba947d36393bd372f2b1556deb2a2f6"
809
810
  dependencies = [
810
811
  "phf_macros",
811
812
  "phf_shared",
@@ -814,9 +815,9 @@ dependencies = [
814
815
 
815
816
  [[package]]
816
817
  name = "phf_codegen"
817
- version = "0.13.1"
818
+ version = "0.14.0"
818
819
  source = "registry+https://github.com/rust-lang/crates.io-index"
819
- checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1"
820
+ checksum = "41b585a510fb76fdebead6897982ef2a03a21d8e6cbcca904999742a4afc6ffe"
820
821
  dependencies = [
821
822
  "phf_generator",
822
823
  "phf_shared",
@@ -824,9 +825,9 @@ dependencies = [
824
825
 
825
826
  [[package]]
826
827
  name = "phf_generator"
827
- version = "0.13.1"
828
+ version = "0.14.0"
828
829
  source = "registry+https://github.com/rust-lang/crates.io-index"
829
- checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737"
830
+ checksum = "aeb62e0959d5a1bebc965f4d15d9e2b7cea002b6b0f5ba8cde6cc26738467100"
830
831
  dependencies = [
831
832
  "fastrand",
832
833
  "phf_shared",
@@ -834,9 +835,9 @@ dependencies = [
834
835
 
835
836
  [[package]]
836
837
  name = "phf_macros"
837
- version = "0.13.1"
838
+ version = "0.14.0"
838
839
  source = "registry+https://github.com/rust-lang/crates.io-index"
839
- checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef"
840
+ checksum = "5fa8d0ca26d424d27630da600c6624696e7dec8bf7b3b492b383c5dc49e5e085"
840
841
  dependencies = [
841
842
  "phf_generator",
842
843
  "phf_shared",
@@ -847,9 +848,9 @@ dependencies = [
847
848
 
848
849
  [[package]]
849
850
  name = "phf_shared"
850
- version = "0.13.1"
851
+ version = "0.14.0"
851
852
  source = "registry+https://github.com/rust-lang/crates.io-index"
852
- checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266"
853
+ checksum = "c6fd9027e2d9319be6349febd1db4e8d02aa544921200c9b777720ac34a3aa89"
853
854
  dependencies = [
854
855
  "siphasher",
855
856
  ]
@@ -1193,17 +1194,15 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
1193
1194
 
1194
1195
  [[package]]
1195
1196
  name = "selectors"
1196
- version = "0.40.0"
1197
+ version = "0.41.0"
1197
1198
  source = "registry+https://github.com/rust-lang/crates.io-index"
1198
- checksum = "eeee001a77567bb05e71652718d3ff76d8697c151ac523e68e9513194eb2b75a"
1199
+ checksum = "8322aebea94a6cc288ff884e8a49bdbceef60206c400c6c46d22e62fc4fd91a6"
1199
1200
  dependencies = [
1200
1201
  "bitflags",
1201
1202
  "cssparser",
1202
1203
  "derive_more",
1203
1204
  "log",
1204
1205
  "new_debug_unreachable",
1205
- "phf",
1206
- "phf_codegen",
1207
1206
  "precomputed-hash",
1208
1207
  "rustc-hash",
1209
1208
  "servo_arc",
@@ -1279,9 +1278,9 @@ dependencies = [
1279
1278
 
1280
1279
  [[package]]
1281
1280
  name = "servo_arc"
1282
- version = "0.4.3"
1281
+ version = "0.5.0"
1283
1282
  source = "registry+https://github.com/rust-lang/crates.io-index"
1284
- checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930"
1283
+ checksum = "b4eceb519ec34d8290aaf6e46f435102f2bfa63f22b05a144fb16ab266d3b198"
1285
1284
  dependencies = [
1286
1285
  "stable_deref_trait",
1287
1286
  ]
@@ -1334,9 +1333,9 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
1334
1333
 
1335
1334
  [[package]]
1336
1335
  name = "string_cache"
1337
- version = "0.9.0"
1336
+ version = "0.11.0"
1338
1337
  source = "registry+https://github.com/rust-lang/crates.io-index"
1339
- checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901"
1338
+ checksum = "ffa8a5dbe8b3f0bbe29d4c3225daafaeead63afdc1b65fc4c01a1384166038e6"
1340
1339
  dependencies = [
1341
1340
  "new_debug_unreachable",
1342
1341
  "parking_lot",
@@ -1346,9 +1345,9 @@ dependencies = [
1346
1345
 
1347
1346
  [[package]]
1348
1347
  name = "string_cache_codegen"
1349
- version = "0.6.1"
1348
+ version = "0.11.2"
1350
1349
  source = "registry+https://github.com/rust-lang/crates.io-index"
1351
- checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69"
1350
+ checksum = "928dcdf75e47626b3617a976ec205d9f057584c371c1f23b782129268d0e6edc"
1352
1351
  dependencies = [
1353
1352
  "phf_generator",
1354
1353
  "phf_shared",
@@ -1670,9 +1669,9 @@ dependencies = [
1670
1669
 
1671
1670
  [[package]]
1672
1671
  name = "web_atoms"
1673
- version = "0.2.3"
1672
+ version = "0.3.0"
1674
1673
  source = "registry+https://github.com/rust-lang/crates.io-index"
1675
- checksum = "57a9779e9f04d2ac1ce317aee707aa2f6b773afba7b931222bff6983843b1576"
1674
+ checksum = "7572660c8890448ba236b7376f27e389c6a7e1c70195622faced601f855c0ada"
1676
1675
  dependencies = [
1677
1676
  "phf",
1678
1677
  "phf_codegen",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "css-inline-ruby"
3
- version = "0.22.0"
3
+ version = "0.22.1"
4
4
  authors = ["Dmitry Dygalo <dmitry@dygalo.dev>"]
5
5
  edition = "2024"
6
6
  readme = "README.rdoc"
@@ -20,6 +20,6 @@ magnus = "0.9"
20
20
  rayon = "1"
21
21
 
22
22
  [dependencies.css-inline]
23
- version = "0.21.1"
23
+ version = "0.22.1"
24
24
  default-features = false
25
25
  features = ["http", "file", "stylesheet-cache"]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: css_inline
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.22.0
4
+ version: 0.22.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Dygalo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-01 00:00:00.000000000 Z
11
+ date: 2026-10-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rb_sys