template-helpers
Version:
Generic JavaScript helpers that can be used with any template engine. Handlebars, Lo-Dash, Underscore, or any engine that supports helper functions.
270 lines (234 loc) • 5.98 kB
JavaScript
const hasOwn = Object.prototype.hasOwnProperty;
const get = require('get-value');
const utils = require('../utils');
/**
* Specify a fallback value to use when the desired
* value is undefined. Note that undefined variables
* that are _not object properties_ with throw an error.
*
* ```js
* // when `title` is undefined, use the generic `site.title`
* <%= fallback(page.title, site.title) %>
* ```
* @param {*} `a` The desired value.
* @param {*} `b` The fallback ("default") value
* @return {*} Either `a` or `b`
* @api public
*/
exports.fallback = (a, b) => a != null ? a : b;
/**
* Stringify an object using `JSON.stringify()`.
*
* ```js
* <%= stringify({a: "a"}) %>
* //=> '{"a":"a"}'
* ```
* @param {Object} `object`
* @return {String}
* @api public
*/
exports.stringify = obj => JSON.stringify(obj);
/**
* Parse a string into an object using `JSON.parse()`.
*
* ```js
* <%= parse('{"foo":"bar"}')["foo"] %>
* //=> 'bar'
* ```
* @param {String} `str` The string to parse.
* @return {Object} The parsed object.
* @api public
*/
exports.parse = str => utils.isString(str) ? JSON.parse(str) : void 0;
/**
* Use property paths (`a.b.c`) get a nested value from an object.
*
* ```js
* <%= get({a: {b: 'c'}}, 'a.b') %>
* //=> 'c'
* ```
* @param {Object} `object`
* @param {String} `path` Dot notation for the property to get.
* @return {String}
* @api public
*/
exports.get = (obj, prop, options) => get(obj, prop, options);
/**
* Returns an array of keys from the given `object`.
*
* ```js
* <%= keys({a: 'b', c: 'd'}) %>
* //=> '["a", "c"]'
* ```
* @param {Object} `object`
* @return {Array} Keys from `object`
* @api public
*/
exports.keys = obj => Object.keys(obj);
/**
* Return true if the given `value` is an object, and
* not `null` or an array.
*
* ```js
* <%= isObject(['a', 'b', 'c']) %>
* //=> 'false'
*
* <%= isObject({a: 'b'}) %>
* //=> 'true'
* ```
* @param {Object} `value` The value to check.
* @return {Boolean}
* @api public
*/
exports.isObject = utils.isObject;
/**
* Return true if the given `value` is a plain object.
*
* ```js
* <%= isPlainObject(['a', 'b', 'c']) %>
* //=> 'false'
*
* <%= isPlainObject({a: 'b'}) %>
* //=> 'true'
*
* <%= isPlainObject(/foo/g) %>
* //=> 'false'
* ```
* @param {Object} `value` The value to check.
* @return {Boolean}
* @api public
*/
exports.isPlainObject = val => utils.isPlainObject(val);
/**
* Return true if `key` is an own, enumerable property
* of the given `obj`.
*
* @param {Object} `object`
* @param {String} `key`
* @return {Boolean}
* @api public
*/
exports.hasOwn = (obj, key) => hasOwn.call(obj, key);
/**
* Return a copy of `object` exclusing the given `keys`.
*
* ```js
* <%= omit({a: 'a', b: 'b', c: 'c'}, ['a', 'c']) %>
* //=> '{b: "b"}'
* ```
* @param {Object} `object` Object with keys to omit.
* @param {String} `keys` Keys to omit.
* @return {Boolean}
* @api public
*/
exports.omit = (obj, keys) => utils.omit(obj, keys);
/**
* Iterate over the own and inherited enumerable properties of an object,
* and return an object with properties that evaluate to true from the
* callback. Exit early by returning `false`.
*
* ```js
* const context = { values: { a: 'b', c: 'd' } };
* const str = '<% forIn(values, function(val, key) { %><%= val %><% }) %>';
* const fn = _.template(str, { imports: helpers });
* assert.equal(fn(context), 'bd');
* ```
* @param {Object} `object` Object with keys to omit.
* @param {String} `keys` Keys to omit.
* @return {Boolean}
* @api public
*/
exports.forIn = (obj, fn, context) => {
for (let key in obj) {
if (fn.call(context, obj[key], key, obj) === false) {
break;
}
}
};
/**
* Iterate over the own enumerable properties of an object,
* and return an object with properties that evaluate to true
* from the callback. Exit early by returning `false`
*
* ```js
* const context = { values: { a: 'b', c: 'd' } };
* const str = '<% forOwn(values, function(val, key) { %><%= key %><% }) %>';
* const fn = _.template(str, { imports: helpers });
* console.log(fn(context)) //=> 'ac'
* ```
* @param {Object} `object` Object with keys to omit.
* @param {String} `keys` Keys to omit.
* @return {Boolean}
* @api public
*/
exports.forOwn = (obj, fn, context) => {
exports.forIn(obj, (val, key) => {
if (hasOwn.call(obj, key)) {
return fn.call(context, obj[key], key, obj);
}
});
};
/**
* Extend `o` with properties of other `objects`.
*
* @param {Object} `o` The target object. Pass an empty object to shallow clone.
* @param {Object} `objects`
* @return {Object}
* @api public
*/
exports.extend = (obj, ...rest) => {
if (!utils.isObject(obj)) return '';
let last = rest[rest.length - 1];
if (utils.isObject(last) && last.hash) {
rest.pop();
}
let len = rest.length;
if (len === 0) {
return obj;
}
for (let ele of rest) {
if (utils.isObject(ele)) {
for (let key in ele) {
if (exports.hasOwn(ele, key)) {
obj[key] = ele[key];
}
}
}
}
return obj;
};
/**
* Recursively combine the properties of `o` with the
* properties of other `objects`.
*
* @param {Object} `o` The target object. Pass an empty object to shallow clone.
* @param {Object} `objects`
* @return {Object}
* @api public
*/
exports.merge = (obj, ...rest) => {
if (!utils.isObject(obj)) return '';
let last = rest[rest.length - 1];
if (utils.isObject(last) && last.hash) {
rest.pop();
}
let len = rest.length;
if (len === 0) {
return obj;
}
for (let ele of rest) {
if (utils.isObject(ele)) {
for (let key in ele) {
if (exports.hasOwn(ele, key)) {
if (utils.isObject(ele[key])) {
obj[key] = exports.merge(obj[key], ele[key]);
} else {
obj[key] = ele[key];
}
}
}
}
}
return obj;
};
;