Skip to content

Commit 96a35e6

Browse files
committed
fix: preserve mime type, fix tests, bump deps
1 parent 15312f1 commit 96a35e6

File tree

13 files changed

+4675
-3078
lines changed

13 files changed

+4675
-3078
lines changed

.npmignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.nyc_output
2+
coverage

.travis.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
language: node_js
22
node_js:
33
- '8'
4+
- '10'
5+
script:
6+
npm run test-coverage
47
after_success:
58
npm run coverage

README.md

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
[![made with lass](https://img.shields.io/badge/made_with-lass-95CC28.svg)](https://lass.js.org)
88
[![license](https://img.shields.io/github/license/ladjs/nodemailer-base64-to-s3.svg)](<>)
99

10-
> Convert your Base64-Encoded Data URI's in <img> tags to Amazon S3/CloudFront URL's
10+
> Convert your base64 encoded data URI's in `<img>` tags to Amazon S3/CloudFront URL's
1111
1212
<img src="https://cdn.rawgit.com/ladjs/nodemailer-base64-to-s3/master/media/screenshot.png" width="361.5" height="80.75" />
1313

@@ -29,12 +29,13 @@
2929

3030
> **Tip:** This package is bundled with [Lad][] and already pre-configured for you.
3131
32-
* :cloud: Converts `<img>` tags with Base64-Encoded Data URI's to absolute paths stored on [S3][] (or optionally [CloudFront][]).
33-
* :muscle: Supports all image types (`png|jpg|jpeg|gif|svg`) and converts them to optimized `png` using [sharp][]. Unfortunately Gmail does not have great SVG support, and not all clients can render SVG – so we use PNG's for now.
34-
* :lock: Uses [rev-hash][] to prevent asset naming collisions in your S3 bucket (and to avoid Gmail image cache issues).
35-
* :zap: Encodes your images using gzip so your downloads are [compressed and faster][s3-article] (uses `zlib.gzip`) via [zlib][].
36-
* :tada: Perfect alternative to [cid][cid-url] embedded images.
37-
* :crystal_ball: Built for [Lad][] and [font-awesome-assets][].
32+
* Converts `<img>` tags with base64 encoded data URI's to absolute paths stored on [S3][] (or optionally [CloudFront][]).
33+
* Supports all standard data URI image types (PNG, JPEG, GIF, SVG)
34+
* Checks Amazon S3 bucket before uploading images to prevent a redundant double-upload of the same file (better performance)
35+
* Uses [rev-hash][] to prevent asset naming collisions in your S3 bucket (and to avoid Gmail image cache issues).
36+
* Encodes your images using gzip so your downloads are [compressed and faster][s3-article] (uses `zlib.gzip`) via [zlib][].
37+
* Perfect alternative to [cid][cid-url] embedded images.
38+
* Built for [Lad][] and [font-awesome-assets][].
3839

3940

4041
## Install
@@ -131,8 +132,6 @@ Here's a snippet from the navbar shown in the screenshot above. We utilize [font
131132

132133
[cid-url]: https://sendgrid.com/blog/embedding-images-emails-facts/
133134

134-
[sharp]: https://github.com/lovell/sharp
135-
136135
[s3-article]: http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/ServingCompressedFiles.html
137136

138137
[nodemailer-doc]: https://nodemailer.com/using-embedded-images/

example.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
const ms = require('ms');
22
const nodemailer = require('nodemailer');
33

4-
const base64ToS3 = require('./');
4+
const base64ToS3 = require('.');
55

66
const html =
7-
// eslint-disable-next-line max-len
87
'<img width="16" height="16" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAAAsTAAALEwEAmpwYAAABYklEQVRoge2ZsUoDQRCGv4uigsRgEQOBwya1WlsIdnmYPIOFvW1eRPuAjyDkBS6VsQlaxeYssgfnarzc3OpkZT5YuOV2Z/6fnTtub0HONTAF8oZt6mL9KcfAoqHwclu4mLVpCQ1cAB3h3O/oAOeSibvChPte/xZ4qRmjC9yU+gdCLSKGfC6BgSDGwIsxlAiRltDW4JfQHpACScW8vtc/FeT25/SpXskcmAHv/o0UeACWhHuz/FZbOq1pIb4HzLdAWN02B3o7wBi49JckAg6BbgJklJYjMrKE1XJES/SvUTOgjRnQxgxoYwa0SYCRtggjdkJ9nxeMfhhTLtcgeaN/iKM3EHI/UPwIOAOu1ox5BJ7cdZC8IQ0cAW8bjm0DryGShiyhO1bCqmi7sUGwLaU2ZkAbM6DNvzAw0xbRgKwFTLRVNGAC8f5efwZOCiexHXDcO81fjpI2PWLSImfNEZNhGIYOH2xyJC1ddamJAAAAAElFTkSuQmCC" />';
98

109
const transport = nodemailer.createTransport({

index.js

Lines changed: 46 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
const { promisify } = require('util');
22
const zlib = require('zlib');
3+
const mime = require('mime-types');
34
const _ = require('lodash');
45
const ms = require('ms');
56
const AWS = require('aws-sdk');
67
const revHash = require('rev-hash');
78

89
const regexp = new RegExp(
9-
// eslint-disable-next-line max-len
1010
/(<img[\s\S]*? src=")data:(image\/(?:png|jpe?g|gif|svg\+xml));base64,([\s\S]*?)("[\s\S]*?>)/g
1111
);
1212

@@ -44,7 +44,18 @@ const base64ToS3 = opts => {
4444
let result;
4545
do {
4646
result = regexp.exec(html);
47-
if (result) promises.push(transformImage(...result));
47+
if (result) {
48+
const [original, start, mimeType, base64, end] = result;
49+
promises.push(
50+
transformImage({
51+
original,
52+
start,
53+
mimeType,
54+
base64,
55+
end
56+
})
57+
);
58+
}
4859
} while (result);
4960

5061
// fulfill promises
@@ -65,45 +76,39 @@ const base64ToS3 = opts => {
6576
}
6677
}
6778

68-
function transformImage(original, start, mimeType, base64, end) {
69-
return new Promise(async (resolve, reject) => {
70-
try {
71-
// create a buffer of the base64 image
72-
// and convert it to a png
73-
const buffer = Buffer.from(base64, 'base64');
74-
75-
// apply transformation and gzip file
76-
const Body = await promisify(zlib.gzip).bind(zlib)(buffer);
77-
78-
// generate random filename
79-
// get the file extension based on mimeType
80-
const Key = `${opts.dir}${revHash(base64)}.png`;
81-
82-
const obj = {
83-
Key,
84-
ACL: 'public-read',
85-
Body,
86-
CacheControl: `public, max-age=${opts.maxAge}`,
87-
ContentEncoding: 'gzip',
88-
ContentType: 'image/png'
89-
};
90-
91-
// we cannot currently use this since it does not return a promise
92-
// <https://github.com/aws/aws-sdk-js/pull/1079>
93-
// await s3obj.upload({ Body }).promise();
94-
//
95-
// so instead we use promisify to convert it to a promise
96-
const data = await promisify(s3.upload).bind(s3)(obj);
97-
98-
const replacement = _.isString(opts.cloudFrontDomainName)
99-
? `${start}https://${opts.cloudFrontDomainName}/${data.key}${end}`
100-
: `${start}${data.Location}${end}`;
101-
102-
resolve([original, replacement]);
103-
} catch (err) {
104-
reject(err);
105-
}
106-
});
79+
async function transformImage({ original, start, mimeType, base64, end }) {
80+
// create a buffer of the base64 image
81+
// and convert it to a png
82+
const buffer = Buffer.from(base64, 'base64');
83+
84+
// apply transformation and gzip file
85+
const Body = await promisify(zlib.gzip).bind(zlib)(buffer);
86+
87+
// generate random filename
88+
// get the file extension based on mimeType
89+
const Key = `${opts.dir}${revHash(base64)}.${mime.extension(mimeType)}`;
90+
91+
const obj = {
92+
Key,
93+
ACL: 'public-read',
94+
Body,
95+
CacheControl: `public, max-age=${opts.maxAge}`,
96+
ContentEncoding: 'gzip',
97+
ContentType: 'image/png'
98+
};
99+
100+
// we cannot currently use this since it does not return a promise
101+
// <https://github.com/aws/aws-sdk-js/pull/1079>
102+
// await s3obj.upload({ Body }).promise();
103+
//
104+
// so instead we use promisify to convert it to a promise
105+
const data = await promisify(s3.upload).bind(s3)(obj);
106+
107+
const replacement = _.isString(opts.cloudFrontDomainName)
108+
? `${start}https://${opts.cloudFrontDomainName}/${data.key}${end}`
109+
: `${start}${data.Location}${end}`;
110+
111+
return [original, replacement];
107112
}
108113

109114
return compile;

package.json

Lines changed: 75 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,113 @@
11
{
22
"name": "nodemailer-base64-to-s3",
3-
"description":
4-
"Convert your Base64-Encoded Data URI's in <img> tags to Amazon S3/CloudFront URL's",
3+
"description": "Convert your Base64-Encoded Data URI's in <img> tags to Amazon S3/CloudFront URL's",
54
"version": "0.0.4",
65
"author": "Nick Baugh <niftylettuce@gmail.com> (http://niftylettuce.com/)",
6+
"ava": {
7+
"failFast": true,
8+
"verbose": true
9+
},
710
"bugs": {
811
"url": "https://github.com/ladjs/nodemailer-base64-to-s3/issues",
912
"email": "niftylettuce@gmail.com"
1013
},
14+
"commitlint": {
15+
"extends": [
16+
"@commitlint/config-conventional"
17+
]
18+
},
1119
"contributors": [
1220
"Nick Baugh <niftylettuce@gmail.com> (http://niftylettuce.com/)"
1321
],
1422
"dependencies": {
15-
"aws-sdk": "^2.122.0",
16-
"lodash": "^4.17.4",
23+
"aws-sdk": "^2.421.0",
24+
"lodash": "^4.17.11",
25+
"mime-types": "^2.1.22",
1726
"rev-hash": "^2.0.0"
1827
},
19-
"ava": {
20-
"failFast": true,
21-
"verbose": true
22-
},
2328
"devDependencies": {
24-
"auto-bind": "^1.1.0",
25-
"ava": "^0.22.0",
29+
"@commitlint/cli": "^7.5.2",
30+
"@commitlint/config-conventional": "^7.5.0",
31+
"auto-bind": "^2.0.0",
32+
"ava": "^1.3.1",
2633
"cheerio": "^1.0.0-rc.2",
27-
"codecov": "^2.3.0",
28-
"cross-env": "^5.0.5",
29-
"dotenv": "^4.0.0",
30-
"eslint": "^4.5.0",
31-
"eslint-config-prettier": "^2.3.0",
32-
"eslint-plugin-prettier": "^2.2.0",
33-
"husky": "^0.14.3",
34-
"lint-staged": "^4.0.4",
35-
"ms": "^2.0.0",
36-
"nodemailer": "^4.1.0",
37-
"nyc": "^11.1.0",
38-
"prettier": "^1.6.1",
39-
"remark-cli": "^4.0.0",
40-
"remark-preset-github": "^0.0.6",
41-
"validator": "^9.0.0",
42-
"xo": "^0.19.0"
34+
"codecov": "^3.2.0",
35+
"cross-env": "^5.2.0",
36+
"dotenv": "^7.0.0",
37+
"eslint": "^5.15.1",
38+
"eslint-config-xo-lass": "^1.0.3",
39+
"eslint-plugin-node": "^8.0.1",
40+
"fixpack": "^2.3.1",
41+
"husky": "^1.3.1",
42+
"image-to-uri": "^1.0.0",
43+
"lint-staged": "^8.1.5",
44+
"ms": "^2.1.1",
45+
"nodemailer": "^5.1.1",
46+
"nyc": "^13.3.0",
47+
"remark-cli": "^6.0.1",
48+
"remark-preset-github": "^0.0.13",
49+
"validator": "^10.11.0",
50+
"xo": "^0.24.0"
4351
},
4452
"engines": {
4553
"node": ">=8.3"
4654
},
4755
"homepage": "https://github.com/ladjs/nodemailer-base64-to-s3",
48-
"keywords": ["nodemailer-base64-to-s3", "lass"],
56+
"husky": {
57+
"hooks": {
58+
"pre-commit": "lint-staged && npm test",
59+
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
60+
}
61+
},
62+
"keywords": [
63+
"lass",
64+
"nodemailer-base64-to-s3"
65+
],
4966
"license": "MIT",
5067
"lint-staged": {
51-
"*.{js,jsx,mjs,ts,tsx,css,less,scss,json,graphql}": [
52-
"prettier --write --single-quote --trailing-comma none",
53-
"git add"
54-
],
55-
"*.md": ["remark . -qfo", "git add"]
68+
"linters": {
69+
"*.js": [
70+
"xo --fix",
71+
"git add"
72+
],
73+
"*.md": [
74+
"remark . -qfo",
75+
"git add"
76+
],
77+
"package.json": [
78+
"fixpack",
79+
"git add"
80+
]
81+
}
5682
},
5783
"main": "index.js",
84+
"prettier": {
85+
"singleQuote": true,
86+
"bracketSpacing": true,
87+
"trailingComma": "none"
88+
},
5889
"remarkConfig": {
59-
"plugins": ["preset-github"]
90+
"plugins": [
91+
"preset-github"
92+
]
6093
},
6194
"repository": {
6295
"type": "git",
6396
"url": "https://github.com/ladjs/nodemailer-base64-to-s3"
6497
},
6598
"scripts": {
99+
"ava": "cross-env NODE_ENV=test ava",
66100
"coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov",
67101
"lint": "xo && remark . -qfo",
68-
"precommit": "lint-staged && npm test",
69-
"test": "npm run lint && npm run test-coverage",
70-
"test-coverage": "cross-env NODE_ENV=test nyc ava"
102+
"nyc": "cross-env NODE_ENV=test nyc ava",
103+
"test": "npm run lint && npm run ava",
104+
"test-coverage": "npm run lint && npm run nyc"
71105
},
72106
"xo": {
73-
"extends": "prettier",
74-
"plugins": ["prettier"],
75-
"parserOptions": {
76-
"sourceType": "script"
77-
},
78-
"rules": {
79-
"prettier/prettier": [
80-
"error",
81-
{
82-
"singleQuote": true,
83-
"bracketSpacing": true,
84-
"trailingComma": "none"
85-
}
86-
],
87-
"max-len": [
88-
"error",
89-
{
90-
"code": 80,
91-
"ignoreUrls": true
92-
}
93-
],
94-
"max-params": ["error", 5],
95-
"capitalized-comments": "off",
96-
"camelcase": "off",
97-
"no-warning-comments": "off"
98-
},
99-
"space": true
107+
"prettier": true,
108+
"space": true,
109+
"extends": [
110+
"xo-lass"
111+
]
100112
}
101113
}

test/fixtures/image.gif

49 Bytes
Loading

test/fixtures/image.jpeg

521 Bytes
Loading

test/fixtures/image.jpg

998 Bytes
Loading

test/fixtures/image.png

139 Bytes
Loading

0 commit comments

Comments
 (0)