Coder Social home page Coder Social logo

leemunroe / grunt-email-workflow Goto Github PK

View Code? Open in Web Editor NEW
3.0K 93.0 340.0 1.61 MB

A Grunt workflow for designing and testing responsive HTML email templates with SCSS.

License: MIT License

JavaScript 23.42% HTML 1.71% EJS 13.66% SCSS 39.01% Handlebars 22.19%
litmus grunt css transactional-emails email template mailgun responsive

grunt-email-workflow's Introduction

Grunt Email Design Workflow

by github.com/leemunroe

Changelog

v0.2.0 - 24/01/2024

  • update packages and dependencies for current node versions: nvm not needed
  • remove obsolete tasks and packages

Purpose

Designing and testing emails is a pain. HTML tables, inline CSS, various devices and clients to test, and varying support for the latest web standards.

This Grunt task helps simplify things.

  1. Compiles your SCSS to CSS
  2. Builds your HTML email templates
  3. Inlines your CSS

Requirements

You may already have these installed on your system. If not, you'll have to install them.

Getting started

If you haven't used Grunt before check out Chris Coyier's post on getting started with Grunt.

1. Setup

Clone this repo, cd to the directory, run npm install to install the necessary packages.

cd grunt-email-workflow
npm install

The very first installation may take a while. Please wait patiently until completion.

2. Run Grunt

Run grunt build and check out your /dist folder to see your compiled and inlined email templates. Run grunt serve, a new live-reload browser tab will open. Happy coding :)

3. Create secrets.json

If you're using Mailgun and/or Amazon S3 create a secrets.json file in your project root as outlined below under "Sensitive Information".

If you don't use or need these services it's ok to skip this step.

Sensitive information

We encourage you not to store sensitive data in your git repository. If you must, please look into git-encrypt or some other method of encrypting your configuration secrets.

  1. Create a file secrets.json in your project root.
  2. Paste the following sample code in secrets.json and enter the appropriate credentials for the services you want to connect with.
{
  "mailgun": {
    "api_key": "YOUR MG PRIVATE API KEY",
    "domain": "YOURDOMAIN.COM",
    "sender": "E.G. [email protected]",
    "recipient": "WHO YOU WANT TO SEND THE EMAIL TO"
  },
  "s3": {
    "key": "AMAZON S3 KEY",
    "secret": "AMAZON S3 SECRET",
    "region": "AMAZON S3 REGION",
    "bucketname": "AMAZON S3 BUCKET NAME",
    "bucketdir": "AMAZON S3 BUCKET SUBDIRECTORY (optional)",
    "bucketuri": "AMAZON S3 PATH (ex: https://s3.amazonaws.com/)"
  }
}

After this you should be good to go. Run grunt build and your email templates should appear automagically in a /dist folder.

How it works

CSS

This project uses SCSS. You don't need to touch the .css files, these are compiled automatically.

For changes to CSS, modify the .scss files.

Media queries and responsive styles are in a separate style sheet so that they don't get inlined. Note that only a few clients support media queries e.g. iOS Mail app.

Email templates and content

Handlebars and Assemble are used for templating.

/layouts contains the standard header/footer HTML wrapper markup. You most likely will only need one layout template, but you can have as many as you like.

/emails is where your email content will go. To start you off I've included example transactional emails based on my simple HTML email template.

/data contains optional .yml or .json data files that can be used in your templates. It's a good way to store commonly used strings and variables. See /data/default.yml and /partials/follow_lee.hbs for an example.

/partials contains optional .hbs files that can be thought of like includes. To use a partial, for example /partials/follow_lee.hbs you would use the following code in your emails template:

{{> follow_lee }}

/partials/components contains optional .hbs files that can help generate your markup. Each component will typically have a corresponding Sass file in src/css/sass/<component_name>.scss. To use a component, for example /partials/components/button.hbs you would use the following code in your emails template. (note: You can use single -or- double quotes for attributes)

{{> button type="primary" align="center" url="LINK GOES HERE" title="ANCHOR TEXT GOES HERE" }}

Generate your email templates

In terminal, run grunt build. This will:

  • Compile your SCSS to CSS
  • Generate your email layout and content
  • Inline your CSS

See the output HTML in the dist folder. Open them and preview it the browser.

Alternatively run grunt serve. This will check for any changes you make to your .scss and .hbs templates, automatically run the tasks, and serve you a preview in the browser on http://localhost:4000. Saves you having to run grunt every time you make a change.

Browser-based previews

In terminal, run grunt serve.

  • This will run the default tasks grunt + the watch task will be initiated
  • A preview UI will automagically open on http://localhost:4000 and you can review your templates
  • Go about your business editing templates and see your template changes live-reload
  • NOTE: The express server stops working when the watch task is not running
image

Sample email templates

I've added a few templates here to help you get started.

More resources

grunt-email-workflow's People

Contributors

aleksa-savic avatar clecompte avatar dependabot[bot] avatar dmyers avatar fgribreau avatar jjasghar avatar jkingyens avatar joeyespo avatar jongotlin avatar kittyfishfrommars avatar leemunroe avatar madshensel avatar majksner avatar markhuge avatar olapersson avatar roozbehk avatar s-be avatar taeo avatar tleyden avatar wietsewarendorff avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

grunt-email-workflow's Issues

[Suggestion] - Outta the box support for assemble partials and data

Partials
I'd digging having partials like /src/partials/signature.hbs that I can re-use in multiple /src/emails/* keeping things DRY and easy to update.

Data
Expanding on partials, I like being able to store commonly used variables (company_phone, website_url, etc..) in /src/data/*.yml and easily render into .hbs templates {{ company_phone }}

I'm happy to send in a PR if y'all feel this is worthwhile out of the box for this project.

Mailgun 403

I haven't started used this project in a while, but recently I opened the project to create an email template and everything worked awesome as usual (great time saver!!!) but the mailgun task returns a 403 error
screen shot 2016-12-22 at 10 09 28
.

Not sure where this error comes from, is this happening to you too?

UI for this project

Hi All,

Not an issue but more a question or an opportunity for a developer. Wondering if anyone created a UI for this workflow they could share or would be interested in developing?

Cheers,

Chris

Outlook >=2007

This is more of a question rather than an "issue", so to speak.

I ran the test email through emailsonacid.com and noticed that it looked great in all major clients bar Outlook >=2007.
It seems that Microsoft in their infinite wisdom have actually remove functionality from their email reader. Is there any fallback or solution around this?

Support Gulp?

I get asked a lot about supporting this workflow in gulp, which I'm definitely open to given the adoption it has.

I'm wondering if it makes sense to have a project that supports both grunt and gulp. The idea being that we're using the same /src files but the developer can decide to use grunt or gulp to run the build tasks depending on their preference.

Open to responses on whether this is a good or bad idea, if it even makes sense, and what the downsides are?

[question] Setting the email clients for litmus

Is it possible to set the email clients for testing with Litmus? Like passing the configuration an array of clients: [ "all outlook", "thunderbird" ] and so on ?

If not, how does the task decide which clients to choose? I can't figure it out.

Thanks!

Process only new/changed files

Is it possible to run the tasks only on new/modified files? I currently have 10 emails in my src/emails folder, and it takes ~30 seconds to run all the tasks on every file.

I toyed around with http://grunt-tasks.com/grunt-newer/ and prepending newer: to the default tasks in aliases.yml, but it didnโ€™t seem to make any difference.

Any ideas?

Thanks for the workflow!

[Suggestion] - secrets.json for sensitive data.

In my exploration with this project, I created a secrets.json file and added it to .gitignore (example below).

I feel it's good practice to get cloners off to a good start, discouraging them from commiting Gruntfiles with sensitive information.

@leemunroe - I'm happy to fork and send a PR if this is something you're into.

Gruntfile.js
Using <%= secrets.mailgun.api_key %> in the mailgun task.

grunt.initConfig({
        secrets: grunt.file.readJSON('secrets.json'),
});

secrets.json

{
    "mailgun": {
        "api_key": "YOUR MG PRIVATE API KEY"
    },
    "litmus": {
        "username" : "LITMUS USER NAME",
        "password" : "LITMUS PASS"
    }
}

Build individual templates

If I wanted to run a grunt command to build individual templates is that possible?

e.g grunt templateOne or grunt templateTwo

premailer error

Hi,
I start grunt in the console and I have an error with premailer:

[grunt-email-workflow master] grunt
Running "sass:dist" (sass) task

Running "assemble:pages" (assemble) task
Assembling dist/branded.html OK
Assembling dist/transaction.html OK

2 pages assembled.

Running "premailer:html" (premailer) task
/usr/local/Cellar/ruby/2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in require': cannot load such file -- premailer (LoadError) from /usr/local/Cellar/ruby/2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:inrequire'
from /Users/xavi/sites/email/grunt-email-workflow/node_modules/grunt-premailer/lib/premailer.rb:3:in <main>' /usr/local/Cellar/ruby/2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:inrequire': cannot load such file -- premailer (LoadError)
from /usr/local/Cellar/ruby/2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in require' from /Users/xavi/sites/email/grunt-email-workflow/node_modules/grunt-premailer/lib/premailer.rb:3:in

'
Fatal error: /usr/local/Cellar/ruby/2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in require': cannot load such file -- premailer (LoadError) from /usr/local/Cellar/ruby/2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:inrequire'
from /Users/xavi/sites/email/grunt-email-workflow/node_modules/grunt-premailer/lib/premailer.rb:3:in `'
[grunt-email-workflow master]

Thank you

[Suggestion] - Easy browser-based preview UI.

Thinking a quick and easy way to do browser based testing would be a sweet addition to this project.
I've done the proof of concept locally and feel like its a good addition to this project.

Why?

  • Don't have to send an email for every change
  • No need to remember local url's to navigate to (would load from localhost:[PORT])
  • Side-by side desktop / mobile preview
  • Lightweight

Implementation Thoughts

  • Implement express server w/ grunt
  • Live reload for changes to /dist files
  • Grunt open to launch a browser (optional)
  • Serve dist files into the ui example below for quick side-by-side previews

Here's a screenshot of what I cooked up locally as proof of concept:
screen shot 2015-03-21 at 12 04 06 pm

Other host for image

Hi Lee ๐Ÿ˜„

Is there a easy way to point all image source to a specified URL if we are hosting our own image? I thought about using updating grunt replace.js, but wasn't sure if that's the best way to go about it?

Any thoughts would be appreciated :)

Button partial error

Having an issue with the partial buttons, I get this error when running 'grunt'

Assembling dist/email.html ERROR
Warning: Parse error on line 32:
... {{> button type="primary" align="ce
-----------------------^
Expecting 'CLOSE', 'CLOSE_UNESCAPED', 'STRING', 'INTEGER', 'BOOLEAN', 'OPEN_SEXPR', 'CLOSE_SEXPR', 'ID', 'DATA', 'SEP',
got 'EQUALS' Use --force to continue.

I'm also recreating this workflow from scratch so I can better understand how you've done it and why, so far my .json file has these dependencies:

"devDependencies": {
"assemble": "^0.4.42",
"grunt": "~0.4.5",
"grunt-assemble": "^0.4.0",
"grunt-cdn": "^0.6.5",
"grunt-contrib-clean": "^0.7.0",
"grunt-inline-css": "^0.1.5",
"grunt-juice-email": "^0.1.4",
"grunt-sass": "^1.1.0"
}

// UPDATE

Might have resolved this by changing the loadNpmTask to 'grunt-assemble' from 'assemble'

Feature Suggestion: Email compilation file type + preview with dummy data.

Not all use cases for this workflow benefit from pure .html compiled templates. Many times we are loading our templates into various third party systems with a variety of compilers and subsequent dynamic data. The objective is to have pristine templates compiled, but allow live-ish previewing and testing.

Approach:

  • Allow dist/ files to be written as whatever type (.liquid, .hbs, .php, etc...)
  • Incorporate better helpers / utilities to pass hbs or other curly brace syntax through assemble, uncompiled
  • Allow the preview tool to render supported template types
  • Enable the preview tool load and render templates with optional dummy data for testing

Thoughts, questions, issues, likes...?

FYI @ericdfields

cdnify weirdness

It's probably just me, but I can't get the cdnify/cloudfiles part to work properly with Rackspace. What I'm seeing is image assets are uploaded to my container, in

<rackspace container>/src/img/...

but the paths in my emails get changed to

http://<rackspace uri>/dist/img/...

paths to files method

I'm trying to use the <%= paths.src %> method to reference files

e.g src: ['<%= paths.src %>/emails/*.hbs'],

it gives me an error:
Warning: An error occurred while processing a template (Cannot read property 'src' of undefined).

Is there a dev dependency that this requires to run? mine currently looks like this:
"devDependencies": {
"assemble": "^0.4.42",
"grunt": "~0.4.5",
"grunt-assemble": "^0.4.0",
"grunt-cdn": "^0.6.5",
"grunt-contrib-clean": "^0.7.0",
"grunt-inline-css": "^0.1.5",
"grunt-juice-email": "^0.1.4",
"grunt-sass": "^1.1.0"
},

Feature request: Implement browser sync

Each time I have a change the server connect well and disconnect?

I add it int registerTask:
grunt.registerTask('default', ['browserSync','sass','assemble','premailer','imagemin','replace:src_images']);

Then after the litmus part.
browserSync: { dev: { bsFiles: { src : [ '<%= paths.dist %>/*.html' ] }, options: { watchTask: true, server: "http://localhost:8888/grunt-email-workflow/dist/transaction.html" } } }

Thanks

CSS inlining

Hi Lee,

let me first thank you for this awesome email workflow, you have already saved me a huge amount of time.
I am just wondering about inlining the CSS style: In saas:dist the whole css file (main.css) which contains all global resets, external classes etc. is build. Then "assemble" creates html but when you run grunt task "juice", only media queries are actually inserted (to the head) and the rest is of course inlined. But there is no global resets, external classes. Or I have something overlooked?

Only run necessary tasks when in grunt serve

Hello,

Is there any way to only run relevant tasks when new files are added and the local server is running? I would like to be able to change a css file without imagemin running again, for example. Or is it better to just remove files from the src folder so they no longer affect the process?

Assemble Parse Error

How can I use PHP echo statements in my templates?

I'm creating a whole set of html email template to drop into a PHP application. The end result in the templates once they've been compiled, needs to retain the <?= $variable; ?> so that they can be parsed in the application.

I tried escaping them with {{{ <?= $variable; ?> }}} but assemble throws an error.

Warning: Parse error on line 25:
...         <h1>Hi {{{ <?= $variable; ?> }
-----------------------^
Expecting 'ID', 'STRING', 'NUMBER', 'BOOLEAN', 'UNDEFINED', 'NULL', 'DATA', 
got 'INVALID' Use --force to continue.

Template looks like so:

---
layout: template.hbs
subject: Forgotten Password
preheader: Whoops, looks like you've forgotten your password.

---
<table class="main">
  <tr>
    <td class="wrapper">
      <table>
        <tr>
          <td>
            <h1>Hi {{{ <?= $firstname; ?> }}}</h1>
            <p>
              Either you or someone claiming to be you has requested to change
              your password on the <a href="{{ default.site_url }}">{{ default.site_name }}</a> website.
            </p>
            {{> divider }}
            <p>Here is your new password:</p>
            {{> notice type='info' class='align-center' text='<strong>{{{ <?= $password; ?> }}}</strong>'}}
            {{> divider }}
            <p>
              Please use this password to login to your account, then change
              your password to something you'll remember and is more secure.
            </p>
            <p>Thanks.</p>
            <p>{{ default.team_text }}</p>
          </td>
        </tr>
      </table>
    </td>
  </tr>
</table>

Ideas?

Destination Sub-directories

Hi Lee,

I love this repo. Up till now I've used it just for real small projects, but now I have a big project and I need to add sub-directories to my destination path to keep all these emails organized.

Globbing the src directory fetches them all correctly:

src: ['<%= paths.src %>/emails/**/*.hbs'],

but can you please advise on how to retain my sub-directories in the dist directory?

Thanks.

Fatal error in clean

I have a weird problem I'm seeing on Windows 10. The first time I run grunt serve everything seems to be fine. Subsequently, however, it loads ok but as soon as I make a change in a template I get the following error:

Running "clean:clean" (clean) task
Fatal error: watch null EPERM
>> 1 path cleaned.
Fatal error: watch null EPERM
Fatal error: watch null EPERM
Error: ENOENT: no such file or directory, scandir 'C:\Users\Toby\Documents\projects\myproject\email-examples\dist'
  at Error (native)
  at Object.fs.readdirSync (fs.js:808:18)
  at getTemplates (C:\Users\Toby\Documents\projects\myproject\email-examples\server.js:33:28)
  at C:\Users\Toby\Documents\projects\myproject\email-examples\server.js:19:18
  at Layer.handle [as handle_request] (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\layer.js:95:5)
  at next (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\route.js:131:13)
  at Route.dispatch (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\route.js:112:3)
  at Layer.handle [as handle_request] (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\layer.js:95:5)
  at C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:277:22
  at Function.process_params (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:330:12)
  at next (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:271:10)
  at expressInit (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\middleware\init.js:33:5)
  at Layer.handle [as handle_request] (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\layer.js:95:5)
  at trim_prefix (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:312:13)
  at C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:280:7
  at Function.process_params (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:330:12)
  at next (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:271:10)
  at query (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\middleware\query.js:49:5)
  at Layer.handle [as handle_request] (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\layer.js:95:5)
  at trim_prefix (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:312:13)
  at C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:280:7
  at Function.process_params (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:330:12)
  at next (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:271:10)
  at livereload (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\grunt-express\node_modules\connect-livereload\index.js:151:5)
  at Layer.handle [as handle_request] (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\layer.js:95:5)
  at trim_prefix (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:312:13)
  at C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:280:7
  at Function.process_params (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:330:12)
  at next (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:271:10)
  at Function.handle (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:176:3)
  at EventEmitter.handle (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\application.js:173:10)
  at Server.app (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\express.js:38:9)
  at emitTwo (events.js:87:13)
  at Server.emit (events.js:172:7)
  at HTTPParser.parserOnIncoming [as onIncoming] (_http_server.js:528:12)
  at HTTPParser.parserOnHeadersComplete (_http_common.js:88:23)

Error: ENOENT: no such file or directory, scandir 'C:\Users\Toby\Documents\projects\myproject\email-examples\dist'
  at Error (native)
  at Object.fs.readdirSync (fs.js:808:18)
  at getTemplates (C:\Users\Toby\Documents\projects\myproject\email-examples\server.js:33:28)
  at C:\Users\Toby\Documents\projects\myproject\email-examples\server.js:19:18
  at Layer.handle [as handle_request] (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\layer.js:95:5)
  at next (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\route.js:131:13)
  at Route.dispatch (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\route.js:112:3)
  at Layer.handle [as handle_request] (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\layer.js:95:5)
  at C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:277:22
  at Function.process_params (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:330:12)
  at next (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:271:10)
  at expressInit (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\middleware\init.js:33:5)
  at Layer.handle [as handle_request] (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\layer.js:95:5)
  at trim_prefix (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:312:13)
  at C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:280:7
  at Function.process_params (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:330:12)
  at next (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:271:10)
  at query (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\middleware\query.js:49:5)
  at Layer.handle [as handle_request] (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\layer.js:95:5)
  at trim_prefix (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:312:13)
  at C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:280:7
  at Function.process_params (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:330:12)
  at next (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:271:10)
  at livereload (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\grunt-express\node_modules\connect-livereload\index.js:151:5)
  at Layer.handle [as handle_request] (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\layer.js:95:5)
  at trim_prefix (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:312:13)
  at C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:280:7
  at Function.process_params (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:330:12)
  at next (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:271:10)
  at Function.handle (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\router\index.js:176:3)
  at EventEmitter.handle (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\application.js:173:10)
  at Server.app (C:\Users\Toby\Documents\projects\myproject\email-examples\node_modules\express\lib\express.js:38:9)
  at emitTwo (events.js:87:13)
  at Server.emit (events.js:172:7)
  at HTTPParser.parserOnIncoming [as onIncoming] (_http_server.js:528:12)
  at HTTPParser.parserOnHeadersComplete (_http_common.js:88:23)

It looks like server.js tries to access dist, which has just been removed by the clean task?

Once this has happened the only way I've found to get the serve task working again is by commenting out 'clean' in the default task (in grunt\aliases.yaml)

[question] Mailgun stoped wotking after creating external task

Hello, im geetin this error after i separeted my tasks from gruntfile.js to a folder _tasks, ATM im using grunt-load-configs and grunt-load-tasks on my project.
Since i made this change, mailgun stoped working.

// _tasks/mailgun.js
module.exports = {
  mailer: {
    options: {
      key: '<%= cfg.email.key %>',
      sender: '<%= cfg.email.sender %>',
      recipient: '<%= cfg.email.recipient %>',
      subject: '<%= cfg.email.subject %>'
    },
    src: ['html/email.html)] 
// it send the email test but the task never stop running,  i have to cancel it with ctrl+c. 

    src: ['<%= cfg.dist %>'+grunt.option('template')] 
// cmd line : grunt send --template=email.html, i use this method to send test email.
and i get the error below.
  }
}; 
Loading _workflow/_tasks/mailgun.js...Loading "Gruntfile.js" tasks...ERROR
>> ReferenceError: grunt is not defined
>>     at Object.<anonymous> (C:\sites\bymailer\_workflow\_tasks\mailgun.js:9:29)
>>     at Module._compile (module.js:460:26)
>>     at Object.Module._extensions..js (module.js:478:10)
>>     at Module.load (module.js:355:32)
>>     at Function.Module._load (module.js:310:12)
>>     at Module.require (module.js:365:17)
>>     at require (module.js:384:17)
>>     at C:\sites\bymailer\node_modules\load-grunt-configs\lib\load-grunt-configs.js:72:30
>>     at Array.forEach (native)
>>     at module.exports (C:\sites\bymailer\node_modules\
load-grunt-configs\lib\load-grunt-configs.js:53:11)

Running tasks: send
>> [no-notifications] title: BY_Mailer
>> [no-notifications] message: Task "send" not found.
Warning: Task "send" not found. Use --force to continue.

If i change my module.exports like this to correct the missing grunt call:

// _tasks/mailgun.js
module.exports = function(grunt) {
  mailer: {
    options: {
      key: '<%= cfg.email.key %>',
      sender: '<%= cfg.email.sender %>',
      recipient: '<%= cfg.email.recipient %>',
      subject: '<%= cfg.email.subject %>'
    },
    src: ['<%= cfg.dist %>'+grunt.option('template')]
 // cmd line : grunt send --template=email.html
  }
}; 

i get this error:

Loading _workflow/_tasks/mailgun.js...Loading "Gruntfile.js" tasks...ERROR
>> C:\sites\bymailer\_workflow\_tasks\mailgun.js:5
>>       sender : '<%= cfg.email.sender %>',
>>                 ^

My taks worked fine on Gruntfile.js like this:

// gruntfile.js
'use strict';

module.exports = function(grunt) {

  var globalConfig = {
    cfg: globalConfig,
    email: {
      key:        'key-xpto',
      sender:     '[email protected]',
      recipient:  '[email protected]', 
    },
  };

  grunt.initConfig({

      cfg: globalConfig,
      pkg: grunt.file.readJSON('package.json'),

      // Use Mailgun option if you want to email the design to your inbox or to something like Litmus
      mailgun: {
        mailer: {
          options: {
            key:        '<%= cfg.email.key %>',         // Enter your Mailgun API key here
            sender:     '<%= cfg.email.sender %>',      // Change this
            recipient:  '<%= cfg.email.recipient %>',   // Change this
            subject:    '<%= cfg.email.subject %>'      // Change this
          },
          src: ['html/'+grunt.option('template')]
        }
      },

  });

  grunt.registerTask('send', ['mailgun']);
};

I must be missing something here, can you help me?

Best regards.

How to pass through an element without rendering?

Is there a way to pass through an element and leave it un-modified in the output?
I am using this (great!) tool to prepare loads of transaction emails for use in our Netsuite/ecommerce implementation. In the example below I need to leave this line alone <#list transaction.item as item><#if item_index==0> for Netsuites templating engine to iterate on on send. For all the rest of the email, I need the optimised html output from the dist folder. (I have about 60 differing emails to do here!)

<p>The details of your order are;</p> <table> **<#list transaction.item as item><#if item_index==0>** <thead> <p>Foo1</p> <tr><th>{item.quantity@label}</th><th>{item.itemname@label}</th></tr> </thead> <tbody> </#if> <tr><td>${item.quantity}</td><td>${item.description}</td></tr> </#list>

How are you excluding the media queries files

Hi - I'm looking to streamline our email design/testing workflow and came across this! It looks great. But how do i alter / choose which styles get 'ignored' ? I see that the media query styles here get ignored - but I cant see how you've done it?

cdn task errors

currently throwing errors on grunt cdn.

Running "cdn:dist" (cdn) task
Configuration error: please set "cwd" and "dest" properly

Change this:

        cdn: {
          options: {
            cdn: 'Rackspace Cloud CDN URI', // Change this
            flatten: true,
            supportedTypes: 'html'
          },
          dist: {
            src: ['./dist/*.html']
          }
        },

to include cwd and dest:

        cdn: {
          options: {
            cdn: 'Rackspace Cloud CDN URI', // Change this
            flatten: true,
            supportedTypes: 'html'
          },
          dist: {
            cwd: './dist/',
            dest: './dist/',
            src: ['*.html']
          }
        },

Permissions issue

Running on Windows
I've run through the setup instructions, have added MailGun details too.

'grunt' works fine, but 'grunt serve' results in an error:
Assembling dist/branded.html Assembling dist/branded.html ERROR
Warning: Unable to create directory "path-to-project\email\grunt-email-workflow\grunt-ema
il-workflow\dist" (Error code: EPERM). Use --force to continue.

grunt was asking whether I had 'grunt-parallel' installed so I installed that also but still had the same issue.

Uninstalled 'grunt-parallel' as it made no difference, but now 'grunt' on it's own doesn't work now. Not really sure if it's a permission issue here but can't seem to get it to run.

/ UPDATE:
My bad, had a dist file open in my editor! Now all works fine. Sorry for any inconvenience. Really like this solution and will be implementing this for all my projects.

Dependency deprecation

Running npm install on npm v2.5.1 threw a bunch of deprecation warnings. Example:

ERROR: Premailer gem not installed. Issue "gem install premailer" to install it
npm WARN deprecated [email protected]: the module is now available as 'css-what'
npm WARN deprecated [email protected]: the module is now available as 'css-select'
npm WARN engine [email protected]: wanted: {"node":"0.8.x || 0.10.x"} (current: {"node":"0.12.0","npm":"2.5.1"})

Just wondering if this is on your radar.

Mailgun no longer working

Firstly thank you so much for this amazing workflow, it has made my life so much easier.

The last month or so I have noticed the mailgun task is no longer working. I have done some digging around and it would seem mailgun have updated their API but unfortunately I have not been successful in updating and changing this within the workflow. If its any help windows 10 throws a 301 error and OSX appears to send the email but it doesnt work.

Many thanks.

Inlined images (breaks in gmail)

Is there any reason not to set webResources.images to false? Gmail doesn't display inlined images, I think it's worth the extra requests to have them linked rather than inlining small images and having them break in gmail.

Seems like it'd be sensible to remove the inlining from the defaults. I can open a PR.

juice: {
  your_target: {
    options: {
      [...]
      webResources: {
        images: false
      }
    },
},

Task flow mind map and paths.

@leemunroe & @clecompte
I'm trying to get my head around some of the image path related flow we're seeing that will result in inconsistent behavior for #26 #25 and #28

Here's my stab at a high level outline. Your thoughts?
(Let me know if you think i'm crazy...)

  • assemble
    • premailer
      • imagemin
        • TASK NEEDED to replace ./dist/*.html files image sources from ./src/img to ./dist/img

Anything sent to cdn (cloudfiles, aws, etc..) and replaced in cdn task(s) should always have /dist/img/ consistent or this falls apart.

  • cloudfiles || aws_s3
    (should send ./dist/img/**/*.{png,jpg,gif} to: http://cdnurl.com/some-optional-path/+dist/img/**/*.{png,jpg,gif})
    • cdn: cloudfiles || aws_s3
      (should replace ./dist/img/PATH-TO-FILE with http://cdnurl.com/some-optional-path/)

Using FTP not CDN?

Hi @leemunroe i wanted to try and test your wonderful project but using ftp for images

can you help me sort this out ? any ideas ? or workaround ?

willing to support in a small way after i finish this up ^_^v

Get this error for mailgun

Loading "mailgun.js" tasks...ERROR

SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode
Warning: Task "mailgun" not found. Use --force to continue.
Aborted due to warnings.

I sent @markhuge a pull request to his node-mailgun-send that fixes this issue.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.