Skip to content

Commit ce6d443

Browse files
committed
feat: added simpleParser and returnHTML options, added sandbox and no-referrer to iframe, bump deps
1 parent 1a52484 commit ce6d443

File tree

5 files changed

+66
-23
lines changed

5 files changed

+66
-23
lines changed

README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,11 @@ npm install preview-email
4848

4949
As of v3.0.6 we have built-in support for previewing emails in the iOS Simulator (in addition to rendering them in your default web browser).
5050

51-
This is only applicable if you are using macOS and if not running in a CI environment. If you wish to disable this default behavior, then set `openSimulator` to `false` in the [options](#options).
51+
This is only applicable if you are using macOS and if not running in a CI environment. If you wish to disable this default behavior, then set `openSimulator` to `false` in the [options](#options).
5252

53-
Otherwise you will need to install XCode from the [App Store][app-store] or [Apple Developer Website][apple-developer-website]. We have built-in friendly macOS notifications that will alert you if there are any issues while attempting to load the iOS Simulator.
53+
Otherwise you will need to install XCode from the [App Store][app-store] or [Apple Developer Website][apple-developer-website]. We have built-in friendly macOS notifications that will alert you if there are any issues while attempting to load the iOS Simulator.
5454

55-
**After installing XCode**, you will need to open it and agree to the terms and conditions. Then you will need to [assign Command Line Tools](https://stackoverflow.com/a/36726612).
55+
**After installing XCode**, you will need to open it and agree to the terms and conditions. Then you will need to [assign Command Line Tools](https://stackoverflow.com/a/36726612).
5656

5757
**Once the Simulator is opened** – if you need to inspect the rendered email, then you can [use the Web Inspector in Safari Developer Tools](https://webkit.org/web-inspector/enabling-web-inspector/).
5858

@@ -78,7 +78,7 @@ const message = {
7878
subject: 'Hello world',
7979
html: '<p>Hello world</p>',
8080
text: 'Hello world',
81-
attachments: [ { filename: 'hello-world.txt', content: 'Hello world' } ]
81+
attachments: [{ filename: 'hello-world.txt', content: 'Hello world' }]
8282
};
8383

8484
// note that `attachments` will not be parsed unless you use
@@ -100,7 +100,9 @@ const path = require('path');
100100

101101
// ...
102102

103-
previewEmail(message, { template: path.join(__dirname, 'my-custom-preview-template.pug') })
103+
previewEmail(message, {
104+
template: path.join(__dirname, 'my-custom-preview-template.pug')
105+
})
104106
.then(console.log)
105107
.catch(console.error);
106108
```
@@ -125,6 +127,8 @@ NODE_DEBUG=preview-email node app.js
125127
* `template` (String) - a file path to a `pug` template file (defaults to preview-email's [template.pug](template.pug) by default) - **this is where you can pass a custom template for rendering email previews, e.g. your own stylesheet**
126128
* `urlTransform` (Function (path) => url) - a function to build preview url from file path (defaults to `(path) => 'file://[file path]'`) - *this is where you can customize the opened path to handle WSL to Windows transformation or build a http url if `dir` is served.*
127129
* `openSimulator` (Boolean) - whether or not to open the iOS Simulator with the preview url file path (defaults to `true` via `process.env.NODE_ENV !== 'test'` and will only run if macOS detected and not in a CI environment)
130+
* `simpleParser` (Object) - an options Object to pass to `mailparser`'s `simpleParser` method (see [mailparser docs](https://nodemailer.com/extras/mailparser/#options) for available options – note that `Iconv` option is always overridden for safeguard)
131+
* `returnHTML` (Boolean) - whether or not to return HTML only – and subsequently not write nor open the file preview file (defaults to `false`)
128132

129133

130134
## Contributors

index.js

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ const os = require('os');
55
const path = require('path');
66
const process = require('process');
77
const util = require('util');
8-
98
const displayNotification = require('display-notification');
109
const getPort = require('get-port');
1110
const nodemailer = require('nodemailer');
@@ -14,6 +13,7 @@ const pEvent = require('p-event');
1413
const pWaitFor = require('p-wait-for');
1514
const pug = require('pug');
1615
const uuid = require('uuid');
16+
const { Iconv } = require('iconv');
1717
const { isCI } = require('ci-info');
1818
const { simpleParser } = require('mailparser');
1919

@@ -35,6 +35,9 @@ const previewEmail = async (message, options) => {
3535
template: templateFilePath,
3636
urlTransform: (path) => `file://${path}`,
3737
openSimulator: process.env.NODE_ENV !== 'test',
38+
returnHTML: false,
39+
// <https://nodemailer.com/extras/mailparser/#options>
40+
simpleParser: {},
3841
...options
3942
};
4043
debug('message', message, 'options', options);
@@ -44,7 +47,10 @@ const previewEmail = async (message, options) => {
4447

4548
const response = await transport.sendMail(message);
4649

47-
const parsed = await simpleParser(response.message);
50+
const parsed = await simpleParser(response.message, {
51+
...options.simpleParser,
52+
Iconv
53+
});
4854

4955
const html = await renderFilePromise(
5056
options.template,
@@ -55,10 +61,12 @@ const previewEmail = async (message, options) => {
5561
);
5662

5763
const filePath = `${options.dir}/${options.id}.html`;
58-
await writeFile(filePath, html);
59-
6064
const url = options.urlTransform(filePath);
61-
if (options.open) await open(url, options.open);
65+
66+
if (!options.returnHTML) {
67+
await writeFile(filePath, html);
68+
if (options.open) await open(url, options.open);
69+
}
6270

6371
//
6472
// if on macOS then send a toast notification about XCode and Simulator for iOS
@@ -216,7 +224,7 @@ const previewEmail = async (message, options) => {
216224
}
217225
}
218226

219-
return url;
227+
return options.returnHTML ? html : url;
220228
};
221229

222230
module.exports = previewEmail;

package.json

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,31 +11,32 @@
1111
"Nick Baugh <niftylettuce@gmail.com> (http://niftylettuce.com/)"
1212
],
1313
"dependencies": {
14-
"ci-info": "^3.7.1",
14+
"ci-info": "^3.8.0",
1515
"display-notification": "2.0.0",
1616
"fixpack": "^4.0.0",
1717
"get-port": "5.1.1",
18-
"mailparser": "^3.6.3",
19-
"nodemailer": "^6.9.1",
18+
"iconv": "^3.0.1",
19+
"mailparser": "^3.6.4",
20+
"nodemailer": "^6.9.2",
2021
"open": "7",
2122
"p-event": "4.2.0",
2223
"p-wait-for": "3.2.0",
2324
"pug": "^3.0.2",
2425
"uuid": "^9.0.0"
2526
},
2627
"devDependencies": {
27-
"@commitlint/cli": "^17.4.2",
28-
"@commitlint/config-conventional": "^17.4.2",
28+
"@commitlint/cli": "^17.6.3",
29+
"@commitlint/config-conventional": "^17.6.3",
2930
"ava": "^5.2.0",
3031
"cross-env": "^7.0.3",
31-
"eslint": "^8.33.0",
32+
"eslint": "^8.40.0",
3233
"eslint-config-xo-lass": "^2.0.1",
3334
"husky": "^8.0.3",
34-
"lint-staged": "^13.1.1",
35+
"lint-staged": "^13.2.2",
3536
"nyc": "^15.1.0",
3637
"remark-cli": "^11.0.0",
3738
"remark-preset-github": "^4.0.4",
38-
"xo": "^0.53.1"
39+
"xo": "^0.54.2"
3940
},
4041
"engines": {
4142
"node": ">=14"

template.pug

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ html
4040
code= 'Unnamed file'
4141
if html
4242
tr: td(colspan=2): strong: code HTML Version:
43-
tr: td(colspan=2): iframe(seamless='seamless', srcdoc=`<base target='_top'>${html}`)#html
43+
tr: td(colspan=2): iframe(sandbox, referrerpolicy='no-referrer', seamless='seamless', srcdoc=`<base target='_top'>${html}`)#html
4444
if text
4545
tr: td(colspan=2): strong: code Text Version:
46-
tr: td(colspan=2): iframe(seamless='seamless', srcdoc=`<pre>${text}</pre>`)#text
46+
tr: td(colspan=2): iframe(sandbox, referrerpolicy='no-referrer', seamless='seamless', srcdoc=`<pre>${text}</pre>`)#text

test/test.js

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
const fs = require('fs');
22
const path = require('path');
3-
43
const test = require('ava');
54
const nodemailer = require('nodemailer');
6-
75
const previewEmail = require('..');
86

97
const transport = nodemailer.createTransport({ jsonTransport: true });
@@ -42,6 +40,38 @@ test('opens a preview email', async (t) => {
4240
t.true(typeof url === 'string');
4341
});
4442

43+
test('returns HTML only', async (t) => {
44+
const message = {
45+
from: 'niftylettuce <niftylettuce+from@gmail.com>',
46+
to: 'niftylettuce+to@gmail.com, niftylettuce <niftylettuce+test@gmail.com>',
47+
subject: 'Hello world',
48+
html: `<p>Hello world</p>`,
49+
text: 'Hello world',
50+
replyTo: 'niftylettuce <niftylettuce+replyto@gmail.com>',
51+
inReplyTo: 'in reply to',
52+
attachments: [
53+
{ filename: 'hello-world.txt', content: 'Hello world' },
54+
{ path: path.join(__dirname, '..', '.editorconfig') },
55+
{ path: path.join(__dirname, '..', 'media', 'browser.png') },
56+
{
57+
filename: 'test.txt',
58+
content: fs.createReadStream(path.join(__dirname, 'test.txt'))
59+
}
60+
],
61+
headers: {
62+
'X-Some-Custom-Header': 'Some Custom Value'
63+
},
64+
list: {
65+
unsubscribe: 'https://niftylettuce.com/unsubscribe'
66+
}
67+
};
68+
const response = await transport.sendMail(message);
69+
const html = await previewEmail(JSON.parse(response.message), {
70+
returnHTML: true
71+
});
72+
t.true(html.startsWith('<!DOCTYPE html>'));
73+
});
74+
4575
test('does not open', async (t) => {
4676
const url = await previewEmail({}, { open: false });
4777
t.true(typeof url === 'string');

0 commit comments

Comments
 (0)