UNPKG

load-helpers

Version:

Load helpers with patterns, as an object, key-value pair, or module.

266 lines (231 loc) 6.45 kB
'use strict'; var path = require('path'); var glob = require('matched'); var set = require('set-value'); var typeOf = require('kind-of'); var extend = require('extend-shallow'); var Emitter = require('component-emitter'); var isValidGlob = require('is-valid-glob'); var isGlob = require('is-glob'); var utils = require('./utils'); /** * Create an instance of `Loader` with the given `options`. * * ```js * var Loader = require('load-helpers'); * var loader = new Loader(); * ``` * @param {Object} `options` * @api public */ function Loader(options) { if (!(this instanceof Loader)) { return new Loader(options); } this.options = options || {}; this.cache = this.options.helpers || {}; } /** * Inherit `Emitter` */ Loader.prototype = Object.create(Emitter.prototype); Loader.prototype.constructor = Loader; /** * Register a helper function by `name`. * * ```js * loader.addHelper('foo', function() { * // do stuff * }); * ``` * @param {String} `name` * @param {Function} `fn` * @param {Object} `options` * @api public */ Loader.prototype.addHelper = function(name, fn, options) { if (typeof name !== 'string') { throw new TypeError('expected helper name to be a string'); } if (typeof fn !== 'function') { throw new TypeError('expected helper to be a function'); } if (typeof options === 'boolean') { options = { async: options }; } if (options && options.async === true) { fn.async = true; } set(this.cache, name, fn); this.emit('helper', name, fn); return this; }; /** * Register an object of helper functions. * * ```js * loader.addHelpers({ * foo: function() {}, * bar: function() {}, * baz: function() {} * }); * ``` * @param {Object} `helpers` * @param {Object} `options` * @api public */ Loader.prototype.addHelpers = function(helpers, options) { var keys = Object.keys(helpers); for (var i = 0; i < keys.length; i++) { var key = keys[i]; this.addHelper(key, helpers[key], options); } return this; }; /** * Load one or more helpers from a filepath, glob pattern, object, or * an array of any of those things. This method detects the type of * value to be handled then calls one of the other methods to do the * actual loading. * * ```js * var loader = new Loader(); * console.log(loader.load(['foo/*.hbs', 'bar/*.hbs'])); * console.log(loader.load({path: 'a/b/c.md'})); * console.log(loader.load('index', {path: 'a/b/c.md'})); * ``` * @param {Object} `helpers` * @param {Object} `options` * @return {Object} Returns the views from `loader.helpers` * @api public */ Loader.prototype.load = function(val, options) { if (typeof options === 'boolean') { options = { isAsync: options }; } switch (typeOf(val)) { case 'string': case 'function': return this.loadHelper.apply(this, arguments); case 'object': return this.loadHelpers.apply(this, arguments); case 'array': if (isValidGlob(val)) { var cache = this.loadGlob.apply(this, arguments); return this.loadHelpers(cache, options); } return this.loadHelpers.apply(this, arguments); default: { throw new TypeError('expected name to be an object, array or string'); } } }; Loader.prototype.loadHelper = function(name, fn, options) { if (utils.isObject(fn)) { options = fn; fn = undefined; } if (isGlob(name)) { var cache = this.loadGlob.apply(this, arguments); return this.loadHelpers(cache, options); } var opts = utils.options(this, options); if (typeof fn === 'string') { fn = utils.tryRequire(fn, opts); } if (typeof fn !== 'function') { var val = this.loadFile(name, options); if (utils.isObject(val)) { return this.addHelpers(val, options); } } this.addHelper(name, fn, options); return this; }; Loader.prototype.loadHelpers = function(helpers, options) { if (Array.isArray(helpers) && helpers.length) { for (var i = 0; i < helpers.length; i++) { this.load(helpers[i], options); } } else if (utils.isObject(helpers)) { var keys = Object.keys(helpers); var len = keys.length; var idx = -1; while (++idx < len) { var name = keys[idx]; var helper = helpers[name]; var type = typeOf(helper); switch (type) { case 'function': this.loadHelper(name, helper, options); break; case 'object': this.loadGroup(name, helper, options); break; case 'string': this.loadHelper(name, utils.tryRequire(helper), options); break; case 'array': if (isValidGlob(helper)) { var cache = this.loadGlob(helper, options); this.loadGroup(name, cache, options); } else { for (var j = 0; j < helper.length; j++) { var val = helper[j]; if (isValidGlob(val)) { val = this.loadGlob(val, options); } this.loadGroup(name, val, options); } } break; default: { throw new TypeError('unsupported helper type: ' + type); } } } } return this; }; Loader.prototype.loadGroup = function(name, helpers, options) { if (typeof helpers === 'function') { this.addHelper(name, helpers); } var keys = Object.keys(helpers); for (var j = 0; j < keys.length; j++) { var key = keys[j]; this.addHelper(name + '.' + key, helpers[key], options); } return this; }; Loader.prototype.loadFile = function(name, options) { if (typeof name !== 'string') { throw new TypeError('expected filepath or module name to be a string'); } var opts = utils.options(this, options); var val = utils.tryRequire(name, opts); if (typeof val === 'function') { var cache = {}; name = utils.renameHelper(name, opts); cache[name] = val; return cache; } return val; }; Loader.prototype.loadGlob = function(patterns, options) { var opts = utils.options(this, options); var cache = {}; if (typeof patterns === 'string' && !isGlob(patterns)) { return this.loadFile(patterns, options); } var files = glob.sync(patterns, opts); for (var i = 0; i < files.length; i++) { var name = path.resolve(opts.cwd, files[i]); cache = extend({}, cache, this.loadFile(name, opts)); } return cache; }; /** * Expose `Loader` */ module.exports = Loader;