Skip to content

Commit

Permalink
Init
Browse files Browse the repository at this point in the history
  • Loading branch information
Daylon committed Jun 8, 2016
0 parents commit d2a60f4
Show file tree
Hide file tree
Showing 25 changed files with 601 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
@@ -0,0 +1,5 @@
node_modules
.*.yml
__templates/variants
dist
styles
24 changes: 24 additions & 0 deletions .stylelintrc
@@ -0,0 +1,24 @@
{
"rules": {
"block-no-empty": null,
"color-no-invalid-hex": true,
"declaration-colon-space-after": "always",
"indentation": [
2,
{
"except": ["value"],
"indentInsideParens": ["once"]
}
],
"max-empty-lines": 2,
"unit-whitelist": [
"em"
, "rem"
, "%"
, "px"
, "s"
, "vw"
, "vh"
]
}
}
30 changes: 30 additions & 0 deletions README.md
@@ -0,0 +1,30 @@
# gulp-mjml-hbs-pipeline
Gulp pipeline example dedicated to build HTML emails.

## Usage

```
git clone https://github.com/Daylon/gulp-mjml-hbs-pipeline.git
```

## Templating

### Why using Handlebars

Handlebars allows us to use a neat built-in feature that make it skip partials declaration on first pass.

`\{{>partialName}}`

([Source](https://stackoverflow.com/questions/22249235/render-double-curly-brackets-inside-handlebars-partial))

### Two pass compilation

#### First round

Variants are built up.

#### Second pass

Data in embed in.


33 changes: 33 additions & 0 deletions __styles/source-stylesheet.scss
@@ -0,0 +1,33 @@
mj-text, mj-list, mj-button, mj-table, .is-heading{
font-family: 'Roboto Condensed', 'Roboto', sans-serif;
color: #333;
}

.theme-forest{
background-color: #2FDFAA;
}

.theme-tomato{
background-color: tomato;
}

mj-section{
full-width: "full-width";
padding: 0;
}

mj-column{
padding-left: 0;
padding-right: 0;
background-color: #f2f2f2;
}

mj-text{
padding: 0;
}

.is-empty{
padding: 0;
font-size: 0;
line-height: 0;
}
28 changes: 28 additions & 0 deletions __templates/index.tpl
@@ -0,0 +1,28 @@
<!--
You better keep the css file path as is,
since it's automatically built by Gulp
and automatically inlined afterwards.
-->
<link href="../../styles/source-stylesheet.css" rel="stylesheet">
<mjml>
<mj-body>
<mj-container class="theme-{{_d.theme}}">
<mj-raw>
<!--
Some raw HTML code.
-->
</mj-raw>
<!-- PREHEADER -->
<mj-section class="section-header">
<mj-column>{{> preheader}}</mj-column>
</mj-section>
<!-- BODY -->
<mj-section>
<mj-column>
<!-- actual content -->
{{> content}}
</mj-column>
</mj-section>
</mj-container>
</mj-body>
</mjml>
6 changes: 6 additions & 0 deletions __templates/partials/content.hbs
@@ -0,0 +1,6 @@
<mj-text>{{_d.title}}</mj-text>
<mj-text>
Some content.
The produced HTML can also host its own template variable:
\{{anotherPostProductionVariable}} easily tag along with {{_d.staticContent}} prerendered variables.
</mj-text>
1 change: 1 addition & 0 deletions __templates/partials/empty-text.hbs
@@ -0,0 +1 @@
<div class="is-empty">&nbsp;</div>
3 changes: 3 additions & 0 deletions __templates/partials/preheader.hbs
@@ -0,0 +1,3 @@
<mj-text class="mj-text as-header">
{{_d.preheader}}
</mj-text>
3 changes: 3 additions & 0 deletions gulpfile.js
@@ -0,0 +1,3 @@
'use strict';

const TASKS_DIR = require( 'require-dir' )( './gulptasks', { recurse: true } )
21 changes: 21 additions & 0 deletions gulptasks/build.js
@@ -0,0 +1,21 @@
'use strict'

const GULP = require( 'gulp' )
, SEQUENCE = require( 'gulp-sequence' )

let build = function(){
return SEQUENCE(
'core-clean'
, 'core-watch-notification'
, 'variants-prepare'
, 'style-compile'
, 'style-inject'
, 'variants-build'
)()
}

GULP.task(
'build'
, build
)
module.exports = build
18 changes: 18 additions & 0 deletions gulptasks/core/core-clean.js
@@ -0,0 +1,18 @@
'use strict'

const GULP = require( 'gulp' )
, DEL = require( 'del' )
, PATHS = require( './core-paths' )

let cleanUp = function(){
console.log( '\n\u001b[38;5;202mDeleting…\u001b[0m')
return DEL([
`${PATHS.dir.variants.variants}**/*`
, `${PATHS.dir.variants.dist}*`
, `${PATHS.dir.styles.dist}*`
])
}

GULP.task( 'core-clean', cleanUp )

// module.exports = [ 'core']
11 changes: 11 additions & 0 deletions gulptasks/core/core-errors.js
@@ -0,0 +1,11 @@
'use strict';

const GULP = require( 'gulp' )
, GUTIL = require( 'gulp-util' )

let onError = function( taskName, err ){
GUTIL.log( GUTIL.colors.red( 'ERROR', taskName ), err);
this.emit( 'end', new GUTIL.PluginError( taskName, err, { showStack: true } ) );
}

module.exports = onError
99 changes: 99 additions & 0 deletions gulptasks/core/core-mjml.js
@@ -0,0 +1,99 @@
'use strict'

const MJML_DICTIONARY = {
'table, tr, td, img': [
'width'
, 'height'
, 'align'
]
, 'table, td': [ 'bgcolor' ]
, 'table': [
, 'cellpadding'
, 'cellspacing'
]
, 'td': [
'valign'
]
, 'img': [
'border'
]
, 'v\\:rect': [
'width'
, 'height'
, 'color'
, 'strokecolor'
, 'arcsize'
]
, 'mj-body, mj-container, mj-section, mj-column, mj-button': [ 'background-color' ]
, 'mj-container': [
'width'
, 'font-size'
, 'background-color'
]
, 'mj-section': [
'full-width'
, 'background-color'
, 'background-url'
, 'background-repeat'
, 'background-size'
, 'vertical-align'
, 'text-align'
, 'padding'
, 'padding-top'
, 'padding-bottom'
, 'padding-left'
, 'padding-right'
]
, 'mj-column': [
'width'
, 'vertical-align'
, 'background-color'
]
, 'mj-text': [
'color'
, 'font-family'
, 'font-size'
, 'font-style'
, 'font-weight'
, 'line-height'
, 'text-decoration'
, 'align'
, 'container-background-color'
, 'padding'
, 'padding-top'
, 'padding-bottom'
, 'padding-left'
, 'padding-right'
]
, 'mj-button': [
'container-background-color'
, 'border-radius'
, 'font-style'
, 'font-size'
, 'font-weight'
, 'font-family'
, 'color'
, 'border'
, 'text-decoration'
, 'align'
, 'vertical-align'
, 'href'
, 'padding'
, 'padding-top'
, 'padding-bottom'
, 'padding-left'
, 'padding-right'
]
, 'mj-table': [
'color'
, 'font-family'
, 'font-size'
, 'line-height'
, 'container-background-color'
, 'padding'
, 'width'
, 'table-layout'
]
}

module.exports = MJML_DICTIONARY
27 changes: 27 additions & 0 deletions gulptasks/core/core-paths.js
@@ -0,0 +1,27 @@
'use strict';

const PATHS = {
dir: {
base: './'
, variants: {
source: './__templates/'
, partials: './__templates/partials/'
, variants: './__templates/variants/'
, dist: './dist/'
}
, styles: {
source: './__styles/'
, dist: './styles/'
}
}
, file: {
template: '.+(tpl|hbs)'
, variant: '.mjml'
, lessStylesheets: '.less'
, sassStylesheets: '.scss'
, stylesheet: '.css'
, production: '.html'
}
}

module.exports = PATHS;
30 changes: 30 additions & 0 deletions gulptasks/core/core-templates.js
@@ -0,0 +1,30 @@
'use strict';

let templates = function(){
const TEMPLATE_DATA = [
{
name: 'email-A'
, _d: {
title: 'My title A'
, preheader: 'A catchy headline that\'ll boost up your opens rate'
, staticContent: 'any'
, theme: 'forest'
}
}
, {
name: 'email-B'
, _d: {
title: 'Some random title B'
, preheader: 'Preheader headline'
, staticContent: 'all'
, theme: 'tomato'
}
}
]

return {
data: TEMPLATE_DATA
}
}

module.exports = templates();
8 changes: 8 additions & 0 deletions gulptasks/core/core-variant-list.js
@@ -0,0 +1,8 @@
'use strict'

const EMAIL_VARIANTS = {
'confirm-subscripton': {}
, 'fragment': {}
}

module.exports = EMAIL_VARIANTS
17 changes: 17 additions & 0 deletions gulptasks/core/core-watch-notification.js
@@ -0,0 +1,17 @@
'use strict'

const GULP = require( 'gulp' )
, PATHS = require( './core-paths' )
, FILES_TO_WATCH_OUT = [
`${PATHS.dir.styles.source}**/*${PATHS.file.sourceStylesheet}`
, `${PATHS.dir.variants.source}**/*${PATHS.file.template}`
]

let watchNotification = function(){
console.log( '\n\u001b[38;5;202;1m( ͡° ͜ʖ ͡°) WATCHED\n\u001b[0m')
return true
}

GULP.task( 'core-watch-notification', watchNotification )

module.export = watchNotification
13 changes: 13 additions & 0 deletions gulptasks/core/core-watch.js
@@ -0,0 +1,13 @@
'use strict'

const GULP = require( 'gulp' )
, PATHS = require( './core-paths' )
, FILES_TO_WATCH_OUT = [
`${PATHS.dir.styles.source}**/*+(${PATHS.file.lessStylesheets}|${PATHS.file.sassStylesheets})`
, `${PATHS.dir.variants.source}**/*${PATHS.file.template}`
]

let watchAll = () => GULP.watch( FILES_TO_WATCH_OUT, [ 'build' ], [ 'change' ] ) // https://github.com/floatdrop/gulp-watch#api

GULP.task( 'core-watch', watchAll )
module.export = watchAll

0 comments on commit d2a60f4

Please sign in to comment.