Compare commits

...

163 Commits

Author SHA1 Message Date
dependabot[bot] fa285fc598
Bump copy-props from 2.0.4 to 2.0.5 (#103)
Bumps [copy-props](https://github.com/gulpjs/copy-props) from 2.0.4 to 2.0.5.
- [Release notes](https://github.com/gulpjs/copy-props/releases)
- [Changelog](https://github.com/gulpjs/copy-props/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gulpjs/copy-props/compare/2.0.4...2.0.5)

---
updated-dependencies:
- dependency-name: copy-props
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-23 11:36:19 +01:00
dependabot[bot] 8fb1037afa
Bump is-my-json-valid from 2.15.0 to 2.20.6 (#104)
Bumps [is-my-json-valid](https://github.com/mafintosh/is-my-json-valid) from 2.15.0 to 2.20.6.
- [Release notes](https://github.com/mafintosh/is-my-json-valid/releases)
- [Commits](https://github.com/mafintosh/is-my-json-valid/compare/v2.15.0...v2.20.6)

---
updated-dependencies:
- dependency-name: is-my-json-valid
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-11 10:10:29 +01:00
dependabot[bot] 8b371bb49c
Bump trim-off-newlines from 1.0.1 to 1.0.3 (#102)
Bumps [trim-off-newlines](https://github.com/stevemao/trim-off-newlines) from 1.0.1 to 1.0.3.
- [Release notes](https://github.com/stevemao/trim-off-newlines/releases)
- [Commits](https://github.com/stevemao/trim-off-newlines/compare/v1.0.1...v1.0.3)

---
updated-dependencies:
- dependency-name: trim-off-newlines
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-11 10:10:00 +01:00
dependabot[bot] e721efe2ca
Bump node-sass from 4.14.1 to 7.0.0 (#101)
Bumps [node-sass](https://github.com/sass/node-sass) from 4.14.1 to 7.0.0.
- [Release notes](https://github.com/sass/node-sass/releases)
- [Changelog](https://github.com/sass/node-sass/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sass/node-sass/compare/v4.14.1...v7.0.0)

---
updated-dependencies:
- dependency-name: node-sass
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-10 08:39:21 +01:00
Dan Mindru 96e868bdf2 Bump version 2021-09-04 09:39:10 +02:00
Anuj Punjani d7baeec5ab
Added custom inline-image plugin & modified build.js (#100)
* Modified build.js to use custom inline-image plugin
* Added inlineRemoteUrl property for inline image processing

Co-authored-by: Dan Mindru <nadurdnim@icloud.com>
2021-09-04 09:36:47 +02:00
Dan Mindru 0c1ff6d3dc
Build on PR 2021-09-01 21:18:08 +02:00
dependabot[bot] 6bc6ab5c9d
Bump postcss from 7.0.2 to 7.0.36 (#96)
Bumps [postcss](https://github.com/postcss/postcss) from 7.0.2 to 7.0.36.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/7.0.2...7.0.36)

---
updated-dependencies:
- dependency-name: postcss
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-16 14:39:12 +02:00
dependabot[bot] b9ef0953de
Bump normalize-url from 4.5.0 to 4.5.1 (#95)
Bumps [normalize-url](https://github.com/sindresorhus/normalize-url) from 4.5.0 to 4.5.1.
- [Release notes](https://github.com/sindresorhus/normalize-url/releases)
- [Commits](https://github.com/sindresorhus/normalize-url/commits)

---
updated-dependencies:
- dependency-name: normalize-url
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-10 12:51:54 +02:00
dependabot[bot] 5f68bf303a
Bump browserslist from 4.8.7 to 4.16.6 (#94)
Bumps [browserslist](https://github.com/browserslist/browserslist) from 4.8.7 to 4.16.6.
- [Release notes](https://github.com/browserslist/browserslist/releases)
- [Changelog](https://github.com/browserslist/browserslist/blob/main/CHANGELOG.md)
- [Commits](https://github.com/browserslist/browserslist/compare/4.8.7...4.16.6)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-25 16:31:55 +02:00
dependabot[bot] 5b2710d3c2
Bump lodash from 4.17.15 to 4.17.21 (#93)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.21)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-07 09:14:24 +02:00
dependabot[bot] deb81dd037
Bump y18n from 3.2.1 to 3.2.2 (#92)
Bumps [y18n](https://github.com/yargs/y18n) from 3.2.1 to 3.2.2.
- [Release notes](https://github.com/yargs/y18n/releases)
- [Changelog](https://github.com/yargs/y18n/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yargs/y18n/commits)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-30 13:12:37 +02:00
dependabot[bot] f57fd62ce9
Bump ini from 1.3.4 to 1.3.7 (#91)
Bumps [ini](https://github.com/isaacs/ini) from 1.3.4 to 1.3.7.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.4...v1.3.7)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-18 15:00:51 +01:00
Ben Mares 1c27a30ab4
Fix typo which prevents clearing cached config (#90)
Closes #84
2020-10-30 08:33:35 +01:00
Dan Mindru 83c3389945 Merge branch 'master' of github.com:fadeit/responsive-html-email-signature 2020-09-21 22:38:21 +02:00
Dan Mindru aeaadaae53 Bump version 2020-09-21 22:37:49 +02:00
Dan Mindru 51857f1129
Add support for node 12 (#88)
* Add support for node 12

* Remove deps task completely
2020-09-21 22:35:10 +02:00
Dan Mindru ba355c2c78
Rework npm commands to increase windows compat (#82)
* Rework npm commands to increase windows compat

* Test up to node 12
2020-06-10 11:33:50 +02:00
dependabot[bot] 6da4298b1f
Bump acorn from 7.1.0 to 7.1.1 (#78)
Bumps [acorn](https://github.com/acornjs/acorn) from 7.1.0 to 7.1.1.
- [Release notes](https://github.com/acornjs/acorn/releases)
- [Commits](https://github.com/acornjs/acorn/compare/7.1.0...7.1.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-03-16 11:31:49 +01:00
Dan Mindru 054c18687e Add TODO 2020-02-23 23:04:52 +01:00
Dan Mindru 6955a0b796 Fix checks 2020-02-23 23:02:27 +01:00
Dan Mindru a0c8b69119 Remove windiws checks, won't fix 2020-02-23 23:02:27 +01:00
Dan Mindru c0c64b3449 Remove husky 2020-02-23 23:02:27 +01:00
Dan Mindru 12ea9b461c Add open collective post install to fix failing buil 2020-02-23 23:02:27 +01:00
Dan Mindru 468418fb17 Update husky 2020-02-23 23:02:27 +01:00
Dan Mindru 3f3e92fe29 Fix tests 2020-02-23 23:02:27 +01:00
Dan Mindru 12facfd8c2 Add prettier config 2020-02-23 23:02:27 +01:00
Dan Mindru 6113d1b575 Move css link tag generation to util 2020-02-23 23:02:27 +01:00
Dan Mindru 9610d244dd Finish unused config (barely) 2020-02-23 23:02:27 +01:00
Dan Mindru f298f8bd85 Refactor for tests 2020-02-23 23:02:27 +01:00
Dan Mindru b3e0c77988 Add empty tests 2020-02-23 23:02:27 +01:00
Dan Mindru 1ad1ef9106 Update pre-push to run e2e test 2020-02-23 23:02:27 +01:00
Dan Mindru d55786f744 Check for unused initial (untested) implementation 2020-02-23 23:02:27 +01:00
Dan Mindru e599bef72e Remove unused package 2020-02-23 23:02:27 +01:00
Dan Mindru 8ba0323aaa Fix readme text 2020-02-23 23:02:27 +01:00
Dan Mindru 86944f3cee Fix tests 2020-02-23 23:02:27 +01:00
Dan Mindru e44a52c89f Re-format tests 2020-02-23 23:02:27 +01:00
Dan Mindru 8bcabaa10f Increase version (to not forget) 2020-02-23 23:02:27 +01:00
Dan Mindru d45a069c89 Sketch look for unused task 2020-02-23 23:02:27 +01:00
Dan Mindru ab54d1e3e6 Update prettier conf 2020-02-23 23:02:27 +01:00
Dan Mindru a04d8a225a Remove todos from html templates 2020-02-23 23:02:27 +01:00
Dan Mindru 7cd7e53300 Refactor build to split up reading and gettings configs into utils 2020-02-23 23:02:27 +01:00
Dan Mindru 6ab7b8d962 Format all source files 2020-02-23 23:02:27 +01:00
Dan Mindru d950d1d265 Add constants file 2020-02-23 23:02:27 +01:00
Dan Mindru 4adabe4ecd Rollback husky 2020-02-23 22:28:18 +01:00
Dan Mindru 77953b721a Nuke package lock 2020-02-23 22:22:51 +01:00
Dan Mindru f68b5104ce Drop v9 2020-02-23 22:17:52 +01:00
Dan Mindru 3c22a3921c Last chance for windows 2020-02-23 22:13:08 +01:00
Dan Mindru fedff3d67c Include node v13 2020-02-23 22:11:09 +01:00
Dan Mindru abecdb6371 Remove windows from tests 2020-02-23 22:09:06 +01:00
Dan Mindru 31f824b664 Tweaks scripts 2020-02-23 22:08:24 +01:00
Dan Mindru 6f05af60ad Update node-sass 2020-02-23 21:59:11 +01:00
Dan Mindru 0150856d77 Try to install on win using unsafe perm 2020-02-23 21:55:15 +01:00
Dan Mindru 1c6ff143b1 Test on node 9-12 2020-02-23 21:50:02 +01:00
Dan Mindru 5fcbf1ca19 Downgrade node-sass 2020-02-23 21:47:10 +01:00
Dan Mindru 6ecb318ca6 Fix action node version 2020-02-23 21:41:03 +01:00
Dan Mindru 455999b910 Test on LTS node only 2020-02-23 21:37:33 +01:00
Dan Mindru 4618b491d7 Rollback gulp-rename 2020-02-23 21:32:50 +01:00
Dan Mindru d1d656d375 Roll back ava to prevent broken tests on windows 2020-02-23 21:25:37 +01:00
Dan Mindru be239820d1 Include cli as dep 2020-02-23 21:19:03 +01:00
Dan Mindru 46011d12ea Update dark test fixutre 2020-02-23 21:18:44 +01:00
Dan Mindru 0edb498614 Include semis 2020-02-23 21:18:34 +01:00
Dan Mindru 51702a2c0f Include node 13 in test matrix 2020-02-23 21:11:02 +01:00
Dan Mindru 2a43f4309d Remove newline eof 2020-02-23 21:09:56 +01:00
Dan Mindru 9e1780eb08 Update dependencies 2020-02-23 21:03:37 +01:00
Dan Mindru 8ecbee58c0
Add cname (#75) 2019-09-08 21:50:04 +02:00
Dan Mindru 54ba657926
Setup eslint, prettier & husky (#72) 2019-09-03 21:20:18 +02:00
Dan Mindru 9c2e12104c
Set linguist-vendored to true 2019-09-03 13:33:23 +02:00
Dan Mindru 0b869c8359 Merge branch 'master' of github.com:danmindru/responsive-html-email-signature 2019-09-03 12:48:25 +02:00
Dan Mindru 3f6dc14c78 Fix typo 2019-09-03 12:48:11 +02:00
Dan Mindru c177e1f24f
Fix other node-version to node_version typo 2019-09-03 11:47:54 +02:00
Dan Mindru 265f955af5 Merge branch 'master' of github.com:danmindru/responsive-html-email-signature 2019-09-03 11:37:05 +02:00
Dan Mindru 5bac55caac Don't use spread to increase backwards compat 2019-09-03 11:36:49 +02:00
Dan Mindru 1dece49925
Fix typo: node-version to node_version 2019-09-03 11:23:41 +02:00
Dan Mindru 1d00da17ff
Add action badge 2019-09-03 11:21:23 +02:00
Dan Mindru 356ddd41cb Merge branch 'master' of github.com:danmindru/responsive-html-email-signature 2019-09-03 11:16:06 +02:00
Dan Mindru f8687ca6d9 Update documentation 2019-09-03 11:13:33 +02:00
Dan Mindru 80160e8135
Use node number syntax as opposed to x.x 2019-09-03 11:00:55 +02:00
Dan Mindru b316aa6caa
Run tests on windows, mac & ubuntu 2019-09-03 10:49:22 +02:00
Dan Mindru 558ccc2a9e
Rename test action 2019-09-03 10:46:53 +02:00
Dan Mindru 2970388579 Bump version 2019-09-03 10:44:06 +02:00
Dan Mindru 53226eec93 Update readme 2019-09-03 10:43:42 +02:00
Dan Mindru 06f7ac73fe Resolve paths in test 2019-09-03 10:43:33 +02:00
Dan Mindru ead960958b Create a gulp task to run pipeline once 2019-09-03 10:43:11 +02:00
Dan Mindru 41f6dced8f Refactor build to use native promises 2019-09-03 10:42:46 +02:00
Dan Mindru d8baffdca4
Set linguist-vendored to false 2019-09-02 17:05:31 +02:00
Dan Mindru d9d966c1f7
Add wildcard to vendored paths 2019-09-02 17:02:22 +02:00
Dan Mindru dd9f8ef381
Add sample data as vendor code 2019-09-02 17:01:36 +02:00
Dan Mindru ad525373b7
Create test workflow for github actions 2019-08-31 17:42:55 +02:00
Dan Mindru 284d97efa5 Bump version 2019-08-31 17:30:04 +02:00
Dan Mindru bbd274c7f7 Update packages 2019-08-31 17:29:39 +02:00
Dan Mindru 238daab855 refactor gulpfile to work with gulp v4 2019-08-31 16:55:15 +02:00
Dan Mindru 29354f0920 Update postcss task 2019-08-31 16:55:01 +02:00
Dan Mindru 268cbed282 Update lint task 2019-08-31 16:54:51 +02:00
Dan Mindru 3c47d1f4bc Update sass task 2019-08-31 16:54:40 +02:00
Dan Mindru bfb19dec08 Update less task 2019-08-31 16:54:26 +02:00
Dan Mindru 5e10fab4c7 Update dupe task 2019-08-31 16:54:12 +02:00
Dan Mindru 4c10c45222 Update check deps task 2019-08-31 16:54:02 +02:00
Dan Mindru 6c30066e4d Update build task 2019-08-31 16:53:34 +02:00
Dan Mindru 2f56f93d79 Delete watch task 2019-08-31 16:53:26 +02:00
Dan Mindru fad368ecb2 Update to gulp 4 2019-08-31 16:53:12 +02:00
Dan Mindru b969802d87 Bump node version 2019-08-31 16:53:01 +02:00
Dan Mindru 1f24c76467 Add some basic tests 2019-08-31 16:52:41 +02:00
dependabot[bot] 06badf0964 Bump mixin-deep from 1.3.1 to 1.3.2 (#64)
Bumps [mixin-deep](https://github.com/jonschlinkert/mixin-deep) from 1.3.1 to 1.3.2.
- [Release notes](https://github.com/jonschlinkert/mixin-deep/releases)
- [Commits](https://github.com/jonschlinkert/mixin-deep/compare/1.3.1...1.3.2)

Signed-off-by: dependabot[bot] <support@github.com>
2019-08-29 13:37:51 +02:00
dependabot[bot] e42f190e40 Bump lodash.mergewith from 4.6.1 to 4.6.2 (#63)
Bumps [lodash.mergewith](https://github.com/lodash/lodash) from 4.6.1 to 4.6.2.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2019-07-11 11:47:07 +02:00
dependabot[bot] 07e8fb7952 Bump lodash.merge from 4.6.1 to 4.6.2 (#62)
Bumps [lodash.merge](https://github.com/lodash/lodash) from 4.6.1 to 4.6.2.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2019-07-11 11:45:54 +02:00
dependabot[bot] 1bd69ddb88 Bump tar from 2.2.1 to 2.2.2 (#59)
Bumps [tar](https://github.com/npm/node-tar) from 2.2.1 to 2.2.2.
- [Release notes](https://github.com/npm/node-tar/releases)
- [Commits](https://github.com/npm/node-tar/compare/v2.2.1...v2.2.2)
2019-06-06 00:54:49 +03:00
dependabot[bot] f2e268bd9c Bump fstream from 1.0.10 to 1.0.12 (#60)
Bumps [fstream](https://github.com/npm/fstream) from 1.0.10 to 1.0.12.
- [Release notes](https://github.com/npm/fstream/releases)
- [Commits](https://github.com/npm/fstream/compare/v1.0.10...v1.0.12)
2019-06-06 00:54:25 +03:00
dependabot[bot] d6300db1e6 Bump js-yaml from 3.12.0 to 3.13.1 (#61)
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 3.12.0 to 3.13.1.
- [Release notes](https://github.com/nodeca/js-yaml/releases)
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/3.12.0...3.13.1)
2019-06-06 00:54:01 +03:00
Dan Mindru a5f0b3e83f
Bugfix/build failing on node10 (#58)
* Update dependencies
* Add note about create command and fix outdated docs
2019-04-24 22:03:21 +02:00
Dan Mindru d567e67d7e
Update documentation with some notes on email clients (#54) 2018-11-16 22:16:07 +01:00
Dan Mindru ecaf31b42d
Merge pull request #51 from danmindru/develop
Install newer lodash
2018-11-03 18:36:10 +01:00
Dan Mindru dbf0951627 Install newer lodash 2018-11-03 18:34:11 +01:00
Dan Mindru af88ce036c
Merge pull request #49 from gstevenson/patch-1
Fix a couple of README typos
2018-09-25 11:07:57 +02:00
Greg Stevenson 3699600c47
Fix a couple of README typos 2018-09-24 20:27:31 +01:00
Dan Mindru 823a7be173 Update deps 2018-09-03 22:12:00 +02:00
Dan Mindru bae078590a Update docs 2018-09-03 21:56:50 +02:00
Dan Mindru 207737f437 Bump version 2017-11-24 15:07:03 +01:00
Dan Mindru 23ae549b64 Remove console log 2017-11-24 15:06:18 +01:00
Dan Mindru bcb013e39d Swap html image inline plugin 2017-11-23 10:52:38 +01:00
Dan Mindru f2aa0c2366 Update example config to match v4 #35 2017-10-06 11:27:02 +02:00
Dan Mindru f921b882a0 Merge pull request #34 from seanlane/patch-1
Fix minor typo
2017-09-19 12:23:20 +02:00
Sean Lane 95460a4e0c Fix minor typo
Just saw a minor error. Thanks for making this, looks great!
2017-09-18 17:32:59 -06:00
Dan Mindru 160263f899 Fix /pulls URL 2017-07-14 15:15:27 +02:00
Dan Mindru 201737e166 Update deps, bump version 2017-07-14 15:04:48 +02:00
Dan Mindru e9dbb5b37e Add badges, closes #14 2017-07-14 15:04:29 +02:00
Dan Mindru a6c2561d1e Split some main gulpfile calls for better readability 2017-07-14 14:11:31 +02:00
Dan Mindru 9f22e6d4f4 Fix some typos in README 2017-07-14 14:08:02 +02:00
Sander Sink fe82f7ab16 HTTPS clone instead of ssh 2017-07-12 13:40:15 +03:00
Sander Sink fa45c63ce8 Made readme a bit readable 2017-07-12 13:38:32 +03:00
Dan Mindru e479d03613 Note on node version, bump npm version 2017-05-18 12:28:48 +02:00
Dan Mindru 108747ee1d Fix footer underline 2017-05-17 14:57:20 +02:00
Dan Mindru aba228edbe Merge pull request #31 from bryant1410/master
Fix broken headings in Markdown files
2017-04-18 11:35:09 +02:00
Santiago Castro cf2f38358f Fix broken Markdown headings 2017-04-16 17:01:25 -03:00
Dan Mindru 1fe90ee748 Fix README credits 2017-03-23 20:07:16 +01:00
Dan Mindru 9dcf115781 Fix i18n readme markdown 2017-03-14 22:22:20 +01:00
Dan Mindru a33140224b Merge pull request #29 from nenebale/master
File extension in watchTask now matches conf.json
2017-03-11 15:20:11 +01:00
Lionel Voser 2bd5f576ee File extension in watchTask matches conf.json 2017-03-09 16:59:50 +01:00
Dan Mindru 9b2d698c33 Fix some readme typos 2017-03-06 15:08:32 +01:00
Dan Mindru 27af00d9fe Bump version 2017-02-08 18:19:28 +01:00
Dan Mindru 12bffe8169 #26 Use path.win32.basename to resolve stylesheet paths and be consistent on WIN & UNIX 2017-02-08 16:26:05 +01:00
Dan Mindru 5b907d3bf9 Fix syntax highlighting in README 2017-02-08 00:45:07 +01:00
Dan Mindru 03ab7366de Update demo 2017-02-08 00:41:10 +01:00
Dan Mindru 59804bd735 Update deploy command 2017-02-07 23:50:46 +01:00
Dan Mindru cfbfd696f1 Docs 2017-02-07 23:45:19 +01:00
Dan Mindru 16f00eb247 Upgrade node 2017-02-07 23:45:12 +01:00
Dan Mindru 090b3f03ed Remove examples 2017-02-07 23:45:02 +01:00
Dan Mindru 3348ad9dc1 Rename src ➡️ templates 2017-02-07 23:44:47 +01:00
Dan Mindru cbb3743613 Setup demo 2017-02-07 23:44:25 +01:00
Dan Mindru 18606cff77 Improve build 2017-02-07 23:44:17 +01:00
Dan Mindru f4d1bc65db Merge master into 100 2017-01-03 11:57:50 +02:00
Dan Mindru b3528c9aff Merge branch 'master' of github.com:fadeit/responsive-html-email-signature 2017-01-03 11:56:56 +02:00
Dan Mindru 64371a56af Add .nvmrc with node ~v4 2017-01-03 11:56:37 +02:00
Dan Mindru 514ad88dad Update ISSUE_TEMPLATE.md 2016-11-11 17:53:24 +01:00
Dan Mindru c9a6096d10 Create ISSUE_TEMPLATE.md 2016-10-25 21:14:27 +02:00
Dan Mindru e5cd2fd649 Allow single conf objects too 2016-08-19 22:47:53 +02:00
Dan Mindru 1bdbed78c9 #18 convert conf files to JSON & lint 2016-08-19 21:04:02 +02:00
Dan Mindru 6816ef605f #12 Update docs 2016-08-19 19:46:15 +02:00
Dan Mindru c2bad1843c #12 Get rid of wrench in favor of fs & fs-extra 2016-08-19 19:42:23 +02:00
Dan Mindru 05c40b8083 Doc checklist changes 2016-08-19 19:39:02 +02:00
Dan Mindru 4317d545f4 #12 Move translation to own dir 2016-08-19 18:32:33 +02:00
Dan Mindru 0efa1f986b #9 Ignore dist & real people confs 2016-08-19 18:20:10 +02:00
Dan Mindru 8ddeded1e1 HTTP => HTTPS 2016-06-22 21:46:14 +02:00
98 changed files with 30256 additions and 662 deletions

18
.eslintrc.js Normal file
View File

@ -0,0 +1,18 @@
module.exports = {
env: {
browser: true,
commonjs: true,
es6: true
},
extends: ['standard', 'prettier'],
globals: {
Atomics: 'readonly',
SharedArrayBuffer: 'readonly'
},
parserOptions: {
ecmaVersion: 2018
},
rules: {
semi: 0
}
};

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
demo/* linguist-vendored
tests/sample/* linguist-vendored

29
.github/workflows/nodejs.yml vendored Normal file
View File

@ -0,0 +1,29 @@
name: Test template output
on: [push, pull_request]
jobs:
test:
name: Test on node ${{ matrix.node_version }} and ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
node_version: [10, 11, 12, 13, 14]
os: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- uses: actions/checkout@v1
- name: Use Node.js ${{ matrix.node_version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node_version }}
- name: npm install & test
run: |
npm -v
node -v
npm install
npm run test
env:
CI: true

5
.gitignore vendored
View File

@ -1,4 +1,7 @@
/vendor/ /vendor/
/node_modules/ /node_modules/
/tmp/ /tmp/
.DS_Store .DS_Store
/dist
IDEAS.md
npm_debug.log

1
.nvmrc Normal file
View File

@ -0,0 +1 @@
v12

5
.prettierrc Normal file
View File

@ -0,0 +1,5 @@
{
"semi": true,
"singleQuote": true,
"printWidth": 120
}

1
CNAME Normal file
View File

@ -0,0 +1 @@
responsive-html-email-signature-generator.com

7
ISSUE_TEMPLATE.md Normal file
View File

@ -0,0 +1,7 @@
Hi there, please provide some info about your enviornment before submiting this issue:
- device :
- OS :
- email client :
- node version :
- npm version :
- error message :

221
README.md
View File

@ -1,128 +1,243 @@
# Responsive HTML email signature(s) # Responsive HTML email signature(s)
### Let's punch email clients in the stomach!
[![npm](https://img.shields.io/npm/v/responsive-html-email-signature.svg)](https://www.npmjs.com/package/responsive-html-email-signature)
[![license](https://img.shields.io/github/license/danmindru/responsive-html-email-signature.svg)](/LICENSE)
[![test action status](https://github.com/danmindru/responsive-html-email-signature/workflows/Test%20template%20output/badge.svg)](https://github.com/danmindru/responsive-html-email-signature/actions)
### Let's punch email clients in the stomach 👊
When you need some basic responsive email signatures that work on mobile.<br/> When you need some basic responsive email signatures that work on mobile.<br/>
...and your colleagues need them too.<br/> ...and your colleagues need them too.<br/>
...but you don't want to deal with tables and inline styles. ...but you don't want to deal with tables and inline styles.
[Read the docs in other languages](/i18n) ↗️
## Preview ## Preview
Here's how the samples look:
Here are some examples:
![responsive emails-01](https://cloud.githubusercontent.com/assets/1515742/10591900/13889d32-76b9-11e5-8dc0-b89d80189e93.png) ![responsive emails-01](https://cloud.githubusercontent.com/assets/1515742/10591900/13889d32-76b9-11e5-8dc0-b89d80189e93.png)
![responsive emails-02](https://cloud.githubusercontent.com/assets/1515742/10591901/139c4954-76b9-11e5-80f7-5b0ccaf5af81.png) ![responsive emails-02](https://cloud.githubusercontent.com/assets/1515742/10591901/139c4954-76b9-11e5-80f7-5b0ccaf5af81.png)
## Read the docs in other languages ## Getting started
[Read the docs in Korean!](/README_kor.md)
- Clone repo `git clone https://github.com/danmindru/responsive-html-email-signature.git`
- Run `npm install`
- Run `npm start` to generate templates from configuration. This will continue to watch your files and re-make the template until you exit.
### Customizing templates
- Edit files in _/templates_
- Open files from `./dist` in your fav browser to check them out
> When you're done, check out [how to add them to your email client of choice](#usage-with-different-email-clients) if in doubt.
## Motivation ## Motivation
Let's make writing HTML emails & email signatures easier. We won't fix all email clients, but we can surely make our lives easier with some neat automation. <br/>
See a fairly comprehensive rant on the subject (and not only) [in this article](http://fadeit.dk/blog/post/html-emails-and-email-signatures-how-hard-can-it-be).
Writing HTML emails & email signatures sucks. Let's make it easier. We can't fix all email clients, but we can surely make our lives easier with some automation. <br/>
## What does it do ## What does this pile of code do
- [x] config-based template generation
- [x] allows generating multiple templates (for your colleagues too!) - [x] generates email templates from your config
- [x] allows generating multiple templates at once (for your colleagues too!)
- [x] transforms linked (`<link>`) CSS into inline styles - [x] transforms linked (`<link>`) CSS into inline styles
- [x] embeds local `img[src]` into the template (base64).* - [x] embeds local `img[src]` into the template (base64).\*
- [x] minifies the template - [x] minifies the template
- [x] media queries for mail clients that support them - [x] ads some basic media queries for mail clients that support them
- [x] can build templates from multiple sources - [x] can build templates from multiple sources
- [x] watches HTML/CSS files for changes and re-builds - [x] watches HTML / CSS files for changes and re-builds
- [x] supports LESS / SASS / PostCSS
- [x] autoprefixer, so you don't have to worry about your `-moz-`s or `-webkit-`s
- [x] linting, checks for used template config parameters and more!
**Some mail clients don't support them, so an external URL might be a good idea. Also, some clients might complain about the size, so keep an eye out.* \*_Some mail clients might have [hard limits](https://support.google.com/a/answer/176652?hl=en) regarding the email size, so don't include large images if possible. If you need to, use a URL instead and host the image somewhere else._
## Docs
### Installing
## Getting started
```bash ```bash
$ npm install $ npm install
$ gulp # By default, HTML & CSS files in './src' will be watched for changes $ npm start # By default, templates will be created in `./dist` and HTML & CSS files in './templates' will be watched for changes.
``` ```
Take a look at `src/light/` for an example. Copy / Paste, rename it and change `src/light/conf.js` to suite your needs. Run `gulp` to build the templates (into `/dist`). > Note: works well with node v10+. Earlier and later versions might also work.
### Configuring
To make a basic email from existing templates, you only have to edit the `conf.json` file in each template.
For example, the dark template accepts the following:
```json
{
"id": "<will-be-used-for-filename>",
"signature": "<signature-of-choice>",
"name": "<your-name>",
"contactMain": "<phone-or-email-or-html>",
"contactMail": "<email>",
"slogan": "<a-basic-slogan>",
"logoUrl": "</assets/dark.png?>",
"logoAlt": "<text-in-case-logo-is-blocked>",
"website": "<http://dark.dk>"
}
```
### Generating multiple emails from the same config (for your colleagues too!)
To generate multiple templates, use an array instead of an object in `conf.json`, like so:
```json
[{ ...conf1 }, { ...conf2 }]
```
### Using config values in HTML
Config variables are made available in all HTML files. <br/>
Add any variable to the configuration file and use it in HTML like so:
```html
<p><!-- @echo yourCustomVariable --></p>
```
Where the configuration contains:
```json
{
"yourCustomVariable": "Custom!"
}
```
> NB: config variables also accept HTML. That's useful for including links.
### Adding CSS & pre-processing
Any number of CSS, SASS or LESS files in a template directory & they will be automatically processed & inlined into the files outputed in `./dist`.
### Multiple emails in the same template
Templates can contain multiple HTML files from which to build emails. For example, the dark template has `signature.html` and `signature-reply.html`, which is a simpler version.
Each HTML file will be treated as an email template, except for `*.inc.html`. See below ⬇️
### Using partials (\*.inc.html)
By naming files with `*.inc.html`, they become partials. Partials will not be treated as templates (ignored), but they can be included in any HTML template using the `@include` HTML comment.
```html
<section>
<!-- @include footer.inc.html -->
</section
```
Partials are useful if you have bits of HTML that repeat, like headers, footers, etc.
### Advanced templating
Inside HTML files, any [preprocess directive](https://github.com/jsoverson/preprocess#all-directives) is supported, such as `@if`, `@extend`, `@exec`, etc.
## Template structure (examples) ## Template structure (examples)
There are no rules regarding how to structure templates, but it's a good idea to create directories for a template group. <br/>
There are 2 examples of template structures, one for the `light` email template and one for the `dark` one. There are 2 examples of template structures, one for the `light` email template and one for the `dark` one.
Here's how the dark one looks: Here's how the dark one is structured:
```bash ```bash
./src ./templates
├── dark ├── dark
├── conf.js # Template strings, logo, etc. ├── assets
├── dark.png # Image to embed as base64
├── conf.json # Template strings, logo, etc.
├── dark.css # Stylesheet. ├── dark.css # Stylesheet.
├── footer.inc.html # Contact info & logo ├── footer.inc.html # Contact info & logo
├── head.inc.html # 'Responsive' CSS goes here ├── head.inc.html # 'Responsive' CSS goes here
├── signature-reply.inc.html # Simplified signature (loads head)
├── signature.html # Full signature (loads head/footer) ├── signature.html # Full signature (loads head/footer)
├── signature-reply.html # Simplified signature (loads head)
``` ```
Here's how the light one looks: Here's how the light one is structured:
```bash ```bash
./src ./templates
├── light ├── light
├── conf.js # Template strings, logo, etc. ├── assets
├── light.png # Image to embed as base64
├── conf.json # Template strings, logo, etc.
├── footer.inc.html # Contact info & logo ├── footer.inc.html # Contact info & logo
├── full-mail.html # Body + signature ├── full-mail.html # Body + signature
├── head.inc.html # 'Responsive' CSS goes here ├── head.inc.html # 'Responsive' CSS goes here
├── signature-reply.inc.html # Simplified signature (loads head) ├── light.css # Stylesheet.
├── signature.html # Full signature (loads head/footer) ├── signature.html # Full signature (loads head/footer)
├── signature-reply.html # Simplified signature (loads head)
``` ```
Files are included via [gulp-preprocess](https://www.npmjs.com/package/gulp-preprocess).
There's one convention you have to keep in mind: `all files that you wish to include should follow the *.inc.html format`. The gulp task ignores `*.inc.html` files, but will try to process & create email templates from all `.html` files. There's one convention you have to keep in mind: `all files that you wish to include should follow the *.inc.html format`. The gulp task ignores `*.inc.html` files, but will try to process & create email templates from all `.html` files.
You are of course encouraged to change the default structure for your use case. You are of course encouraged to change the default structure for your use case.
## Overview of the build process ## Overview of the build process
The diagram below shows what happens to your email templates.
Each folder in 'src' is considered a `template group`. A template file will be generated for each of the configuration objects you add have in the template group -> `conf.js`.
![Responsive HTML email template/signatures diagram](http://fadeit.dk/posts/html-emails-and-email-signatures-how-hard-can-it-be/html-responsive-email-template-build-diagram.png)
The diagram below shows what happens to your email templates.
Each folder in 'templates' is considered a `template group`. A template file will be generated for each of the configuration objects you add have in the template group -> `conf.js`.
![Responsive HTML email template/signatures diagram](https://user-images.githubusercontent.com/1515742/45000195-35268300-afc3-11e8-82b4-7507430c48a0.png)
## CSS Support ## CSS Support
Remember, it's HTML mails, so you need to check a big-ass table to find out nothing's gonna work. Remember, it's HTML mails, so you need to check a big-ass table to find out nothing's gonna work.
See [this](https://www.campaignmonitor.com/css/) for more info. [Gulp-inline-css](https://www.npmjs.com/package/gulp-inline-css) is being used to convert whatever CSS you throw at it to inline styles, but it probably won't handle everything. See [this](https://www.campaignmonitor.com/css/) for more info. [Gulp-inline-css](https://www.npmjs.com/package/gulp-inline-css) is being used to convert whatever CSS you throw at it to inline styles, but it probably won't handle everything.
Some bonuses of using `gulp-inline-css`: many css props will be converted to attributes. For example, the 'background-color' prop will be added as 'bgcolor' attribute to table elements. Some bonuses of using `gulp-inline-css`: many css props will be converted to attributes. For example, the 'background-color' prop will be added as 'bgcolor' attribute to table elements.
For more details take a look at the [inline-css mappings](https://github.com/jonkemp/inline-css/blob/master/lib/setTableAttrs.js). For more details take a look at the [inline-css mappings](https://github.com/jonkemp/inline-css/blob/master/lib/setTableAttrs.js).
## Usage with different email clients
## Usage with different e-mail clients
### Thunderbird ### Thunderbird
There are several Thunderbird plugins which can automatically insert signatures when composing e-mails. We recommend [SmartTemplate4](https://addons.mozilla.org/en-us/thunderbird/addon/smarttemplate4) as one of the options. It can use different templates for new e-mails, replies and forwarded e-mails. There are several Thunderbird plugins which can automatically insert signatures when composing e-mails. We recommend [SmartTemplate4](https://addons.mozilla.org/en-us/thunderbird/addon/smarttemplate4) as one of the options. It can use different templates for new e-mails, replies and forwarded e-mails.
### Gmail
Go to your mailbox settings & paste the generated signature.
> **NB**: Gmail doesn't seem to support inlined (base64) images. You have to use absolute `http(s)//...`.
### Office 365 / outlook.live.com
It's a bit hacky to set up, but possible. See [this issue](https://github.com/danmindru/responsive-html-email-signature/issues/52).
### Apple Mail / OS X (oh boy) ### Apple Mail / OS X (oh boy)
#### Solution 1 #### Solution 1
- Open Mail.app and go to `Mail` -> `Preferences` -> `Signatures` - Open Mail.app and go to `Mail` -> `Preferences` -> `Signatures`
- Create a new signature and write some placeholder text (doesn't matter what it is, but you have to identify it later). - Create a new signature and write some placeholder text (doesn't matter what it is, but you have to identify it later).
- Close Mail.app. - Close Mail.app.
- Open terminal, then open the signature files using TextEdit (might be different for iCloud drive check the article below). - Open terminal, then open the signature files using TextEdit (might be different for iCloud drive check the article below).
``` ```
$ open -a TextEdit ~/Library/Mobile\ Documents/com~apple~mail/Data/V3/MailData/Signatures/ubiquitous_*.mailsignature $ open -a TextEdit ~/Library/Mobile\ Documents/com~apple~mail/Data/V3/MailData/Signatures/ubiquitous_*.mailsignature
``` ```
- Keep the file with the placeholder open, close the other ones. - Keep the file with the placeholder open, close the other ones.
- Replace the `<body>...</body>` and it's contents with the template of your choice. *Don't remove the meta information at the top!* - Replace the `<body>...</body>` and it's contents with the template of your choice. _Don't remove the meta information at the top!_
- Open Mail.app and compose a new mail. Select the signature from the list to test it out. - Open Mail.app and compose a new mail. Select the signature from the list to test it out.
**NB**: Images won't appear in the signature preview, but will work fine when you compose a message. > **NB**: Images won't appear in the signature preview, but will work fine when you compose a message.
#### Solution 2
####Solution 2
You can also open the HTML files in `/dist` in a browser, CMD + A, CMD + C and then paste into the signature box. This won't copy the `<html>` part or the `<style>` part that includes media queries. Follow the guide if you want it. You can also open the HTML files in `/dist` in a browser, CMD + A, CMD + C and then paste into the signature box. This won't copy the `<html>` part or the `<style>` part that includes media queries. Follow the guide if you want it.
#### Troubleshooting #### Troubleshooting
If solution #1 doesn't work, you can repeat the steps and lock the signature files before you open Mail.app again. If solution #1 doesn't work, you can repeat the steps and lock the signature files before you open Mail.app again.
Lock Files: Lock Files:
``` ```
$ chflags uchg ~/Library/Mail/V3/MailData/Signatures/*.mailsignature $ chflags uchg ~/Library/Mail/V3/MailData/Signatures/*.mailsignature
``` ```
If you want to do changes later, you have to unlock the files: If you want to do changes later, you have to unlock the files:
``` ```
$ chflags nouchg ~/Library/Mail/V3/MailData/Signatures/*.mailsignature $ chflags nouchg ~/Library/Mail/V3/MailData/Signatures/*.mailsignature
``` ```
@ -131,16 +246,17 @@ If you are using iCloud drive or having problems with it, you might also want to
### Outlook 2010 Client for Windows 7 ### Outlook 2010 Client for Windows 7
#### Solution 1 #### Solution 1
- Open Outlook 2010 and go to `File > Option > Mail > Signature`
- Open Outlook 2010 and go to `File > Option > Mail > Signature`
- Create new signature (with a placeholder for your convenience) - Create new signature (with a placeholder for your convenience)
- Open signature folder using CMD - Open signature folder using CMD
> As the AppData folder is hidden, I'd recommend you to opne it via CMD. > As the AppData folder is hidden, I'd recommend you to open it via CMD.
``` ```
cd AppData\Roamin\Microsoft cd AppData\Roaming\Microsoft
start Signatures start Signatures
``` ```
- Within this folder, find a file named with your placeholder then right click this file and select edit. - Within this folder, find a file named with your placeholder then right click this file and select edit.
@ -148,18 +264,21 @@ start Signatures
- Open Outlook again and check your signature - Open Outlook again and check your signature
#### Solution 2 #### Solution 2
Unfortnately, Outlook 2010 client dosen't support HTML file import features for your email template. But you can add your own signatures by simple Copy and paste like **Solution 2** above.
- Open built html file on `/dist` folder and Ctrl A + C Unfortnately, Outlook 2010 client dosen't support HTML file import features for your email template. But you can add your own signatures by simple Copy and paste like **Solution 2** above.
- Open Outlook 2010 and go to `File > Option > Mail > Signature`
- Open built html file on `/dist` folder and Ctrl A + C
- Open Outlook 2010 and go to `File > Option > Mail > Signature`
- Create new signature and paste copyed one - Create new signature and paste copyed one
> **NB**: base 64 will not be shown on Outlook 2010 client. So, I recommend to use external url if you want to use images. > **NB**: base 64 will not be shown on Outlook 2010 client. So, I recommend to use external url if you want to use images.
=================== ## Other commands
<br/>
<a href="http:fadeit.dk"><img src="http://fadeit.dk/src/assets/img/brand/fadeit_logo_full.svg" alt="The fadeit logo" style="width:200px;"/></a><br/><br/>
####About fadeit ### `npm run test`
We build awesome software, web and mobile applications.
See more at [fadeit.dk](http://fadeit.dk) Runs tests once.
### `npm run once`
Creates templates and exits; does not watch files.

11
TODO.md
View File

@ -1,11 +0,0 @@
#Future release notes & todo
This is a pre-release, stuff will be mostly ready when the items bellow are done. Feel free to contribute!
##Todo
- use fs-extra instead of wrench
- [100⭐] issues
##Doc updates
- Stylesheets are included automatically, place them wherever in the dir
- Autoprefixer is there to help
- use SASS, LESS or postcss.

6
constants.js Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
SOURCE_DIR: 'templates',
DIST_DIR: 'dist',
WORKING_DIR: 'tmp',
CONFIGURATION_FILE: 'conf.json'
}

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,14 @@
{
"id": "ONEstore",
"name": "0 0 0 매니저",
"team": "스토어 기획팀",
"title": "Dev Relations",
"welcome": "안녕하세요.",
"introParagraph": "Thanks for writing up this email.<br/> We are delighted to reply with a responsive template.",
"contactMain": "<a href='tel:+821012345678'><span>+82-10-1234-5678</span></a> | ",
"contactMail": "devhelper@onestore.co.kr",
"contactSecondary": "성남시 분당구 판교역로 188 SK플래닛 건물 11층",
"logoUrl": "assets/type01.png",
"logoAlt": "Onestore logo",
"website": "http://onesto.re/"
}

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

@ -0,0 +1,15 @@
{
"id": "ONEstoreBlack",
"signature": "Best regards,",
"name": "Joo Hyung Park",
"team": "Service Planning Team",
"title": "Dev Relations",
"welcome": "안녕하세요.",
"introParagraph": "Thanks for writing up this email.<br/> We are delighted to reply with a responsive template.",
"contactMain": "Call <a href='tel:+821012345678'><span>+82-10-1234-5678</span></a> or email us at",
"contactMail": "devhelper@onestore.co.kr",
"contactSecondary": "188, Pangyoyeok-ro, Bundang-gu, Seongnam-si, Gyeonggi-do, Korea",
"logoUrl": "assets/type03.png",
"logoAlt": "ONEstore. logo",
"website": "http://onesto.re/"
}

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -0,0 +1,13 @@
{
"id": "fadeit",
"signature": "Yours truly,",
"name": "Jane Whatsmyname",
"welcome": "Hi there,",
"introParagraph": "Thanks for writing up this email.<br/> We are delighted to reply with a responsive template.",
"contactMain": "Call <a href='tel:81100200'><span>81100200</span></a> or email us at",
"contactMail": "info@fadeit.dk",
"contactSecondary": "Anelystparken 31, DK-8381 Tilst, Aarhus",
"logoUrl": "http://fadeit.dk/src/assets/img/brand/fadeit-logo.png",
"logoAlt": "fadeit logo",
"website": "http://fadeit.dk"
}

View File

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -0,0 +1,11 @@
{
"id": "play",
"signature": "Best regards,",
"name": "Jane Whatsmyname",
"contactMain": "Call <a href='tel:004581100200'><span>(45) 81100200</span></a> or email us at",
"contactMail": "info@tryplay.dk",
"slogan": "LED Pylon. LED Wall. Digital Signage.",
"logoUrl": "https://informationscreen.com/manage/assets/images/play-logo.png",
"logoAlt": "Play. logo",
"website": "http://tryplay.dk"
}

223
demo/index.html vendored Normal file
View File

@ -0,0 +1,223 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Responsive HTML emails & emails signature</title>
<meta name="description" content="Automate the creation of HTML emails and email signatures. Generate multiple emails for your colleagues, friends or enemies too!">
<style>
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
box-sizing: border-box;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
body {
font-size: 12px;
background-color: rgb(21, 21, 21);
}
body, h1, h2, h3, h4, h5, h6, p {
font-family: -apple-system, BlinkMacSystemFont, Arial, sans-serif;
font-weight: 300;
color: white;
margin: 0;
}
p {
line-height: 1.3em;
}
h1 { font-size: 3em; }
h2 { font-size: 2.2em; }
h3 { font-size: 1.8em; margin-bottom: 0.3em; }
img {
max-width: 100%;
}
.dark { min-height: 430px; }
.light { min-height: 350px; }
iframe {
border: 0;
}
header {
min-height: 100vh;
padding: 5em;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
}
header h1 {
max-width: 600px;
}
header a {
opacity: 0.7;
font-size: 1.2em;
color: white;
text-decoration: none;
font-weight: 300;
border: 1px solid white;
border-radius: 4px;
max-width: 280px;
margin-top: 2em;
padding: 1em 2em;
text-align: center;
display: inline-block;
line-height: 1.2em;
transition: opacity 0.3s;
}
header a:hover,
header a:focus {
opacity: 1;
transition: opacity 0.3s;
}
figure {
margin-top: -60px;
display: flex;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
}
figure svg { height: 60px; width: 40px; }
footer {
padding: 5em;
}
footer a {
opacity: 0.7;
font-size: 1em;
color: white;
text-decoration: none;
font-weight: 300;
border: 1px solid white;
border-radius: 4px;
max-width: 280px;
margin-top: 2em;
padding: 0.6em 1em;
text-align: center;
display: inline-block;
line-height: 1.2em;
transition: opacity 0.3s;
}
footer a:hover,
footer a:focus {
opacity: 1;
transition: opacity 0.3s;
}
@media only screen and (min-width: 767px) {
header {
font-size: 16px;
}
}
</style>
</head>
<body>
<header>
<h1>HTML emails & email signatures should be easier than this.</h1>
<a href="https://github.com/fadeit/responsive-html-email-signature">Get started on Github</a>
</header>
<figure>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<path fill="white" d="M349.7,322.2c-3.1-3.1-8-3-11.3,0L264,388.6V104c0-4.4-3.6-8-8-8c-4.4,0-8,3.6-8,8v284.6l-74.4-66.3
c-3.4-2.9-8.1-3.2-11.2-0.1c-3.1,3.1-3.3,8.5-0.1,11.4c0,0,87,79.2,88,80s2.8,2.4,5.7,2.4s4.9-1.6,5.7-2.4s88-80,88-80
c1.5-1.5,2.3-3.6,2.3-5.7C352,325.8,351.2,323.8,349.7,322.2z"/>
</svg>
</figure>
<main>
<img src="https://cloud.githubusercontent.com/assets/1515742/10591901/139c4954-76b9-11e5-80f7-5b0ccaf5af81.png" alt="Email / email signature HTML template dark">
<img src="https://cloud.githubusercontent.com/assets/1515742/10591900/13889d32-76b9-11e5-8dc0-b89d80189e93.png" alt="Email / email signature HTML template white">
</main>
<footer>
<h3>HTML emails, please.</h3>
<p>
Grab the code on Github to simplify how HTML emails are built.
</p>
<a href="https://github.com/fadeit/responsive-html-email-signature">Get started on Github</a>
</footer>
<!-- can do demos later! -->
<!-- <iframe class="dark" src="./dist/dark/signature-dark.html"></iframe>
<iframe class="light" src="./dist/light/signature-light.html"></iframe> -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/webfont/1.6.27/webfontloader.js"></script>
<script>
WebFontLoader.load({
active () {
const body = document.querySelector('body')
if (body) body.style.fontFamily = 'Roboto, -apple-system, BlinkMacSystemFont, Arial, sans-serif'
}
google: {
families: ['Roboto:300,400']
}
})
</script>
</body>
</html>

View File

@ -1,21 +0,0 @@
/*
* NB: for images, if you pass in a relative path ('assets/logo.png'), they will be automatically embedded (base64). If you pass in a URL, it will stay as it is.
*/
module.exports = [
{
id: 'ONEstore', // Will be appended to the built templates.
// signature: '감사합니다.',
name: '0 0 0 매니저',
team: '스토어 기획팀',
title: 'Dev Relations',
welcome: '안녕하세요.',
introParagraph: 'Thanks for writing up this email.<br/> We are delighted to reply with a responsive template.',
contactMain: '<a href="tel:+821012345678"><span>+82-10-1234-5678</span></a> | ', // Wrap phone numbers like this to prevent iOS mail from making them blue.
contactMail: 'devhelper@onestore.co.kr',
contactSecondary: '성남시 분당구 판교역로 188 SK플래닛 건물 11층',
logoUrl: 'assets/type01.png',
logoAlt: 'Onestore logo',
website: 'http://onesto.re/'
}
];

View File

@ -1,22 +0,0 @@
/*
* NB: for images, if you pass in a relative path ('assets/logo.png'), they will be automatically embedded (base64). If you pass in a URL, it will stay as it is.
*/
module.exports = [
{
id: 'ONEstoreBlack', // Will be appended to the built templates.
signature: 'Best regards,',
name: 'Joo Hyung Park',
team: 'Service Planning Team',
title: 'Dev Relations',
welcome: '안녕하세요.',
introParagraph: 'Thanks for writing up this email.<br/> We are delighted to reply with a responsive template.',
contactMain: 'Call <a href="tel:+821012345678"><span>+82-10-1234-5678</span></a> or email us at', // Wrap phone numbers like this to prevent iOS mail from making them blue.
contactMail: 'devhelper@onestore.co.kr',
// slogan: '\"The 1st integrated Android App store in Korea\"',
contactSecondary: '188, Pangyoyeok-ro, Bundang-gu, Seongnam-si, Gyeonggi-do, Korea',
logoUrl: 'assets/type03.png',
logoAlt: 'ONEstore. logo',
website: 'http://onesto.re/'
}
];

View File

@ -1,19 +0,0 @@
/*
* NB: for images, if you pass in a relative path ('assets/logo.png'), they will be automatically embedded (base64). If you pass in a URL, it will stay as it is.
*/
module.exports = [
{
id: 'fadeit', // Will be appended to the built templates.
signature: 'Yours truly,',
name: 'Jane Whatsmyname',
welcome: 'Hi there,',
introParagraph: 'Thanks for writing up this email.<br/> We are delighted to reply with a responsive template.',
contactMain: 'Call <a href="tel:81100200"><span>81100200</span></a> or email us at', // Wrap phone numbers like this to prevent iOS mail from making them blue.
contactMail: 'info@fadeit.dk',
contactSecondary: 'Anelystparken 31, DK-8381 Tilst, Aarhus',
logoUrl: 'http://fadeit.dk/src/assets/img/brand/fadeit-logo.png',
logoAlt: 'fadeit logo',
website: 'http://fadeit.dk'
}
];

View File

@ -1,13 +0,0 @@
module.exports = [
{
id: 'play',
signature: 'Best regards,',
name: 'Jane Whatsmyname',
contactMain: 'Call <a href="tel:004581100200"><span>(45) 81100200</span></a> or email us at', // Wrap phone numbers like this to prevent iOS mail from making them blue.
contactMail: 'info@tryplay.dk',
slogan: 'LED Pylon. LED Wall. Digital Signage.',
logoUrl: 'https://informationscreen.com/manage/assets/images/play-logo.png',
logoAlt: 'Play. logo',
website: 'http://tryplay.dk'
}
];

View File

@ -1,30 +1,50 @@
'use strict'; const gulp = require('gulp');
const plumber = require('gulp-plumber');
var gulp = require('gulp'), const { SOURCE_DIR, DIST_DIR, WORKING_DIR, CONFIGURATION_FILE } = require('./constants');
wrench = require('wrench'),
plumber = require('gulp-plumber');
var options = { const options = {
source: 'src', sourceDir: SOURCE_DIR,
dist: 'dist', distDir: DIST_DIR,
workingDir: 'tmp', workingDir: WORKING_DIR,
src: function plumbedSrc(){ configurationFile: CONFIGURATION_FILE,
src: function plumbedSrc() {
return gulp.src.apply(gulp, arguments).pipe(plumber()); return gulp.src.apply(gulp, arguments).pipe(plumber());
} }
}; };
/** Load tasks from the '/tasks' directory. /**
* Look for .js & .coffee files. * Load tasks from the '/tasks' directory.
* Each file should correspond to a task.
*/ */
wrench require('./tasks/build')(options);
.readdirSyncRecursive('./tasks') require('./tasks/dupe')(options);
.filter(function readJSFiles(file) { require('./tasks/less')(options);
return (/\.(js|coffee)$/i).test(file); require('./tasks/lint')(options);
}) require('./tasks/postcss')(options);
.map(function loadTasks(file) { require('./tasks/sass')(options);
require('./tasks/' + file)(options); require('./tasks/check-for-unused').checkForUnusedTask(options);
});
/** By default templates will be built into '/dist', then gulp will watch for changes in '/src'. */ /* Runs the entire pipeline once. */
gulp.task('default', ['dupe', 'less', 'sass', 'postcss', 'build', 'watch']); gulp.task(
'run-pipeline',
gulp.series('dupe', 'less', 'sass', 'postcss', 'lint', 'build', gulp.parallel('check-for-unused'))
);
/* By default templates will be built into '/dist'. */
gulp.task(
'default',
gulp.series('run-pipeline', () => {
/* gulp will watch for changes in '/templates'. */
gulp.watch(
[
options.sourceDir + '/**/*.html',
options.sourceDir + '/**/*.css',
options.sourceDir + '/**/*.scss',
options.sourceDir + '/**/*.less',
options.sourceDir + '/**/conf.json'
],
{ delay: 500 },
gulp.series('run-pipeline')
);
})
);

19
i18n/README.md Normal file
View File

@ -0,0 +1,19 @@
# Translated docs
The [main README](https://github.com/fadeit/responsive-html-email-signature) is always going to be the most up-to-date, so if something doesn't add up, take a look there.
*Available translations are:*
- [Korean](ko-KR.md) by [JooHyung Park](https://github.com/dusskapark)
<!-- SOON! - [Romanian](ro-RO.md) by [Dan Mindru](https://github.com/danmindru) -->
## Contributing
You're very welcome to contribute & maintain translations.
Mini-guide:
1. Fork the repo
2. Create a translation file and name it (i18n standard format, e.g. en-US)
3. Put this file in the i18n folder
4. Translate the original English version to be current with the latest changes
5. Add a new line to this file with a link to your translation
6. Make a Pull Request

View File

@ -14,10 +14,7 @@
![responsive emails-02](https://cloud.githubusercontent.com/assets/1515742/10591901/139c4954-76b9-11e5-80f7-5b0ccaf5af81.png) ![responsive emails-02](https://cloud.githubusercontent.com/assets/1515742/10591901/139c4954-76b9-11e5-80f7-5b0ccaf5af81.png)
## 만들게된 동기 (Motivation) ## 만들게된 동기 (Motivation)
이메일용 html은 일반적인 HTML과 달리 모든 코드를 in-line CSS로 만들어야 합니다. 그래서 이메일 템플릿이랑 서명을 만들기가 굉장히 어렵습니다. "이런 HTML 템플릿과 서명을 편하게 만들어보자!" 라는 생각에서 이 템플릿을 만들었습니다. 물론 모든 이메일 클라이언트의 이슈를 전부 다 고칠 수는 없지만, 이 템플릿을 활용해서 조금 더 쉽고 깔끔한 반응형 이메일을 만들어서 받는 사람 보내는 사람 모두가 만족하게 만들 수 있습니다! 이메일용 html은 일반적인 HTML과 달리 모든 코드를 in-line CSS로 만들어야 합니다. 그래서 이메일 템플릿이랑 서명을 만들기가 굉장히 어렵습니다. "이런 HTML 템플릿과 서명을 편하게 만들어보자!" 라는 생각에서 이 템플릿을 만들었습니다. 물론 모든 이메일 클라이언트의 이슈를 전부 다 고칠 수는 없지만, 이 템플릿을 활용해서 조금 더 쉽고 깔끔한 반응형 이메일을 만들어서 받는 사람 보내는 사람 모두가 만족하게 만들 수 있습니다!
이 [블로그 포스팅](http://fadeit.dk/blog/post/html-emails-and-email-signatures-how-hard-can-it-be)에서 fadeit 의 더 많은 이애기를 들어보세요.
## 이 템플릿에서 무엇을 할 수 있나? (What does it do?) ## 이 템플릿에서 무엇을 할 수 있나? (What does it do?)
- [x] config-based template generation - [x] config-based template generation
@ -38,7 +35,7 @@ $ npm install
$ gulp $ gulp
``` ```
`src/fadeit/` 를 확인해보시면 멋진 2개의 이메일 템플릿 샘플이 있습니다. 폴더를 통째로 다른 이름으로 복사/붙여넣기 하시고 그 안에 있는 `src/fadeit/conf.js` 파일을 입맛대로 바꿔보세요. 다음으로는 `gulp`를 실행해서 여러분 만의 이메일 템플릿을 빌드하세요. gulp 의 task가 동작하면서 기본 html CSS 파일을 확인하고 새로운 html 파일을 `/dist`에 저장합니다. `src/dark/` 를 확인해보시면 멋진 2개의 이메일 템플릿 샘플이 있습니다. 폴더를 통째로 다른 이름으로 복사/붙여넣기 하시고 그 안에 있는 `src/dark/conf.js` 파일을 입맛대로 바꿔보세요. 다음으로는 `gulp`를 실행해서 여러분 만의 이메일 템플릿을 빌드하세요. gulp 의 task가 동작하면서 기본 html CSS 파일을 확인하고 새로운 html 파일을 `/dist`에 저장합니다.
## 살펴보기 (Overview) ## 살펴보기 (Overview)
아래 순서도는 여러분이 만든 템플릿이 어떻게 빌드가 되는지를 보여줍니다. 아래 순서도는 여러분이 만든 템플릿이 어떻게 빌드가 되는지를 보여줍니다.
@ -61,15 +58,15 @@ $ gulp
## 다른 이메일 클라이언트에서 사용하는 법 (Usage with different e-mail clients) ## 다른 이메일 클라이언트에서 사용하는 법 (Usage with different e-mail clients)
### Thunderbird ### Thunderbird
썬더버드 이메일 클라이언트에 html 서명을 자동으로 넣어주는 몇몇 플러그인이 있스빈다. 일단 우리는 [SmartTemplate4](https://addons.mozilla.org/en-us/thunderbird/addon/smarttemplate4) 를 추천합니다. 이 플러그인을 쓰면 새 이메일, 답장, 전달 등을 할 때마다 각각 다른 이메일 템플릿을 쓸 수 있습니다. 썬더버드 이메일 클라이언트에 html 서명을 자동으로 넣어주는 몇몇 플러그인이 있스빈다. 일단 우리는 [SmartTemplate4](https://addons.mozilla.org/en-us/thunderbird/addon/smarttemplate4) 를 추천합니다. 이 플러그인을 쓰면 새 이메일, 답장, 전달 등을 할 때마다 각각 다른 이메일 템플릿을 쓸 수 있습니다.
### Apple Mail / OS X (꼭.. 써야 한다면?) ### Apple Mail / OS X (꼭.. 써야 한다면?)
#### 방법 1 #### 방법 1
- 이메일 앱을 열고 `Mail` -> `Preferences` -> `Signatures` 로 이동하세요. - 이메일 앱을 열고 `Mail` -> `Preferences` -> `Signatures` 로 이동하세요.
- 아무거나 새 이메일 서명을 만들세요. (나중에 찾기 편하게 placeholder 를 써두는 것이 좋습니다.) - 아무거나 새 이메일 서명을 만들세요. (나중에 찾기 편하게 placeholder 를 써두는 것이 좋습니다.)
- 이메일을 닫으세요. - 이메일을 닫으세요.
- 터미널을 열고 TextEdit으로 서명 파일을 열어보세요. (iCloud drive를 사용하는 경우는 잘 작동하지 않는 경우가 있습니다. 아래 troubleshooting을 참고하세요. ) - 터미널을 열고 TextEdit으로 서명 파일을 열어보세요. (iCloud drive를 사용하는 경우는 잘 작동하지 않는 경우가 있습니다. 아래 troubleshooting을 참고하세요. )
``` ```
@ -79,16 +76,16 @@ $ open -a TextEdit ~/Library/Mobile\ Documents/com~apple~mail/Data/V3/MailData/S
- `<body>...</body>` 의 모든 html 코드를 여러분이 빌드한 `/dist/`의 html 파일로 교체 합니다. (`<body>`외에 다른 메타 정보는 수정하지 마세요!) - `<body>...</body>` 의 모든 html 코드를 여러분이 빌드한 `/dist/`의 html 파일로 교체 합니다. (`<body>`외에 다른 메타 정보는 수정하지 마세요!)
- 메일앱을 다시 실행하고 새로 바뀐 서명을 확인해보세요 :) - 메일앱을 다시 실행하고 새로 바뀐 서명을 확인해보세요 :)
> **주의**: 이미지 파일은 원래 `preference`의 미리보기 화면에서는 나오지 않습니다. 하지만 이메일을 만들고 발송하는 곳에서는 잘 보여지니깐 걱정마세요! > **주의**: 이미지 파일은 원래 `preference`의 미리보기 화면에서는 나오지 않습니다. 하지만 이메일을 만들고 발송하는 곳에서는 잘 보여지니깐 걱정마세요!
#### 방법 2 #### 방법 2
다른 방법으로는 `/dist` 에 빌드한 html 파일을 CMD + A, CMD + C 해서 설정에 이메일 서명 박스에 붙여넣기 해서 쓸 수도 있습니다. 하지만 이렇게 복붙을 하는 경우 파일의 `<html>` 이나 반응형 웹을 위한 미디어쿼리가 들어있는 `<style>` 부분을 복붙하는 것이 아니라 정상적으로 작동하지 않을 수 있습니다. 다른 방법으로는 `/dist` 에 빌드한 html 파일을 CMD + A, CMD + C 해서 설정에 이메일 서명 박스에 붙여넣기 해서 쓸 수도 있습니다. 하지만 이렇게 복붙을 하는 경우 파일의 `<html>` 이나 반응형 웹을 위한 미디어쿼리가 들어있는 `<style>` 부분을 복붙하는 것이 아니라 정상적으로 작동하지 않을 수 있습니다.
#### Troubleshooting #### Troubleshooting
만약 위의 방법 1이 정상적으로 작동하지 않는다면 파일을 수정 후 메일 앱을 열기전에 아래 방법으로 파일을 잠궈야 합니다. 만약 위의 방법 1이 정상적으로 작동하지 않는다면 파일을 수정 후 메일 앱을 열기전에 아래 방법으로 파일을 잠궈야 합니다.
Lock Files: Lock Files:
``` ```
@ -104,39 +101,30 @@ $ chflags nouchg ~/Library/Mail/V3/MailData/Signatures/*.mailsignature
### 아웃룩 2010에서 사용하기 (Outlook 2010) ### 아웃룩 2010에서 사용하기 (Outlook 2010)
#### 방법 1 #### 방법 1
- 아웃룩 2010을 켜고 `File > Option > Mail > Signature` 으로 접속하세요. - 아웃룩 2010을 켜고 `File > Option > Mail > Signature` 으로 접속하세요.
- 새 서명을 만드세요. (나중에 확인하기 좋게, 표시를 해두시면 좋습니다.) - 새 서명을 만드세요. (나중에 확인하기 좋게, 표시를 해두시면 좋습니다.)
- CMD를 켜고 아래와 같이 입력하세요. - CMD를 켜고 아래와 같이 입력하세요.
> AppData 폴더가 숨김 폴더라 가급적이면 CMD를 사용하는 것을 권장합니다. > AppData 폴더가 숨김 폴더라 가급적이면 CMD를 사용하는 것을 권장합니다.
``` ```
cd AppData\Roamin\Microsoft cd AppData\Roamin\Microsoft
start Signatures start Signatures
``` ```
- 이 폴더에서 아까 만든 서명파일을 찾아서 우클릭해서 편집을 하세요. - 이 폴더에서 아까 만든 서명파일을 찾아서 우클릭해서 편집을 하세요.
- 모든 HTML코드를 여러분이 새로 만든 코드로 바꿔버리세요. - 모든 HTML코드를 여러분이 새로 만든 코드로 바꿔버리세요.
- 아웃룩을 열고 서명이 적용됐는지 확인해주세요. - 아웃룩을 열고 서명이 적용됐는지 확인해주세요.
> **주의** 위 방법으로 진행할 경우, 한글 등 유니코드가 깨지는 문제가 있습니다. 한글 이메일 서명의 경우 아래 **방법2**을 사용하시기 바랍니다. > **주의** 위 방법으로 진행할 경우, 한글 등 유니코드가 깨지는 문제가 있습니다. 한글 이메일 서명의 경우 아래 **방법2**을 사용하시기 바랍니다.
#### 방법 2 #### 방법 2
아웃룩에서는 html 파일을 넣는 방법을 제공하지 않습니다. (아...) 그래서 위의 **방법 2** 처럼 파일을 복사/붙여넣기 하는 방법으로 사용할 수 있습니다. 아웃룩에서는 html 파일을 넣는 방법을 제공하지 않습니다. (아...) 그래서 위의 **방법 2** 처럼 파일을 복사/붙여넣기 하는 방법으로 사용할 수 있습니다.
- `/dist` 에 빌드한 html 파일을 브라우저에서 열고, ctrl A + C 로 복사하세요. - `/dist` 에 빌드한 html 파일을 브라우저에서 열고, ctrl A + C 로 복사하세요.
- Outlook 을 열고 `파일 > 옵션 > 메일 > 서명` 으로 들어가세요. - Outlook 을 열고 `파일 > 옵션 > 메일 > 서명` 으로 들어가세요.
- 새 서명을 만들고 아래 박스에 Ctrl + V 로 파일을 붙여넣기 하세요. - 새 서명을 만들고 아래 박스에 Ctrl + V 로 파일을 붙여넣기 하세요.
> base 64로 만든 이메일 파일은 잘 작동하지 않을 수 있습니다. 외부 링크를 활용하시기를 권장합니다. > base 64로 만든 이메일 파일은 잘 작동하지 않을 수 있습니다. 외부 링크를 활용하시기를 권장합니다.
===================
<br/>
<a href="http:fadeit.dk"><img src="http://fadeit.dk/src/assets/img/brand/fadeit_logo_full.svg" alt="The fadeit logo" style="width:200px;"/></a><br/><br/>
####About fadeit
We build awesome software, web and mobile applications.
See more at [fadeit.dk](http://fadeit.dk)

0
i18n/ro-RO.md Normal file
View File

28783
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,45 +1,94 @@
{ {
"name": "responsive-html-email-signature", "name": "responsive-html-email-signature",
"version": "4.0.0-rc.1", "version": "6.1.0",
"description": "Responsive template for email signatures", "description": "Responsive template for emails & email signatures.",
"main": "index.js", "main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/fadeit/responsive-html-email-signature.git" "url": "git+https://github.com/danmindru/responsive-html-email-signature.git"
}, },
"keywords": [ "keywords": [
"responsive", "responsive",
"template", "template",
"email", "email",
"signature" "signature",
"email-signatures",
"inline-styles",
"watches-html"
], ],
"author": "Dan Mindru <mindrudan@gmail.com> (http://mindrudan.com/)", "author": "Dan Mindru <mindrudan@gmail.com> (https://mindrudan.com/)",
"license": "MIT", "license": "MIT",
"bugs": { "bugs": {
"url": "https://github.com/fadeit/responsive-html-email-signature/issues" "url": "https://github.com/danmindru/responsive-html-email-signature/issues"
}, },
"homepage": "https://github.com/fadeit/responsive-html-email-signature#readme", "homepage": "https://github.com/danmindru/responsive-html-email-signature#readme",
"devDependencies": { "scripts": {
"autoprefixer": "^6.3.6", "start": "./node_modules/.bin/gulp",
"del": "^2.2.0", "once": "./node_modules/.bin/gulp run-pipeline",
"gulp": "~3.9.1", "deploy": "npm run test && cp -r dist demo && git push origin `git subtree split --prefix demo develop`:gh-pages --force",
"gulp-autoprefixer": "^3.1.0", "test": "npm run once && npm run _test",
"gulp-david": "~0.4.2", "test:watch": "npm run once && npm run _test:watch",
"gulp-inline-css": "~3.1.0", "format": "./node_modules/.bin/prettier {tasks,tests}/**/*.js gulpfile.js .eslintrc.js --write",
"gulp-inline-image-html": "~0.2.1", "lint": "./node_modules/.bin/eslint ./**/*.js gulpfile.js",
"gulp-less": "^3.1.0", "_test": "./node_modules/.bin/ava",
"_test:watch": "./node_modules/.bin/ava --watch"
},
"dependencies": {
"autoprefixer": "^9.6.1",
"chalk": "^2.4.2",
"cheerio": "^0.22.0",
"del": "^5.1.0",
"gulp": "^4.0.2",
"gulp-autoprefixer": "^7.0.1",
"gulp-david": "^1.0.1",
"gulp-inline-css": "^3.5.0",
"gulp-inline-images-no-http": "^1.3.3",
"gulp-jsonlint": "^1.3.2",
"gulp-less": "^4.0.1",
"gulp-minify-html": "~1.0.5", "gulp-minify-html": "~1.0.5",
"gulp-minify-inline": "~0.2.1", "gulp-minify-inline": "^1.1.0",
"gulp-plumber": "^1.1.0", "gulp-plumber": "^1.2.1",
"gulp-postcss": "^6.1.1", "gulp-postcss": "^8.0.0",
"gulp-preprocess": "~2.0.0", "gulp-preprocess": "^3.0.3",
"gulp-rename": "^1.2.2", "gulp-rename": "^2.0.0",
"gulp-sass": "^2.3.1", "gulp-sass": "^4.1.0",
"q": "~1.4.1", "klaw": "^3.0.0",
"wrench": "~1.5.8" "node-sass": "^7.0.0",
"plugin-error": "^1.0.1",
"through2": "^2.0.5"
}, },
"dependencies": {} "devDependencies": {
"ava": "^2.4.0",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.11.0",
"eslint-config-standard": "^14.1.1",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^3.1.4",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
"graceful-fs": "^4.2.4",
"gulp-cli": "^2.3.0",
"opencollective-postinstall": "^2.0.3",
"prettier": "^1.19.1",
"pretty-quick": "^2.0.1"
},
"resolutions": {
"graceful-fs": "^4.2.4",
"vinyl-fs": "^3.0.3"
},
"browserslist": [
"last 5 versions"
],
"husky": {
"hooks": {
"pre-push": "npm run test",
"pre-commit": "npm run lint && node ./node_modules/.bin/pretty-quick --staged --pattern ./**/*.js"
}
},
"ava": {
"helpers": [
"**/util.js"
]
}
} }

View File

@ -1,21 +0,0 @@
/**
* @notes
*
* - for images, if you pass in a relative path ('assets/logo.png'), they will be automatically embedded (base64). If you pass in a URL, it will stay as it is.
* - the 'id' will be appended to the built template names, e.g. '/dist/signature-dark.html'
* - wrap phone numbers like this to prevent iOS mail from making them blue, e.g. `<a href="tel:004580100100"><span>(45) 80100100</span></a>`
*
*/
module.exports = [
{
id: 'dark',
signature: 'Best regards,',
name: 'The dark mail team',
contactMain: 'Call <a href="tel:004580100100"><span>(45) 80100100</span></a> or email us at',
contactMail: 'info@dark.dk',
slogan: 'LED Pylon. LED Wall. Digital Signage.',
logoUrl: '/assets/dark.png',
logoAlt: 'dark logo',
website: 'http://dark.dk'
}
];

View File

@ -1,25 +0,0 @@
/**
* @notes
*
* - for images, if you pass in a relative path ('assets/logo.png'), they will be automatically embedded (base64). If you pass in a URL, it will stay as it is.
* - the 'id' will be appended to the built template names, e.g. '/dist/signature-light.html'
* - wrap phone numbers like this to prevent iOS mail from making them blue, e.g. `<a href="tel:004580100100"><span>(45) 80100100</span></a>`
* - wrap address like this to prevent iOS mail from making it blue, e.g. `<a href="https://mapurl.com"><span>Address</span></a>`
*
*/
module.exports = [
{
id: 'light',
signature: 'Yours truly,',
name: 'The light mail team',
welcome: 'Hi there,',
introParagraph: 'Thanks for writing up this email.<br/> We are delighted to reply with a responsive template.',
contactMain: 'Call <a href="tel:80100100"><span>80100100</span></a> or email us at',
contactMail: 'info@light.dk',
contactSecondary: '<a href="https://mapurl.com"><span>Happy Steet 31, DK-8000 Aarhus C, Denmark</span></a>',
logoUrl: '/assets/light.png',
logoAlt: 'light logo',
website: 'http://light.dk'
}
];

View File

@ -1,163 +0,0 @@
/*
* =====================================
* 1. Common styles for general table things.
* =====================================
*/
img {
max-width: 100%;
}
body {
-webkit-font-smoothing: antialiased;
-webkit-text-size-adjust: none;
}
tr{
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
box-sizing: border-box;
font-size: 14px;
line-height: 20px;
}
.rbcc{
/*
* rbcc -> reset - border - cellspacing - cellpading
*
* Resets table attributes.
*/
border:0;
cellpadding:0;
cellspacing:0;
}
.background{
width:100%;
}
.main{
width:100%;
background-color: #ffffff;
padding-top:15px;
}
.sp{ /* Separator tr; props are actually contained by it's inner element atm. */ }
.sp__inner{
padding: 15px 0;
}
.gray-hr{ /* 100% width light grey line; props are actually contained by inner elements atm. */ }
.gray-hr td{
width: 100%;
}
.gray-hr hr{
border-bottom:1px solid #E4E4E4;
border-top:none;
margin-bottom:20px;
margin-top:20px;
color: transparent;
background:transparent;
}
a{
text-decoration: none;
color: #0fade1;
}
/*
* =================
* 2. Content styles.
* ==================
*/
.main__welcome{
color: #000;
padding: 10px 30px 0 30px;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 22px;
}
.main__content{
color: #000;
padding: 10px 30px 0 30px;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 14px;
}
/*
* ================
* 3. Footer styles.
* ================
*/
.footer{
background-color: #f5f5f5;
padding: 20px 30px 0px 30px;
color: #888;
border-top: 8px solid #EAEAEA;
}
.footer a{
color: #888;
}
.footer--simple{
padding-bottom: 20px;
background-color: #FFFFFF;
}
.footer__main{
/* NB: This prop fucks up the width on OS X, needs to be *JUST* attribute. */
width:100%;
}
.footer__main__signature{
font-size: 14px;
color: #888;
/* @todo gulp-inline-css doesn't parse align */
align:left;
}
.footer__main__col1{
width:70%;
margin-bottom:30px;
/* @todo gulp-inline-css doesn't parse align */
align:left;
}
.footer__main__col1__td{
font-size: 14px;
color: #888;
/* @todo gulp-inline-css doesn't parse align */
align:left;
}
.footer__main a > span{
/* Revert apple blue-link style. */
color: #888!important;
text-decoration:none!important;
}
.footer__main__col2{
width:30%;
/* @todo gulp-inline-css doesn't parse align */
align:right;
}
.footer__main__col2__td{
font-size: 14px;
color: #888;
/* @todo gulp-inline-css doesn't parse align */
align:right;
}
.footer__main__col2__td__img{
border: 0;
padding-top: 6px;
padding-left:10px;
max-width: 100%;
max-height:38px;
height: auto;
}

View File

@ -1,89 +1,77 @@
'use strict'; const gulp = require('gulp');
const inlineCss = require('gulp-inline-css');
const minifyHTML = require('gulp-minify-html');
const minifyInline = require('gulp-minify-inline');
const preprocess = require('gulp-preprocess');
const rename = require('gulp-rename');
const del = require('del');
const { inlineImg } = require('./check-for-image-url');
const { getConfigsForDir, getFilePathsForDir, getCssLinkTagsFromFilelist } = require('./util/util');
var gulp = require('gulp'), function buildTask(options) {
inlineCss = require('gulp-inline-css'), // Requires: 'dupe', 'less', 'sass', 'postcss', 'lint'.
minifyHTML = require('gulp-minify-html'), gulp.task('build', function build(done) {
minifyInline = require('gulp-minify-inline'), /**
preprocess = require('gulp-preprocess'), * Makes templates for a given directory & its configurations.
rename = require('gulp-rename'), *
wrench = require('wrench'),
Q = require('q'),
del = require('del'),
inlineimg = require('gulp-inline-image-html');
function buildTask(options){
gulp.task('build', ['dupe', 'less', 'sass', 'postcss'], function build() {
var promises = [];
/** Makes templates for a given directory & its configurations.
* @function makeTemplates * @function makeTemplates
* @param {String} dir Directory to make templates from. * @param {String} dir Directory to make templates from.
* @param {Array} confItems A list of configurations objects (usually persons) to make templates from. * @param {Array} confItems A list of configurations objects (usually persons) to make templates from.
*/ */
function makeTemplates(dir, confItems){ function makeTemplates(dir, confItems) {
confItems return confItems.map(async conf => {
.forEach(function handleConf(conf){ const cwd = `${options.workingDir}/${dir}`;
var cwd = options.workingDir + '/' + dir;
/** /**
* Find stylesheets relative to the CWD & generate <link> tags. * Find stylesheets relative to the CWD & generate <link> tags.
* This way we can automagically inject them into <head>. * This way we can automagically inject them into <head>.
*/ */
conf.stylesheets = wrench const files = await getFilePathsForDir(cwd);
.readdirSyncRecursive(cwd) const context = Object.assign(conf, {
.filter(function filterFiles(file) { stylesheets: getCssLinkTagsFromFilelist(files)
/* Read only CSS files. */ });
return (file.match(/.*\.css/)) ? file : false;
})
.reduce(function(prev, current, index, acc){
return prev += '<link rel="stylesheet" href="' + current + '">';
}, '');
options return options
.src([cwd + '/**/*.html', '!' + cwd + '/**/*.inc.html']) .src([cwd + '/**/*.html', '!' + cwd + '/**/*.inc.html'])
.pipe(preprocess({ .pipe(preprocess({ context }))
context: conf .pipe(inlineImg({ getHTTP: confItems[0]['inlineRemoteUrl'] }))
})) .pipe(
.pipe(inlineimg(cwd)) inlineCss({
.pipe(inlineCss({
applyTableAttributes: true, applyTableAttributes: true,
applyWidthAttributes: true, applyWidthAttributes: true,
preserveMediaQueries: true, preserveMediaQueries: true,
removeStyleTags: false removeStyleTags: false
})) })
.pipe(minifyHTML({quotes: true})) )
.pipe(minifyInline()) .pipe(minifyHTML({ quotes: true }))
.pipe(rename(function rename(path){ .pipe(minifyInline())
.pipe(
rename(function rename(path) {
path.dirname = dir; path.dirname = dir;
path.basename += '-' + conf.id; path.basename += '-' + conf.id;
return path; return path;
})) })
.pipe(gulp.dest(options.dist)); )
}); .pipe(gulp.dest(options.distDir));
});
} }
/** Clean up & then read 'src' to generate templates (build entry point). */ /*
del(options.dist).then(function buildStart(){ * Clean up & then read from workingDir to generate templates.
/** * For each found config, a template group will be generated through `makeTemplates`.
* Loop through dirs and load their conf files. */
* Promisify all 'makeTemplate' calls and when resolved, make a call to the task `cb` to let gulp know we're done. return del(options.distDir)
*/ .then(() => {
wrench /**
.readdirSyncRecursive('./' + options.workingDir) * Loop through dirs and load their conf files.
.filter(function filterFiles(file) { * Promisify all 'makeTemplate' calls and when resolved, let gulp know we're done.
/* Read only folders, skip files. */ */
return (!file.match('/') && !file.match(/^\.+/g)) ? file : false; const configs = getConfigsForDir(options.workingDir, options.configurationFile);
}) return Promise.all(configs.map(({ dir, confItems }) => makeTemplates(dir, confItems)));
.forEach(function readConfigurations(dir){ })
/** NB: For 'watch' to properly work, the cache needs to be deleted before each require. */ .then(() => done())
var confPath = './../' + options.workingDir + '/' + dir + '/conf.js'; .catch(err => console.log(err));
delete require.cache[require.resolve(confPath)];
promises.push(makeTemplates(dir, require(confPath)));
});
Q.all(promises);
});
}); });
} }
module.exports = buildTask; module.exports = buildTask;

View File

@ -1,14 +0,0 @@
'use strict';
var gulp = require('gulp'),
david = require('gulp-david');
function checkDepsTask(){
gulp.task('check-deps', function checkDeps(){
gulp
.src('package.json')
.pipe(david());
});
}
module.exports = checkDepsTask;

View File

@ -0,0 +1,155 @@
'use strict';
const https = require('https');
const http = require('http');
const path = require('path');
const url = require('url');
const fs = require('fs');
const PluginError = require('plugin-error');
const through = require('through2');
const cheerio = require('cheerio');
const { log } = require('./util/util');
const PLUGIN_NAME = 'gulp-inline-images';
const MIME_TYPE_REGEX = /.+\/([^\s]*)/;
const INLINE_ATTR = 'inline';
const NOT_INLINE_ATTR = `!${INLINE_ATTR}`;
function inlineImg(options = {}) {
const selector = options.selector || 'img[src]';
const attribute = options.attribute || 'src';
const getHTTP = options.getHTTP || false;
return through.obj(function(file, encoding, callback) {
if (file.isStream()) {
this.emit('error', new PluginError(PLUGIN_NAME, 'Streams are not supported!'));
return callback();
}
if (file.isBuffer()) {
const contents = file.contents.toString(encoding);
// Load it into cheerio's virtual DOM for easy manipulation
const $ = cheerio.load(contents);
const inlineFlag = $(`img[${INLINE_ATTR}]`);
// If images with an inline attr are found that is the selection we want
const imgTags = inlineFlag.length ? inlineFlag : $(selector);
let count = 0;
imgTags.each(function() {
const $img = $(this);
const src = $img.attr(attribute);
// Save the file format from the extension
const extFormat = path.extname(src).substr(1);
// If inlineFlag tags were found we want to remove the inline tag
if (inlineFlag.length) {
$img.removeAttr(INLINE_ATTR);
}
// Find !inline attribute
const notInlineFlag = $img.attr(NOT_INLINE_ATTR);
if (typeof notInlineFlag !== typeof undefined && notInlineFlag !== false) {
// Remove the tag and don't process this file
return $img.removeAttr(NOT_INLINE_ATTR);
}
// Count async ops
count++;
getSrcBase64(options.basedir || file.base, getHTTP, src, (err, result, resFormat, skipFormatting) => {
if (err) {
log.warn(`Failed to load http image. Check the format of ${src}.`);
log.error(err);
} else {
// Need a format in and a result for this to work
if (!skipFormatting) {
if (result && (extFormat || resFormat)) {
$img.attr('src', `data:image/${extFormat};base64,${result}`);
} else {
$img.attr('src', ``);
$img.attr('alt', `Image not found, Please check Url`);
log.warn(`Failed to read image. Check the format of ${src}.`);
}
}
if (!--count) {
file.contents = Buffer.from($.html());
callback(null, file);
}
}
});
});
// If no files are processing we don't need to wait as none were ever started
if (!imgTags.length) {
file.contents = Buffer.from($.html());
callback(null, file);
}
}
});
}
function getHTTPBase64(url, callback) {
// Get applicable library
const lib = url.startsWith('https') ? https : http;
// Initiate a git request to our URL
const req = lib.get(url, res => {
// Check for redirect
if (res.statusCode >= 301 && res.statusCode < 400 && res.headers.location) {
// Redirect
return getHTTPBase64(res.headers.location, callback);
}
// Check for HTTP errors
if (res.statusCode < 200 || res.statusCode >= 400) {
return callback(new Error('Failed to load page, status code: ' + res.statusCode));
}
// Get file format
let format;
if (res.headers['content-type']) {
const matches = res.headers['content-type'].match(MIME_TYPE_REGEX);
if (matches) {
format = matches[1];
}
}
// Create an empty buffer to store the body in
let body = Buffer.from([]);
// Append each chunk to the body
res.on('data', chunk => (body = Buffer.concat([body, chunk])));
// Done callback
res.on('end', () => callback(null, body.toString('base64'), format));
});
// Listen for network errors
req.on('error', err => callback(err));
}
function getSrcBase64(base, getHTTP, src, callback) {
// TODO: @deprecated — since v11.0.0 url.parse should be replaced with url.URL() ctor
if (!url.parse(src).hostname) {
// Get local file
const filePath = path.join(base, src);
if (fs.existsSync(filePath)) {
fs.readFile(filePath, 'base64', callback);
} else {
callback(null);
}
} else {
// Get remote file
if (getHTTP) {
return getHTTPBase64(src, callback);
} else {
callback(null, src, null, true);
}
}
}
module.exports = {
inlineImg,
getHTTPBase64,
getSrcBase64
};

View File

@ -0,0 +1,10 @@
const gulp = require('gulp');
function checkForUnusedTask(options) {
gulp.task('check-for-missing', async done => {
// TODO
done();
});
}
module.exports = checkForUnusedTask;

75
tasks/check-for-unused.js Normal file
View File

@ -0,0 +1,75 @@
const gulp = require('gulp');
const chalk = require('chalk');
const { getConfigsForDir, getFilePathsForDir, getHtmlTemplatesFromFilelist, log } = require('./util/util');
const OUTPUT_KEYWORD = '@echo';
function checkForUnusedTask(options) {
gulp.task('check-for-unused', async done => {
const configs = getConfigsForDir(options.workingDir, options.configurationFile);
const unusedItems = await checkForUnusedItemsInConfigs(options.workingDir, configs);
outputWarningsForUnusedItems(unusedItems, configs);
done();
});
}
/**
* Outputs warnings for unused items.
*
* @param { Array<Array<string>> } unusedItems
* @param { Array<object> } configs
*/
const outputWarningsForUnusedItems = (unusedItems, configs) => {
const find = OUTPUT_KEYWORD;
const regex = new RegExp(find, 'g');
unusedItems.forEach((unusedInConfigs, index) => {
const { dir } = configs[index];
unusedInConfigs.forEach(unusedInConfItems => {
const unusedItemsToLog = unusedInConfItems
.filter(item => item !== `${OUTPUT_KEYWORD} id`)
.filter(item => item !== '@echo inlineRemoteUrl');
if (unusedItemsToLog.length) {
log.warn(
`${unusedItemsToLog.length} unused properties in ${dir}: ${unusedItemsToLog
.reduce((acc, cur) => (acc ? `${acc}, ${chalk.white(cur)}` : chalk.white(cur)), '')
.replace(regex, '')}`
);
}
});
});
};
/**
* In a directory, checks for unused configs.
*
* @param { string } rootDir
* @param { Array } configs Array of configs.
*/
const checkForUnusedItemsInConfigs = (rootDir, configs) =>
Promise.all(
configs.map(async ({ dir, confItems }) => {
return Promise.all(
confItems.map(async confItem => {
const definedStrings = Object.keys(confItem).map(key => `${OUTPUT_KEYWORD} ${key}`);
const cwd = `${rootDir}/${dir}`;
const files = await getFilePathsForDir(cwd);
const htmlTemplates = await getHtmlTemplatesFromFilelist(files);
const concatenatedTemplates = htmlTemplates.join('');
return definedStrings.filter(str => !concatenatedTemplates.includes(str));
})
);
})
);
const self = {
checkForUnusedTask,
outputWarningsForUnusedItems,
checkForUnusedItemsInConfigs
};
module.exports = self;

View File

@ -1,16 +1,12 @@
'use strict'; const gulp = require('gulp');
const del = require('del');
var gulp = require('gulp'), function dupeTask(options) {
del = require('del'); gulp.task('dupe', function() {
function dupeTask(options){
gulp.task('dupe', function(){
del.sync([options.workingDir]); del.sync([options.workingDir]);
return options return options.src([options.sourceDir + '/**/*']).pipe(gulp.dest('./' + options.workingDir));
.src(['src/**/*'])
.pipe(gulp.dest('./' + options.workingDir));
}); });
} }
module.exports = dupeTask; module.exports = dupeTask;

View File

@ -1,21 +1,18 @@
'use strict'; const gulp = require('gulp');
const less = require('gulp-less');
const autoprefixer = require('gulp-autoprefixer');
const rename = require('gulp-rename');
var gulp = require('gulp'), function lessTask(options) {
less = require('gulp-less'), // Requires: dupe.
autoprefixer = require('gulp-autoprefixer'), gulp.task('less', function() {
rename = require('gulp-rename');
function lessTask(options){
gulp.task('less', ['dupe'], function(){
return options return options
.src(options.workingDir + '/**/*.less') .src(options.workingDir + '/**/*.less')
.pipe(less()) .pipe(less())
.pipe(autoprefixer({ .pipe(autoprefixer())
browsers: ['last 5 versions']
}))
.pipe(rename({ extname: '.css' })) .pipe(rename({ extname: '.css' }))
.pipe(gulp.dest(options.workingDir)); .pipe(gulp.dest(options.workingDir));
}); });
} }
module.exports = lessTask; module.exports = lessTask;

14
tasks/lint.js Normal file
View File

@ -0,0 +1,14 @@
const gulp = require('gulp');
const jsonlint = require('gulp-jsonlint');
function lintTask(options) {
// Requiers: dupe.
gulp.task('lint', function() {
return options
.src(options.workingDir + '/**/conf.json')
.pipe(jsonlint())
.pipe(jsonlint.reporter());
});
}
module.exports = lintTask;

View File

@ -1,16 +1,11 @@
'use strict'; const gulp = require('gulp');
const postcss = require('gulp-postcss');
const autoprefixer = require('autoprefixer');
var gulp = require('gulp'), function postcssTask(options) {
postcss = require('gulp-postcss'), // Requires: dupe.
autoprefixer = require('autoprefixer'); gulp.task('postcss', function() {
var processors = [autoprefixer()];
function postcssTask(options){
gulp.task('postcss', ['dupe'], function () {
var processors = [
autoprefixer({
browsers: ['last 5 versions']
})
];
return options return options
.src(options.workingDir + '/**/*.css') .src(options.workingDir + '/**/*.css')
@ -19,4 +14,4 @@ function postcssTask(options){
}); });
} }
module.exports = postcssTask; module.exports = postcssTask;

View File

@ -1,21 +1,21 @@
'use strict'; const gulp = require('gulp');
const autoprefixer = require('gulp-autoprefixer');
const sass = require('gulp-sass');
const rename = require('gulp-rename');
var gulp = require('gulp'), function sassTask(options) {
autoprefixer = require('gulp-autoprefixer'), // Requires: dupe.
sass = require('gulp-sass'), gulp.task(
rename = require('gulp-rename'); 'sass',
gulp.series('dupe', function() {
function sassTask(options){ return options
gulp.task('sass', ['dupe'], function(){ .src(options.workingDir + '/**/*.scss')
return options .pipe(sass())
.src(options.workingDir + '/**/*.scss') .pipe(autoprefixer())
.pipe(sass()) .pipe(rename({ extname: '.css' }))
.pipe(autoprefixer({ .pipe(gulp.dest(options.workingDir));
browsers: ['last 5 versions'] })
})) );
.pipe(rename({ extname: '.css' }))
.pipe(gulp.dest(options.workingDir));
});
} }
module.exports = sassTask; module.exports = sassTask;

131
tasks/util/util.js Normal file
View File

@ -0,0 +1,131 @@
const fs = require('fs');
const path = require('path');
const klaw = require('klaw');
const chalk = require('chalk');
// todo test
/**
* Given a directory, scans all directories in it (not deep) and returns found config items.
*
* @param { string } rootDir Dir to look into.
* @param { string } configFileName Files to look for in each dir, i.e. conf.json
*/
const getConfigsForDir = (rootDir, configFileName) => {
return fs
.readdirSync(rootDir)
.map(dir => {
const confPath = `${dir}/${configFileName}`;
/** Exit with warn if no configuration file found. */
if (!fs.existsSync(path.resolve(rootDir, confPath))) {
self.log.warn(`Missing configuration in "${dir}". Did you remember to create "${dir}/${configFileName}"?`);
return false;
}
let current = null;
let confItems;
const resolvedPath = path.resolve(rootDir, confPath);
delete require.cache[resolvedPath]; // NB: For 'watch' to properly work, the cache needs to be deleted before each require.
current = require(resolvedPath);
// Handle single objects or arrays of configs.
if (current && current.length) {
confItems = [...current];
} else {
confItems = [current];
}
return {
dir,
confItems
};
})
.filter(config => config);
};
/**
* Given a directory, gets all file paths in it.
*
* @param { string } dir Dir to get files paths for.
*/
const getFilePathsForDir = dir => {
const files = [];
return new Promise(resolve => {
klaw(dir)
.on('readable', function walkTemplateDir() {
let file;
while ((file = this.read())) {
const relativePath = `${__dirname.substring(0, __dirname.lastIndexOf('/'))}/${dir}`;
files.push(file.path.replace(relativePath, ''));
}
})
.on('end', function finishedTemplateDirWalk() {
resolve(files);
});
});
};
/**
* Gets an array of html files in a filelist.
*
* @param { Array } filelist
*/
const getHtmlTemplatesFromFilelist = filelist => {
return Promise.all(
filelist
.filter(file => file.match(/.*\.html/) || file.match(/.*\.inc*\.html/))
.map(
htmlTemplate =>
new Promise((resolve, reject) => {
fs.readFile(htmlTemplate, 'utf8', (error, data) => {
if (error) {
reject(error);
}
resolve(data);
});
})
)
);
};
/**
* Gets an array of css link tags from a filelist (if css files are in the filelist).
*
* @param { Array } filelist
*/
const getCssLinkTagsFromFilelist = filelist => {
return filelist
.filter(file => !!file.match(/.*\.css/)) // Read only CSS files.
.reduce((acc, cur) => {
const cssPath = path.win32.basename(cur);
return (acc += '<link rel="stylesheet" href="' + cssPath + '">');
}, '');
};
const log = {
warn: (...messages) => {
console.warn('🔵 ', chalk.yellow(messages));
},
log: (...messages) => {
console.log('🔘 ', chalk.gray(messages));
},
error: (...messages) => {
console.error('🔴 ', chalk.red(messages));
}
};
const self = {
log,
getConfigsForDir,
getFilePathsForDir,
getHtmlTemplatesFromFilelist,
getCssLinkTagsFromFilelist
};
module.exports = self;

View File

@ -1,20 +0,0 @@
'use strict';
var gulp = require('gulp');
function watchTask(options){
gulp.task('watch', ['dupe', 'build'], function(){
gulp.watch(
[
options.source + '/**/*.html',
options.source + '/**/*.css',
options.source + '/**/*.scss',
options.source + '/**/*.less',
options.source + '/**/conf.js'
],
['dupe', 'less', 'sass', 'postcss', 'build']
);
});
}
module.exports = watchTask;

View File

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

11
templates/dark/conf.json Normal file
View File

@ -0,0 +1,11 @@
{
"id": "dark",
"signature": "Best regards,",
"name": "The dark mail team",
"contactMain": "Call <a href='tel:004580100100'><span>(45) 80100100</span></a> or email us at",
"contactMail": "info@dark.dk",
"slogan": "LED Pylon. LED Wall. Digital Signage.",
"logoUrl": "/assets/dark.png",
"logoAlt": "dark logo",
"website": "http://dark.dk"
}

View File

@ -12,42 +12,46 @@ body {
-webkit-text-size-adjust: none; -webkit-text-size-adjust: none;
} }
tr{ tr {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
box-sizing: border-box; box-sizing: border-box;
font-size: 14px; font-size: 14px;
line-height: 22px; line-height: 22px;
} }
.main{ .main {
width:100%; width: 100%;
background-color: #ffffff; background-color: #ffffff;
} }
.rbcc{ .rbcc {
/* /*
* rbcc -> reset - border - cellspacing - cellpading * rbcc -> reset - border - cellspacing - cellpading
* *
* Resets table attributes. * Resets table attributes.
*/ */
border:0; border: 0;
cellpadding:0; cellpadding: 0;
cellspacing:0; cellspacing: 0;
} }
.sp{ /* Separator tr; props are actually contained by it's inner element atm. */ } .sp {
/* Separator tr; props are actually contained by it's inner element atm. */
}
.sp__inner{ .sp__inner {
padding: 15px 0; padding: 15px 0;
} }
.spd{ /* Separator tr (double); props are actually contained by it's inner element atm. */ } .spd {
/* Separator tr (double); props are actually contained by it's inner element atm. */
}
.spd__inner{ .spd__inner {
height: 60px; height: 60px;
} }
a{ a {
text-decoration: none; text-decoration: none;
color: #0fade1; color: #0fade1;
} }
@ -57,7 +61,7 @@ a{
* 2. Content styles. * 2. Content styles.
* ================== * ==================
*/ */
.main__welcome{ .main__welcome {
color: #000; color: #000;
padding: 10px 30px 0 30px; padding: 10px 30px 0 30px;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
@ -65,93 +69,92 @@ a{
line-height: 22px; line-height: 22px;
} }
.main__content{ .main__content {
color: #000; color: #000;
padding: 10px 30px 0 30px; padding: 10px 30px 0 30px;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 14px; font-size: 14px;
} }
/* /*
* ================ * ================
* 3. Footer styles. * 3. Footer styles.
* ================ * ================
*/ */
.footer{ .footer {
background-color: #303030; background-color: #303030;
padding: 20px 30px 0px 30px; padding: 20px 30px 0px 30px;
color: #f5f5f5; color: #f5f5f5;
border-top: 8px solid #585858; border-top: 8px solid #585858;
} }
.footer a{ .footer a {
color: #f5f5f5; color: #f5f5f5;
} }
.footer--simple{ .footer--simple {
padding-bottom: 20px; padding-bottom: 20px;
background-color: #FFFFFF; background-color: #ffffff;
} }
.footer--simple tr td{ .footer--simple tr td {
color: #888; color: #888;
} }
.footer__main{ .footer__main {
/* This style property fucks up the width on OS X, needs to be *JUST* attribute */ /* This style property fucks up the width on OS X, needs to be *JUST* attribute */
width:100%; width: 100%;
} }
.footer__main__signature{ .footer__main__signature {
font-size: 14px; font-size: 14px;
color: #f5f5f5; color: #f5f5f5;
/* @todo gulp-inline-css doesn't parse align */ /* @todo gulp-inline-css doesn't parse align; it needs to be duplicated in the HTML template */
align:left; align: left;
} }
.footer__main__col1{ .footer__main__col1 {
width:70%; width: 70%;
margin-bottom:40px; margin-bottom: 40px;
/* @todo gulp-inline-css doesn't parse align */ /* @todo gulp-inline-css doesn't parse align; it needs to be duplicated in the HTML template */
align:left; align: left;
} }
.footer__main__col1__td{ .footer__main__col1__td {
color: #9E9E9E; color: #9e9e9e;
/* @todo gulp-inline-css doesn't parse align */ /* @todo gulp-inline-css doesn't parse align; it needs to be duplicated in the HTML template */
align:left; align: left;
padding-top: 15px; padding-top: 15px;
} }
.footer__main__col1__td > span{ .footer__main__col1__td > span {
font-size:18px; font-size: 18px;
margin-bottom:5px; margin-bottom: 5px;
} }
.footer__main a > span{ .footer__main a > span {
/* Revert apple blue-link style. */ /* Revert apple blue-link style. */
color: #f5f5f5!important; color: #f5f5f5 !important;
text-decoration:none!important; text-decoration: none !important;
} }
.footer__main__col2{ .footer__main__col2 {
width:30%; width: 30%;
/* @todo gulp-inline-css doesn't parse align */ /* @todo gulp-inline-css doesn't parse align; it needs to be duplicated in the HTML template */
align:right; align: right;
} }
.footer__main__col2__td{ .footer__main__col2__td {
font-size: 14px; font-size: 14px;
color: #f5f5f5; color: #f5f5f5;
/* @todo gulp-inline-css doesn't parse align */ /* @todo gulp-inline-css doesn't parse align; it needs to be duplicated in the HTML template */
align:right; align: right;
} }
.footer__main__col2__td__img{ .footer__main__col2__td__img {
border: 0; border: 0;
padding-left:20px; padding-left: 20px;
max-width: 100%; max-width: 100%;
max-height:65px; max-height: 65px;
height: auto; height: auto;
} }

View File

@ -1,35 +1,32 @@
<td class="footer"> <td class="footer">
<table class="rbcc footer__main"> <table class="rbcc footer__main">
<tr> <tr>
<!-- @todo gulp-inline-css doesn't parse align -->
<td class="footer__main__signature" align="left"> <td class="footer__main__signature" align="left">
<!-- @echo signature --><br /> <!-- @echo signature --><br />
<strong><!-- @echo name --></strong><br /> <strong><!-- @echo name --></strong><br />
</td> </td>
</tr> </tr>
<tr class="spd"><td class="spd__inner"></td></tr> <tr class="spd">
<td class="spd__inner"></td>
</tr>
<tr> <tr>
<!-- @todo gulp-inline-css doesn't parse align -->
<table class="rbcc footer__main__col2" align="right"> <table class="rbcc footer__main__col2" align="right">
<!-- @todo gulp-inline-css doesn't parse align -->
<td class="footer__main__col2__td" align="right"> <td class="footer__main__col2__td" align="right">
<a href="<!-- @echo website -->"> <a href="<!-- @echo website -->">
<img src="<!-- @echo logoUrl -->" alt="<!-- @echo logoAlt -->" class="footer__main__col2__td__img"/> <img src="<!-- @echo logoUrl -->" alt="<!-- @echo logoAlt -->" class="footer__main__col2__td__img" />
</a> </a>
</td> </td>
</table> </table>
<!-- @todo gulp-inline-css doesn't parse align -->
<table class="rbcc footer__main__col1" align="left"> <table class="rbcc footer__main__col1" align="left">
<!-- @todo gulp-inline-css doesn't parse align -->
<td class="footer__main__col1__td" align="left"> <td class="footer__main__col1__td" align="left">
<span><!-- @echo slogan --></span><br/> <span><!-- @echo slogan --></span><br />
<!-- @echo contactMain --> <!-- @echo contactMain -->
<a href="mailto:<!-- @echo contactMail -->" target="_blank"><!-- @echo contactMail --></a> <a href="mailto:<!-- @echo contactMail -->" target="_blank"><!-- @echo contactMail --></a>
</td> </td>
</table> </table>
</tr> </tr>
</table> </table>
</td> </td>

View File

@ -1,16 +1,18 @@
<!-- @include head.inc.html --> <!-- @include head.inc.html -->
<body> <body>
<br/> <!-- <br/> Makes it easier to add text when composing --> <br />
<!-- <br/> Makes it easier to add text when composing -->
<table class="main rbcc"> <table class="main rbcc">
<tr class="sp"><td class="sp__inner"></td></tr> <tr class="sp">
<td class="sp__inner"></td>
</tr>
<tr class="rbcc"> <tr class="rbcc">
<td class="footer footer--simple"> <td class="footer footer--simple">
<table class="rbcc footer__main"> <table class="rbcc footer__main">
<tr> <tr>
<!-- @todo gulp-inline-css doesn't parse align -->
<td class="footer__main__signature" align="left"> <td class="footer__main__signature" align="left">
<!-- @echo signature --><br /> <!-- @echo signature --><br />
<!-- @echo name --><br /> <!-- @echo name --><br />
@ -20,4 +22,4 @@
</td> </td>
</tr> </tr>
</table> </table>
</body> </body>

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

13
templates/light/conf.json Normal file
View File

@ -0,0 +1,13 @@
{
"id": "light",
"signature": "Yours truly,",
"name": "The light mail team",
"welcome": "Hi there,",
"introParagraph": "Thanks for writing up this email.<br/> We are delighted to reply with a responsive template.",
"contactMain": "Call <a href='tel:80100100'><span>80100100</span></a> or email us at",
"contactMail": "info@light.dk",
"contactSecondary": "<a href='https://mapurl.com'><span>Happy Steet 31, DK-8000 Aarhus C, Denmark</span></a>",
"logoUrl": "/assets/light.png",
"logoAlt": "light logo",
"website": "http://light.dk"
}

View File

@ -1,36 +1,32 @@
<td class="footer"> <td class="footer">
<table class="rbcc footer__main"> <table class="rbcc footer__main">
<tr> <tr>
<!-- @todo gulp-inline-css doesn't parse align -->
<td class="footer__main__signature" align="left"> <td class="footer__main__signature" align="left">
<!-- @echo signature --><br /> <!-- @echo signature --><br />
<!-- @echo name --><br /> <!-- @echo name --><br />
</td> </td>
</tr> </tr>
<tr class="gray-hr"> <tr class="gray-hr">
<td><hr class="gray-hr"/></td> <td><hr class="gray-hr" /></td>
</tr> </tr>
<tr> <tr>
<!-- @todo gulp-inline-css doesn't parse align -->
<table class="rbcc footer__main__col1" align="left"> <table class="rbcc footer__main__col1" align="left">
<td class="footer__main__col1__td" align="left"> <td class="footer__main__col1__td" align="left">
<strong> <strong>
<!-- @echo contactMain --> <!-- @echo contactMain -->
<a href="mailto:<!-- @echo contactMail -->" target="_blank"><!-- @echo contactMail --></a> <a href="mailto:<!-- @echo contactMail -->" target="_blank"><!-- @echo contactMail --></a> </strong
</strong><br /> ><br />
<!-- @echo contactSecondary --> <!-- @echo contactSecondary -->
</td> </td>
</table> </table>
<!-- @todo gulp-inline-css doesn't parse align -->
<table class="rbcc footer__main__col2" align="right"> <table class="rbcc footer__main__col2" align="right">
<!-- @todo gulp-inline-css doesn't parse align -->
<td class="footer__main__col2__td" align="right"> <td class="footer__main__col2__td" align="right">
<a href="<!-- @echo website -->"> <a href="<!-- @echo website -->">
<img src="<!-- @echo logoUrl -->" alt="<!-- @echo logoAlt -->" class="footer__main__col2__td__img"/> <img src="<!-- @echo logoUrl -->" alt="<!-- @echo logoAlt -->" class="footer__main__col2__td__img" />
</a> </a>
</td> </td>
</table> </table>
</tr> </tr>
</table> </table>
</td> </td>

166
templates/light/light.css Normal file
View File

@ -0,0 +1,166 @@
/*
* =====================================
* 1. Common styles for general table things.
* =====================================
*/
img {
max-width: 100%;
}
body {
-webkit-font-smoothing: antialiased;
-webkit-text-size-adjust: none;
}
tr {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
box-sizing: border-box;
font-size: 14px;
line-height: 20px;
}
.rbcc {
/*
* rbcc -> reset - border - cellspacing - cellpading
*
* Resets table attributes.
*/
border: 0;
cellpadding: 0;
cellspacing: 0;
}
.background {
width: 100%;
}
.main {
width: 100%;
background-color: #ffffff;
padding-top: 15px;
}
.sp {
/* Separator tr; props are actually contained by it's inner element atm. */
}
.sp__inner {
padding: 15px 0;
}
.gray-hr {
/* 100% width light grey line; props are actually contained by inner elements atm. */
}
.gray-hr td {
width: 100%;
}
.gray-hr hr {
border-bottom: 1px solid #e4e4e4;
border-top: none;
margin-bottom: 20px;
margin-top: 20px;
color: transparent;
background: transparent;
}
a {
text-decoration: none;
color: #0fade1;
}
/*
* =================
* 2. Content styles.
* ==================
*/
.main__welcome {
color: #000;
padding: 10px 30px 0 30px;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 22px;
}
.main__content {
color: #000;
padding: 10px 30px 0 30px;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 14px;
}
/*
* ================
* 3. Footer styles.
* ================
*/
.footer {
background-color: #f5f5f5;
padding: 20px 30px 0px 30px;
color: #888;
border-top: 8px solid #eaeaea;
}
.footer a {
color: #888;
}
.footer--simple {
padding-bottom: 20px;
background-color: #ffffff;
}
.footer__main {
/* NB: This prop fucks up the width on OS X, needs to be *JUST* attribute. */
width: 100%;
}
.footer__main__signature {
font-size: 14px;
color: #888;
/* @todo gulp-inline-css doesn't parse align; it needs to be duplicated in the HTML template */
align: left;
}
.footer__main__col1 {
width: 70%;
margin-bottom: 30px;
/* @todo gulp-inline-css doesn't parse align; it needs to be duplicated in the HTML template */
align: left;
}
.footer__main__col1__td {
font-size: 14px;
color: #888;
/* @todo gulp-inline-css doesn't parse align; it needs to be duplicated in the HTML template */
align: left;
}
.footer__main a > span {
/* Revert apple blue-link style. */
color: #888 !important;
text-decoration: none !important;
}
.footer__main__col2 {
width: 30%;
/* @todo gulp-inline-css doesn't parse align; it needs to be duplicated in the HTML template */
align: right;
}
.footer__main__col2__td {
font-size: 14px;
color: #888;
/* @todo gulp-inline-css doesn't parse align; it needs to be duplicated in the HTML template */
align: right;
}
.footer__main__col2__td__img {
border: 0;
padding-top: 6px;
padding-left: 10px;
max-width: 100%;
max-height: 38px;
height: auto;
}

View File

@ -1,15 +1,16 @@
<!-- @include head.inc.html --> <!-- @include head.inc.html -->
<body> <body>
<br/> <br />
<table class="main rbcc"> <table class="main rbcc">
<tr class="sp"><td class="sp__inner"></td></tr> <tr class="sp">
<td class="sp__inner"></td>
</tr>
<tr class="rbcc"> <tr class="rbcc">
<td class="footer footer--simple"> <td class="footer footer--simple">
<table class="rbcc footer__main"> <table class="rbcc footer__main">
<tr> <tr>
<!-- @todo gulp-inline-css doesn't parse align -->
<td class="footer__main__signature" align="left"> <td class="footer__main__signature" align="left">
<!-- @echo signature --><br /> <!-- @echo signature --><br />
<!-- @echo name --><br /> <!-- @echo name --><br />
@ -19,4 +20,4 @@
</td> </td>
</tr> </tr>
</table> </table>
</body> </body>

View File

@ -0,0 +1,37 @@
const test = require('ava');
const { readFileSync } = require('../util');
test('dark signature output', async t => {
const expected = readFileSync('tests/sample/dark/signature-dark.html');
const built = readFileSync('dist/dark/signature-dark.html');
t.deepEqual(expected, built);
});
test('dark signature reply output', async t => {
const expected = readFileSync('tests/sample/dark/signature-reply-dark.html');
const built = readFileSync('dist/dark/signature-reply-dark.html');
t.deepEqual(expected, built);
});
test('light signature output', async t => {
const expected = readFileSync('tests/sample/light/signature-light.html');
const built = readFileSync('dist/light/signature-light.html');
t.deepEqual(expected, built);
});
test('light signature reply output', async t => {
const expected = readFileSync('tests/sample/light/signature-reply-light.html');
const built = readFileSync('dist/light/signature-reply-light.html');
t.deepEqual(expected, built);
});
test('light full mail output', async t => {
const expected = readFileSync('tests/sample/light/full-mail-light.html');
const built = readFileSync('dist/light/full-mail-light.html');
t.deepEqual(expected, built);
});

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta name="viewport" content="width=device-width"><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><style type="text/css">@media only screen and (max-width:560px){.footer td{font-size:12px!important}.footer__main__col1{width:100%!important}.footer__main__col1__td{text-align:left}.footer__main__col1__td>span{margin-bottom:10px}.footer__main__col2{width:100%!important}.footer__main__col2__td{text-align:left;padding-bottom:20px}.footer__main__col2__td__img{padding-left:0!important}}</style></head><body style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;"><br><table class="main rbcc" style="border: 0; cellpadding: 0; cellspacing: 0;" border="0" cellpadding="0" cellspacing="0" bgcolor="#ffffff" width="100%"><tr class="sp" style="-webkit-box-sizing: border-box; box-sizing: border-box; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 22px;"><td class="sp__inner" style="padding: 15px 0;"></td></tr><tr class="rbcc" style="-webkit-box-sizing: border-box; border: 0; box-sizing: border-box; cellpadding: 0; cellspacing: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 22px;"><td class="footer footer--simple" style="border-top: 8px solid #585858; color: #f5f5f5; padding: 20px 30px 0px 30px; padding-bottom: 20px;" bgcolor="#ffffff"><table class="rbcc footer__main" style="border: 0; cellpadding: 0; cellspacing: 0;" border="0" cellpadding="0" cellspacing="0" width="100%"><tr style="-webkit-box-sizing: border-box; box-sizing: border-box; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 22px;"><td class="footer__main__signature" align="left" style="align: left; color: #888; font-size: 14px;">Best regards,<br>The dark mail team<br></td></tr></table></td></tr></table></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta name="viewport" content="width=device-width"><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><style type="text/css">@media only screen and (max-width:480px){.footer td{font-size:12px!important}.footer__main__col1{width:100%!important}.footer__main__col2{width:100%!important}.footer__main__col2__td{text-align:left;padding-bottom:20px}.footer__main__col2__td__img{padding-left:0!important}.gray-hr hr{margin-bottom:10px!important;margin-top:10px!important}}@media only screen and (min-width:1025px){.body-with-bg{background-color:#f1f1f1}.body-with-bg .main{border:1px solid #e9e9e9!important;max-width:960px;margin:0 auto}.background{padding:30px;background-color:#f1f1f1}}</style></head><body style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;"><br><table class="main rbcc" style="border: 0; cellpadding: 0; cellspacing: 0; padding-top: 15px;" border="0" cellpadding="0" cellspacing="0" bgcolor="#ffffff" width="100%"><tr class="sp" style="-webkit-box-sizing: border-box; box-sizing: border-box; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px;"><td class="sp__inner" style="padding: 15px 0;"></td></tr><tr class="rbcc" style="-webkit-box-sizing: border-box; border: 0; box-sizing: border-box; cellpadding: 0; cellspacing: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px;"><td class="footer footer--simple" style="border-top: 8px solid #eaeaea; color: #888; padding: 20px 30px 0px 30px; padding-bottom: 20px;" bgcolor="#ffffff"><table class="rbcc footer__main" style="border: 0; cellpadding: 0; cellspacing: 0;" border="0" cellpadding="0" cellspacing="0" width="100%"><tr style="-webkit-box-sizing: border-box; box-sizing: border-box; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px;"><td class="footer__main__signature" align="left" style="align: left; color: #888; font-size: 14px;">Yours truly,<br>The light mail team<br></td></tr></table></td></tr></table></body></html>

5
tests/util.js Normal file
View File

@ -0,0 +1,5 @@
const fs = require('fs');
module.exports = {
readFileSync: path => fs.readFileSync(('./', path), 'utf8')
};