mirror of
https://github.com/vale981/metalsmith-collections
synced 2025-03-05 17:31:38 -05:00

Adds a `file` property to the collection item's data which contains the file name of the generated file. For example, this can be used in templates to create links. Includes test case.
190 lines
4.2 KiB
JavaScript
190 lines
4.2 KiB
JavaScript
|
|
var debug = require('debug')('metalsmith-collections');
|
|
var extend = require('extend');
|
|
var Matcher = require('minimatch').Minimatch;
|
|
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){
|
|
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
|
|
|
|
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.
|
|
*/
|
|
|
|
keys.forEach(function(key) {
|
|
metadata[key] = metadata[key] || [];
|
|
});
|
|
|
|
/**
|
|
* 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[sort];
|
|
b = 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;
|
|
});
|
|
|
|
/**
|
|
* 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 };
|
|
}
|
|
|
|
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] = new Matcher(opts.pattern);
|
|
});
|
|
|
|
return function(file, data){
|
|
var matches = [];
|
|
|
|
if (data.collection) {
|
|
var collection = data.collection;
|
|
if (!Array.isArray(collection)) collection = [collection];
|
|
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 && m.match(file)) matches.push(key);
|
|
}
|
|
|
|
data.collection = unique(matches);
|
|
return data.collection;
|
|
};
|
|
}
|