mjml-rb 0.5.0 → 0.5.2
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 +3 -1
- data/lib/mjml-rb/components/attributes.rb +5 -5
- data/lib/mjml-rb/components/base.rb +4 -0
- data/lib/mjml-rb/components/breakpoint.rb +1 -1
- data/lib/mjml-rb/components/head.rb +3 -3
- data/lib/mjml-rb/components/html_attributes.rb +3 -3
- data/lib/mjml-rb/components/social.rb +2 -2
- data/lib/mjml-rb/components/table.rb +1 -1
- data/lib/mjml-rb/node_compat.rb +15 -0
- data/lib/mjml-rb/parser.rb +21 -38
- data/lib/mjml-rb/renderer.rb +36 -62
- data/lib/mjml-rb/validator.rb +15 -4
- data/lib/mjml-rb/version.rb +1 -1
- data/lib/mjml-rb.rb +1 -1
- metadata +2 -2
- data/lib/mjml-rb/ast_node.rb +0 -37
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ffb73c167fce50a47b0513aa071f478625bd28de94a47904980746905f731e8e
|
|
4
|
+
data.tar.gz: 67977b5a7c57afb011f6e1f7f1b1de4d1872cca1847eb0f61c6413a60b93c919
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1eb9c9b43804484c8c0a5e89e04c83f85bcd0699ada32f6e77b566341795372386bac5fc41766bdc98d6f2844be69e6c456b461c9649971f42a4dbc97016226e
|
|
7
|
+
data.tar.gz: d49c215799a867e70ced384893081933c596ea65cc2e325782110c796f8f690b410f5a24cb1858e91c3699d635893a3ce273154cd12900575331899c108af54b
|
data/README.md
CHANGED
|
@@ -24,6 +24,8 @@ A pure-Ruby MJML v4 compiler — no Node.js required.
|
|
|
24
24
|
|
|
25
25
|
## Installation
|
|
26
26
|
|
|
27
|
+
`mjml-rb` requires Ruby 3.3 or newer.
|
|
28
|
+
|
|
27
29
|
```ruby
|
|
28
30
|
# Gemfile
|
|
29
31
|
gem "mjml-rb"
|
|
@@ -95,7 +97,7 @@ Supports Slim and Haml via `config.mjml_rb.rails_template_language = :slim`. See
|
|
|
95
97
|
MJML string → Parser → AST → Validator → Renderer → HTML
|
|
96
98
|
```
|
|
97
99
|
|
|
98
|
-
1. **Parser** — normalizes source, expands `mj-include`,
|
|
100
|
+
1. **Parser** — normalizes source, expands `mj-include`, produces a Nokogiri XML node tree
|
|
99
101
|
2. **Validator** — checks structure, hierarchy, and attribute types
|
|
100
102
|
3. **Renderer** — resolves head metadata, applies defaults, emits responsive HTML
|
|
101
103
|
|
|
@@ -23,16 +23,16 @@ module MjmlRb
|
|
|
23
23
|
attributes_node.element_children.each do |child|
|
|
24
24
|
case child.tag_name
|
|
25
25
|
when "mj-all"
|
|
26
|
-
context[:global_defaults].merge!(child
|
|
26
|
+
context[:global_defaults].merge!(node_string_attributes(child))
|
|
27
27
|
when "mj-class"
|
|
28
|
-
name = child
|
|
28
|
+
name = child["name"]
|
|
29
29
|
next unless name
|
|
30
30
|
|
|
31
31
|
context[:classes][name] ||= {}
|
|
32
|
-
context[:classes][name].merge!(child.
|
|
32
|
+
context[:classes][name].merge!(node_string_attributes(child).reject { |key, _| key == "name" })
|
|
33
33
|
|
|
34
34
|
defaults = child.element_children.each_with_object({}) do |class_child, memo|
|
|
35
|
-
memo[class_child.tag_name] = class_child
|
|
35
|
+
memo[class_child.tag_name] = node_string_attributes(class_child)
|
|
36
36
|
end
|
|
37
37
|
next if defaults.empty?
|
|
38
38
|
|
|
@@ -40,7 +40,7 @@ module MjmlRb
|
|
|
40
40
|
context[:classes_default][name].merge!(defaults)
|
|
41
41
|
else
|
|
42
42
|
context[:tag_defaults][child.tag_name] ||= {}
|
|
43
|
-
context[:tag_defaults][child.tag_name].merge!(child
|
|
43
|
+
context[:tag_defaults][child.tag_name].merge!(node_string_attributes(child))
|
|
44
44
|
end
|
|
45
45
|
end
|
|
46
46
|
end
|
|
@@ -42,14 +42,14 @@ module MjmlRb
|
|
|
42
42
|
context[:preview] = raw_inner(node).strip
|
|
43
43
|
when "mj-style"
|
|
44
44
|
css = raw_inner(node)
|
|
45
|
-
if node
|
|
45
|
+
if node["inline"] == "inline"
|
|
46
46
|
context[:inline_styles] << css
|
|
47
47
|
else
|
|
48
48
|
context[:user_styles] << css
|
|
49
49
|
end
|
|
50
50
|
when "mj-font"
|
|
51
|
-
name = node
|
|
52
|
-
href = node
|
|
51
|
+
name = node["name"]
|
|
52
|
+
href = node["href"]
|
|
53
53
|
context[:fonts][name] = href if name && href
|
|
54
54
|
end
|
|
55
55
|
end
|
|
@@ -32,16 +32,16 @@ module MjmlRb
|
|
|
32
32
|
node.element_children.each do |selector|
|
|
33
33
|
next unless selector.tag_name == "mj-selector"
|
|
34
34
|
|
|
35
|
-
path = selector
|
|
35
|
+
path = selector["path"].to_s.strip
|
|
36
36
|
next if path.empty?
|
|
37
37
|
|
|
38
38
|
custom_attrs = selector.element_children.each_with_object({}) do |child, memo|
|
|
39
39
|
next unless child.tag_name == "mj-html-attribute"
|
|
40
40
|
|
|
41
|
-
name = child
|
|
41
|
+
name = child["name"].to_s.strip
|
|
42
42
|
next if name.empty?
|
|
43
43
|
|
|
44
|
-
memo[name] = child.
|
|
44
|
+
memo[name] = child.content
|
|
45
45
|
end
|
|
46
46
|
next if custom_attrs.empty?
|
|
47
47
|
|
|
@@ -237,7 +237,7 @@ module MjmlRb
|
|
|
237
237
|
|
|
238
238
|
def render_social_element(node, attrs)
|
|
239
239
|
a = attrs # already merged with defaults by caller
|
|
240
|
-
net_name = node
|
|
240
|
+
net_name = node["name"]
|
|
241
241
|
network = SOCIAL_NETWORKS[net_name] || {}
|
|
242
242
|
|
|
243
243
|
# Resolve href: if network has a share-url, substitute [[URL]] with the raw href
|
|
@@ -325,7 +325,7 @@ module MjmlRb
|
|
|
325
325
|
HTML
|
|
326
326
|
|
|
327
327
|
# Content cell (text)
|
|
328
|
-
content = node.
|
|
328
|
+
content = node.content.strip
|
|
329
329
|
content_cell = if content.empty?
|
|
330
330
|
""
|
|
331
331
|
else
|
data/lib/mjml-rb/parser.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
require "nokogiri"
|
|
2
2
|
|
|
3
|
-
require_relative "
|
|
3
|
+
require_relative "node_compat"
|
|
4
4
|
|
|
5
5
|
module MjmlRb
|
|
6
6
|
class Parser
|
|
@@ -57,7 +57,8 @@ module MjmlRb
|
|
|
57
57
|
xml = replace_html_entities(xml)
|
|
58
58
|
doc = Nokogiri::XML(xml) { |config| config.strict }
|
|
59
59
|
normalize_root_head_elements(doc)
|
|
60
|
-
|
|
60
|
+
prepare_node(doc.root, keep_comments: opts[:keep_comments])
|
|
61
|
+
doc.root
|
|
61
62
|
rescue Nokogiri::XML::SyntaxError => e
|
|
62
63
|
raise ParseError.new("XML parse error: #{e.message}")
|
|
63
64
|
end
|
|
@@ -402,55 +403,37 @@ module MjmlRb
|
|
|
402
403
|
raise Errno::ENOENT, include_path
|
|
403
404
|
end
|
|
404
405
|
|
|
405
|
-
def
|
|
406
|
+
def prepare_node(element, keep_comments:)
|
|
406
407
|
raise ParseError, "Missing XML root element" unless element
|
|
407
408
|
|
|
408
|
-
#
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
meta_line = element.line
|
|
412
|
-
meta_file = element["data-mjml-file"]
|
|
413
|
-
attrs = {}
|
|
414
|
-
element.attributes.each do |name, attr|
|
|
415
|
-
attrs[name] = attr.value unless name.start_with?("data-mjml-")
|
|
409
|
+
# Mark non-mj elements for raw HTML handling
|
|
410
|
+
unless element.name.start_with?("mj-") || element.name == "mjml"
|
|
411
|
+
element["data-mjml-raw"] = "true"
|
|
416
412
|
end
|
|
417
|
-
attrs["data-mjml-raw"] = "true" unless element.name.start_with?("mj-") || element.name == "mjml"
|
|
418
413
|
|
|
419
|
-
# For ending-tag elements whose content was wrapped in CDATA,
|
|
420
|
-
#
|
|
414
|
+
# For ending-tag elements whose content was wrapped in CDATA,
|
|
415
|
+
# mark them so raw_inner knows to extract content directly.
|
|
421
416
|
if ENDING_TAGS_FOR_CDATA.include?(element.name)
|
|
422
|
-
|
|
423
|
-
return
|
|
424
|
-
tag_name: element.name,
|
|
425
|
-
attributes: attrs,
|
|
426
|
-
children: [],
|
|
427
|
-
content: raw_content.empty? ? nil : raw_content,
|
|
428
|
-
line: meta_line,
|
|
429
|
-
file: meta_file
|
|
430
|
-
)
|
|
417
|
+
element["data-mjml-ending-tag"] = "true"
|
|
418
|
+
return
|
|
431
419
|
end
|
|
432
420
|
|
|
433
|
-
|
|
421
|
+
# Strip ignorable whitespace text nodes, unwanted comments,
|
|
422
|
+
# and recurse into element children.
|
|
423
|
+
element.children.to_a.each do |child|
|
|
434
424
|
if child.element?
|
|
435
|
-
|
|
425
|
+
prepare_node(child, keep_comments: keep_comments)
|
|
436
426
|
elsif child.text? || child.cdata?
|
|
437
427
|
text = child.content
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
memo << AstNode.new(tag_name: "#text", content: text)
|
|
428
|
+
if text.empty? || (text.strip.empty? && ignorable_whitespace_text?(text, parent_element_name: element.name))
|
|
429
|
+
child.remove
|
|
430
|
+
end
|
|
442
431
|
elsif child.comment?
|
|
443
|
-
|
|
432
|
+
child.remove unless keep_comments
|
|
433
|
+
else
|
|
434
|
+
child.remove
|
|
444
435
|
end
|
|
445
436
|
end
|
|
446
|
-
|
|
447
|
-
AstNode.new(
|
|
448
|
-
tag_name: element.name,
|
|
449
|
-
attributes: attrs,
|
|
450
|
-
children: children,
|
|
451
|
-
line: meta_line,
|
|
452
|
-
file: meta_file
|
|
453
|
-
)
|
|
454
437
|
end
|
|
455
438
|
|
|
456
439
|
# Lenient XML parse used during include expansion and intermediate steps.
|
data/lib/mjml-rb/renderer.rb
CHANGED
|
@@ -26,8 +26,6 @@ require_relative "components/spacer"
|
|
|
26
26
|
|
|
27
27
|
module MjmlRb
|
|
28
28
|
class Renderer
|
|
29
|
-
HTML_VOID_TAGS = Set.new(%w[area base br col embed hr img input link meta param source track wbr]).freeze
|
|
30
|
-
|
|
31
29
|
DEFAULT_FONTS = {
|
|
32
30
|
"Open Sans" => "https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,700",
|
|
33
31
|
"Droid Sans" => "https://fonts.googleapis.com/css?family=Droid+Sans:300,400,500,700",
|
|
@@ -72,9 +70,9 @@ module MjmlRb
|
|
|
72
70
|
|
|
73
71
|
context = build_context(head, options)
|
|
74
72
|
context[:before_doctype] = root_file_start_raw(document)
|
|
75
|
-
context[:lang] = options[:lang] || document
|
|
76
|
-
context[:dir] = options[:dir] || document
|
|
77
|
-
context[:force_owa_desktop] = document
|
|
73
|
+
context[:lang] = options[:lang] || document["lang"] || "und"
|
|
74
|
+
context[:dir] = options[:dir] || document["dir"] || "auto"
|
|
75
|
+
context[:force_owa_desktop] = document["owa"] == "desktop"
|
|
78
76
|
context[:printer_support] = options[:printer_support] || options[:printerSupport]
|
|
79
77
|
context[:column_widths] = {}
|
|
80
78
|
append_component_head_styles(document, context)
|
|
@@ -227,7 +225,7 @@ module MjmlRb
|
|
|
227
225
|
return [100] if total == 0
|
|
228
226
|
|
|
229
227
|
widths = columns.map do |col|
|
|
230
|
-
w = col
|
|
228
|
+
w = col["width"]
|
|
231
229
|
if w && w.to_s =~ /(\d+(?:\.\d+)?)\s*%/
|
|
232
230
|
$1.to_f
|
|
233
231
|
elsif w && w.to_s =~ /(\d+(?:\.\d+)?)\s*px/
|
|
@@ -670,7 +668,7 @@ module MjmlRb
|
|
|
670
668
|
attrs.merge!(context[:global_defaults] || {})
|
|
671
669
|
attrs.merge!(context[:tag_defaults][node.tag_name] || {})
|
|
672
670
|
|
|
673
|
-
node_classes = node
|
|
671
|
+
node_classes = node["mj-class"].to_s.split(/\s+/).reject(&:empty?)
|
|
674
672
|
class_attrs = node_classes.each_with_object({}) do |klass, memo|
|
|
675
673
|
mj_class_attrs = (context[:classes] || {})[klass] || {}
|
|
676
674
|
if memo["css-class"] && mj_class_attrs["css-class"]
|
|
@@ -685,43 +683,29 @@ module MjmlRb
|
|
|
685
683
|
attrs.merge!(((context[:classes_default] || {})[klass] || {})[node.tag_name] || {})
|
|
686
684
|
end
|
|
687
685
|
|
|
688
|
-
attrs.merge!(node
|
|
686
|
+
attrs.merge!(node_string_attributes(node))
|
|
689
687
|
attrs
|
|
690
688
|
end
|
|
691
689
|
|
|
692
690
|
def html_inner(node)
|
|
693
|
-
|
|
694
|
-
node.children.map do |child|
|
|
695
|
-
if child.text?
|
|
696
|
-
serialize_text_content(escape_html(child.content.to_s))
|
|
697
|
-
elsif child.comment?
|
|
698
|
-
"<!--#{child.content}-->"
|
|
699
|
-
else
|
|
700
|
-
serialize_node(child)
|
|
701
|
-
end
|
|
702
|
-
end.join
|
|
703
|
-
else
|
|
704
|
-
escape_html(node.text_content)
|
|
705
|
-
end
|
|
691
|
+
node.inner_html
|
|
706
692
|
end
|
|
707
693
|
|
|
708
694
|
def raw_inner(node)
|
|
709
|
-
# For ending-tag nodes whose content was preserved as raw HTML by the parser
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
if node.
|
|
713
|
-
node.children.
|
|
714
|
-
if child.text?
|
|
715
|
-
serialize_text_content(child.content.to_s)
|
|
716
|
-
elsif child.comment?
|
|
717
|
-
"<!--#{child.content}-->"
|
|
718
|
-
else
|
|
719
|
-
serialize_node(child)
|
|
720
|
-
end
|
|
721
|
-
end.join
|
|
722
|
-
else
|
|
723
|
-
node.text_content
|
|
695
|
+
# For ending-tag nodes whose content was preserved as raw HTML by the parser.
|
|
696
|
+
# The parser marks these with data-mjml-ending-tag; their children are CDATA
|
|
697
|
+
# nodes containing the raw HTML content.
|
|
698
|
+
if node.element? && node["data-mjml-ending-tag"]
|
|
699
|
+
return node.children.select { |c| c.cdata? || c.text? }.map(&:content).join
|
|
724
700
|
end
|
|
701
|
+
|
|
702
|
+
node.children.map do |child|
|
|
703
|
+
if child.text?
|
|
704
|
+
child.content.to_s
|
|
705
|
+
else
|
|
706
|
+
child.to_html
|
|
707
|
+
end
|
|
708
|
+
end.join
|
|
725
709
|
end
|
|
726
710
|
|
|
727
711
|
def annotate_raw_html(content)
|
|
@@ -750,30 +734,6 @@ module MjmlRb
|
|
|
750
734
|
text.to_s.gsub(" ", " ").gsub("\t", "	")
|
|
751
735
|
end
|
|
752
736
|
|
|
753
|
-
def serialize_node(node)
|
|
754
|
-
attrs = node.attributes.map { |k, v| %( #{k}="#{escape_attr(v)}") }.join
|
|
755
|
-
return "<#{node.tag_name}#{attrs} />" if node.children.empty? && html_void_tag?(node.tag_name)
|
|
756
|
-
return "<#{node.tag_name}#{attrs}></#{node.tag_name}>" if node.children.empty?
|
|
757
|
-
|
|
758
|
-
inner = node.children.map { |child| child.text? ? serialize_text_content(child.content.to_s) : serialize_node(child) }.join
|
|
759
|
-
"<#{node.tag_name}#{attrs}>#{inner}</#{node.tag_name}>"
|
|
760
|
-
end
|
|
761
|
-
|
|
762
|
-
def serialize_text_content(text)
|
|
763
|
-
value = text.to_s
|
|
764
|
-
return value unless significant_whitespace_text?(value)
|
|
765
|
-
|
|
766
|
-
value.gsub(" ", " ").gsub("\t", "	")
|
|
767
|
-
end
|
|
768
|
-
|
|
769
|
-
def significant_whitespace_text?(text)
|
|
770
|
-
!text.empty? && text.strip.empty? && !text.match?(/[\r\n]/)
|
|
771
|
-
end
|
|
772
|
-
|
|
773
|
-
def html_void_tag?(tag_name)
|
|
774
|
-
HTML_VOID_TAGS.include?(tag_name.to_s.downcase)
|
|
775
|
-
end
|
|
776
|
-
|
|
777
737
|
def style_join(hash)
|
|
778
738
|
hash.each_with_object([]) do |(key, value), memo|
|
|
779
739
|
next if value.nil? || value.to_s.empty?
|
|
@@ -791,7 +751,7 @@ module MjmlRb
|
|
|
791
751
|
|
|
792
752
|
def with_inherited_mj_class(context, node)
|
|
793
753
|
previous = context[:inherited_mj_class]
|
|
794
|
-
current = node
|
|
754
|
+
current = node["mj-class"]
|
|
795
755
|
context[:inherited_mj_class] = (current && !current.empty?) ? current : previous
|
|
796
756
|
yield
|
|
797
757
|
ensure
|
|
@@ -801,7 +761,7 @@ module MjmlRb
|
|
|
801
761
|
def root_file_start_raw(document)
|
|
802
762
|
document.element_children.filter_map do |child|
|
|
803
763
|
next unless child.tag_name == "mj-raw"
|
|
804
|
-
next unless child
|
|
764
|
+
next unless child["position"] == "file-start"
|
|
805
765
|
|
|
806
766
|
raw_inner(child)
|
|
807
767
|
end.join("\n")
|
|
@@ -815,6 +775,20 @@ module MjmlRb
|
|
|
815
775
|
result
|
|
816
776
|
end
|
|
817
777
|
|
|
778
|
+
# Internal-only attributes set by the parser for metadata tracking.
|
|
779
|
+
# These are excluded from the public attributes hash; data-mjml-raw
|
|
780
|
+
# is intentionally kept because it is used by the rendering pipeline.
|
|
781
|
+
INTERNAL_ATTRIBUTES = %w[data-mjml-file data-mjml-ending-tag].freeze
|
|
782
|
+
|
|
783
|
+
def node_string_attributes(node)
|
|
784
|
+
result = {}
|
|
785
|
+
node.attributes.each do |name, attr|
|
|
786
|
+
next if INTERNAL_ATTRIBUTES.include?(name)
|
|
787
|
+
result[name] = attr.respond_to?(:value) ? attr.value : attr.to_s
|
|
788
|
+
end
|
|
789
|
+
result
|
|
790
|
+
end
|
|
791
|
+
|
|
818
792
|
def escape_html(value)
|
|
819
793
|
CGI.escapeHTML(value.to_s)
|
|
820
794
|
end
|
data/lib/mjml-rb/validator.rb
CHANGED
|
@@ -16,7 +16,7 @@ module MjmlRb
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def validate(mjml_or_ast, options = {})
|
|
19
|
-
root = mjml_or_ast.is_a?(
|
|
19
|
+
root = mjml_or_ast.is_a?(Nokogiri::XML::Node) ? mjml_or_ast : parse_ast(mjml_or_ast, options)
|
|
20
20
|
unless root&.tag_name == "mjml"
|
|
21
21
|
return { errors: [error("Root element must be <mjml>", tag_name: root&.tag_name)], warnings: [] }
|
|
22
22
|
end
|
|
@@ -97,7 +97,7 @@ module MjmlRb
|
|
|
97
97
|
def validate_required_attributes(node, errors)
|
|
98
98
|
required = REQUIRED_BY_TAG[node.tag_name] || []
|
|
99
99
|
required.each do |attr|
|
|
100
|
-
next if node.
|
|
100
|
+
next if node.has_attribute?(attr)
|
|
101
101
|
|
|
102
102
|
errors << error("Attribute `#{attr}` is required for <#{node.tag_name}>",
|
|
103
103
|
tag_name: node.tag_name, line: node.line, file: node.file)
|
|
@@ -108,7 +108,7 @@ module MjmlRb
|
|
|
108
108
|
allowed_attributes = allowed_attributes_for(node.tag_name)
|
|
109
109
|
return if allowed_attributes.empty?
|
|
110
110
|
|
|
111
|
-
node.
|
|
111
|
+
string_attrs(node).each_key do |attribute_name|
|
|
112
112
|
next if allowed_attributes.key?(attribute_name)
|
|
113
113
|
next if GLOBAL_ALLOWED_ATTRIBUTES.include?(attribute_name)
|
|
114
114
|
|
|
@@ -121,7 +121,7 @@ module MjmlRb
|
|
|
121
121
|
allowed_attributes = allowed_attributes_for(node.tag_name)
|
|
122
122
|
return if allowed_attributes.empty?
|
|
123
123
|
|
|
124
|
-
node.
|
|
124
|
+
string_attrs(node).each do |attribute_name, attribute_value|
|
|
125
125
|
next if GLOBAL_ALLOWED_ATTRIBUTES.include?(attribute_name)
|
|
126
126
|
|
|
127
127
|
expected_type = allowed_attributes[attribute_name]
|
|
@@ -218,6 +218,17 @@ module MjmlRb
|
|
|
218
218
|
end
|
|
219
219
|
end
|
|
220
220
|
|
|
221
|
+
INTERNAL_ATTRIBUTES = %w[data-mjml-file data-mjml-ending-tag data-mjml-raw].freeze
|
|
222
|
+
|
|
223
|
+
def string_attrs(node)
|
|
224
|
+
result = {}
|
|
225
|
+
node.attributes.each do |name, attr|
|
|
226
|
+
next if INTERNAL_ATTRIBUTES.include?(name)
|
|
227
|
+
result[name] = attr.respond_to?(:value) ? attr.value : attr.to_s
|
|
228
|
+
end
|
|
229
|
+
result
|
|
230
|
+
end
|
|
231
|
+
|
|
221
232
|
def error(message, line: nil, tag_name: nil, file: nil)
|
|
222
233
|
location = [
|
|
223
234
|
("line #{line}" if line),
|
data/lib/mjml-rb/version.rb
CHANGED
data/lib/mjml-rb.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
require_relative "mjml-rb/version"
|
|
2
2
|
require_relative "mjml-rb/result"
|
|
3
|
-
require_relative "mjml-rb/
|
|
3
|
+
require_relative "mjml-rb/node_compat"
|
|
4
4
|
require_relative "mjml-rb/dependencies"
|
|
5
5
|
require_relative "mjml-rb/component_registry"
|
|
6
6
|
require_relative "mjml-rb/config_file"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mjml-rb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrei Andriichuk
|
|
@@ -51,7 +51,6 @@ files:
|
|
|
51
51
|
- Rakefile
|
|
52
52
|
- bin/mjml
|
|
53
53
|
- lib/mjml-rb.rb
|
|
54
|
-
- lib/mjml-rb/ast_node.rb
|
|
55
54
|
- lib/mjml-rb/cli.rb
|
|
56
55
|
- lib/mjml-rb/compiler.rb
|
|
57
56
|
- lib/mjml-rb/component_registry.rb
|
|
@@ -80,6 +79,7 @@ files:
|
|
|
80
79
|
- lib/mjml-rb/components/text.rb
|
|
81
80
|
- lib/mjml-rb/config_file.rb
|
|
82
81
|
- lib/mjml-rb/dependencies.rb
|
|
82
|
+
- lib/mjml-rb/node_compat.rb
|
|
83
83
|
- lib/mjml-rb/parser.rb
|
|
84
84
|
- lib/mjml-rb/railtie.rb
|
|
85
85
|
- lib/mjml-rb/renderer.rb
|
data/lib/mjml-rb/ast_node.rb
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
module MjmlRb
|
|
2
|
-
class AstNode
|
|
3
|
-
attr_reader :tag_name, :attributes, :children, :content, :line, :file
|
|
4
|
-
|
|
5
|
-
def initialize(tag_name:, attributes: {}, children: [], content: nil, line: nil, file: nil)
|
|
6
|
-
@tag_name = tag_name.to_s
|
|
7
|
-
@attributes = attributes.transform_keys(&:to_s)
|
|
8
|
-
@children = Array(children)
|
|
9
|
-
@content = content
|
|
10
|
-
@line = line
|
|
11
|
-
@file = file
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def text?
|
|
15
|
-
@tag_name == "#text"
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def comment?
|
|
19
|
-
@tag_name == "#comment"
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def element?
|
|
23
|
-
!text? && !comment?
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def text_content
|
|
27
|
-
return @content.to_s if text?
|
|
28
|
-
result = +""
|
|
29
|
-
@children.each { |child| result << child.text_content }
|
|
30
|
-
result
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def element_children
|
|
34
|
-
@element_children ||= @children.select(&:element?)
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|