From 4a2d283cf5118e484ceeab18ed4693a938514062 Mon Sep 17 00:00:00 2001 From: Jeremie Parker Date: Wed, 11 Jun 2014 16:20:54 +0200 Subject: [PATCH 01/10] added options.data - changed partials and helpers processing options.data can be a string or a boolean or undefined if options.data is a string, it should represent a path to a folder containing data files (json/yaml) if options.data is set to TRUE, the compiler will look for json/yaml files sharing the template files name at the same level. if options.data is undefined or FALSE, no data file will be processed --- partials and helpers parameters should be strings or array of strings it should be path to folders containing partial files or helper file helper file should contain only the logic and not be wrapped in Handlebars.registerHelper() as the plugin does it for you. --- .jshintrc | 24 ++++++++++ index.js | 129 ++++++++++++++++++++++++++++++++++++++++----------- package.json | 6 ++- 3 files changed, 130 insertions(+), 29 deletions(-) create mode 100644 .jshintrc diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..638acbd --- /dev/null +++ b/.jshintrc @@ -0,0 +1,24 @@ +{ + "node": true, + "esnext": true, + "bitwise": true, + "camelcase": true, + "curly": true, + "eqeqeq": true, + "immed": true, + "indent": 4, + "latedef": true, + "newcap": true, + "noarg": true, + "quotmark": "single", + "regexp": true, + "undef": true, + "unused": true, + "strict": true, + "trailing": true, + "smarttabs": true, + "laxcomma": true, + "laxbreak": true, + "shadow": true, + "devel": true +} diff --git a/index.js b/index.js index ac836e4..b5f64d0 100644 --- a/index.js +++ b/index.js @@ -1,43 +1,91 @@ 'use strict'; -var gutil = require('gulp-util'); -var through = require('through2'); -var Handlebars = require('handlebars'); -var fs = require('fs'); +var _ = require('lodash'), +gutil = require('gulp-util'), +through = require('through2'), +Handlebars = require('handlebars'), +yaml = require('js-yaml'), +fs = require('fs'), +path = require('path'); module.exports = function (data, opts) { var options = opts || {}; - //Go through a partials object - if(options.partials){ - for(var p in options.partials){ - Handlebars.registerPartial(p, options.partials[p]); + var register = function(directories, fn, context) { + if(!directories || !_.isFunction(fn)) { + return; } - } - //Go through a helpers object - if(options.helpers){ - for(var h in options.helpers){ - Handlebars.registerHelper(h, options.helpers[h]); + + if(_.isString(directories)) { + directories = [directories]; } - } - // Go through a partials directory array - if(options.batch){ - // Allow single string - if(typeof options.batch === 'string') options.batch = [options.batch]; - - options.batch.forEach(function (b) { - var filenames = fs.readdirSync(b); + directories.forEach(function(dir) { + var filenames = fs.readdirSync(dir); filenames.forEach(function (filename) { - // Needs a better name extractor (maybe with the path module) - var name = filename.split('.')[0]; // Don't allow hidden files - if(!name.length) return; - var template = fs.readFileSync(b + '/' + filename, 'utf8'); - Handlebars.registerPartial(b.split('/').pop() + '/' + name, template); + if(filename.indexOf('.') === 0) { + return; + } + + var name = path.basename(filename, path.extname(filename)); + var template = fs.readFileSync(dir + path.sep + filename, 'utf8'); + console.log(name, filename); + if(context) { + fn.call(context, name, template); + } else { + fn(name, template); + } }); }); + }; + + if (options.partials) { + register(options.partials, Handlebars.registerPartial, Handlebars); + } + + if (options.helpers) { + register(options.helpers, Handlebars.registerHelper, Handlebars); } + // // Go through a partials object + // if (options.partials) { + // for (var p in options.partials) { + // Handlebars.registerPartial(p, options.partials[p]); + // } + // } + + // // Go through a helpers object + // if (options.helpers) { + // for (var h in options.helpers) { + // Handlebars.registerHelper(h, options.helpers[h]); + // } + // } + + // // Go through a partials directory array + // if (options.batch) { + // // Allow single string + // if (typeof options.batch === 'string') { + // options.batch = [options.batch]; + // } + + // options.batch.forEach(function (b) { + // var filenames = fs.readdirSync(b); + + // filenames.forEach(function (filename) { + // // Needs a better name extractor (maybe with the path module) + + // var name = filename.split('.')[0]; + // // Don't allow hidden files + // if (!name.length) { + // return; + // } + // var template = fs.readFileSync(b + '/' + filename, 'utf8'); + // Handlebars.registerPartial(b.split('/') + // .pop() + '/' + name, template); + // }); + // }); + // } + return through.obj(function (file, enc, cb) { if (file.isNull()) { @@ -51,7 +99,34 @@ module.exports = function (data, opts) { } try { - var template = Handlebars.compile(file.contents.toString()); + var template = Handlebars.compile(file.contents.toString()), + jsonFile = '', + yamlFile = '', + dataFile = '', + dataFromFile = {}; + + if (options.data) { + if (_.isString(options.data)) { + var dataPath = process.cwd() + path.sep + options.data + path.sep; + jsonFile = dataPath + path.basename(gutil.replaceExtension(file.path, '.json')); + yamlFile = dataPath + path.basename(gutil.replaceExtension(file.path, '.yaml')); + } else if(_.isBoolean(options.data)) { + jsonFile = gutil.replaceExtension(file.path, '.json'); + yamlFile = gutil.replaceExtension(file.path, '.yaml'); + } else { + throw new Error('options.data should be a string or a boolean'); + } + + if (fs.existsSync(jsonFile)) { + dataFile = jsonFile; + dataFromFile = JSON.parse(fs.readFileSync(jsonFile, 'utf8')); + } else if (fs.existsSync(yamlFile)) { + dataFile = yamlFile; + dataFromFile = yaml.safeLoad(fs.readFileSync(yamlFile, 'utf8')); + } + } + + data = _.extend({}, data, dataFromFile); file.contents = new Buffer(template(data)); } catch (err) { this.emit('error', new gutil.PluginError('gulp-compile-handlebars', err)); diff --git a/package.json b/package.json index 9002524..a4844bf 100644 --- a/package.json +++ b/package.json @@ -29,8 +29,10 @@ ], "dependencies": { "gulp-util": "~2.2.10", - "through2": "~0.4.0", - "handlebars": "~2.0" + "handlebars": "~2.0", + "js-yaml": "^3.0.2", + "lodash": "^2.4.1", + "through2": "~0.4.0" }, "devDependencies": { "mocha": "*" From d64661210fd0894efd9e0d791e2ffcba1a3f7e1b Mon Sep 17 00:00:00 2001 From: Jeremie Parker Date: Wed, 11 Jun 2014 19:12:55 +0200 Subject: [PATCH 02/10] fixed helpers import, added tests, code cleanup --- index.js | 53 ++++++------------------------- readme.md | 55 ++++++++++++++++++++------------- test.js | 23 ++++++++------ test/data/test.yaml | 1 + test/helpers/toLower.js | 4 +++ test/partials/header.handlebars | 1 + 6 files changed, 61 insertions(+), 76 deletions(-) create mode 100644 test/data/test.yaml create mode 100644 test/helpers/toLower.js create mode 100644 test/partials/header.handlebars diff --git a/index.js b/index.js index b5f64d0..233c2af 100644 --- a/index.js +++ b/index.js @@ -28,12 +28,17 @@ module.exports = function (data, opts) { } var name = path.basename(filename, path.extname(filename)); - var template = fs.readFileSync(dir + path.sep + filename, 'utf8'); - console.log(name, filename); + var content; + if(path.extname(filename) === '.js') { + content = require(__dirname + path.sep + dir + path.sep + filename)[name]; + } else { + content = fs.readFileSync(__dirname + path.sep + dir + path.sep + filename, 'utf8'); + } + if(context) { - fn.call(context, name, template); + fn.call(context, name, content); } else { - fn(name, template); + fn(name, content); } }); }); @@ -47,46 +52,6 @@ module.exports = function (data, opts) { register(options.helpers, Handlebars.registerHelper, Handlebars); } - // // Go through a partials object - // if (options.partials) { - // for (var p in options.partials) { - // Handlebars.registerPartial(p, options.partials[p]); - // } - // } - - // // Go through a helpers object - // if (options.helpers) { - // for (var h in options.helpers) { - // Handlebars.registerHelper(h, options.helpers[h]); - // } - // } - - // // Go through a partials directory array - // if (options.batch) { - // // Allow single string - // if (typeof options.batch === 'string') { - // options.batch = [options.batch]; - // } - - // options.batch.forEach(function (b) { - // var filenames = fs.readdirSync(b); - - // filenames.forEach(function (filename) { - // // Needs a better name extractor (maybe with the path module) - - // var name = filename.split('.')[0]; - // // Don't allow hidden files - // if (!name.length) { - // return; - // } - // var template = fs.readFileSync(b + '/' + filename, 'utf8'); - // Handlebars.registerPartial(b.split('/') - // .pop() + '/' + name, template); - // }); - // }); - // } - - return through.obj(function (file, enc, cb) { if (file.isNull()) { this.push(file); diff --git a/readme.md b/readme.md index b0c66e5..3ec8ff3 100644 --- a/readme.md +++ b/readme.md @@ -1,28 +1,43 @@ # [gulp](https://github.com/wearefractal/gulp)-compile-handlebars -Forked from [gulp-template](https://github.com/sindresorhus/gulp-template) -Inspired by [grunt-compile-handlebars](https://github.com/patrickkettner/grunt-compile-handlebars) > Compile [Handlebars templates](http://www.handlebarsjs.com/) ## Install -Install with [npm](https://npmjs.org/package/gulp-compile-handlebars) - -``` -npm install --save-dev gulp-compile-handlebars -``` - +_Soon_ ## Example ### `src/hello.handlebars` ```erb -

Hello {{firstName}}

-

HELLO! {{capitals firstName}}

+

Hello {{firstName}} {{lastName}}

+

HELLO! {{capitals firstName}} {{capitals lastName}}

{{> footer}} ``` +### `src/partials/footer.handlebars` + +```erb +
the end
+``` + +### `src/helpers/capitals.js` + +```javascript +function(str){ + return str.toUpperCase(); +} +``` + +### `src/data/hello.json` + +```json +{ + "lastName": "Parker" +} +``` + ### `gulpfile.js` ```js @@ -31,20 +46,16 @@ var handlebars = require('gulp-compile-handlebars'); gulp.task('default', function () { var templateData = { - firstName: 'Kaanon' + firstName: 'Jérémie' }, options = { - partials : { - footer : '
the end
' - }, - helpers : { - capitals : function(str){ - return str.toUpperCase(); - } + data: 'src/data' + partials : 'src/partials', + helpers : 'src/helpers' } } - return gulp.src('src/hello.handlebars') + return gulp.src('src/*.handlebars') .pipe(handlebars(templateData, options)) .pipe(rename('hello.html')) .pipe(gulp.dest('dist')); @@ -54,11 +65,11 @@ gulp.task('default', function () { ### `dist/hello.html` ```html -

Hello Kaanon

-

HELLO! KAANON

+

Hello Jérémie Parker

+

HELLO! JÉRÉMIE PARKER

the end
``` ## License -MIT © [Kaanon MacFarlane](http://kaanon.com) +MIT © [Kaanon MacFarlane](http://kaanon.com) & [Jérémie PARKER](http://jeremie-parker.com) diff --git a/test.js b/test.js index 5cefc90..ca7abef 100644 --- a/test.js +++ b/test.js @@ -1,18 +1,20 @@ 'use strict'; var assert = require('assert'); var gutil = require('gulp-util'); -var template = require('./index'); +var template = require('./index.js'); it('should compile Handlebars templates', function (cb) { - var stream = template( - { - people: ['foo', 'bar'], - message: 'BAZ' - }, - { - partials : { header : '
' }, - helpers : { toLower : function(str) { return str.toLowerCase(); } } - }); + var data = { + people: ['foo', 'bar'] + }; + + var options = { + data: 'test/data', + partials: 'test/partials', + helpers: 'test/helpers' + }; + + var stream = template(data, options); stream.on('data', function (data) { assert.equal(data.contents.toString(), '
  • foo
  • bar
  • baz'); @@ -20,6 +22,7 @@ it('should compile Handlebars templates', function (cb) { }); stream.write(new gutil.File({ + path: 'test/test.handlebars', contents: new Buffer('{{> header}}{{#each people}}
  • {{.}}
  • {{/each}} {{toLower message}}') })); diff --git a/test/data/test.yaml b/test/data/test.yaml new file mode 100644 index 0000000..5f9a0d8 --- /dev/null +++ b/test/data/test.yaml @@ -0,0 +1 @@ +message: BAZ \ No newline at end of file diff --git a/test/helpers/toLower.js b/test/helpers/toLower.js new file mode 100644 index 0000000..8717ca9 --- /dev/null +++ b/test/helpers/toLower.js @@ -0,0 +1,4 @@ +'use strict'; +module.exports.toLower = function (str) { + return str ? str.toLowerCase() : undefined; +}; \ No newline at end of file diff --git a/test/partials/header.handlebars b/test/partials/header.handlebars new file mode 100644 index 0000000..363f54c --- /dev/null +++ b/test/partials/header.handlebars @@ -0,0 +1 @@ +
    \ No newline at end of file From f8af21e030b1f5e55a1656dbd5d306cbc805a0c6 Mon Sep 17 00:00:00 2001 From: Jeremie Parker Date: Wed, 11 Jun 2014 19:19:05 +0200 Subject: [PATCH 03/10] updates readme's example --- readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 3ec8ff3..0943842 100644 --- a/readme.md +++ b/readme.md @@ -25,9 +25,9 @@ _Soon_ ### `src/helpers/capitals.js` ```javascript -function(str){ +module.exports.capitals = function (str) { return str.toUpperCase(); -} +}; ``` ### `src/data/hello.json` From 21cfc3560daaa1c7efb1d224b01e5283eb514a8f Mon Sep 17 00:00:00 2001 From: Jeremie Parker Date: Wed, 11 Jun 2014 19:19:45 +0200 Subject: [PATCH 04/10] updated test file --- test/helpers/toLower.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/helpers/toLower.js b/test/helpers/toLower.js index 8717ca9..14b582b 100644 --- a/test/helpers/toLower.js +++ b/test/helpers/toLower.js @@ -1,4 +1,4 @@ 'use strict'; module.exports.toLower = function (str) { - return str ? str.toLowerCase() : undefined; + return str.toLowerCase(); }; \ No newline at end of file From c429d23db95fb0be9268a35999ca08c37309571a Mon Sep 17 00:00:00 2001 From: Jeremie Parker Date: Wed, 11 Jun 2014 19:26:31 +0200 Subject: [PATCH 05/10] fix path error --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 233c2af..c278285 100644 --- a/index.js +++ b/index.js @@ -30,9 +30,9 @@ module.exports = function (data, opts) { var name = path.basename(filename, path.extname(filename)); var content; if(path.extname(filename) === '.js') { - content = require(__dirname + path.sep + dir + path.sep + filename)[name]; + content = require(path.resolve(dir + path.sep + filename))[name]; } else { - content = fs.readFileSync(__dirname + path.sep + dir + path.sep + filename, 'utf8'); + content = fs.readFileSync(dir + path.sep + filename, 'utf8'); } if(context) { From 94f0d5a3c71af5109ec813294ce05386d0546f90 Mon Sep 17 00:00:00 2001 From: Jeremie Parker Date: Wed, 11 Jun 2014 21:01:09 +0200 Subject: [PATCH 06/10] handle subfolders for data --- index.js | 5 ++--- readme.md | 5 +++-- test.js | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index c278285..7c3dca3 100644 --- a/index.js +++ b/index.js @@ -72,9 +72,8 @@ module.exports = function (data, opts) { if (options.data) { if (_.isString(options.data)) { - var dataPath = process.cwd() + path.sep + options.data + path.sep; - jsonFile = dataPath + path.basename(gutil.replaceExtension(file.path, '.json')); - yamlFile = dataPath + path.basename(gutil.replaceExtension(file.path, '.yaml')); + jsonFile = gutil.replaceExtension(file.path.replace(options.templates, options.data), '.json'); + yamlFile = gutil.replaceExtension(file.path.replace(options.templates, options.data), '.yaml'); } else if(_.isBoolean(options.data)) { jsonFile = gutil.replaceExtension(file.path, '.json'); yamlFile = gutil.replaceExtension(file.path, '.yaml'); diff --git a/readme.md b/readme.md index 0943842..4ef4bfc 100644 --- a/readme.md +++ b/readme.md @@ -8,7 +8,7 @@ _Soon_ ## Example -### `src/hello.handlebars` +### `src/templates/hello.handlebars` ```erb

    Hello {{firstName}} {{lastName}}

    @@ -49,13 +49,14 @@ gulp.task('default', function () { firstName: 'Jérémie' }, options = { + templates: 'src/templates', data: 'src/data' partials : 'src/partials', helpers : 'src/helpers' } } - return gulp.src('src/*.handlebars') + return gulp.src(['src/templates/*.handlebars']) .pipe(handlebars(templateData, options)) .pipe(rename('hello.html')) .pipe(gulp.dest('dist')); diff --git a/test.js b/test.js index ca7abef..775d96e 100644 --- a/test.js +++ b/test.js @@ -9,6 +9,7 @@ it('should compile Handlebars templates', function (cb) { }; var options = { + templates: 'test/templates', data: 'test/data', partials: 'test/partials', helpers: 'test/helpers' From e2bcee20809e5f636262752fbe07796d09f34092 Mon Sep 17 00:00:00 2001 From: Jeremie Parker Date: Thu, 12 Jun 2014 01:43:11 +0200 Subject: [PATCH 07/10] readme format --- readme.md | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/readme.md b/readme.md index 4ef4bfc..91a761c 100644 --- a/readme.md +++ b/readme.md @@ -8,39 +8,37 @@ _Soon_ ## Example -### `src/templates/hello.handlebars` +```handlebars +{{!-- src/templates/hello.handlebars --}} -```erb

    Hello {{firstName}} {{lastName}}

    HELLO! {{capitals firstName}} {{capitals lastName}}

    {{> footer}} ``` -### `src/partials/footer.handlebars` +```handlebars +{{!-- src/partials/footer.handlebars --}} -```erb
    the end
    ``` -### `src/helpers/capitals.js` - ```javascript +// src/helpers/capitals.js + module.exports.capitals = function (str) { return str.toUpperCase(); }; ``` -### `src/data/hello.json` +```yml +# src/data/hello.yaml -```json -{ - "lastName": "Parker" -} +lastName: "Parker" ``` -### `gulpfile.js` - ```js +// gulpfile.js + var gulp = require('gulp'); var handlebars = require('gulp-compile-handlebars'); @@ -63,9 +61,9 @@ gulp.task('default', function () { }); ``` -### `dist/hello.html` - +Result: ```html +

    Hello Jérémie Parker

    HELLO! JÉRÉMIE PARKER

    the end
    From 7c9581451dcb948103b270b0baf3709843e2ae37 Mon Sep 17 00:00:00 2001 From: Jeremie Parker Date: Thu, 12 Jun 2014 09:23:54 +0200 Subject: [PATCH 08/10] readme --- readme.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 91a761c..d74e762 100644 --- a/readme.md +++ b/readme.md @@ -4,7 +4,8 @@ ## Install -_Soon_ +_Currently:_ +`npm install https+git@github.com:p-j/gulp-compile-handlebars.git --save-dev` ## Example @@ -70,5 +71,6 @@ Result: ``` ## License +Based on [Kaanon MacFarlane](http://kaanon.com) works which was itself base on [Sindre Sorhus](http://sindresorhus.com)'s. -MIT © [Kaanon MacFarlane](http://kaanon.com) & [Jérémie PARKER](http://jeremie-parker.com) +[MIT](http://opensource.org/licenses/MIT) © [Jérémie PARKER](http://jeremie-parker.com) From cf8cbd17a014af47df0305a34243c9a794fcf76b Mon Sep 17 00:00:00 2001 From: Jeremie Parker Date: Thu, 12 Jun 2014 09:35:45 +0200 Subject: [PATCH 09/10] what's new in this fork --- readme.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/readme.md b/readme.md index d74e762..9aa283c 100644 --- a/readme.md +++ b/readme.md @@ -2,6 +2,28 @@ > Compile [Handlebars templates](http://www.handlebarsjs.com/) +## New in this fork + +In this fork, the focus is to make it dead simple to build static website using handlebars, in a well structured project. + +It means that it is pretty opinionated as to how you should organised your files. + +The project structure that is working for me looks like this : + +``` +|-src +|---data +|-----en +|-----fr +|---helpers +|---partials +|---templates +|-----en +|-----fr +``` + +I've taken the example of a multilang website to insist on the fact that the data folder structure should mimic the templates folder structure. + ## Install _Currently:_ From 1cf2edfc862e454c6980e0aa5e81dda24d6e7be9 Mon Sep 17 00:00:00 2001 From: Jeremie Parker Date: Mon, 29 Sep 2014 14:42:48 +0200 Subject: [PATCH 10/10] updated readme (fixed npm install instruction) --- readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 9aa283c..fde6b75 100644 --- a/readme.md +++ b/readme.md @@ -27,7 +27,7 @@ I've taken the example of a multilang website to insist on the fact that the dat ## Install _Currently:_ -`npm install https+git@github.com:p-j/gulp-compile-handlebars.git --save-dev` +`npm install p-j/gulp-compile-handlebars --save-dev` ## Example @@ -95,4 +95,4 @@ Result: ## License Based on [Kaanon MacFarlane](http://kaanon.com) works which was itself base on [Sindre Sorhus](http://sindresorhus.com)'s. -[MIT](http://opensource.org/licenses/MIT) © [Jérémie PARKER](http://jeremie-parker.com) +[MIT](http://opensource.org/licenses/MIT) © [Jérémie Parker](http://jeremie-parker.com)