premailer 1.16.0 → 1.27.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.
- checksums.yaml +4 -4
- data/README.md +49 -29
- data/bin/premailer +1 -1
- data/lib/premailer/adapter/nokogiri.rb +21 -19
- data/lib/premailer/adapter/nokogiri_fast.rb +23 -20
- data/lib/premailer/adapter/nokogumbo.rb +20 -15
- data/lib/premailer/adapter/rgb_to_hex.rb +9 -7
- data/lib/premailer/adapter.rb +4 -3
- data/lib/premailer/cached_rule_set.rb +13 -0
- data/lib/premailer/executor.rb +5 -4
- data/lib/premailer/html_to_plain_text.rb +4 -3
- data/lib/premailer/premailer.rb +29 -23
- data/lib/premailer/version.rb +2 -1
- data/lib/premailer.rb +2 -0
- metadata +30 -14
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 31108244f6afc74508eef1337a9b99adc7f2643b5412e2aacf808ecb8c520101
|
|
4
|
+
data.tar.gz: b2f10188d4b7143050eb86e04776c38fc220ebb834da7871e838fe47db5fe9c5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4cd3655f0aa411da6d874dd50e2e1ab1c6f1cae4f9882cfa84a6ec2022174f5d9670d439527bb27618774f7f731f7ac24b35420c58926db159b4f81e85cb660d
|
|
7
|
+
data.tar.gz: aa583335ee3a7b0de99320aff3c162d233825c99fbfe287ae13f12790c4b54728b7b0460861f928efe9ddae07416aafb229058f698267214e8b740ff94ea20a8
|
data/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
For the best HTML e-mail delivery results, CSS should be inline. This is a
|
|
6
6
|
huge pain and a simple newsletter becomes un-managable very quickly. This
|
|
7
|
-
|
|
7
|
+
gem is a solution.
|
|
8
8
|
|
|
9
9
|
* CSS styles are converted to inline style attributes
|
|
10
10
|
- Checks `style` and `link[rel=stylesheet]` tags and preserves existing inline attributes
|
|
@@ -12,35 +12,26 @@ script is my solution.
|
|
|
12
12
|
- Checks links in `href`, `src` and CSS `url('')`
|
|
13
13
|
* CSS properties are checked against e-mail client capabilities
|
|
14
14
|
- Based on the Email Standards Project's guides
|
|
15
|
-
* A [plain text version](
|
|
15
|
+
* A [plain text version](#plain-text-version) is created (optional)
|
|
16
16
|
|
|
17
17
|
## Installation
|
|
18
18
|
|
|
19
|
-
Install the Premailer gem from RubyGems.
|
|
20
|
-
|
|
21
19
|
```bash
|
|
22
20
|
gem install premailer
|
|
23
21
|
```
|
|
24
22
|
|
|
25
|
-
or add it to your `Gemfile` and run `bundle`.
|
|
26
|
-
|
|
27
23
|
## Example
|
|
28
24
|
|
|
29
25
|
```ruby
|
|
30
26
|
require 'premailer'
|
|
31
27
|
|
|
32
|
-
premailer = Premailer.new('http://example.com/myfile.html', :
|
|
28
|
+
premailer = Premailer.new('http://example.com/myfile.html', warn_level: Premailer::Warnings::SAFE)
|
|
33
29
|
|
|
34
|
-
# Write the plain-text output
|
|
35
|
-
|
|
36
|
-
File.open("output.txt", "w") do |fout|
|
|
37
|
-
fout.puts premailer.to_plain_text
|
|
38
|
-
end
|
|
30
|
+
# Write the plain-text output (must come before to_inline_css)
|
|
31
|
+
File.write "output.txt", premailer.to_plain_text
|
|
39
32
|
|
|
40
33
|
# Write the HTML output
|
|
41
|
-
File.
|
|
42
|
-
fout.puts premailer.to_inline_css
|
|
43
|
-
end
|
|
34
|
+
File.write "output.html", premailer.to_inline_css
|
|
44
35
|
|
|
45
36
|
# Output any CSS warnings
|
|
46
37
|
premailer.warnings.each do |w|
|
|
@@ -50,17 +41,13 @@ end
|
|
|
50
41
|
|
|
51
42
|
## Adapters
|
|
52
43
|
|
|
53
|
-
Premailer's default adapter is nokogiri if both nokogiri and nokogumbo are included in the Gemfile list. However, if you want to use a different adapter, you can choose to.
|
|
54
|
-
|
|
55
|
-
There are three adapters in total (as of premailer 1.10.0)
|
|
56
|
-
|
|
57
44
|
1. nokogiri (default)
|
|
58
|
-
2. nokogiri_fast
|
|
45
|
+
2. nokogiri_fast (20x speed, more memory)
|
|
59
46
|
3. nokogumbo
|
|
60
47
|
|
|
61
|
-
hpricot adapter removed
|
|
48
|
+
(hpricot adapter removed, use `~>1.9.0` version if you need it)
|
|
62
49
|
|
|
63
|
-
|
|
50
|
+
Picking an adapter:
|
|
64
51
|
|
|
65
52
|
```ruby
|
|
66
53
|
Premailer::Adapter.use = :nokogiri_fast
|
|
@@ -97,22 +84,56 @@ will result in
|
|
|
97
84
|
<table cellspacing='5' width='500'>
|
|
98
85
|
```
|
|
99
86
|
|
|
100
|
-
##
|
|
87
|
+
## Plain text version
|
|
88
|
+
|
|
89
|
+
Premailer can generate a plain text version of your HTML. Links and images will be inlined.
|
|
101
90
|
|
|
102
|
-
|
|
91
|
+
For example
|
|
103
92
|
|
|
104
|
-
|
|
93
|
+
```html
|
|
94
|
+
<a href="https://example.com" >
|
|
95
|
+
<img src="https://github.com/premailer.png" alt="Premailer Logo" />
|
|
96
|
+
</a>
|
|
97
|
+
```
|
|
105
98
|
|
|
99
|
+
will become
|
|
100
|
+
|
|
101
|
+
```text
|
|
102
|
+
Premailer Logo ( https://example.com )
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
To ignore/omit a section of HTML content from the plain text version, wrap it with the following comments.
|
|
106
|
+
|
|
107
|
+
```html
|
|
108
|
+
<!-- start text/html -->
|
|
109
|
+
<p>This will be omitted from the plain text version.</p>
|
|
110
|
+
<p>
|
|
111
|
+
This is extremely helpful for <strong>removing email headers and footers</strong>
|
|
112
|
+
that aren't needed in the text version.
|
|
113
|
+
</p>
|
|
114
|
+
<!-- end text/html -->
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Configuration options
|
|
118
|
+
|
|
119
|
+
For example:
|
|
106
120
|
```ruby
|
|
107
|
-
|
|
121
|
+
Premailer.new(
|
|
122
|
+
html, # html as string
|
|
123
|
+
with_html_string: true,
|
|
124
|
+
drop_unmergeable_css_rules: true
|
|
125
|
+
)
|
|
108
126
|
```
|
|
109
127
|
|
|
110
|
-
[
|
|
128
|
+
[available options](https://premailer.github.io/premailer/Premailer.html#initialize-instance_method)
|
|
111
129
|
|
|
112
130
|
|
|
113
131
|
## Contributions
|
|
114
132
|
|
|
115
|
-
Contributions are most welcome.
|
|
133
|
+
Contributions are most welcome.
|
|
134
|
+
Premailer was rotting away in a private SVN repository for too long and could use some TLC.
|
|
135
|
+
Fork and patch to your heart's content.
|
|
136
|
+
Please don't increment the version numbers.
|
|
116
137
|
|
|
117
138
|
A few areas that are particularly in need of love:
|
|
118
139
|
|
|
@@ -129,4 +150,3 @@ and to [Campaign Monitor](https://www.campaignmonitor.com/) for supporting the w
|
|
|
129
150
|
The source code can be found on [GitHub](https://github.com/premailer/premailer).
|
|
130
151
|
|
|
131
152
|
Copyright by Alex Dunae (dunae.ca, e-mail 'code' at the same domain), 2007-2017. See [LICENSE.md](https://github.com/premailer/premailer/blob/master/LICENSE.md) for license details.
|
|
132
|
-
|
data/bin/premailer
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require 'nokogiri'
|
|
2
3
|
|
|
3
4
|
class Premailer
|
|
4
5
|
module Adapter
|
|
5
6
|
# Nokogiri adapter
|
|
6
7
|
module Nokogiri
|
|
8
|
+
WIDTH_AND_HIGHT = ['width', 'height'].freeze
|
|
7
9
|
|
|
8
10
|
include AdapterHelper::RgbToHex
|
|
9
11
|
# Merge CSS into the HTML document.
|
|
@@ -27,13 +29,13 @@ class Premailer
|
|
|
27
29
|
selector.gsub!(/([\s]|^)([\w]+)/) { |m| $1.to_s + $2.to_s.downcase }
|
|
28
30
|
|
|
29
31
|
if Premailer.is_media_query?(media_types) || selector =~ Premailer::RE_UNMERGABLE_SELECTORS
|
|
30
|
-
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration), media_types) unless @options[:preserve_styles]
|
|
32
|
+
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selectors: selector, block: declaration), media_types) unless @options[:preserve_styles]
|
|
31
33
|
else
|
|
32
34
|
begin
|
|
33
|
-
if
|
|
35
|
+
if Premailer::RE_RESET_SELECTORS.match?(selector)
|
|
34
36
|
# this is in place to preserve the MailChimp CSS reset: http://github.com/mailchimp/Email-Blueprints/
|
|
35
37
|
# however, this doesn't mean for testing pur
|
|
36
|
-
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration)) unless !@options[:preserve_reset]
|
|
38
|
+
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selectors: selector, block: declaration)) unless !@options[:preserve_reset]
|
|
37
39
|
end
|
|
38
40
|
|
|
39
41
|
# Change single ID CSS selectors into xpath so that we can match more
|
|
@@ -61,15 +63,21 @@ class Premailer
|
|
|
61
63
|
doc.search("*[@style]").each do |el|
|
|
62
64
|
style = el.attributes['style'].to_s
|
|
63
65
|
|
|
64
|
-
declarations = []
|
|
65
|
-
|
|
66
|
-
rs
|
|
67
|
-
|
|
66
|
+
declarations = style.scan(/\[SPEC\=([\d]+)\[(.[^\]\]]*)\]\]/m).filter_map do |declaration|
|
|
67
|
+
rs = Premailer::CachedRuleSet.new(block: declaration[1].to_s, specificity: declaration[0].to_i)
|
|
68
|
+
rs.expand_shorthand!
|
|
69
|
+
rs
|
|
70
|
+
rescue ArgumentError => e
|
|
71
|
+
raise e if @options[:rule_set_exceptions]
|
|
68
72
|
end
|
|
69
73
|
|
|
70
74
|
# Perform style folding
|
|
71
75
|
merged = CssParser.merge(declarations)
|
|
72
|
-
|
|
76
|
+
begin
|
|
77
|
+
merged.expand_shorthand!
|
|
78
|
+
rescue ArgumentError => e
|
|
79
|
+
raise e if @options[:rule_set_exceptions]
|
|
80
|
+
end
|
|
73
81
|
|
|
74
82
|
# Duplicate CSS attributes as HTML attributes
|
|
75
83
|
if Premailer::RELATED_ATTRIBUTES.has_key?(el.name) && @options[:css_to_attributes]
|
|
@@ -84,7 +92,7 @@ class Premailer
|
|
|
84
92
|
new_val.gsub!(/;$|\s*!important/, '').strip!
|
|
85
93
|
|
|
86
94
|
# For width and height tags, remove px units
|
|
87
|
-
new_val.gsub!(/(\d+)px/, '\1') if
|
|
95
|
+
new_val.gsub!(/(\d+)px/, '\1') if WIDTH_AND_HIGHT.include?(html_attr)
|
|
88
96
|
|
|
89
97
|
# For color-related tags, convert RGB to hex if specified by options
|
|
90
98
|
new_val = ensure_hex(new_val) if css_attr.end_with?('color') && @options[:rgb_to_hex_attributes]
|
|
@@ -93,7 +101,7 @@ class Premailer
|
|
|
93
101
|
end
|
|
94
102
|
|
|
95
103
|
unless @options[:preserve_style_attribute]
|
|
96
|
-
merged.instance_variable_get(
|
|
104
|
+
merged.instance_variable_get(:@declarations).tap do |declarations|
|
|
97
105
|
declarations.delete(css_attr)
|
|
98
106
|
end
|
|
99
107
|
end
|
|
@@ -229,18 +237,12 @@ class Premailer
|
|
|
229
237
|
|
|
230
238
|
# Handle HTML entities
|
|
231
239
|
if @options[:replace_html_entities] == true and thing.is_a?(String)
|
|
240
|
+
thing = +thing
|
|
232
241
|
HTML_ENTITIES.map do |entity, replacement|
|
|
233
242
|
thing.gsub! entity, replacement
|
|
234
243
|
end
|
|
235
244
|
end
|
|
236
|
-
|
|
237
|
-
# However, we really don't want to hardcode this. ASCII-8BIT should be the default, but not the only option.
|
|
238
|
-
encoding = if thing.is_a?(String) and RUBY_VERSION =~ /1.9/
|
|
239
|
-
thing = thing.force_encoding(@options[:input_encoding]).encode!
|
|
240
|
-
@options[:input_encoding]
|
|
241
|
-
else
|
|
242
|
-
@options[:input_encoding] || (RUBY_PLATFORM == 'java' ? nil : 'BINARY')
|
|
243
|
-
end
|
|
245
|
+
encoding = @options[:input_encoding] || (RUBY_PLATFORM == 'java' ? nil : 'BINARY')
|
|
244
246
|
doc = if @options[:html_fragment]
|
|
245
247
|
::Nokogiri::HTML.fragment(thing, encoding)
|
|
246
248
|
else
|
|
@@ -250,7 +252,7 @@ class Premailer
|
|
|
250
252
|
# Fix for removing any CDATA tags from both style and script tags inserted per
|
|
251
253
|
# https://github.com/sparklemotion/nokogiri/issues/311 and
|
|
252
254
|
# https://github.com/premailer/premailer/issues/199
|
|
253
|
-
|
|
255
|
+
['style', 'script'].each do |tag|
|
|
254
256
|
doc.search(tag).children.each do |child|
|
|
255
257
|
child.swap(child.text()) if child.cdata?
|
|
256
258
|
end
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require 'nokogiri'
|
|
2
3
|
|
|
3
4
|
class Premailer
|
|
4
5
|
module Adapter
|
|
5
6
|
# NokogiriFast adapter
|
|
6
7
|
module NokogiriFast
|
|
8
|
+
WIDTH_AND_HEIGHT = ['width', 'height'].freeze
|
|
7
9
|
|
|
8
10
|
include AdapterHelper::RgbToHex
|
|
9
11
|
# Merge CSS into the HTML document.
|
|
@@ -33,13 +35,13 @@ class Premailer
|
|
|
33
35
|
selector.gsub!(/([\s]|^)([\w]+)/) { |m| $1.to_s + $2.to_s.downcase }
|
|
34
36
|
|
|
35
37
|
if Premailer.is_media_query?(media_types) || selector =~ Premailer::RE_UNMERGABLE_SELECTORS
|
|
36
|
-
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration), media_types) unless @options[:preserve_styles]
|
|
38
|
+
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selectors: selector, block: declaration), media_types) unless @options[:preserve_styles]
|
|
37
39
|
else
|
|
38
40
|
begin
|
|
39
|
-
if
|
|
41
|
+
if Premailer::RE_RESET_SELECTORS.match?(selector)
|
|
40
42
|
# this is in place to preserve the MailChimp CSS reset: http://github.com/mailchimp/Email-Blueprints/
|
|
41
43
|
# however, this doesn't mean for testing pur
|
|
42
|
-
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration)) unless !@options[:preserve_reset]
|
|
44
|
+
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selectors: selector, block: declaration)) unless !@options[:preserve_reset]
|
|
43
45
|
end
|
|
44
46
|
|
|
45
47
|
# Try the new index based technique. If not supported, fall back to the old brute force one.
|
|
@@ -66,14 +68,22 @@ class Premailer
|
|
|
66
68
|
style = el.attributes['style'].to_s
|
|
67
69
|
|
|
68
70
|
declarations = []
|
|
69
|
-
style.scan(/\[SPEC\=([\d]+)\[(.[^\]\]]*)\]\]/).each do |declaration|
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
style.scan(/\[SPEC\=([\d]+)\[(.[^\]\]]*)\]\]/m).each do |declaration|
|
|
72
|
+
begin
|
|
73
|
+
rs = CssParser::RuleSet.new(block: declaration[1].to_s, specificity: declaration[0].to_i)
|
|
74
|
+
declarations << rs
|
|
75
|
+
rescue ArgumentError => e
|
|
76
|
+
raise e if @options[:rule_set_exceptions]
|
|
77
|
+
end
|
|
72
78
|
end
|
|
73
79
|
|
|
74
80
|
# Perform style folding
|
|
75
81
|
merged = CssParser.merge(declarations)
|
|
76
|
-
|
|
82
|
+
begin
|
|
83
|
+
merged.expand_shorthand!
|
|
84
|
+
rescue ArgumentError => e
|
|
85
|
+
raise e if @options[:rule_set_exceptions]
|
|
86
|
+
end
|
|
77
87
|
|
|
78
88
|
# Duplicate CSS attributes as HTML attributes
|
|
79
89
|
if Premailer::RELATED_ATTRIBUTES.has_key?(el.name) && @options[:css_to_attributes]
|
|
@@ -88,7 +98,7 @@ class Premailer
|
|
|
88
98
|
new_val.gsub!(/;$|\s*!important/, '').strip!
|
|
89
99
|
|
|
90
100
|
# For width and height tags, remove px units
|
|
91
|
-
new_val.gsub!(/(\d+)px/, '\1') if
|
|
101
|
+
new_val.gsub!(/(\d+)px/, '\1') if WIDTH_AND_HEIGHT.include?(html_attr)
|
|
92
102
|
|
|
93
103
|
# For color-related tags, convert RGB to hex if specified by options
|
|
94
104
|
new_val = ensure_hex(new_val) if css_attr.end_with?('color') && @options[:rgb_to_hex_attributes]
|
|
@@ -97,7 +107,7 @@ class Premailer
|
|
|
97
107
|
end
|
|
98
108
|
|
|
99
109
|
unless @options[:preserve_style_attribute]
|
|
100
|
-
merged.instance_variable_get(
|
|
110
|
+
merged.instance_variable_get(:@declarations).tap do |declarations|
|
|
101
111
|
declarations.delete(css_attr)
|
|
102
112
|
end
|
|
103
113
|
end
|
|
@@ -237,14 +247,7 @@ class Premailer
|
|
|
237
247
|
thing.gsub! entity, replacement
|
|
238
248
|
end
|
|
239
249
|
end
|
|
240
|
-
|
|
241
|
-
# However, we really don't want to hardcode this. ASCII-8BIT should be the default, but not the only option.
|
|
242
|
-
encoding = if thing.is_a?(String) and RUBY_VERSION =~ /1.9/
|
|
243
|
-
thing = thing.force_encoding(@options[:input_encoding]).encode!
|
|
244
|
-
@options[:input_encoding]
|
|
245
|
-
else
|
|
246
|
-
@options[:input_encoding] || (RUBY_PLATFORM == 'java' ? nil : 'BINARY')
|
|
247
|
-
end
|
|
250
|
+
encoding = @options[:input_encoding] || (RUBY_PLATFORM == 'java' ? nil : 'BINARY')
|
|
248
251
|
doc = if @options[:html_fragment]
|
|
249
252
|
::Nokogiri::HTML.fragment(thing, encoding)
|
|
250
253
|
else
|
|
@@ -254,7 +257,7 @@ class Premailer
|
|
|
254
257
|
# Fix for removing any CDATA tags from both style and script tags inserted per
|
|
255
258
|
# https://github.com/sparklemotion/nokogiri/issues/311 and
|
|
256
259
|
# https://github.com/premailer/premailer/issues/199
|
|
257
|
-
|
|
260
|
+
['style', 'script'].each do |tag|
|
|
258
261
|
doc.search(tag).children.each do |child|
|
|
259
262
|
child.swap(child.text()) if child.cdata?
|
|
260
263
|
end
|
|
@@ -313,7 +316,7 @@ class Premailer
|
|
|
313
316
|
index.default = Set.new
|
|
314
317
|
descendants.default = Set.new
|
|
315
318
|
|
|
316
|
-
|
|
319
|
+
[index, Set.new(all_nodes), descendants]
|
|
317
320
|
end
|
|
318
321
|
|
|
319
322
|
# @param doc The top level document
|
|
@@ -353,7 +356,7 @@ class Premailer
|
|
|
353
356
|
# It will return nil when such a selector is passed, so you can take
|
|
354
357
|
# action on the falsity of the return value.
|
|
355
358
|
def match_selector(index, all_nodes, descendants, selector)
|
|
356
|
-
if /[^-a-zA-Z0-9_\s.#]/.match(selector) then
|
|
359
|
+
if /[^-a-zA-Z0-9_\s.#]/.match?(selector) then
|
|
357
360
|
return nil
|
|
358
361
|
end
|
|
359
362
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
class Premailer
|
|
2
3
|
module Adapter
|
|
3
4
|
# Nokogiri adapter
|
|
4
5
|
module Nokogumbo
|
|
6
|
+
WIDTH_AND_HEIGHT = ['width', 'height'].freeze
|
|
5
7
|
|
|
6
8
|
include AdapterHelper::RgbToHex
|
|
7
9
|
# Merge CSS into the HTML document.
|
|
@@ -25,13 +27,13 @@ class Premailer
|
|
|
25
27
|
selector.gsub!(/([\s]|^)([\w]+)/) { |m| $1.to_s + $2.to_s.downcase }
|
|
26
28
|
|
|
27
29
|
if Premailer.is_media_query?(media_types) || selector =~ Premailer::RE_UNMERGABLE_SELECTORS
|
|
28
|
-
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration), media_types) unless @options[:preserve_styles]
|
|
30
|
+
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selectors: selector, block: declaration), media_types) unless @options[:preserve_styles]
|
|
29
31
|
else
|
|
30
32
|
begin
|
|
31
|
-
if
|
|
33
|
+
if Premailer::RE_RESET_SELECTORS.match?(selector)
|
|
32
34
|
# this is in place to preserve the MailChimp CSS reset: http://github.com/mailchimp/Email-Blueprints/
|
|
33
35
|
# however, this doesn't mean for testing pur
|
|
34
|
-
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration)) unless !@options[:preserve_reset]
|
|
36
|
+
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selectors: selector, block: declaration)) unless !@options[:preserve_reset]
|
|
35
37
|
end
|
|
36
38
|
|
|
37
39
|
# Change single ID CSS selectors into xpath so that we can match more
|
|
@@ -60,14 +62,22 @@ class Premailer
|
|
|
60
62
|
style = el.attributes['style'].to_s
|
|
61
63
|
|
|
62
64
|
declarations = []
|
|
63
|
-
style.scan(/\[SPEC\=([\d]+)\[(.[^\]\]]*)\]\]/).each do |declaration|
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
style.scan(/\[SPEC\=([\d]+)\[(.[^\]\]]*)\]\]/m).each do |declaration|
|
|
66
|
+
begin
|
|
67
|
+
rs = CssParser::RuleSet.new(block: declaration[1].to_s, specificity: declaration[0].to_i)
|
|
68
|
+
declarations << rs
|
|
69
|
+
rescue ArgumentError => e
|
|
70
|
+
raise e if @options[:rule_set_exceptions]
|
|
71
|
+
end
|
|
66
72
|
end
|
|
67
73
|
|
|
68
74
|
# Perform style folding
|
|
69
75
|
merged = CssParser.merge(declarations)
|
|
70
|
-
|
|
76
|
+
begin
|
|
77
|
+
merged.expand_shorthand!
|
|
78
|
+
rescue ArgumentError => e
|
|
79
|
+
raise e if @options[:rule_set_exceptions]
|
|
80
|
+
end
|
|
71
81
|
|
|
72
82
|
# Duplicate CSS attributes as HTML attributes
|
|
73
83
|
if Premailer::RELATED_ATTRIBUTES.has_key?(el.name) && @options[:css_to_attributes]
|
|
@@ -82,7 +92,7 @@ class Premailer
|
|
|
82
92
|
new_val.gsub!(/;$|\s*!important/, '').strip!
|
|
83
93
|
|
|
84
94
|
# For width and height tags, remove px units
|
|
85
|
-
new_val.gsub!(/(\d+)px/, '\1') if
|
|
95
|
+
new_val.gsub!(/(\d+)px/, '\1') if WIDTH_AND_HEIGHT.include?(html_attr)
|
|
86
96
|
|
|
87
97
|
# For color-related tags, convert RGB to hex if specified by options
|
|
88
98
|
new_val = ensure_hex(new_val) if css_attr.end_with?('color') && @options[:rgb_to_hex_attributes]
|
|
@@ -91,7 +101,7 @@ class Premailer
|
|
|
91
101
|
end
|
|
92
102
|
|
|
93
103
|
unless @options[:preserve_style_attribute]
|
|
94
|
-
merged.instance_variable_get(
|
|
104
|
+
merged.instance_variable_get(:@declarations).tap do |declarations|
|
|
95
105
|
declarations.delete(css_attr)
|
|
96
106
|
end
|
|
97
107
|
end
|
|
@@ -231,11 +241,6 @@ class Premailer
|
|
|
231
241
|
thing.gsub! entity, replacement
|
|
232
242
|
end
|
|
233
243
|
end
|
|
234
|
-
# Default encoding is ASCII-8BIT (binary) per http://groups.google.com/group/nokogiri-talk/msg/0b81ef0dc180dc74
|
|
235
|
-
# However, we really don't want to hardcode this. ASCII-8BIT should be the default, but not the only option.
|
|
236
|
-
if thing.is_a?(String) and RUBY_VERSION =~ /1.9/
|
|
237
|
-
thing = thing.force_encoding(@options[:input_encoding]).encode!
|
|
238
|
-
end
|
|
239
244
|
doc = if @options[:html_fragment]
|
|
240
245
|
::Nokogiri::HTML5.fragment(thing)
|
|
241
246
|
else
|
|
@@ -245,7 +250,7 @@ class Premailer
|
|
|
245
250
|
# Fix for removing any CDATA tags from both style and script tags inserted per
|
|
246
251
|
# https://github.com/sparklemotion/nokogiri/issues/311 and
|
|
247
252
|
# https://github.com/premailer/premailer/issues/199
|
|
248
|
-
|
|
253
|
+
['style', 'script'].each do |tag|
|
|
249
254
|
doc.search(tag).children.each do |child|
|
|
250
255
|
child.swap(child.text()) if child.cdata?
|
|
251
256
|
end
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
# RGB helper for adapters, currently only nokogiri supported
|
|
2
3
|
|
|
3
4
|
module AdapterHelper
|
|
@@ -9,13 +10,14 @@ module AdapterHelper
|
|
|
9
10
|
def is_rgb?(color)
|
|
10
11
|
pattern = %r{
|
|
11
12
|
rgb
|
|
12
|
-
\(\s*
|
|
13
|
-
(\d{1,3})
|
|
14
|
-
|
|
15
|
-
(\d{1,3})
|
|
16
|
-
|
|
17
|
-
(\d{1,3})
|
|
18
|
-
\s*\)
|
|
13
|
+
\(\s* # literal open, with optional whitespace
|
|
14
|
+
(\d{1,3}) # capture 1-3 digits
|
|
15
|
+
(?:\s*,\s*|\s+) # comma or whitespace
|
|
16
|
+
(\d{1,3}) # capture 1-3 digits
|
|
17
|
+
(?:\s*,\s*|\s+) # comma or whitespacee
|
|
18
|
+
(\d{1,3}) # capture 1-3 digits
|
|
19
|
+
\s*(?:\/\s*\d*\.?\d*%?)? # optional alpha modifier
|
|
20
|
+
\s*\) # literal close, with optional whitespace
|
|
19
21
|
}x
|
|
20
22
|
|
|
21
23
|
pattern.match(color)
|
data/lib/premailer/adapter.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
class Premailer
|
|
2
3
|
# Manages the adapter classes. Currently supports:
|
|
3
4
|
#
|
|
@@ -14,7 +15,7 @@ class Premailer
|
|
|
14
15
|
REQUIREMENT_MAP = [
|
|
15
16
|
["nokogiri", :nokogiri],
|
|
16
17
|
["nokogiri", :nokogiri_fast],
|
|
17
|
-
["nokogumbo", :nokogumbo]
|
|
18
|
+
["nokogumbo", :nokogumbo]
|
|
18
19
|
]
|
|
19
20
|
|
|
20
21
|
# Returns the adapter to use.
|
|
@@ -42,7 +43,7 @@ class Premailer
|
|
|
42
43
|
end
|
|
43
44
|
end
|
|
44
45
|
|
|
45
|
-
raise
|
|
46
|
+
raise "No suitable adapter for Premailer was found, please install nokogiri or nokogumbo"
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
# Sets the adapter to use.
|
|
@@ -56,7 +57,7 @@ class Premailer
|
|
|
56
57
|
def self.find(adapter)
|
|
57
58
|
return adapter if adapter.is_a?(Module)
|
|
58
59
|
|
|
59
|
-
Premailer::Adapter.const_get("#{adapter.to_s.split('_').map{|s| s.capitalize}.join
|
|
60
|
+
Premailer::Adapter.const_get("#{adapter.to_s.split('_').map{|s| s.capitalize}.join}")
|
|
60
61
|
rescue NameError
|
|
61
62
|
raise ArgumentError, "Invalid adapter: #{adapter}"
|
|
62
63
|
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
class Premailer
|
|
3
|
+
class CachedRuleSet < CssParser::RuleSet
|
|
4
|
+
# we call this early to find errors but css-parser calls it in .merge again
|
|
5
|
+
# so to prevent slowdown and bugs we refuse to run it twice on the same ruleset
|
|
6
|
+
# ideally should be upstreamed into css-parser
|
|
7
|
+
def expand_shorthand!
|
|
8
|
+
super unless @expand_shorthand
|
|
9
|
+
ensure
|
|
10
|
+
@expand_shorthand = true
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
data/lib/premailer/executor.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require 'optparse'
|
|
2
3
|
require 'premailer'
|
|
3
4
|
|
|
@@ -8,12 +9,12 @@ options = {
|
|
|
8
9
|
:remove_classes => false,
|
|
9
10
|
:verbose => false,
|
|
10
11
|
:line_length => 65,
|
|
11
|
-
:adapter => :nokogiri
|
|
12
|
+
:adapter => :nokogiri
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
mode = :html
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
parser = OptionParser.new do |opts|
|
|
17
18
|
opts.banner = "Improve the rendering of HTML emails by making CSS inline among other things. Takes a path to a local file, a URL or a pipe as input.\n\n"
|
|
18
19
|
opts.define_head "Usage: premailer <optional uri|optional path> [options]"
|
|
19
20
|
opts.separator ""
|
|
@@ -79,7 +80,7 @@ opts = OptionParser.new do |opts|
|
|
|
79
80
|
exit
|
|
80
81
|
end
|
|
81
82
|
end
|
|
82
|
-
|
|
83
|
+
parser.parse!
|
|
83
84
|
|
|
84
85
|
$stderr.puts "Processing in #{mode} mode with options #{options.inspect}" if options[:verbose]
|
|
85
86
|
|
|
@@ -98,7 +99,7 @@ end
|
|
|
98
99
|
if input
|
|
99
100
|
premailer = Premailer.new(input, options)
|
|
100
101
|
else
|
|
101
|
-
puts
|
|
102
|
+
puts parser
|
|
102
103
|
exit 1
|
|
103
104
|
end
|
|
104
105
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
require 'htmlentities'
|
|
3
4
|
|
|
4
5
|
# Support functions for Premailer
|
|
@@ -13,8 +14,8 @@ module HtmlToPlainText
|
|
|
13
14
|
#
|
|
14
15
|
# TODO: add support for DL, OL
|
|
15
16
|
# TODO: this is not safe and needs a real html parser to work
|
|
16
|
-
def convert_to_text(html, line_length = 65,
|
|
17
|
-
txt = html
|
|
17
|
+
def convert_to_text(html, line_length = 65, _from_charset = 'UTF-8')
|
|
18
|
+
txt = +html
|
|
18
19
|
|
|
19
20
|
# strip text ignored html. Useful for removing
|
|
20
21
|
# headers and footers that aren't needed in the
|
|
@@ -50,7 +51,7 @@ module HtmlToPlainText
|
|
|
50
51
|
|
|
51
52
|
if text.empty?
|
|
52
53
|
''
|
|
53
|
-
elsif href.nil? || text.strip.
|
|
54
|
+
elsif href.nil? || text.strip.casecmp(href.strip) == 0
|
|
54
55
|
text.strip
|
|
55
56
|
else
|
|
56
57
|
text.strip + ' ( ' + href.strip + ' )'
|
data/lib/premailer/premailer.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
# Premailer processes HTML and CSS to improve e-mail deliverability.
|
|
2
3
|
#
|
|
3
4
|
# Premailer's main function is to render all CSS as inline <tt>style</tt>
|
|
@@ -15,7 +16,7 @@
|
|
|
15
16
|
# fout.close
|
|
16
17
|
#
|
|
17
18
|
# # Write the plain-text output
|
|
18
|
-
# fout = File.open("
|
|
19
|
+
# fout = File.open("output.txt", "w")
|
|
19
20
|
# fout.puts premailer.to_plain_text
|
|
20
21
|
# fout.close
|
|
21
22
|
#
|
|
@@ -141,7 +142,7 @@ class Premailer
|
|
|
141
142
|
include Warnings
|
|
142
143
|
|
|
143
144
|
# Waning level names
|
|
144
|
-
WARN_LABEL =
|
|
145
|
+
WARN_LABEL = ['NONE', 'SAFE', 'POOR', 'RISKY']
|
|
145
146
|
|
|
146
147
|
# Create a new Premailer object.
|
|
147
148
|
#
|
|
@@ -168,7 +169,8 @@ class Premailer
|
|
|
168
169
|
# @option options [Boolean] :preserve_reset Whether to preserve styles associated with the MailChimp reset code. Default is true.
|
|
169
170
|
# @option options [Boolean] :with_html_string Whether the html param should be treated as a raw string. Default is false.
|
|
170
171
|
# @option options [Boolean] :verbose Whether to print errors and warnings to <tt>$stderr</tt>. Default is false.
|
|
171
|
-
# @option options [Boolean] :io_exceptions Throws exceptions on I/O errors.
|
|
172
|
+
# @option options [Boolean] :io_exceptions Throws exceptions on I/O errors. Default is false.
|
|
173
|
+
# @option options [Boolean] :rule_set_exceptions Throws exceptions on invalid values in CSS Parser rule sets. Default is true.
|
|
172
174
|
# @option options [Boolean] :include_link_tags Whether to include css from <tt>link rel=stylesheet</tt> tags. Default is true.
|
|
173
175
|
# @option options [Boolean] :include_style_tags Whether to include css from <tt>style</tt> tags. Default is true.
|
|
174
176
|
# @option options [String] :input_encoding Manually specify the source documents encoding. This is a good idea. Default is ASCII-8BIT.
|
|
@@ -200,6 +202,7 @@ class Premailer
|
|
|
200
202
|
:verbose => false,
|
|
201
203
|
:debug => false,
|
|
202
204
|
:io_exceptions => false,
|
|
205
|
+
:rule_set_exceptions => true,
|
|
203
206
|
:include_link_tags => true,
|
|
204
207
|
:include_style_tags => true,
|
|
205
208
|
:input_encoding => 'ASCII-8BIT',
|
|
@@ -210,8 +213,7 @@ class Premailer
|
|
|
210
213
|
:create_shorthands => true,
|
|
211
214
|
:html_fragment => false,
|
|
212
215
|
:adapter => Adapter.use,
|
|
213
|
-
:drop_unmergeable_css_rules => false
|
|
214
|
-
}.merge(options)
|
|
216
|
+
:drop_unmergeable_css_rules => false}.merge(options)
|
|
215
217
|
|
|
216
218
|
@html_file = html
|
|
217
219
|
@is_local_file = @options[:with_html_string] || Premailer.local_data?(html)
|
|
@@ -233,7 +235,8 @@ class Premailer
|
|
|
233
235
|
@css_parser = CssParser::Parser.new({
|
|
234
236
|
:absolute_paths => true,
|
|
235
237
|
:import => true,
|
|
236
|
-
:io_exceptions => @options[:io_exceptions]
|
|
238
|
+
:io_exceptions => @options[:io_exceptions],
|
|
239
|
+
:rule_set_exceptions => @options[:rule_set_exceptions]
|
|
237
240
|
})
|
|
238
241
|
|
|
239
242
|
@adapter_class = Adapter.find @options[:adapter]
|
|
@@ -261,11 +264,11 @@ class Premailer
|
|
|
261
264
|
|
|
262
265
|
protected
|
|
263
266
|
def load_css_from_local_file!(path)
|
|
264
|
-
css_block = ''
|
|
265
|
-
path.
|
|
267
|
+
css_block = +''
|
|
268
|
+
path.delete_prefix!('file:')
|
|
266
269
|
begin
|
|
267
270
|
File.open(path, "r") do |file|
|
|
268
|
-
while line = file.gets
|
|
271
|
+
while (line = file.gets)
|
|
269
272
|
css_block << line
|
|
270
273
|
end
|
|
271
274
|
end
|
|
@@ -352,11 +355,12 @@ public
|
|
|
352
355
|
media_types.split(/[\s]+|,/).any? { |media_type| media_type.strip =~ /screen|handheld|all/i }
|
|
353
356
|
end
|
|
354
357
|
|
|
355
|
-
def append_query_string(doc,
|
|
356
|
-
return doc if
|
|
358
|
+
def append_query_string(doc, queries)
|
|
359
|
+
return doc if queries.nil?
|
|
357
360
|
|
|
358
|
-
|
|
359
|
-
|
|
361
|
+
queries = +queries
|
|
362
|
+
queries.to_s.gsub!(/^[\?]*/, '').strip!
|
|
363
|
+
return doc if queries.empty?
|
|
360
364
|
|
|
361
365
|
begin
|
|
362
366
|
current_host = @base_url.host
|
|
@@ -364,18 +368,18 @@ public
|
|
|
364
368
|
current_host = nil
|
|
365
369
|
end
|
|
366
370
|
|
|
367
|
-
$stderr.puts "Attempting to append_query_string: #{
|
|
371
|
+
$stderr.puts "Attempting to append_query_string: #{queries}" if @options[:verbose]
|
|
368
372
|
|
|
369
|
-
doc.search('a').each do|el|
|
|
373
|
+
doc.search('a').each do |el|
|
|
370
374
|
href = el.attributes['href'].to_s.strip
|
|
371
375
|
next if href.nil? or href.empty?
|
|
372
376
|
|
|
373
|
-
next if
|
|
377
|
+
next if /[\#\{\[\<\%]/.match?(href[0,1]) # don't bother with anchors or special-looking links
|
|
374
378
|
|
|
375
379
|
begin
|
|
376
380
|
href = Addressable::URI.parse(href)
|
|
377
381
|
|
|
378
|
-
if current_host and href.host
|
|
382
|
+
if current_host and !href.host.nil? and href.host != current_host
|
|
379
383
|
$stderr.puts "Skipping append_query_string for: #{href.to_s} because host is no good" if @options[:verbose]
|
|
380
384
|
next
|
|
381
385
|
end
|
|
@@ -387,9 +391,9 @@ public
|
|
|
387
391
|
|
|
388
392
|
if href.query and not href.query.empty?
|
|
389
393
|
amp = @options[:unescaped_ampersand] ? '&' : '&'
|
|
390
|
-
href.query = href.query + amp +
|
|
394
|
+
href.query = href.query + amp + queries
|
|
391
395
|
else
|
|
392
|
-
href.query =
|
|
396
|
+
href.query = queries
|
|
393
397
|
end
|
|
394
398
|
|
|
395
399
|
el['href'] = href.to_s
|
|
@@ -432,11 +436,11 @@ public
|
|
|
432
436
|
tags.each do |tag|
|
|
433
437
|
# skip links that look like they have merge tags
|
|
434
438
|
# and mailto, ftp, etc...
|
|
435
|
-
if
|
|
439
|
+
if /^([\%\<\{\#\[]|data:|tel:|file:|sms:|callto:|facetime:|mailto:|ftp:|gopher:|cid:)/i.match?(tag.attributes[attribute].to_s)
|
|
436
440
|
next
|
|
437
441
|
end
|
|
438
442
|
|
|
439
|
-
if tag.attributes[attribute].to_s
|
|
443
|
+
if /^http/i.match?(tag.attributes[attribute].to_s)
|
|
440
444
|
begin
|
|
441
445
|
merged = Addressable::URI.parse(tag.attributes[attribute])
|
|
442
446
|
rescue; next; end
|
|
@@ -470,9 +474,10 @@ public
|
|
|
470
474
|
|
|
471
475
|
# @private
|
|
472
476
|
def self.resolve_link(path, base_path) # :nodoc:
|
|
477
|
+
path = +path
|
|
473
478
|
path.strip!
|
|
474
479
|
resolved = nil
|
|
475
|
-
if
|
|
480
|
+
if /\A(?:(https?|ftp|file):)\/\//i.match?(path)
|
|
476
481
|
resolved = path
|
|
477
482
|
Premailer.canonicalize(resolved)
|
|
478
483
|
elsif base_path.kind_of?(Addressable::URI)
|
|
@@ -504,7 +509,8 @@ public
|
|
|
504
509
|
|
|
505
510
|
# Check <tt>CLIENT_SUPPORT_FILE</tt> for any CSS warnings
|
|
506
511
|
def check_client_support # :nodoc:
|
|
507
|
-
|
|
512
|
+
kwargs = Psych::VERSION >= '4' ? { aliases: true } : {}
|
|
513
|
+
@client_support ||= Psych.load(File.open(CLIENT_SUPPORT_FILE), **kwargs)
|
|
508
514
|
|
|
509
515
|
warnings = []
|
|
510
516
|
properties = []
|
data/lib/premailer/version.rb
CHANGED
data/lib/premailer.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require 'yaml'
|
|
2
3
|
require 'open-uri'
|
|
3
4
|
require 'digest/sha2'
|
|
@@ -9,3 +10,4 @@ require 'premailer/adapter'
|
|
|
9
10
|
require 'premailer/adapter/rgb_to_hex'
|
|
10
11
|
require 'premailer/html_to_plain_text'
|
|
11
12
|
require 'premailer/premailer'
|
|
13
|
+
require 'premailer/cached_rule_set'
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: premailer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.27.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alex Dunae
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2024-08-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: css_parser
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 1.
|
|
19
|
+
version: 1.19.0
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 1.
|
|
26
|
+
version: 1.19.0
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: htmlentities
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -92,14 +92,14 @@ dependencies:
|
|
|
92
92
|
requirements:
|
|
93
93
|
- - "~>"
|
|
94
94
|
- !ruby/object:Gem::Version
|
|
95
|
-
version: '1.
|
|
95
|
+
version: '1.16'
|
|
96
96
|
type: :development
|
|
97
97
|
prerelease: false
|
|
98
98
|
version_requirements: !ruby/object:Gem::Requirement
|
|
99
99
|
requirements:
|
|
100
100
|
- - "~>"
|
|
101
101
|
- !ruby/object:Gem::Version
|
|
102
|
-
version: '1.
|
|
102
|
+
version: '1.16'
|
|
103
103
|
- !ruby/object:Gem::Dependency
|
|
104
104
|
name: redcarpet
|
|
105
105
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -129,7 +129,7 @@ dependencies:
|
|
|
129
129
|
- !ruby/object:Gem::Version
|
|
130
130
|
version: '0'
|
|
131
131
|
- !ruby/object:Gem::Dependency
|
|
132
|
-
name:
|
|
132
|
+
name: webmock
|
|
133
133
|
requirement: !ruby/object:Gem::Requirement
|
|
134
134
|
requirements:
|
|
135
135
|
- - ">="
|
|
@@ -143,7 +143,7 @@ dependencies:
|
|
|
143
143
|
- !ruby/object:Gem::Version
|
|
144
144
|
version: '0'
|
|
145
145
|
- !ruby/object:Gem::Dependency
|
|
146
|
-
name:
|
|
146
|
+
name: bump
|
|
147
147
|
requirement: !ruby/object:Gem::Requirement
|
|
148
148
|
requirements:
|
|
149
149
|
- - ">="
|
|
@@ -157,7 +157,21 @@ dependencies:
|
|
|
157
157
|
- !ruby/object:Gem::Version
|
|
158
158
|
version: '0'
|
|
159
159
|
- !ruby/object:Gem::Dependency
|
|
160
|
-
name:
|
|
160
|
+
name: rubocop
|
|
161
|
+
requirement: !ruby/object:Gem::Requirement
|
|
162
|
+
requirements:
|
|
163
|
+
- - "~>"
|
|
164
|
+
- !ruby/object:Gem::Version
|
|
165
|
+
version: 1.62.1
|
|
166
|
+
type: :development
|
|
167
|
+
prerelease: false
|
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
169
|
+
requirements:
|
|
170
|
+
- - "~>"
|
|
171
|
+
- !ruby/object:Gem::Version
|
|
172
|
+
version: 1.62.1
|
|
173
|
+
- !ruby/object:Gem::Dependency
|
|
174
|
+
name: rubocop-performance
|
|
161
175
|
requirement: !ruby/object:Gem::Requirement
|
|
162
176
|
requirements:
|
|
163
177
|
- - ">="
|
|
@@ -187,6 +201,7 @@ files:
|
|
|
187
201
|
- lib/premailer/adapter/nokogiri_fast.rb
|
|
188
202
|
- lib/premailer/adapter/nokogumbo.rb
|
|
189
203
|
- lib/premailer/adapter/rgb_to_hex.rb
|
|
204
|
+
- lib/premailer/cached_rule_set.rb
|
|
190
205
|
- lib/premailer/executor.rb
|
|
191
206
|
- lib/premailer/html_to_plain_text.rb
|
|
192
207
|
- lib/premailer/premailer.rb
|
|
@@ -197,7 +212,8 @@ licenses:
|
|
|
197
212
|
- BSD-3-Clause
|
|
198
213
|
metadata:
|
|
199
214
|
yard.run: yri
|
|
200
|
-
|
|
215
|
+
rubygems_mfa_required: 'true'
|
|
216
|
+
post_install_message:
|
|
201
217
|
rdoc_options: []
|
|
202
218
|
require_paths:
|
|
203
219
|
- lib
|
|
@@ -205,15 +221,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
205
221
|
requirements:
|
|
206
222
|
- - ">="
|
|
207
223
|
- !ruby/object:Gem::Version
|
|
208
|
-
version:
|
|
224
|
+
version: '3.0'
|
|
209
225
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
210
226
|
requirements:
|
|
211
227
|
- - ">="
|
|
212
228
|
- !ruby/object:Gem::Version
|
|
213
229
|
version: '0'
|
|
214
230
|
requirements: []
|
|
215
|
-
rubygems_version: 3.
|
|
216
|
-
signing_key:
|
|
231
|
+
rubygems_version: 3.4.10
|
|
232
|
+
signing_key:
|
|
217
233
|
specification_version: 4
|
|
218
234
|
summary: Preflight for HTML e-mail.
|
|
219
235
|
test_files: []
|