Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

load-templates

Package Overview
Dependencies
Maintainers
1
Versions
47
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

load-templates - npm Package Compare versions

Comparing version 0.3.2 to 0.4.0

.travis.yml

41

.verbrc.md

@@ -1,33 +0,4 @@

---
tags: ['verb-tag-jscomments']
---
# {%= name %} {%= badge("fury") %}
> {%= description %}
This is a template loader, pure and simple, no rendering or caching.
Currently any of the following input configurations will return a normalized template object with the same root properties:
```js
loader.load(['abc/*.hbs']);
loader.load('abc/*.md');
loader.load({'abc/def.md': {content: 'ghi.', lmn: 'xyz'});
loader.load({'abc/def.md': {content: 'ghi.'}}, {lmn: 'xyz'});
loader.load({path: 'abc/def.md', content: 'ghi.'}, {lmn: 'xyz'});
loader.load({path: 'abc/def.md', content: 'ghi.', lmn: 'xyz'});
loader.load('abc.md', 'def <%= name %>.');
loader.load('abc.md', 'def <%= name %>.', {name: 'Jon Schlinkert'});
```
**Root properties**
* `path`: file path or key to use for the template.
* `data`: data from the parsed template, e.g. front-matter.
* `locals`: locals pass on one of the loader methods. (keeping locals and data seperate allows you to merge however you need to.)
* `content`: the parsed content of the template
* `orig`: the original content of the template, if parsed
Properties that are not on this list will be moved/copied to the `data` object and retained on `orig`.
## Install

@@ -42,5 +13,13 @@ {%= include("install") %}

## API
{%= jscomments("index.js") %}
## Usage
```js
var loader = require('{%= name %}');
```
### Valid formats
See [the docs](./docs.md) for valid formats. WIP.
## Author

@@ -47,0 +26,0 @@ {%= include("author") %}

@@ -0,36 +1,72 @@

/*!
* load-templates <https://github.com/jonschlinkert/load-templates>
*
* Copyright (c) 2014 Jon Schlinkert, contributors.
* Licensed under the MIT License
*/
'use strict';
var fs = require('fs');
var path = require('path');
var typeOf = require('kind-of');
var extend = require('mixin-deep');
var hasAny = require('has-any');
var debug = require('debug')('load-templates');
var hasAnyDeep = require('has-any-deep');
var omit = require('omit-keys');
var mapFiles = require('map-files');
var matter = require('gray-matter');
var omitEmpty = require('omit-empty');
var reduce = require('reduce-object');
var utils = require('./lib/utils');
var _ = require('lodash');
/**
* Module dependencies
* If we detected a `path` property directly on the object
* that was passed, this means that the object is not
* formatted with a key (as expected).
*
* ```js
* // before
* loader({path: 'a/b/c.md', content: 'this is foo'});
*
* // after
* loader('a/b/c.md': {path: 'a/b/c.md', content: 'this is foo'});
* ```
*
* @param {String} `path`
* @param {Object} `value`
* @return {Object}
*/
var _ = require('lodash');
var fs = require('fs');
var path = require('path');
var glob = require('globby');
var isRelative = require('is-relative');
var arrayify = require('arrayify-compact');
var extend = _.extend;
function createKeyFromPath(filepath, value) {
var o = {};
o[filepath] = value;
return o;
}
/**
* Create the `path` property from the string
* passed in the first arg. This is only used
* when the second arg is a string.
*
* ```js
* var loader = require('load-templates');
* loader('abc', {content: 'this is content'});
* //=> normalize('abc', {path: 'abc', content: 'this is content'});
* ```
* @param {Object} `options` Options to initialize `loader` with
* @option {String} [option] `cwd` Current working directory to use for file paths.
* @option {Function} [option] `parse` Function to use for parsing templates.
* @option {Function} [option] `rename` Renaming function to use on the `key` of each template loaded, `path.basename` is the default.
* @option {Function} [option] `normalize` Function to use for normalizing `file` objects before they are returned.
* @option {Boolean} [option] `norename` Set to `true` to disable the default `rename` function.
* @option {Boolean} [option] `noparse` Set to `true` to disable the default `parse` function.
* @option {Boolean} [option] `nonormalize` Set to `true` to disable the default `normalize` function.
* @api public
*
* @param {Object} a
* @return {Object}
*/
function loader(options) {
loader.options = extend({
cwd: process.cwd()
}, options);
return loader;
function createPathFromStringKey(o) {
for (var key in o) {
if (o.hasOwnProperty(key)) {
o[key].path = o[key].path || key;
}
}
return o;
}

@@ -40,243 +76,546 @@

/**
* Options cache.
* Default function for reading any files resolved.
*
* @type {Object}
* Pass a custom `parseFn` function on the options to change
* how files are parsed.
*
* @param {String} `filepath`
* @param {Object} `options`
* @return {Object}
*/
loader.options = {};
function readFn(filepath, options) {
var opts = extend({ enc: 'utf8' }, options);
if (opts.readFn) {
return opts.readFn(filepath, options);
}
return fs.readFileSync(filepath, opts.enc);
}
/**
* Properties expected on the root of a normalized `file` object.
* Default function for parsing any files resolved.
*
* @type {Array}
* Pass a custom `parseFn` function on the options to change
* how files are parsed.
*
* @param {String} `filepath`
* @param {Object} `options`
* @return {Object}
*/
loader.rootProps = ['data', 'locals', 'content', 'path', 'orig'];
function parseFn(str, options) {
var opts = extend({ autodetect: true }, options);
if (opts.parseFn) {
return opts.parseFn(str, options);
}
opts = omit(options, ['delims']);
return matter(str, opts);
}
/**
* Expand glob patterns, load, read, parse and normalize files from
* file paths, strings, objects, or arrays of these types.
* [parseContent description]
*
* **Examples:**
* @param {[type]} value
* @param {[type]} options
* @return {[type]}
*/
function parseContent(obj, options) {
debug('parsing content', obj);
var o = extend({}, obj);
if (utils.isString(o.content) && !o.hasOwnProperty('orig')) {
var orig = o.content;
o = parseFn(o.content, options);
o.orig = orig;
}
o._parsed = true;
return o;
}
/**
* Rename the key of a template object.
*
* Filepaths or arrays of glob patterns.
* Pass a custom `renameKey` function on the options to change
* how keys are renamed.
*
* ```js
* var temlates = loader.load(['pages/*.hbs']);
* var posts = loader.load('posts/*.md');
* ```
* @param {String} `key`
* @param {Object} `options`
* @return {Object}
*/
function renameKey(key, options) {
debug('renaming key:', key);
var opts = options || {};
if (opts.renameKey) {
return opts.renameKey(key, options);
}
return key;
}
/**
* Map files resolved from glob patterns or file paths.
*
* As strings or objects:
*
* @param {String|Array} `patterns`
* @param {Object} `options`
* @return {Object}
*/
function mapFilesFn(patterns, options) {
debug('mapping files:', patterns);
var files = mapFiles(patterns, extend({
rename: renameKey,
parse: readFn
}, options));
return reduce(files, function (acc, value, key) {
debug('reducing file: %s', key, value);
if (utils.isString(value)) {
value = parseFn(value);
value.path = value.path || key;
}
value._parsed = true;
value._mappedFile = true;
acc[key] = value;
return acc;
}, {});
}
/**
* First arg is a file path or glob pattern.
*
* ```js
* // loader.load(key, value, locals);
* var docs = loader.load({'foo/bar.md': {content: 'this is content.'}}, {foo: 'bar'});
*
* // loader.load(key, value, locals);
* var post = loader.load('abc.md', 'My name is <%= name %>.', {name: 'Jon Schlinkert'});
* loader('a/b/c.md', ...);
* loader('a/b/*.md', ...);
* ```
*
* @param {String|Object|Array} `key` Array, object, string or file paths.
* @param {String|Object} `value` String of content, `file` object with `content` property, or `locals` if the first arg is a file path.
* @param {Object} `options` Options or `locals`.
* @return {Object} Normalized file object.
* @api public
* @param {String} `key`
* @param {Object} `value`
* @return {Object}
*/
loader.load = function (key, value, options) {
var method = loader[typeOf(key)];
if (method) {
return method.call(this, key, value, options);
function normalizeFiles(patterns, locals, options) {
debug('normalizing patterns: %s', patterns);
var files = mapFilesFn(patterns, options);
var locs = {};
var opts = {};
if (locals && utils.isObject(locals)) {
locs = utils.pickLocals(locals);
opts = utils.pickOptions(locals);
}
};
if (options && utils.isObject(options)) {
opts = _.merge({}, opts, options);
}
if (files && Object.keys(files).length === 0) {
return null;
}
return reduce(files, function (acc, value, key) {
debug('reducing normalized file: %s', key);
extend(opts, options);
value.options = utils.flattenOptions(opts);
value.locals = utils.flattenLocals(locs);
acc[key] = value;
return acc;
}, {});
}
/**
* Expand glob patterns, load, read, parse and normalize files
* from file paths or strings.
* First value is a string, second value is a string or
* an object.
*
* @param {String} `key` Glob patterns or file paths.
* @param {String|Object} `value` String of content, `file` object with `content` property, or `locals` if the first arg is a file path.
* @param {Object} `options` Options or `locals`.
* @return {Object} Normalized file object.
* @api public
* - first arg can be a file-path
* - first arg can be a non-file-path string
* - first arg can be a glob pattern
* - second arg can a string
* - when the second arg is a string, the first arg cannot be a file path
* - the second can be an object
* - when the second arg is an object, it may _be_ locals
* - when the second arg is an object, it may _have_ an `options` property
* - the second can be an object
* - in this pattern, when a third arg exists, it _must be_ the options object.
* - when a third arg exists, the second arg may still have an options property
* - when a third arg exists, `options` and `locals.options` are merged.
*
* **Examples:**
*
* ```js
* template.normalize('a/b/c.md');
* template.normalize('a/b/c.md', 'this is content');
* template.normalize('a/b/c.md', {content: 'this is content'});
* template.normalize('a/b/c.md', {path: 'a/b/c.md'});
* template.normalize('a/b/c.md', {path: 'a/b/c.md', content: 'this is content'});
* template.normalize('a/b/c.md', {path: 'a/b/c.md'}, {a: 'b'});
* template.normalize('a/b/c.md', {path: 'a/b/c.md'}, {a: 'b'}, {c: 'd'});
* template.normalize('a/b/c.md', {path: 'a/b/c.md'}, {a: 'b', options: {c: 'd'}});
* template.normalize('a/b/c.md', {path: 'a/b/c.md', locals: {a: 'b'}, options: {c: 'd'}});
* ```
*
* @param {Object} `value` Always an object.
* @param {Object} `locals` Always an object.
* @param {Object} `options` Always an object.
* @return {Object} Returns a normalized object.
*/
loader.string = function (key, value, options) {
var args = [].slice.call(arguments).filter(Boolean);
var file = {}, files = [];
function normalizeString(key, value, locals, options) {
debug('normalizing string: %s', key, value);
if (typeof args[1] === 'string') {
file[key] = {};
file[key].content = value;
} else {
var patterns = arrayify(key).map(function(pattern) {
if (!isRelative) {
return pattern;
}
return loader._cwd(pattern);
});
var objects = utils.valuesOfType('object', arguments);
var args = [].slice.call(arguments, 1);
var props = utils.siftProps.apply(utils.siftProps, args);
var opts = options || props.options;
var locs = props.locals;
var files;
var root = {};
var opt = {};
var o = {};
o[key] = {};
files = glob.sync(patterns, {nonull: false});
if (files.length > 0) {
files.forEach(function (filepath) {
var key = loader.rename(filepath);
file[key] = loader.parse(filepath);
file[key].path = filepath;
file[key].data = extend({}, file[key].data, value);
});
// If only `key` is defined
if (value == null) {
// see if `key` is a value file path
files = normalizeFiles(key);
if (files != null) {
return files;
// if not, add a heuristic
} else {
file[key] = value || {};
o[key]._invalidpath = true;
o[key].path = o[key].path || key;
return o;
}
}
// The object should be parsed and key renamed.
return loader.object(file, options);
};
if ((value && utils.isObject(value)) || objects == null) {
debug('[value] s1o1: %s, %j', key, value);
files = normalizeFiles(key, value, locals, options);
if (files != null) {
return files;
} else {
debug('[value] s1o2: %s, %j', key, value);
root = utils.pickRoot(value);
var loc = {};
opt = {};
loc = _.merge({}, loc, utils.pickLocals(value));
loc = _.merge({}, loc, locals);
opt = _.merge({}, opt, loc.options);
opt = _.merge({}, opt, value.options);
opt = _.merge({}, opt, options);
_.merge(root, utils.pickRoot(loc));
_.merge(root, utils.pickRoot(opt));
o[key] = root;
o[key].locals = loc;
o[key].options = opt;
o[key].path = value.path || key;
var content = value && value.content;
if (o[key].content == null && content != null) {
o[key].content = content;
}
}
}
if (value && utils.isString(value)) {
debug('[value] string: %s, %s', key, value);
root = utils.pickRoot(locals);
o[key] = root;
o[key].content = value;
o[key].path = o[key].path = key;
o[key]._s1s2 = true;
if (objects == null) {
return o;
}
}
// TODO: when would this happen?
if (locals && utils.isObject(locals)) {
o[key]._s1s2o1 = true;
}
// TODO: when would this happen?
if (options && utils.isObject(options)) {
o[key]._s1s2o1o2 = true;
}
opt = utils.flattenOptions(opts);
opt = extend({}, opt, o[key].options);
o[key].options = opt;
locs = omit(locs, 'options');
o[key].locals = utils.flattenLocals(locs);
return o;
}
/**
* Normalize an array of patterns.
* Normalize objects that have `rootKeys` directly on
* the root of the object.
*
* @param {Object} `patterns` Glob patterns or array of filepaths.
* @param {Object} `options` Options or `locals`
* @return {Array} Array of normalized file objects.
* @api public
* **Example**
*
* ```js
* {path: 'a/b/c.md', content: 'this is content.'}
* ```
*
* @param {Object} `value` Always an object.
* @param {Object} `locals` Always an object.
* @param {Object} `options` Always an object.
* @return {Object} Returns a normalized object.
*/
loader.array = function (patterns, options) {
var o = {};
arrayify(patterns).forEach(function (pattern) {
extend(o, loader.load(pattern, options));
});
function normalizeShallowObject(value, locals, options) {
debug('normalizing shallow object: %j', value);
var o = utils.siftLocals(value);
o.options = extend({}, options, o.options);
o.locals = extend({}, locals, o.locals);
return o;
};
}
/**
* Normalize a template object.
* Normalize nested templates that have the following pattern:
*
* @param {Object} `file` The object to normalize.
* @param {Object} `options` Options or `locals`
* @api public
* ```js
* => {'a/b/c.md': {path: 'a/b/c.md', content: 'this is content.'}}
* ```
* or:
*
* ```js
* { 'a/b/a.md': {path: 'a/b/a.md', content: 'this is content.'},
* 'a/b/b.md': {path: 'a/b/b.md', content: 'this is content.'},
* 'a/b/c.md': {path: 'a/b/c.md', content: 'this is content.'} }
*```
*/
loader.object = function (file, options) {
return this.normalize(file, options);
};
function normalizeDeepObject(obj, locals, options) {
debug('normalizing deep object: %j', obj);
return reduce(obj, function (acc, value, key) {
acc[key] = normalizeShallowObject(value, locals, options);
return acc;
}, {});
}
/**
* The current working directory to use. Default is `process.cwd()`.
* When the first arg is an object, all arguments
* should be objects.
*
* @param {String} `filepath`
* @api public
* ```js
* loader({'a/b/c.md', ...});
*
* // or
* loader({path: 'a/b/c.md', ...});
* ```
*
* @param {Object} `object` Template object
* @param {Object} `locals` Possibly locals, with `options` property
* @return {Object} `options` Possibly options
*/
loader._cwd = function (filepath) {
var cwd = path.resolve(this.options.cwd);
return path.join(cwd, filepath);
};
function normalizeObject(o) {
debug('normalizing object: %j', o);
var args = [].slice.call(arguments);
var locals1 = utils.pickLocals(args[1]);
var locals2 = utils.pickLocals(args[2]);
var val;
var opts = args.length === 3 ? locals2 : {};
if (hasAny(o, ['path', 'content'])) {
val = normalizeShallowObject(o, locals1, opts);
return createKeyFromPath(val.path, val);
}
if (hasAnyDeep(o, ['path', 'content'])) {
val = normalizeDeepObject(o, locals1, opts);
return createPathFromStringKey(val);
}
throw new Error('Invalid template object. Must' +
'have a `path` or `content` property.');
}
/**
* Rename the `key` of each template loaded using whatever rename function
* is defined on the options. `path.basename` is the default.
* When the first arg is an array, assume it's glob
* patterns or file paths.
*
* @param {String} `filepath`
* @api public
* ```js
* loader(['a/b/c.md', 'a/b/*.md']);
* ```
*
* @param {Object} `patterns` Template object
* @param {Object} `locals` Possibly locals, with `options` property
* @return {Object} `options` Possibly options
*/
loader.rename = function (filepath) {
if (this.options.rename) {
return this.options.rename(filepath);
}
return filepath;
};
function normalizeArray(patterns, locals, options) {
debug('normalizing array:', patterns);
var opts = extend({}, locals && locals.options, options);
return normalizeFiles(patterns, locals, opts);
}
/**
* Parse the content of each template loaded using whatever parsing function
* is defined on the options. `fs.readFileSync` is used by default.
* When the first arg is an array, assume it's glob
* patterns or file paths.
*
* @param {String} `filepath` The path of the file to read/parse.
* @param {Object} `Options` Options or `locals`.
* @api public
* ```js
* loader(['a/b/c.md', 'a/b/*.md']);
* ```
*
* @param {Object} `patterns` Template object
* @param {Object} `locals` Possibly locals, with `options` property
* @return {Object} `options` Possibly options
*/
loader.parse = function (filepath, options) {
var remove = _.keys(this.options).concat('normalized');
var opts = extend({}, this.options, options);
var o = {};
function normalizeFn(fn, options) {
var file = fn.call(null, options);
debug('normalizing fn:', file);
return file;
}
if (opts.noparse) {
return filepath;
}
if (opts.parse) {
return opts.parse(filepath, _.omit(opts, remove));
/**
* Normalize base template formats.
*/
function normalizeFormat() {
var args = [].slice.call(arguments);
debug('normalize format', args);
switch (typeOf(args[0])) {
case 'string':
return normalizeString.apply(null, args);
case 'object':
return normalizeObject.apply(null, args);
case 'array':
return normalizeArray.apply(null, args);
case 'function':
return normalizeFn.apply(null, args);
default:
return {};
}
}
o.path = filepath;
o.content = fs.readFileSync(filepath, 'utf8');
o.data = _.omit(opts, remove);
return o;
};
/**
* Normalize a template using whatever normalize function is
* defined on the options.
* Final normalization step to remove empty values and rename
* the object key. By now the template should be _mostly_
* loaderd.
*
* @param {Object} `file` The template object to normalize.
* @param {Object} `Options` Options or `locals`.
* @api public
* @param {Object} `object` Template object
* @return {Object}
*/
loader.normalize = function (file, options) {
var remove = _.keys(this.options).concat('normalized');
var opts = _.extend({}, this.options, options);
var loader = function (options) {
options = extend({}, options);
debug('loader', options);
if (opts.nonormalize) {
return file;
}
return function(obj) {
debug('pre-normalize', obj);
if (opts.normalize) {
return opts.normalize(file);
}
var o = {}, data = _.omit(opts, remove);
obj = normalizeFormat.apply(null, arguments);
_.forIn(file, function (value, key) {
value.path = value.path || key;
return reduce(obj, function (acc, value, key) {
if (value && Object.keys(value).length === 0) {
return acc;
}
if (!value.hasOwnProperty('normalized')) {
key = loader.rename(key);
delete value.normalized;
}
// save the content for comparison after parsing
var opts = {};
var root = _.pick(value, loader.rootProps);
root.data = extend({}, data, value.data, _.omit(value, loader.rootProps));
o[key] = root;
});
extend(opts, options, value.options);
value.ext = value.ext || path.extname(value.path);
return o;
var parsed = parseContent(value, opts);
value = _.merge({}, value, parsed);
if (value.content === value.orig) {
value = omit(value, 'orig');
}
if (opts.debug == null) {
value = omit(value, utils.heuristics);
}
value = omitEmpty(value);
acc[renameKey(key, opts)] = value;
loader.normalize(opts, acc, value, key);
return acc;
}, {});
};
};
loader.normalize = function (options, acc, value, key) {
debug('normalize: %s, %value', key);
if (options && options.normalize) {
return options.normalize(acc, value, key);
}
acc[key] = value;
return acc;
};
loader.valueOnly = function (options) {
debug('valueOnly:', options);
var fn = loader(options);
return function(obj) {
return reduce(fn(obj), function(acc, value) {
value.ext = value.ext || path.extname(value.path);
return value;
}, {});
};
};
/**
* Get the type of an object.
* Expose utils
*/
loader.generateKey = utils.generateKey;
loader.generateId = utils.generateId;
/**
* Expose `loader`
*
* @param {*} value
* @return {*}
* @api private
* @type {Object}
*/
function typeOf(value) {
return Object.prototype.toString.call(value).toLowerCase()
.replace(/\[object ([\S]+)\]/, '$1');
}
module.exports = loader;
{
"name": "load-templates",
"description": "Load templates from file paths, globs or objects, and cache them as normalized objects.",
"version": "0.3.2",
"description": "Load templates.",
"version": "0.4.0",
"homepage": "https://github.com/jonschlinkert/load-templates",

@@ -23,66 +23,2 @@ "author": {

],
"keywords": [
"assemble",
"atpl",
"cache",
"compile",
"consolidate",
"content",
"data",
"delimiters",
"delims",
"docs",
"documentation",
"dot",
"dust",
"dustjs-helpers",
"dustjs-linkedin",
"eco",
"ect",
"ejs",
"engine",
"engines",
"express",
"front",
"generate",
"generator",
"gray-matter",
"haml-coffee",
"hamljs",
"handlebars",
"hogan.js",
"jade",
"jazz",
"jqtpl",
"liquor",
"lo-dash",
"lodash",
"markdown",
"matter",
"mocha",
"mote",
"mustache",
"noop",
"nunjucks",
"parse",
"parser",
"parsers",
"pass-through",
"process",
"qejs",
"ractive",
"render",
"should",
"swig",
"template",
"templates",
"templayed",
"toffee",
"underscore",
"verb",
"view",
"walrus",
"whiskers",
"yaml"
],
"main": "index.js",

@@ -96,13 +32,32 @@ "engines": {

"devDependencies": {
"chalk": "^0.5.1",
"gray-matter": "^0.5.1",
"mocha": "*",
"should": "^4.0.4",
"verb": ">= 0.2.6",
"verb-tag-jscomments": "^0.2.2"
"verb-tag-jscomments": ">= 0.2.0"
},
"keywords": [
"load",
"loader",
"cache",
"templates"
],
"dependencies": {
"arrayify-compact": "^0.1.0",
"globby": "^0.1.1",
"is-relative": "^0.1.1",
"lodash": "^2.4.1"
"array-slice": "^0.2.0",
"debug": "^2.0.0",
"gray-matter": "^0.5.0",
"has-any": "^0.1.0",
"has-any-deep": "^0.2.0",
"is-plain-object": "^0.1.0",
"kind-of": "^0.1.0",
"lodash": "^2.4.1",
"map-files": "^0.1.2",
"mixin-deep": "^0.1.0",
"object-pick": "^0.1.0",
"omit-empty": "^0.2.0",
"omit-keys": "^0.1.0",
"reduce-object": "^0.1.2",
"uniqueid": "^0.1.0"
}
}

@@ -1,30 +0,5 @@

# load-templates [![NPM version](https://badge.fury.io/js/load-templates.png)](http://badge.fury.io/js/load-templates)
# load-templates [![NPM version](https://badge.fury.io/js/load-templates.svg)](http://badge.fury.io/js/load-templates)
> Load templates from file paths, globs or objects, and cache them as normalized objects.
> Load templates.
This is a template loader, pure and simple, no rendering or caching.
Currently any of the following input configurations will return a normalized template object with the same root properties:
```js
loader.load(['abc/*.hbs']);
loader.load('abc/*.md');
loader.load({'abc/def.md': {content: 'ghi.', lmn: 'xyz'});
loader.load({'abc/def.md': {content: 'ghi.'}}, {lmn: 'xyz'});
loader.load({path: 'abc/def.md', content: 'ghi.'}, {lmn: 'xyz'});
loader.load({path: 'abc/def.md', content: 'ghi.', lmn: 'xyz'});
loader.load('abc.md', 'def <%= name %>.');
loader.load('abc.md', 'def <%= name %>.', {name: 'Jon Schlinkert'});
```
**Root properties**
* `path`: file path or key to use for the template.
* `data`: data from the parsed template, e.g. front-matter.
* `locals`: locals pass on one of the loader methods. (keeping locals and data seperate allows you to merge however you need to.)
* `content`: the parsed content of the template
* `orig`: the original content of the template, if parsed
Properties that are not on this list will be moved/copied to the `data` object and retained on `orig`.
## Install

@@ -43,7 +18,4 @@ #### Install with [npm](npmjs.org):

## API
### [loader](index.js#L31)
## Usage
* `options` **{Object}**: Options to initialize `loader` with
```js

@@ -53,84 +25,7 @@ var loader = require('load-templates');

### [.load](index.js#L87)
### Valid formats
Expand glob patterns, load, read, parse and normalize files from file paths, strings, objects, or arrays of these types.
See [the docs](./docs.md) for valid formats. WIP.
* `key` **{String|Object|Array}**: Array, object, string or file paths.
* `value` **{String|Object}**: String of content, `file` object with `content` property, or `locals` if the first arg is a file path.
* `options` **{Object}**: Options or `locals`.
* `returns` **{Object}**: Normalized file object.
**Examples:**
Filepaths or arrays of glob patterns.
```js
var temlates = loader.load(['pages/*.hbs']);
var posts = loader.load('posts/*.md');
```
As strings or objects:
```js
// loader.load(key, value, locals);
var docs = loader.load({'foo/bar.md': {content: 'this is content.'}}, {foo: 'bar'});
// loader.load(key, value, locals);
var post = loader.load('abc.md', 'My name is <%= name %>.', {name: 'Jon Schlinkert'});
```
### [.string](index.js#L106)
* `key` **{String}**: Glob patterns or file paths.
* `value` **{String|Object}**: String of content, `file` object with `content` property, or `locals` if the first arg is a file path.
* `options` **{Object}**: Options or `locals`.
* `returns` **{Object}**: Normalized file object.
Expand glob patterns, load, read, parse and normalize files
from file paths or strings.
### [.array](index.js#L149)
* `patterns` **{Object}**: Glob patterns or array of filepaths.
* `options` **{Object}**: Options or `locals`
* `returns` **{Array}**: Array of normalized file objects.
Normalize an array of patterns.
### [.object](index.js#L166)
* `file` **{Object}**: The object to normalize.
* `options` **{Object}**: Options or `locals`
Normalize a template object.
### [._cwd](index.js#L178)
* `filepath` **{String}**
The current working directory to use. Default is `process.cwd()`.
### [.rename](index.js#L192)
* `filepath` **{String}**
Rename the `key` of each template loaded using whatever rename function
is defined on the options. `path.basename` is the default.
### [.parse](index.js#L209)
* `filepath` **{String}**: The path of the file to read/parse.
* `Options` **{Object}**: Options or `locals`.
Parse the content of each template loaded using whatever parsing function
is defined on the options. `fs.readFileSync` is used by default.
### [.normalize](index.js#L238)
* `file` **{Object}**: The template object to normalize.
* `Options` **{Object}**: Options or `locals`.
Normalize a template using whatever normalize function is
defined on the options.
## Author

@@ -149,2 +44,2 @@

_This file was generated by [verb-cli](https://github.com/assemble/verb-cli) on September 04, 2014._
_This file was generated by [verb-cli](https://github.com/assemble/verb-cli) on September 28, 2014._
---
title: AAA
---
This is fixture a.txt
This is from a.txt.
---
title: BBB
---
This is fixture b.txt
This is from b.txt.
---
title: CCC
---
This is fixture c.txt
This is fixture c.md
---
title: CCC
---
This is fixture c.md
This is from c.txt.

@@ -10,127 +10,939 @@ /*!

var fs = require('fs');
var path = require('path');
var chalk = require('chalk');
var matter = require('gray-matter');
var should = require('should');
var loader = require('..');
var fixture = function(filepath) {
return path.join(__dirname, 'fixtures/' + filepath)
.replace(/[\\\/]/g, '/');
};
var _loader = require('..');
var loader = _loader();
var utils = require('../lib/utils');
describe('loader', function () {
it('should use cwd:', function () {
loader({cwd: 'test/fixtures'});
loader.options.cwd.should.equal('test/fixtures');
describe('utils:', function () {
describe('options:', function () {
describe('.pickOptions():', function () {
it('should pick an options object:', function () {
var opts = utils.pickOptions({a: 'b', locals: {c: 'd'}, options: {foo: true}, content: 'This is content.'});
opts.should.eql({options: {foo: true}});
});
});
describe('.pickOptions():', function () {
it('should return an empty object when nothing is found:', function () {
utils.pickOptions({content: 'This is content.'}).should.eql({});
utils.pickOptions({}).should.eql({});
});
});
describe('.flattenOptions():', function () {
it('should flatten an options object', function () {
var opts = utils.flattenOptions({options: {foo: true}, bar: false});
opts.should.eql({foo: true, bar: false});
});
});
describe('.flattenOptions():', function () {
it('should return an empty object when nothing is found:', function () {
utils.flattenOptions({content: 'This is content.'}).should.eql({});
utils.flattenOptions({}).should.eql({});
});
});
describe('.omitOptions():', function () {
it('should omit an options object', function () {
var opts = utils.omitOptions({options: {foo: true}, bar: false});
opts.should.eql({bar: false});
});
});
});
describe('string', function () {
it('should load templates from a string glob pattern', function () {
var actual = loader.load('pages/*.txt');
var key = fixture('pages/a.txt');
describe('locals:', function () {
describe('.pickLocals():', function () {
it('should pick locals from the given object:', function () {
var locals = utils.pickLocals({a: 'b', locals: {c: 'd'}, content: 'This is content.'});
locals.should.eql({a: 'b', locals: {c: 'd'}});
});
});
actual.should.be.an.object;
actual.should.have.property(key);
actual[key].should.have.property('path');
actual[key].should.have.property('data');
actual[key].should.have.property('content');
describe('.pickLocals():', function () {
it('should return an empty object when nothing is found:', function () {
utils.pickLocals({content: 'This is content.'}).should.eql({});
utils.pickLocals({}).should.eql({});
});
});
it('should normalize data passed as a second param', function () {
var actual = loader.load('pages/*.txt', {name: 'Brian Woodward'});
var key = fixture('pages/a.txt');
describe('.flattenLocals():', function () {
it('should flatten a locals object', function () {
var locals = utils.flattenLocals({a: 'b', locals: {c: 'd'}, content: 'This is content.'});
locals.should.eql({a: 'b', c: 'd'});
});
});
actual.should.be.an.object;
actual.should.have.property(key);
actual[key].should.have.property('data');
actual[key].data.name.should.equal('Brian Woodward');
describe('.flattenLocals():', function () {
it('should return an empty object when nothing is found:', function () {
utils.flattenLocals({content: 'This is content.'}).should.eql({});
utils.flattenLocals({}).should.eql({});
});
});
it('should create a path property from the filepath.', function () {
var actual = loader.load('pages/*.txt', {name: 'Brian Woodward'});
var key = fixture('pages/a.txt');
describe('.omitLocals():', function () {
it('should omit locals', function () {
var locals = utils.omitLocals({a: 'b', locals: {c: 'd'}, content: 'This is content.'});
locals.should.eql({a: 'b', content: 'This is content.'});
});
});
actual.should.be.an.object;
actual.should.have.property(key);
actual[key].should.have.property('path');
actual[key].path.should.equal(key);
describe('.omitLocals():', function () {
it('should return an empty object when nothing is found:', function () {
utils.omitLocals({}).should.eql({});
});
});
});
it('should normalize content passed as a second param', function () {
var actual = loader.load('abc.md', 'This is content.', {name: 'Jon Schlinkert'});
var key = 'abc.md';
describe('root:', function () {
describe('.pickRoot():', function () {
it('should pick root properties from the given object:', function () {
var root = utils.pickRoot({a: 'b', locals: {c: 'd'}, content: 'This is content.'});
root.should.eql({content: 'This is content.', locals: {c: 'd'}});
});
});
actual.should.be.an.object;
actual.should.have.property(key);
actual[key].should.have.property('data');
actual[key].content.should.equal('This is content.');
actual[key].data.name.should.equal('Jon Schlinkert');
describe('.omitRoot():', function () {
it('should omit root properties', function () {
var root = utils.omitRoot({a: 'b', locals: {c: 'd'}, content: 'This is content.'});
root.should.eql({a: 'b'});
});
});
});
});
it('should normalize data passed as a second param', function () {
var actual = loader.load(['pages/*.txt'], {name: 'Brian Woodward'});
var key = fixture('pages/a.txt');
actual.should.be.an.object;
actual.should.have.property(key);
actual[key].should.have.property('data');
actual[key].data.name.should.equal('Brian Woodward');
describe('loader.normalize()', function () {
it('should use a custom normalization function to rename the key.', function () {
loader = _loader({
normalize: function(acc, value, key) {
key = path.basename(key);
acc[key] = value;
return acc;
}
});
var actual = loader('test/fixtures/a.txt', {a: 'b'}, {foo: true});
actual.should.have.property('a.txt');
actual['a.txt'].should.have.property('data', { title: 'AAA' });
// restore state
loader = _loader();
});
describe('array', function () {
it('should load templates from an array glob pattern', function () {
var actual = loader.load(['pages/*.txt']);
var key = fixture('pages/a.txt');
it('should use a custom normalization function to add a `name` property.', function () {
loader = _loader({
normalize: function(acc, value, key) {
key = path.basename(key);
value.name = key;
acc[key] = value;
return acc;
}
});
actual.should.be.an.object;
actual.should.have.property(key);
actual[key].should.have.property('path');
actual[key].should.have.property('data');
actual[key].should.have.property('content');
var actual = loader('test/fixtures/a.txt', {a: 'b'}, {foo: true});
actual.should.have.property('a.txt');
actual['a.txt'].should.have.property('name', 'a.txt');
// restore state
loader = _loader();
});
});
describe(chalk.magenta('[ function | object ]') + ' pattern:', function () {
describe(chalk.bold('valid filepath:'), function () {
it('should detect when the string is a filepath:', function () {
var files = loader(function(options) {
var file = matter.read('test/fixtures/a.md');
var o = {};
o[file.path] = file;
return o;
});
files['test/fixtures/a.md'].should.have.property('path', 'test/fixtures/a.md');
});
});
});
it('should normalize data passed as a second param', function () {
var actual = loader.load(['pages/*.txt'], {name: 'Brian Woodward'});
var key = fixture('pages/a.txt');
actual.should.be.an.object;
actual.should.have.property(key);
actual[key].should.have.property('data');
actual[key].data.name.should.equal('Brian Woodward');
describe(chalk.magenta('[ string | object ]') + ' pattern:', function () {
describe(chalk.bold('valid filepath:'), function () {
it('should detect when the string is a filepath:', function () {
var files = loader('test/fixtures/one/a.md');
files['test/fixtures/one/a.md'].should.have.property('path', 'test/fixtures/one/a.md');
});
it('should create a path property from the filepath.', function () {
var actual = loader.load(['pages/*.txt'], {name: 'Brian Woodward'});
var key = fixture('pages/a.txt');
it('should read the file and return an object:', function () {
var files = loader('test/fixtures/a.md');
files['test/fixtures/a.md'].should.be.an.object;
});
actual.should.be.an.object;
actual.should.have.property(key);
actual[key].should.have.property('path');
actual[key].path.should.equal(key);
it('should extend the object with `content` from the file:', function () {
var files = loader('test/fixtures/one/a.md');
files['test/fixtures/one/a.md'].should.have.property('content', 'This is {{title}}');
});
it('should extend the object with the `path` for the file:', function () {
var files = loader('test/fixtures/a.md');
files['test/fixtures/a.md'].should.have.property('path', 'test/fixtures/a.md');
});
it('should extend the object with `content`:', function () {
var files = loader('test/fixtures/a.md');
files['test/fixtures/a.md'].should.have.property('content', 'This is fixture a.md');
});
it('should extend the object with `locals`:', function () {
var files = loader('test/fixtures/a.md', {a: 'b'});
files['test/fixtures/a.md'].should.have.property('locals', {a: 'b'});
});
it('should extend the object with `options`:', function () {
var files = loader('test/fixtures/a.md', {a: 'b'}, {something: true});
files['test/fixtures/a.md'].should.have.property('locals', {a: 'b'});
files['test/fixtures/a.md'].should.have.property('options', {something: true});
});
it('should parse front matter:', function () {
var files = loader('test/fixtures/a.md');
files['test/fixtures/a.md'].should.have.property('data', { title: 'AAA' });
});
it('should create `orig` from parsed file string:', function () {
var files = loader('test/fixtures/a.md');
files['test/fixtures/a.md'].should.have.property('orig', '---\ntitle: AAA\n---\nThis is fixture a.md');
});
it('should keep `locals` and `data` from front matter separate:', function () {
var files = loader('test/fixtures/a.md', {a: 'b'});
files['test/fixtures/a.md'].should.have.property('locals', { a: 'b' });
files['test/fixtures/a.md'].should.have.property('data', { title: 'AAA' });
files['test/fixtures/a.md'].should.have.property('orig', '---\ntitle: AAA\n---\nThis is fixture a.md');
});
it('should get locals from the second argument:', function () {
var files = loader('test/fixtures/one/a.md', {name: 'Brian Woodward'});
files['test/fixtures/one/a.md'].should.have.property('locals', {name: 'Brian Woodward'});
});
});
describe('object', function () {
it('should load templates from an object', function () {
var actual = loader.load({'foo/bar.md': {content: 'this is content.'}});
var key = 'foo/bar.md';
describe(chalk.bold('valid glob pattern:'), function () {
it('should expand glob patterns:', function () {
var files = loader('test/fixtures/*.txt');
files.should.be.an.object;
files['test/fixtures/a.txt'].should.exist;
});
actual.should.be.an.object;
actual.should.have.property(key);
actual[key].should.have.property('path');
actual[key].should.have.property('data');
actual[key].should.have.property('content');
it('should read files and return an object for each:', function () {
var files = loader('test/fixtures/*.txt');
files.should.be.an.object;
files['test/fixtures/a.txt'].should.exist;
files['test/fixtures/b.txt'].should.exist;
files['test/fixtures/c.txt'].should.exist;
});
it('should normalize data passed as a second param', function () {
var actual = loader.load({'foo/bar.md': {content: 'this is content.'}}, {foo: 'bar'});
var key = 'foo/bar.md';
it('should extend the objects with locals:', function () {
var files = loader('test/fixtures/*.txt', {name: 'Brian Woodward'});
files['test/fixtures/a.txt'].should.have.property('locals', {name: 'Brian Woodward'});
files['test/fixtures/b.txt'].should.have.property('locals', {name: 'Brian Woodward'});
files['test/fixtures/c.txt'].should.have.property('locals', {name: 'Brian Woodward'});
});
actual.should.be.an.object;
actual.should.have.property(key);
actual[key].should.have.property('data');
actual[key].data.should.eql({foo: 'bar'});
it('should extend the objects with a `path` property.', function () {
var files = loader('test/fixtures/*.txt');
files['test/fixtures/a.txt'].should.have.property('path', 'test/fixtures/a.txt');
files['test/fixtures/b.txt'].should.have.property('path', 'test/fixtures/b.txt');
files['test/fixtures/c.txt'].should.have.property('path', 'test/fixtures/c.txt');
});
it('should extend the objects with `content` from the file:', function () {
var files = loader('test/fixtures/*.txt');
files['test/fixtures/a.txt'].should.have.property('content', 'This is from a.txt.');
files['test/fixtures/b.txt'].should.have.property('content', 'This is from b.txt.');
files['test/fixtures/c.txt'].should.have.property('content', 'This is from c.txt.');
});
it('should extend the objects with `options`:', function () {
var files = loader('test/fixtures/*.txt', {a: 'b'}, {c: true});
files['test/fixtures/a.txt'].should.have.property('options', {c: true});
files['test/fixtures/b.txt'].should.have.property('options', {c: true});
files['test/fixtures/c.txt'].should.have.property('options', {c: true});
});
it('should detect options passed on the locals object:', function () {
var files = loader('test/fixtures/*.txt', {a: 'b', options: {b: 'b'}}, {c: true});
files['test/fixtures/a.txt'].should.have.property('options', {b: 'b', c: true});
files['test/fixtures/b.txt'].should.have.property('options', {b: 'b', c: true});
files['test/fixtures/c.txt'].should.have.property('options', {b: 'b', c: true});
// ensure that locals is correct
files['test/fixtures/a.txt'].should.have.property('locals', {a: 'b'});
files['test/fixtures/b.txt'].should.have.property('locals', {a: 'b'});
files['test/fixtures/c.txt'].should.have.property('locals', {a: 'b'});
});
it('should parse front matter:', function () {
var files = loader('test/fixtures/*.txt');
files['test/fixtures/a.txt'].should.have.property('data', { title: 'AAA' });
files['test/fixtures/b.txt'].should.have.property('data', { title: 'BBB' });
files['test/fixtures/c.txt'].should.have.property('data', { title: 'CCC' });
});
it('should create `orig` from parsed file string:', function () {
var files = loader('test/fixtures/*.txt');
files['test/fixtures/a.txt'].should.have.property('orig', '---\ntitle: AAA\n---\nThis is from a.txt.');
files['test/fixtures/b.txt'].should.have.property('orig', '---\ntitle: BBB\n---\nThis is from b.txt.');
files['test/fixtures/c.txt'].should.have.property('orig', '---\ntitle: CCC\n---\nThis is from c.txt.');
});
it('should keep `locals` and `data` from front matter separate:', function () {
var files = loader('test/fixtures/*.txt', {a: 'b'});
files['test/fixtures/a.txt'].should.have.property('locals', { a: 'b' });
files['test/fixtures/a.txt'].should.have.property('data', { title: 'AAA' });
});
it('should move arbitrary props on the third arg to `options`:', function () {
var files = loader('test/fixtures/*.md', {a: 'b'}, {engine: 'hbs'});
files['test/fixtures/a.md'].should.have.property('locals', {a: 'b'});
files['test/fixtures/a.md'].should.have.property('options', {engine: 'hbs'});
});
it('should NOT ATTEMPT to resolve glob patterns when second value is a string:', function () {
var files = loader('test/fixtures/*.md', 'flflflfl', {name: 'Brian Woodward'});
files['test/fixtures/*.md'].should.have.property('path', 'test/fixtures/*.md');
files['test/fixtures/*.md'].should.have.property('content', 'flflflfl');
});
});
describe(chalk.bold('non-filepath, non-glob pattern:'), function () {
it('should move arbitrary props on the second arg to `locals`:', function () {
var files = loader('a', {content: 'this is content', layout: 'b'});
files['a'].should.have.property('locals', {layout: 'b'});
});
it('should load individual templates:', function () {
var files = loader('foo1.md', 'This is content', {name: 'Jon Schlinkert'});
files['foo1.md'].should.have.property('content');
});
describe('when a `content` prop and actual content cannot be found:', function () {
it('should not add a content property:', function () {
var files = loader({'bar1.md': {path: 'a/b/c.md', name: 'Jon Schlinkert'}});
files['bar1.md'].should.not.have.property('content');
});
it('should add other prorties found on the object:', function () {
var files = loader({'baz.md': {path: 'a/b/c.md', name: 'Jon Schlinkert'}}, {go: true});
files['baz.md'].should.have.property('path');
});
});
it.skip('should detect locals when passed as a second param', function () {
var files = loader('whatever', {name: 'Brian Woodward'});
files['whatever'].should.have.property('locals', {name: 'Brian Woodward'});
});
it.skip('should return `{content: null}` when content is not defined or detected.', function () {
var files = loader('whatever', {name: 'Brian Woodward'});
files['whatever'].should.have.property('content', null);
});
it('should load when content is a property on an object.', function () {
var files = loader('a.md', {content: 'c'});
files['a.md'].should.have.property('content', 'c');
});
it.skip('should load even if the key is an invalid filepath.', function () {
var files = loader('a.md');
files.should.have.property('__id__1');
});
it.skip('should load even if the key is an invalid filepath.', function () {
var files = loader('a.md', 'b');
files['a.md'].should.have.property('content', 'b');
});
it('should detect content passed as a second arg', function () {
var files = loader('foo/bar/abc.md', 'This is content.');
files['foo/bar/abc.md'].should.have.property('path');
files['foo/bar/abc.md'].content.should.equal('This is content.');
});
it('should detect locals passed as a third arg', function () {
var files = loader('foo/bar/abc.md', 'This is content.', { a: 'b' });
files['foo/bar/abc.md'].should.have.property('locals', { a: 'b' });
});
it('should detect options passed as a fourth arg', function () {
var files = loader('foo/bar/abc.md', 'This is content.', { a: 'b' }, { c: 'd' });
files['foo/bar/abc.md'].should.have.property('locals', { a: 'b' }, { c: 'd' });
});
describe('when the second arg is an object:', function () {
it('should use the first arg as the key.', function () {
var files = loader('a', {content: 'A above\n{{body}}\nA below', layout: 'b'});
files['a'].should.have.property('content', 'A above\n{{body}}\nA below');
files['a'].should.have.property('locals', {layout: 'b'});
});
});
});
});
describe(chalk.magenta('[ string | string ]') + ' pattern:', function () {
it('should assume the second arg is `content`.', function () {
var files = loader('abc.md', 'This is content.');
files['abc.md'].should.have.property('content', 'This is content.');
});
it('should assume the first arg is the template key.', function () {
var files = loader('abc.md', 'This is content.');
files['abc.md'].should.have.property('path', 'abc.md');
});
it('should assume the key is not a file path.', function () {
var files = loader('abc.md', 'This is content.');
files['abc.md'].should.have.property('path', 'abc.md');
});
it('should extend the object with `locals`', function () {
var files = loader('abc.md', 'This is content.', {a: 'b'}, {locals: {c: 'd'}});
files['abc.md'].should.have.property('locals', {a: 'b'});
});
it('should extend the object with `options`', function () {
var files = loader('abc.md', 'This is content.', {a: 'b'}, {c: 'd'});
files['abc.md'].should.have.property('locals', {a: 'b'});
files['abc.md'].should.have.property('options', {c: 'd'});
});
});
describe(chalk.magenta('[ object ]') + ' pattern:', function () {
describe('when templates are formatted as objects', function () {
it('should load multiple templates from objects:', function () {
var files = loader({a: {layout: 'b', content: 'A above\n{{body}}\nA below' }});
files.should.have.property('a');
files.a.locals.should.have.property('layout');
});
it('should load multiple templates from objects:', function () {
var files = loader({b: {layout: 'c', content: 'B above\n{{body}}\nB below' }});
files.should.have.property('b');
files.b.locals.should.have.property('layout');
});
it('should load multiple templates from objects:', function () {
var files = loader({c: {layout: 'd', content: 'C above\n{{body}}\nC below' }});
files.should.have.property('c');
files.c.locals.should.have.property('layout');
});
});
it('should load loader from an object', function () {
var files = loader({'foo/bar.md': {content: 'this is content.'}});
files['foo/bar.md'].should.have.property('path', 'foo/bar.md');
files['foo/bar.md'].should.not.have.property('locals');
files['foo/bar.md'].should.have.property('content', 'this is content.');
});
it('should normalize locals passed as a second param', function () {
var files = loader({'foo/bar.md': {content: 'this is content.'}}, {foo: 'bar'});
files['foo/bar.md'].should.have.property('path', 'foo/bar.md');
files['foo/bar.md'].should.have.property('locals', {foo: 'bar'});
files['foo/bar.md'].locals.should.eql({foo: 'bar'});
});
it('should use the key as the `path`:', function () {
var files = loader({a: {content: 'A above\n{{body}}\nA below' , layout: 'b'}});
files['a'].should.have.property('path', 'a');
files['a'].should.have.property('locals');
files['a'].locals.should.have.property('layout', 'b');
});
});
describe(chalk.magenta('[ array ]') + ' pattern:', function () {
describe(chalk.bold('valid glob pattern:'), function () {
it('should expand an array of glob patterns:', function () {
var files = loader(['test/fixtures/*.txt']);
files.should.be.an.object;
files['test/fixtures/a.txt'].should.exist;
});
it('should read files and return an object for each:', function () {
var files = loader(['test/fixtures/*.txt']);
files.should.be.an.object;
files['test/fixtures/a.txt'].should.exist;
files['test/fixtures/b.txt'].should.exist;
files['test/fixtures/c.txt'].should.exist;
});
it('should create a `path` property from each filepath.', function () {
var files = loader(['test/fixtures/*.txt']);
files['test/fixtures/a.txt'].should.have.property('path', 'test/fixtures/a.txt');
files['test/fixtures/b.txt'].should.have.property('path', 'test/fixtures/b.txt');
files['test/fixtures/c.txt'].should.have.property('path', 'test/fixtures/c.txt');
});
it('should extend the objects with locals:', function () {
var files = loader(['test/fixtures/*.txt'], {name: 'Brian Woodward'});
files['test/fixtures/a.txt'].should.have.property('locals', {name: 'Brian Woodward'});
files['test/fixtures/b.txt'].should.have.property('locals', {name: 'Brian Woodward'});
files['test/fixtures/c.txt'].should.have.property('locals', {name: 'Brian Woodward'});
});
it('should extend the objects with locals and options:', function () {
var files = loader(['test/fixtures/*.md', 'test/fixtures/*.txt'], {a: 'b'}, {
engine: 'hbs'
});
files['test/fixtures/a.md'].should.have.property('locals', {a: 'b'});
files['test/fixtures/a.txt'].should.have.property('locals', {a: 'b'});
files['test/fixtures/a.md'].should.have.property('options', {engine: 'hbs'});
files['test/fixtures/a.txt'].should.have.property('options', {engine: 'hbs'});
});
it('should extend the objects with a `path` property.', function () {
var files = loader(['test/fixtures/*.txt']);
files['test/fixtures/a.txt'].should.have.property('path', 'test/fixtures/a.txt');
files['test/fixtures/b.txt'].should.have.property('path', 'test/fixtures/b.txt');
files['test/fixtures/c.txt'].should.have.property('path', 'test/fixtures/c.txt');
});
it('should extend the objects with `content` from the file:', function () {
var files = loader(['test/fixtures/*.txt']);
files['test/fixtures/a.txt'].should.have.property('content', 'This is from a.txt.');
files['test/fixtures/b.txt'].should.have.property('content', 'This is from b.txt.');
files['test/fixtures/c.txt'].should.have.property('content', 'This is from c.txt.');
});
it('should extend the objects with `options`:', function () {
var files = loader(['test/fixtures/*.txt'], {a: 'b'}, {c: true});
files['test/fixtures/a.txt'].should.have.property('options', {c: true});
files['test/fixtures/b.txt'].should.have.property('options', {c: true});
files['test/fixtures/c.txt'].should.have.property('options', {c: true});
});
it('should detect options passed on the locals object:', function () {
var files = loader(['test/fixtures/*.txt'], {a: 'b', options: {b: 'b'}}, {c: true});
files['test/fixtures/a.txt'].should.have.property('options', {b: 'b', c: true});
files['test/fixtures/b.txt'].should.have.property('options', {b: 'b', c: true});
files['test/fixtures/c.txt'].should.have.property('options', {b: 'b', c: true});
// ensure that locals is correct
files['test/fixtures/a.txt'].should.have.property('locals', {a: 'b'});
files['test/fixtures/b.txt'].should.have.property('locals', {a: 'b'});
files['test/fixtures/c.txt'].should.have.property('locals', {a: 'b'});
});
it('should parse front matter:', function () {
var files = loader(['test/fixtures/*.txt']);
files['test/fixtures/a.txt'].should.have.property('data', { title: 'AAA' });
files['test/fixtures/b.txt'].should.have.property('data', { title: 'BBB' });
files['test/fixtures/c.txt'].should.have.property('data', { title: 'CCC' });
});
it('should create `orig` from parsed file string:', function () {
var files = loader(['test/fixtures/*.txt']);
files['test/fixtures/a.txt'].should.have.property('orig', '---\ntitle: AAA\n---\nThis is from a.txt.');
files['test/fixtures/b.txt'].should.have.property('orig', '---\ntitle: BBB\n---\nThis is from b.txt.');
files['test/fixtures/c.txt'].should.have.property('orig', '---\ntitle: CCC\n---\nThis is from c.txt.');
});
it('should keep `locals` and `data` from front matter separate:', function () {
var files = loader(['test/fixtures/*.txt'], {a: 'b'});
files['test/fixtures/a.txt'].should.have.property('locals', { a: 'b' });
files['test/fixtures/a.txt'].should.have.property('data', { title: 'AAA' });
});
});
});
describe('normalize templates', function () {
describe('path and content properties', function () {
var expected = { 'a/b/c.md': { path: 'a/b/c.md', ext: '.md', content: 'this is content.'}};
it('should detect the key from an object with `path` and `content` properties', function () {
var files = loader({path: 'a/b/c.md', content: 'this is content.'});
files.should.eql(expected);
});
it('should use the key to fill in a missing `path` property', function () {
var files = loader({ 'a/b/c.md': { content: 'this is content.'}});
files.should.eql(expected);
});
it('should detect the key from an object with `path` and `content` properties', function () {
var files = loader('a/b/c.md', {content: 'this is content.'});
files.should.eql(expected);
});
describe('when the first two args are strings:', function () {
it('should create an object with `path` and `content` properties', function () {
var files = loader('a/b/c.md', 'this is content.');
files.should.eql(expected);
});
});
});
describe('sparse fields:', function () {
it('should normalize options', function () {
var files = loader('a', {content: 'This is content.', options: {ext: '.foo'}});
files.should.eql({a: {path: 'a', content: 'This is content.', ext: '.foo', options: {ext: '.foo'}}});
});
it('should normalize locals', function () {
var files = loader('a', {content: 'This is content.'}, {ext: '.foo'});
files.should.eql({a: {path: 'a', content: 'This is content.', ext: '.foo'}});
});
});
describe('multiple templates:', function () {
describe('objects:', function () {
it('should use `path` and/or `content` properties as indicators:', function () {
var expected = {
'a/b/a.md': {path: 'a/b/a.md', ext: '.md', content: 'this is content.'},
'a/b/b.md': {path: 'a/b/b.md', ext: '.md', content: 'this is content.'},
'a/b/c.md': {path: 'a/b/c.md', ext: '.md', content: 'this is content.'}
};
var files = loader({
'a/b/a.md': {content: 'this is content.'},
'a/b/b.md': {content: 'this is content.'},
'a/b/c.md': {content: 'this is content.'}
});
files.should.eql(expected);
});
it('should normalize locals:', function () {
var expected = {
'a/b/a.md': {path: 'a/b/a.md', ext: '.md', content: 'this is content.', locals: {a: {b: 'c'}}},
'a/b/b.md': {path: 'a/b/b.md', ext: '.md', content: 'this is content.', locals: {a: {c: 'd'}}},
'a/b/c.md': {path: 'a/b/c.md', ext: '.md', content: 'this is content.'}
};
var files = loader({
'a/b/a.md': {content: 'this is content.', a: {b: 'c'}},
'a/b/b.md': {content: 'this is content.', locals: {a: {c: 'd'}}},
'a/b/c.md': {content: 'this is content.'}
});
files.should.eql(expected);
});
it('should normalize "method-locals":', function () {
var expected = {
'a/b/a.md': {path: 'a/b/a.md', ext: '.md', content: 'this is content.', locals: {a: {b: 'c'}, foo: 'bar'}},
'a/b/b.md': {path: 'a/b/b.md', ext: '.md', content: 'this is content.', locals: {a: {c: 'd'}, foo: 'bar'}},
'a/b/c.md': {path: 'a/b/c.md', ext: '.md', content: 'this is content.', locals: {foo: 'bar'}}
};
var files = loader({
'a/b/a.md': {content: 'this is content.', a: {b: 'c'}},
'a/b/b.md': {content: 'this is content.', locals: {a: {c: 'd'}}},
'a/b/c.md': {content: 'this is content.'}
}, {foo: 'bar'});
files.should.eql(expected);
});
it('should normalize "method" locals:', function () {
var expected = {
'a/b/a.md': {path: 'a/b/a.md', ext: '.md', content: 'this is content.', locals: {a: {b: 'c'}, bar: 'bar'}},
'a/b/b.md': {path: 'a/b/b.md', ext: '.md', content: 'this is content.', locals: {a: {c: 'd'}, bar: 'bar'}},
'a/b/c.md': {path: 'a/b/c.md', ext: '.md', content: 'this is content.', locals: {bar: 'baz'}}
};
var files = loader({
'a/b/a.md': {content: 'this is content.', a: {b: 'c'}, bar: 'bar'},
'a/b/b.md': {content: 'this is content.', locals: {a: {c: 'd'}, bar: 'bar'}},
'a/b/c.md': {content: 'this is content.'}
}, {bar: 'baz'});
files.should.eql(expected);
});
it('should normalize options:', function () {
var expected = {
'a/b/a.md': {path: 'a/b/a.md', ext: '.md', content: 'this is content.', locals: {a: {b: 'c'}, bar: 'baz'}, options: {foo: true}},
'a/b/b.md': {path: 'a/b/b.md', ext: '.md', content: 'this is content.', locals: {a: {c: 'd'}, bar: 'baz'}, options: {foo: true}},
'a/b/c.md': {path: 'a/b/c.md', ext: '.md', content: 'this is content.', locals: {bar: 'baz'}, options: {foo: true}}
};
var files = loader({
'a/b/a.md': {content: 'this is content.', a: {b: 'c'}},
'a/b/b.md': {content: 'this is content.', locals: {a: {c: 'd'}}},
'a/b/c.md': {content: 'this is content.'}
}, {bar: 'baz'}, {foo: true});
files.should.eql(expected);
});
});
});
describe('locals', function () {
var expected = { 'a/b/c.md': { path: 'a/b/c.md', ext: '.md', content: 'this is content.', locals: {a: 'b'}}};
it('should detect the key from an object with `path` and `content` properties', function () {
var files = loader({path: 'a/b/c.md', content: 'this is content.', locals: {a: 'b'}});
files.should.eql(expected);
});
it('should detect the key from an object with `path` and `content` properties', function () {
var files = loader({path: 'a/b/c.md', content: 'this is content.', a: 'b'});
files.should.eql(expected);
});
it('should use the key to fill in a missing `path` property', function () {
var files = loader({ 'a/b/c.md': { content: 'this is content.', locals: {a: 'b'}}});
files.should.eql(expected);
});
it('should use the key to fill in a missing `path` property', function () {
var files = loader({ 'a/b/c.md': { content: 'this is content.', a: 'b'}});
files.should.eql(expected);
});
it('should detect the key from an object with `path` and `content` properties', function () {
var files = loader('a/b/c.md', {content: 'this is content.', locals: {a: 'b'}});
files.should.eql(expected);
});
it('should detect the key from an object with `path` and `content` properties', function () {
var files = loader('a/b/c.md', {content: 'this is content.', a: 'b'});
files.should.eql(expected);
});
describe('when the first two args are strings:', function () {
it('should create an object with `path` and `content` properties', function () {
var files = loader('a/b/c.md', 'this is content.', {a: 'b'});
files.should.eql(expected);
});
it('should create an object with `path` and `content` properties', function () {
var files = loader('a/b/c.md', 'this is content.', {locals: {a: 'b'}});
files.should.eql(expected);
});
});
});
describe('options', function () {
var expected = { 'a/b/c.md': { path: 'a/b/c.md', ext: '.md', content: 'this is content.', locals: {a: 'b'}, options: {y: 'z'}}};
it('should detect the key from an object with `path` and `content` properties', function () {
var files = loader({path: 'a/b/c.md', content: 'this is content.', locals: {a: 'b'}, options: {y: 'z'}});
files.should.eql(expected);
});
it('should detect the key from an object with `path` and `content` properties', function () {
var files = loader({path: 'a/b/c.md', content: 'this is content.', a: 'b', options: {y: 'z'}});
files.should.eql(expected);
});
it('should use the key to fill in a missing `path` property', function () {
var files = loader({ 'a/b/c.md': { content: 'this is content.', locals: {a: 'b'}, options: {y: 'z'}}});
files.should.eql(expected);
});
it('should use the key to fill in a missing `path` property', function () {
var files = loader({ 'a/b/c.md': { content: 'this is content.', a: 'b', options: {y: 'z'}}});
files.should.eql(expected);
});
it('should detect the key from an object with `path` and `content` properties', function () {
var files = loader('a/b/c.md', {content: 'this is content.', locals: {a: 'b'}, options: {y: 'z'}});
files.should.eql(expected);
});
it('should detect the key from an object with `path` and `content` properties', function () {
var files = loader('a/b/c.md', {content: 'this is content.', a: 'b'}, {y: 'z'});
files.should.eql(expected);
});
it('should detect the key from an object with `path` and `content` properties', function () {
var files = loader('a/b/c.md', {content: 'this is content.', a: 'b'}, {options: {y: 'z'}});
files.should.eql(expected);
});
it('should detect the key from an object with `path` and `content` properties', function () {
var files = loader('a/b/c.md', {content: 'this is content.', a: 'b'}, {options: {y: 'z'}});
files.should.eql(expected);
});
it('should detect the key from an object with `path` and `content` properties', function () {
var files = loader('a/b/c.md', {content: 'this is content.', a: 'b', options: {y: 'z'}});
files.should.eql(expected);
});
describe('when the first two args are strings:', function () {
it('should create an object with `path` and `content` properties', function () {
var files = loader('a/b/c.md', 'this is content.', {a: 'b'}, {options: {y: 'z'}});
files.should.eql(expected);
});
it('should create an object with `path` and `content` properties', function () {
var files = loader('a/b/c.md', 'this is content.', {a: 'b', options: {y: 'z'}});
files.should.eql(expected);
});
it('should create an object with `path` and `content` properties', function () {
var files = loader('a/b/c.md', 'this is content.', {locals: {a: 'b'}, options: {y: 'z'}});
files.should.eql(expected);
});
});
});
});
describe('glob patterns', function () {
describe('arrays', function () {
var expected = {
'test/fixtures/a.txt': {
data: { title: 'AAA' },
content: 'This is from a.txt.',
orig: '---\ntitle: AAA\n---\nThis is from a.txt.',
path: 'test/fixtures/a.txt',
ext: '.txt',
locals: {a: 'b'},
options: {foo: true}
},
'test/fixtures/b.txt': {
data: { title: 'BBB' },
content: 'This is from b.txt.',
orig: '---\ntitle: BBB\n---\nThis is from b.txt.',
path: 'test/fixtures/b.txt',
ext: '.txt',
locals: {a: 'b'},
options: {foo: true}
},
'test/fixtures/c.txt': {
data: { title: 'CCC' },
content: 'This is from c.txt.',
orig: '---\ntitle: CCC\n---\nThis is from c.txt.',
path: 'test/fixtures/c.txt',
ext: '.txt',
locals: {a: 'b'},
options: {foo: true}
}
};
it('should read a glob of files and return an object of templates.', function () {
loader(['test/fixtures/*.txt'], {a: 'b'}, {foo: true}).should.eql(expected);
});
it('should read a glob of files and return an object of templates.', function () {
loader(['test/fixtures/*.txt'], {a: 'b', options: {foo: true}}).should.eql(expected);
});
});
describe('strings', function () {
var expected = {
'test/fixtures/a.txt': {
data: { title: 'AAA' },
content: 'This is from a.txt.',
orig: '---\ntitle: AAA\n---\nThis is from a.txt.',
path: 'test/fixtures/a.txt',
ext: '.txt',
locals: {a: 'b'},
options: {foo: true}
},
'test/fixtures/b.txt': {
data: { title: 'BBB' },
content: 'This is from b.txt.',
orig: '---\ntitle: BBB\n---\nThis is from b.txt.',
path: 'test/fixtures/b.txt',
ext: '.txt',
locals: {a: 'b'},
options: {foo: true}
},
'test/fixtures/c.txt': {
data: { title: 'CCC' },
content: 'This is from c.txt.',
orig: '---\ntitle: CCC\n---\nThis is from c.txt.',
path: 'test/fixtures/c.txt',
ext: '.txt',
locals: {a: 'b'},
options: {foo: true}
}
};
it('should read a glob of files and return an object of templates.', function () {
var actual = loader('test/fixtures/*.txt', {a: 'b'}, {foo: true});
actual.should.eql(expected);
});
});
});
describe('random', function () {
it('should normalize a template with a non-filepath key.', function () {
var files = loader('foo', {content: 'this is content.'});
files.should.eql({'foo': {path: 'foo', content: 'this is content.'}});
});
it('should normalize a template with a non-filepath key.', function () {
var files = loader('foo', {content: 'this is content.', a: 'b'}, {fez: 'foo'});
files.should.eql({'foo': {path: 'foo', content: 'this is content.', locals: {a: 'b'}, options: {fez: 'foo'}}});
});
it('should normalize a template with a non-filepath key.', function () {
var files = loader({'foo': {content: 'this is content.', a: 'b'}}, {fez: 'foo'});
files.should.eql({'foo': {path: 'foo', content: 'this is content.', locals: {a: 'b', fez: 'foo'}}});
});
it('random stuff', function () {
var files = loader({path: 'a/b/c.md', content: 'this is content.', a: 'b', options: {y: 'z'}}, {c: 'd'}, {e: 'f'});
files.should.eql({'a/b/c.md': {path: 'a/b/c.md', ext: '.md', content: 'this is content.', locals: {a: 'b', c: 'd'}, options: {y: 'z', e: 'f'}}});
});
it('random stuff', function () {
var files = loader({path: 'a/b/c.md', content: 'this is foo'}, {foo: 'bar'});
files.should.eql({'a/b/c.md': {path: 'a/b/c.md', ext: '.md', content: 'this is foo', locals: {foo: 'bar'}}});
});
it('random stuff', function () {
var files = loader('a/b/c.md', {content: 'this is baz', a: 'b', options: {foo: 'bar'}}, {bar: 'baz'});
files.should.eql({'a/b/c.md': {path: 'a/b/c.md', ext: '.md', content: 'this is baz', locals: {a: 'b'}, options: {bar: 'baz', foo: 'bar'}}});
});
it('random stuff', function () {
var files = loader('a/b/c.md', {content: 'this is baz', a: 'b', options: {foo: 'bar'}}, {bar: 'baz'});
files.should.eql({'a/b/c.md': {path: 'a/b/c.md', ext: '.md', content: 'this is baz', locals: {a: 'b'}, options: {bar: 'baz', foo: 'bar'}}});
});
it('multiple templates:', function () {
var files = loader({
'a/b/a.md': {content: 'this is content'},
'a/b/b.md': {content: 'this is content'},
'a/b/c.md': {content: 'this is content'}
}, {a: 'b'}, {c: true});
files['a/b/a.md'].should.have.property('path', 'a/b/a.md');
files['a/b/b.md'].should.have.property('path', 'a/b/b.md');
files['a/b/c.md'].should.have.property('path', 'a/b/c.md');
files['a/b/a.md'].should.have.property('content', 'this is content');
files['a/b/b.md'].should.have.property('content', 'this is content');
files['a/b/c.md'].should.have.property('content', 'this is content');
files['a/b/a.md'].should.have.property('locals', { a: 'b' });
files['a/b/b.md'].should.have.property('locals', { a: 'b' });
files['a/b/c.md'].should.have.property('locals', { a: 'b' });
files['a/b/a.md'].should.have.property('options', { c: true } );
files['a/b/b.md'].should.have.property('options', { c: true } );
files['a/b/c.md'].should.have.property('options', { c: true } );
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc