Repository: jdrouet/mrml (language: rust)

Issue:
Support for mj-raw in mj-head
Using `mj-raw` in `mj-head` breaks the mrml-cli compiler. 

The documentation for this is in a weird place on the mjml docs under 'Standard Body Components' but the `<mj-raw>` tag can actually be used within `<mj-body>` and `<mj-head>` components.

From the docs:
> Displays raw HTML that is not going to be parsed by the MJML engine. Anything left inside this tag should be raw, responsive HTML. If placed inside `<mj-head>`, its content will be added at the end of the `<head>`.

Refs:
https://documentation.mjml.io/#mj-raw


Discussion / hints:

Fix patch:
diff --git a/packages/mrml-core/src/mj_head/children.rs b/packages/mrml-core/src/mj_head/children.rs
index 2e7af6af..c98c01ce 100644
--- a/packages/mrml-core/src/mj_head/children.rs
+++ b/packages/mrml-core/src/mj_head/children.rs
@@ -3,6 +3,7 @@ use crate::mj_attributes::MJAttributes;
 use crate::mj_breakpoint::MJBreakpoint;
 use crate::mj_font::MJFont;
 use crate::mj_preview::MJPreview;
+use crate::mj_raw::MJRaw;
 use crate::mj_style::MJStyle;
 use crate::mj_title::MJTitle;
 use crate::{as_child, from_child};
@@ -16,6 +17,7 @@ pub enum MJHeadChild {
     MJBreakpoint(MJBreakpoint),
     MJFont(MJFont),
     MJPreview(MJPreview),
+    MJRaw(MJRaw),
     MJStyle(MJStyle),
     MJTitle(MJTitle),
 }
@@ -29,6 +31,8 @@ as_child!(MJHeadChild, MJFont, as_mj_font);
 from_child!(MJHeadChild, MJFont);
 as_child!(MJHeadChild, MJPreview, as_mj_preview);
 from_child!(MJHeadChild, MJPreview);
+as_child!(MJHeadChild, MJRaw, as_mj_raw);
+from_child!(MJHeadChild, MJRaw);
 as_child!(MJHeadChild, MJStyle, as_mj_style);
 from_child!(MJHeadChild, MJStyle);
 as_child!(MJHeadChild, MJTitle, as_mj_title);
diff --git a/packages/mrml-core/src/mj_head/parse.rs b/packages/mrml-core/src/mj_head/parse.rs
index 1d459ae3..e2efba01 100644
--- a/packages/mrml-core/src/mj_head/parse.rs
+++ b/packages/mrml-core/src/mj_head/parse.rs
@@ -7,6 +7,8 @@ use crate::mj_font::MJFont;
 use crate::mj_font::NAME as MJ_FONT;
 use crate::mj_preview::MJPreview;
 use crate::mj_preview::NAME as MJ_PREVIEW;
+use crate::mj_raw::MJRaw;
+use crate::mj_raw::NAME as MJ_RAW;
 use crate::mj_style::MJStyle;
 use crate::mj_style::NAME as MJ_STYLE;
 use crate::mj_title::MJTitle;
@@ -21,6 +23,7 @@ impl Parsable for MJHeadChild {
             MJ_BREAKPOINT => Ok(MJBreakpoint::parse(tag, tokenizer)?.into()),
             MJ_FONT => Ok(MJFont::parse(tag, tokenizer)?.into()),
             MJ_PREVIEW => Ok(MJPreview::parse(tag, tokenizer)?.into()),
+            MJ_RAW => Ok(MJRaw::parse(tag, tokenizer)?.into()),
             MJ_STYLE => Ok(MJStyle::parse(tag, tokenizer)?.into()),
             MJ_TITLE => Ok(MJTitle::parse(tag, tokenizer)?.into()),
             _ => Err(Error::UnexpectedElement(tag.start())),
@@ -56,6 +59,13 @@ impl Parsable for MJHead {
 
 #[cfg(test)]
 mod tests {
+    #[test]
+    fn raw_children() {
+        let res = crate::mjml::MJML::parse(
+            r#"<mjml><mj-head><mj-raw>Hello World!</mj-raw></mj-head></mjml>"#,
+        );
+        assert!(res.is_ok());
+    }
     #[test]
     fn unexpected_element() {
         let res = crate::mjml::MJML::parse(
diff --git a/packages/mrml-core/src/mj_head/print.rs b/packages/mrml-core/src/mj_head/print.rs
index 54c8fc86..c6f8d674 100644
--- a/packages/mrml-core/src/mj_head/print.rs
+++ b/packages/mrml-core/src/mj_head/print.rs
@@ -9,6 +9,7 @@ impl MJHeadChild {
             Self::MJBreakpoint(elt) => elt,
             Self::MJFont(elt) => elt,
             Self::MJPreview(elt) => elt,
+            Self::MJRaw(elt) => elt,
             Self::MJStyle(elt) => elt,
             Self::MJTitle(elt) => elt,
         }
diff --git a/packages/mrml-core/src/mj_head/render.rs b/packages/mrml-core/src/mj_head/render.rs
index a0f80ea1..473b0f0d 100644
--- a/packages/mrml-core/src/mj_head/render.rs
+++ b/packages/mrml-core/src/mj_head/render.rs
@@ -260,6 +260,21 @@ impl<'e, 'h> MJHeadRender<'e, 'h> {
             .trim()
             .to_string()
     }
+
+    fn render_raw(&self, opts: &Options) -> Result<String, Error> {
+        let siblings = self.element.children.len();
+        self.element
+            .children
+            .iter()
+            .filter_map(|child| child.as_mj_raw())
+            .enumerate()
+            .try_fold(String::default(), |res, (index, child)| {
+                let mut renderer = child.renderer(Rc::clone(&self.header));
+                renderer.set_index(index);
+                renderer.set_siblings(siblings);
+                Ok(res + &renderer.render(opts)?)
+            })
+    }
 }
 
 impl<'e, 'h> Render<'h> for MJHeadRender<'e, 'h> {
@@ -267,7 +282,7 @@ impl<'e, 'h> Render<'h> for MJHeadRender<'e, 'h> {
         self.header.borrow()
     }
 
-    fn render(&self, _opts: &Options) -> Result<String, Error> {
+    fn render(&self, opts: &Options) -> Result<String, Error> {
         let mut buf = String::from("<head>");
         // we write the title even though there is no content
         buf.push_str("<title>");
@@ -287,6 +302,7 @@ impl<'e, 'h> Render<'h> for MJHeadRender<'e, 'h> {
         buf.push_str(&self.render_font_families());
         buf.push_str(&self.render_media_queries());
         buf.push_str(&self.render_styles());
+        buf.push_str(&self.render_raw(opts)?);
         buf.push_str("</head>");
         Ok(buf)
     }
diff --git a/packages/mrml-core/src/mj_raw/render.rs b/packages/mrml-core/src/mj_raw/render.rs
index 0dc1735e..16c1c52d 100644
--- a/packages/mrml-core/src/mj_raw/render.rs
+++ b/packages/mrml-core/src/mj_raw/render.rs
@@ -74,4 +74,14 @@ mod tests {
         let result = root.render(&opts).unwrap();
         compare(expected, result.as_str());
     }
+
+    #[test]
+    fn in_head() {
+        let opts = Options::default();
+        let template = include_str!("../../resources/compare/success/mj-raw-head.mjml");
+        let expected = include_str!("../../resources/compare/success/mj-raw-head.html");
+        let root = MJML::parse(template.to_string()).unwrap();
+        let result = root.render(&opts).unwrap();
+        compare(expected, result.as_str());
+    }
 }
diff --git a/resources/compare/success/mj-raw-head.html b/resources/compare/success/mj-raw-head.html
new file mode 100644
index 00000000..df81cf9c
--- /dev/null
+++ b/resources/compare/success/mj-raw-head.html
@@ -0,0 +1,18 @@
+<!doctype html><html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office"><head><title></title><!--[if !mso]><!--><meta http-equiv="X-UA-Compatible" content="IE=edge"><!--<![endif]--><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1"><style type="text/css">#outlook a { padding:0; }
+          body { margin:0;padding:0;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%; }
+          table, td { border-collapse:collapse;mso-table-lspace:0pt;mso-table-rspace:0pt; }
+          img { border:0;height:auto;line-height:100%; outline:none;text-decoration:none;-ms-interpolation-mode:bicubic; }
+          p { display:block;margin:13px 0; }</style><!--[if mso]>
+        <noscript>
+        <xml>
+        <o:OfficeDocumentSettings>
+          <o:AllowPNG/>
+          <o:PixelsPerInch>96</o:PixelsPerInch>
+        </o:OfficeDocumentSettings>
+        </xml>
+        </noscript>
+        <![endif]--><!--[if lte mso 11]>
+        <style type="text/css">
+          .mj-outlook-group-fix { width:100% !important; }
+        </style>
+        <![endif]--><meta name="description" content="This should stay."></head><body><div></div></body></html>
diff --git a/resources/compare/success/mj-raw-head.mjml b/resources/compare/success/mj-raw-head.mjml
new file mode 100644
index 00000000..a3ea802b
--- /dev/null
+++ b/resources/compare/success/mj-raw-head.mjml
@@ -0,0 +1,7 @@
+<mjml>
+  <mj-head>
+    <mj-raw>
+      <meta name="description" content="This should stay." />
+    </mj-raw>
+  </mj-head>
+</mjml>
diff --git a/resources/compare/success/mj-raw.html b/resources/compare/success/mj-raw.html
index ee0a8a8d..be2f50ea 100644
--- a/resources/compare/success/mj-raw.html
+++ b/resources/compare/success/mj-raw.html
@@ -71,4 +71,4 @@
   </div>
 </body>
 
-</html>
\ No newline at end of file
+</html>


Test patch: