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..7c3dca3 100644 --- a/index.js +++ b/index.js @@ -1,43 +1,56 @@ '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 content; + if(path.extname(filename) === '.js') { + content = require(path.resolve(dir + path.sep + filename))[name]; + } else { + content = fs.readFileSync(dir + path.sep + filename, 'utf8'); + } + + if(context) { + fn.call(context, name, content); + } else { + fn(name, content); + } }); }); + }; + + if (options.partials) { + register(options.partials, Handlebars.registerPartial, Handlebars); } + if (options.helpers) { + register(options.helpers, Handlebars.registerHelper, Handlebars); + } return through.obj(function (file, enc, cb) { if (file.isNull()) { @@ -51,7 +64,33 @@ 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)) { + 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'); + } 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": "*" diff --git a/readme.md b/readme.md index b0c66e5..fde6b75 100644 --- a/readme.md +++ b/readme.md @@ -1,64 +1,98 @@ # [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 +## 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. -Install with [npm](https://npmjs.org/package/gulp-compile-handlebars) +The project structure that is working for me looks like this : ``` -npm install --save-dev gulp-compile-handlebars +|-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:_ +`npm install p-j/gulp-compile-handlebars --save-dev` ## Example -### `src/hello.handlebars` +```handlebars +{{!-- src/templates/hello.handlebars --}} -```erb -

Hello {{firstName}}

-

HELLO! {{capitals firstName}}

+

Hello {{firstName}} {{lastName}}

+

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

{{> footer}} ``` -### `gulpfile.js` +```handlebars +{{!-- src/partials/footer.handlebars --}} + + +``` + +```javascript +// src/helpers/capitals.js + +module.exports.capitals = function (str) { + return str.toUpperCase(); +}; +``` + +```yml +# src/data/hello.yaml + +lastName: "Parker" +``` ```js +// gulpfile.js + var gulp = require('gulp'); var handlebars = require('gulp-compile-handlebars'); gulp.task('default', function () { var templateData = { - firstName: 'Kaanon' + firstName: 'Jérémie' }, options = { - partials : { - footer : '' - }, - helpers : { - capitals : function(str){ - return str.toUpperCase(); - } + templates: 'src/templates', + data: 'src/data' + partials : 'src/partials', + helpers : 'src/helpers' } } - return gulp.src('src/hello.handlebars') + return gulp.src(['src/templates/*.handlebars']) .pipe(handlebars(templateData, options)) .pipe(rename('hello.html')) .pipe(gulp.dest('dist')); }); ``` -### `dist/hello.html` - +Result: ```html -

Hello Kaanon

-

HELLO! KAANON

+ +

Hello Jérémie Parker

+

HELLO! JÉRÉMIE PARKER

``` ## 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) +[MIT](http://opensource.org/licenses/MIT) © [Jérémie Parker](http://jeremie-parker.com) diff --git a/test.js b/test.js index 5cefc90..775d96e 100644 --- a/test.js +++ b/test.js @@ -1,18 +1,21 @@ '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 = { + templates: 'test/templates', + 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 +23,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..14b582b --- /dev/null +++ b/test/helpers/toLower.js @@ -0,0 +1,4 @@ +'use strict'; +module.exports.toLower = function (str) { + return str.toLowerCase(); +}; \ 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