metalsmith-collections/lib/index.js
Valentin Boettcher bac79a2a4d Allow Objects...
2017-09-04 14:31:32 +02:00

227 lines
5.2 KiB
JavaScript

var debug = require('debug')('metalsmith-collections');
var extend = require('extend');
var multimatch = require('multimatch');
var unique = require('uniq');
var read = require('fs').readFileSync;
var loadMetadata = require('read-metadata').sync;
/**
* Expose `plugin`.
*/
module.exports = plugin;
/**
* Metalsmith plugin that adds `collections` of files to the global
* metadata as a sorted array.
*
* @param {Object} collections (optional)
* @return {Function}
*/
function plugin(opts, globalOpts) {
opts = normalize(opts);
var keys = Object.keys(opts);
var match = matcher(opts);
return function(files, metalsmith, done) {
var metadata = metalsmith.metadata();
/**
* Find the files in each collection.
*/
Object.keys(files).forEach(function(file) {
debug('checking file: %s', file);
var data = files[file];
data.path = file;
Object.keys(match(file, data)).forEach(function(key) {
if (key && keys.indexOf(key) < 0) {
opts[key] = {};
keys.push(key);
}
metadata[key] = metadata[key] || [];
metadata[key].push(data);
});
});
/**
* Ensure that a default empty collection exists.
*/
Object.keys(keys).forEach(function(key) {
metadata[key] = metadata[key] || [];
});
/**
* Merge global options with collection options
*/
keys.forEach(function(key) {
for (var gKey in globalOpts) {
opts[key][gKey] = opts[key][gKey] || globalOpts[gKey];
}
});
/**
* Sort the collections.
*/
keys.forEach(function(key) {
debug('sorting collection: %s', key);
var settings = opts[key];
var sort = settings.sortBy || 'date';
var col = metadata[key];
if ('function' == typeof sort) {
col.sort(sort);
} else {
col.sort(function(a, b) {
a = (a.collection[key]) ? (a.collection[key][sort] || a[sort]) : a[sort];
b = (b.collection[key]) ? (b.collection[key][sort] || b[sort]) : b[sort];
if (!a && !b) return 0;
if (!a) return -1;
if (!b) return 1;
if (b > a) return -1;
if (a > b) return 1;
return 0;
});
}
if (settings.reverse) col.reverse();
});
/**
* Add `next` and `previous` references and apply the `limit` option
*/
keys.forEach(function(key) {
debug('referencing collection: %s', key);
var settings = opts[key];
var col = metadata[key];
var last = col.length - 1;
if (opts[key].limit && opts[key].limit < col.length) {
col = metadata[key] = col.slice(0, opts[key].limit);
last = opts[key].limit - 1;
}
if (settings.refer === false) return;
col.forEach(function(file, i) {
if (0 != i) file.previous = col[i - 1];
if (last != i) file.next = col[i + 1];
});
});
/**
* Add collection metadata
*/
keys.forEach(function(key) {
debug('adding metadata: %s', key);
var settings = opts[key];
var col = metadata[key];
col.metadata = (typeof settings.metadata === 'string') ?
loadMetadata(settings.metadata) :
(settings.metadata || {});
Object.assign(col.metadata, globalOpts);
});
/**
* Add them grouped together to the global metadata.
*/
metadata.collections = {};
keys.forEach(function(key) {
return metadata.collections[key] = metadata[key];
});
done();
};
}
/**
* Normalize an `options` dictionary.
*
* @param {Object} options
*/
function normalize(options) {
options = options || {};
for (var key in options) {
var val = options[key];
if ('string' == typeof val) options[key] = {
pattern: val
};
if (val instanceof Array) options[key] = {
pattern: val
};
}
return options;
}
/**
* Generate a matching function for a given set of `collections`.
*
* @param {Object} collections
* @return {Function}
*/
function matcher(cols) {
var keys = Object.keys(cols);
var matchers = {};
keys.forEach(function(key) {
var opts = cols[key];
if (!opts.pattern) {
return;
}
matchers[key] = {
match: function(file) {
return multimatch(file, opts.pattern);
}
};
});
return function(file, data) {
var matches = [];
if (data.collection) {
var collection = data.collection;
let tmpCollection = {};
if (Array.isArray(collection)) {
for (let collItem of collection) {
tmpCollection[collItem] = {};
}
} else if (collection.constructor === String)
tmpCollection[collection] = {};
else if (collection instanceof Object)
tmpCollection = collection;
else {
collection = {};
}
collection = data.collection = tmpCollection;
Object.keys(collection).forEach(function(key) {
matches.push(key);
if (key && keys.indexOf(key) < 0) {
debug('adding new collection through metadata: %s', key);
}
});
}
for (var key in matchers) {
var m = matchers[key];
if (m.match(file).length) {
matches.push(key);
}
}
return data.collection || {};
};
}