Skip to content

Commit

Permalink
auto-encode html and attributes
Browse files Browse the repository at this point in the history
i had mistakenly assumed that mjml would escape these
  • Loading branch information
mifi committed Feb 6, 2023
1 parent 380cf4c commit e864990
Show file tree
Hide file tree
Showing 5 changed files with 367 additions and 19 deletions.
38 changes: 22 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,16 @@ You include a special tag `mj-replace-id="myId"` into any of your `mj-` tags in

### New `mjml2html` option: `replacers`

The `mjml2html` API is exactly the same as the upstream `mjml` API, but there is an added option called `replacers` which is an object where each key corresponds to a `mj-replace-id` and the value is an object with one or more of the following properties:
The `mjml2html()` API of `mjml-dynamic` is exactly the same as the upstream `mjml` API, but there is an added option called `replacers` which is an object where each key corresponds to a `mj-replace-id` and the value is an object with one or more of the following properties:

- `content` (`string`) - Allows you to change the text content of the element.
- `attributes` (`object<string,string>`) - Allows you to change the XML attributes of the element.
- `children` (`array<object>`) - Allows you to change the element's children.
- `tagName` (`string`) - Allows you to change the tag to a completely different tag.
| Property | Type | Description |
| --- | --- | --- |
| `content` | `string` | Change the text or HTML content of the element. |
| `attributes` | `object<string,string>` | Change the HTML attributes of the element. |
| `children` | `array<object>` | Change the element's MJML children elements (for example with `mjml-react`.) |
| `tagName` | `string` | Change the tag to a completely different tag. |

Any of the above `replacers` properties are optional, and you may alternatively supply a function that receives the existing value as its only argument. This can be used to modify the existing values.
Any of the above `replacers` properties are optional. You may alternatively supply a **function** that receives the existing value as its only argument and returns the new value. This can be used to modify the existing values. **`content` and `attributes` replacements are automatically escaped, however if you use the functional `content` replacer, the response is not escaped.**

## Examples

Expand All @@ -59,9 +61,10 @@ import mjml2html from 'mjml-dynamic';

const replacers = {
myId: {
tagName: 'mj-text'
content: 'new text content',
attributes: { color: 'red' },
tagName: 'mj-button',
// these values are all automatically escaped:
content: 'new text content',
attributes: { color: 'red', href: '&' },
},
// ... more `mj-replace-id`s
};
Expand All @@ -77,9 +80,9 @@ This will output the equivalent of the following MJML document:
<mj-section>
<mj-column>

<mj-text color="red">
<mj-button color="red" href="&amp;">
new text content
</mj-text>
</mj-button>

</mj-column>
</mj-section>
Expand All @@ -92,11 +95,13 @@ This will output the equivalent of the following MJML document:
```js
const replacers = {
myId: {
// adds an additional color attribute:
// Add an additional color attribute, while preserving the existing attributes:
attributes: (attributes) => ({ ...attributes, color: 'red' },
// rewrites the text content:

// Rewrite the HTML content:
content: (content) => content.replace('{{something}}', 'something else'),
// (you may use a template engine line handlebars for more sophisticated replacements)
// NOTE! mjml-dynamic does not automatically escape the replacement HTML here, so you need to do it yourself.
// (You may use a template engine like handlebars for more sophisticated replacements)
},
};
```
Expand Down Expand Up @@ -171,7 +176,8 @@ This will output the equivalent of the following MJML document:
Example to replace parts of your `.mjml` document with React code:
You have `template.mjml` that you can preview using official MJML tooling:
Assuming you have a `template.mjml` with an overall email layout that you can preview using official MJML tooling:
```xml
<mjml>
<mj-body>
Expand All @@ -192,7 +198,7 @@ You have `template.mjml` that you can preview using official MJML tooling:
</mjml>
```
Then you can render the contents of `mj-replace-id="peopleList"` using `mjml-react`:
Then you can render the contents of the element `<mj-column mj-replace-id="peopleList">` using `mjml-react`:
```jsx
import readFile from 'fs/promises';
Expand Down
290 changes: 290 additions & 0 deletions __snapshots__/index.test.mjs.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,295 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`escaping escape attributes and content 1`] = `
"<!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]-->
<!--[if !mso]><!-->
<link href="https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700" rel="stylesheet" type="text/css">
<style type="text/css">
@import url(https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700);
</style>
<!--<![endif]-->
<style type="text/css">
@media only screen and (min-width:480px) {
.mj-column-per-100 { width:100% !important; max-width: 100%; }
}
</style>
<style media="screen and (min-width:480px)">
.moz-text-html .mj-column-per-100 { width:100% !important; max-width: 100%; }
</style>
<style type="text/css">
</style>
<style type="text/css">
</style>
</head>
<body style="word-spacing:normal;">
<div
style=""
>
<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
<div style="margin:0px auto;max-width:600px;">
<table
align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;"
>
<tbody>
<tr>
<td
style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;"
>
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
<div
class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;"
>
<table
border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%"
>
<tbody>
<tr>
<td
align="center" vertical-align="middle" style="font-size:0px;padding:10px 25px;word-break:break-word;"
>
<table
border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:separate;line-height:100%;"
>
<tbody>
<tr>
<td
align="center" bgcolor="#414141" role="presentation" style="border:none;border-radius:3px;cursor:auto;mso-padding-alt:10px 25px;background:#414141;" valign="middle"
>
<a
href="&amp;<>&quot;'å;" style="display:inline-block;background:#414141;color:#ffffff;font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:13px;font-weight:normal;line-height:120%;margin:0;text-decoration:none;text-transform:none;padding:10px 25px;mso-padding-alt:0px;border-radius:3px;" target="_blank"
>
&amp;&lt;&gt;&quot;&apos;å;
</a>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
</div>
</body>
</html>
"
`;
exports[`escaping not escape functional content 1`] = `
"<!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]-->
<!--[if !mso]><!-->
<link href="https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700" rel="stylesheet" type="text/css">
<style type="text/css">
@import url(https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700);
</style>
<!--<![endif]-->
<style type="text/css">
@media only screen and (min-width:480px) {
.mj-column-per-100 { width:100% !important; max-width: 100%; }
}
</style>
<style media="screen and (min-width:480px)">
.moz-text-html .mj-column-per-100 { width:100% !important; max-width: 100%; }
</style>
<style type="text/css">
</style>
<style type="text/css">
</style>
</head>
<body style="word-spacing:normal;">
<div
style=""
>
<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
<div style="margin:0px auto;max-width:600px;">
<table
align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;"
>
<tbody>
<tr>
<td
style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;"
>
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
<div
class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;"
>
<table
border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%"
>
<tbody>
<tr>
<td
align="center" vertical-align="middle" style="font-size:0px;padding:10px 25px;word-break:break-word;"
>
<table
border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:separate;line-height:100%;"
>
<tbody>
<tr>
<td
align="center" bgcolor="#414141" role="presentation" style="border:none;border-radius:3px;cursor:auto;mso-padding-alt:10px 25px;background:#414141;" valign="middle"
>
<a
href="&amp;<>&quot;'å;" title="me &amp; you" style="display:inline-block;background:#414141;color:#ffffff;font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:13px;font-weight:normal;line-height:120%;margin:0;text-decoration:none;text-transform:none;padding:10px 25px;mso-padding-alt:0px;border-radius:3px;" target="_blank"
>
<div>not escaped</div>
</a>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
</div>
</body>
</html>
"
`;
exports[`mjml mjml2html 1`] = `
"<!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">
Expand Down
Loading

0 comments on commit e864990

Please sign in to comment.