2014-02-06 22:55:26 -08:00
|
|
|
var debug = require('debug')('metalsmith-collections');
|
2014-02-06 23:16:04 -08:00
|
|
|
var extend = require('extend');
|
2016-10-29 19:22:50 -03:00
|
|
|
var multimatch = require('multimatch');
|
2014-03-06 14:33:37 -08:00
|
|
|
var unique = require('uniq');
|
2014-07-29 15:45:15 -07:00
|
|
|
var read = require('fs').readFileSync;
|
2014-08-05 10:35:59 -07:00
|
|
|
var loadMetadata = require('read-metadata').sync;
|
2014-02-05 15:06:46 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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}
|
|
|
|
*/
|
|
|
|
|
2017-09-01 11:52:15 +02:00
|
|
|
function plugin(opts, globalOpts) {
|
2014-03-06 14:40:19 -08:00
|
|
|
opts = normalize(opts);
|
2014-03-06 14:18:23 -08:00
|
|
|
var keys = Object.keys(opts);
|
2014-03-06 14:33:37 -08:00
|
|
|
var match = matcher(opts);
|
2014-02-05 15:06:46 -08:00
|
|
|
|
2017-09-01 11:52:15 +02:00
|
|
|
return function(files, metalsmith, done) {
|
2014-03-06 14:18:23 -08:00
|
|
|
var metadata = metalsmith.metadata();
|
|
|
|
|
|
|
|
/**
|
2014-03-06 14:33:37 -08:00
|
|
|
* Find the files in each collection.
|
2014-03-06 14:18:23 -08:00
|
|
|
*/
|
2014-02-05 15:06:46 -08:00
|
|
|
|
2017-09-01 11:52:15 +02:00
|
|
|
Object.keys(files).forEach(function(file) {
|
2014-02-06 22:55:26 -08:00
|
|
|
debug('checking file: %s', file);
|
2014-02-05 15:06:46 -08:00
|
|
|
var data = files[file];
|
2017-09-01 11:52:15 +02:00
|
|
|
data.path = file;
|
2016-02-11 12:00:40 +00:00
|
|
|
|
2017-09-01 11:52:15 +02:00
|
|
|
Object.keys(match(file, data)).forEach(function(key) {
|
|
|
|
if (key && keys.indexOf(key) < 0) {
|
2014-10-13 12:55:10 +02:00
|
|
|
opts[key] = {};
|
|
|
|
keys.push(key);
|
|
|
|
}
|
|
|
|
|
2014-03-06 14:33:37 -08:00
|
|
|
metadata[key] = metadata[key] || [];
|
|
|
|
metadata[key].push(data);
|
|
|
|
});
|
2014-02-05 15:06:46 -08:00
|
|
|
});
|
|
|
|
|
2014-05-05 14:27:31 -07:00
|
|
|
/**
|
|
|
|
* Ensure that a default empty collection exists.
|
|
|
|
*/
|
|
|
|
|
2017-09-01 11:52:15 +02:00
|
|
|
Object.keys(keys).forEach(function(key) {
|
2014-05-05 14:27:31 -07:00
|
|
|
metadata[key] = metadata[key] || [];
|
|
|
|
});
|
|
|
|
|
2017-08-25 17:47:36 +02:00
|
|
|
/**
|
|
|
|
* Merge global options with collection options
|
|
|
|
*/
|
|
|
|
|
2017-09-01 11:52:15 +02:00
|
|
|
keys.forEach(function(key) {
|
|
|
|
for (var gKey in globalOpts) {
|
2017-08-25 17:47:36 +02:00
|
|
|
opts[key][gKey] = opts[key][gKey] || globalOpts[gKey];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2014-03-06 14:18:23 -08:00
|
|
|
/**
|
2014-03-25 14:15:52 -07:00
|
|
|
* Sort the collections.
|
2014-03-06 14:18:23 -08:00
|
|
|
*/
|
2017-09-01 11:52:15 +02:00
|
|
|
keys.forEach(function(key) {
|
2014-03-06 14:18:23 -08:00
|
|
|
debug('sorting collection: %s', key);
|
|
|
|
var settings = opts[key];
|
2017-09-01 11:52:15 +02:00
|
|
|
var sort = settings.sortBy || 'date';
|
2014-03-06 14:18:23 -08:00
|
|
|
var col = metadata[key];
|
2014-03-25 14:15:52 -07:00
|
|
|
|
|
|
|
if ('function' == typeof sort) {
|
|
|
|
col.sort(sort);
|
|
|
|
} else {
|
2017-09-01 11:52:15 +02:00
|
|
|
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];
|
2014-03-25 14:15:52 -07:00
|
|
|
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;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-03-06 14:18:23 -08:00
|
|
|
if (settings.reverse) col.reverse();
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
2014-05-21 08:37:57 +02:00
|
|
|
* Add `next` and `previous` references and apply the `limit` option
|
2014-03-06 14:18:23 -08:00
|
|
|
*/
|
|
|
|
|
2017-09-01 11:52:15 +02:00
|
|
|
keys.forEach(function(key) {
|
2014-03-06 14:18:23 -08:00
|
|
|
debug('referencing collection: %s', key);
|
2014-08-30 00:02:15 -04:00
|
|
|
var settings = opts[key];
|
2014-03-06 14:18:23 -08:00
|
|
|
var col = metadata[key];
|
|
|
|
var last = col.length - 1;
|
2014-05-21 08:37:57 +02:00
|
|
|
if (opts[key].limit && opts[key].limit < col.length) {
|
2017-09-01 11:52:15 +02:00
|
|
|
col = metadata[key] = col.slice(0, opts[key].limit);
|
|
|
|
last = opts[key].limit - 1;
|
2014-05-21 08:37:57 +02:00
|
|
|
}
|
2014-08-30 00:02:15 -04:00
|
|
|
if (settings.refer === false) return;
|
2017-09-01 11:52:15 +02:00
|
|
|
col.forEach(function(file, i) {
|
|
|
|
if (0 != i) file.previous = col[i - 1];
|
|
|
|
if (last != i) file.next = col[i + 1];
|
2014-03-06 14:18:23 -08:00
|
|
|
});
|
2014-02-05 15:06:46 -08:00
|
|
|
});
|
|
|
|
|
2014-07-29 15:45:15 -07:00
|
|
|
/**
|
|
|
|
* Add collection metadata
|
|
|
|
*/
|
|
|
|
|
2017-09-01 11:52:15 +02:00
|
|
|
keys.forEach(function(key) {
|
2014-07-29 15:45:15 -07:00
|
|
|
debug('adding metadata: %s', key);
|
|
|
|
var settings = opts[key];
|
|
|
|
var col = metadata[key];
|
2014-07-30 10:37:46 -07:00
|
|
|
col.metadata = (typeof settings.metadata === 'string') ?
|
|
|
|
loadMetadata(settings.metadata) :
|
2017-09-01 11:52:15 +02:00
|
|
|
(settings.metadata || {});
|
|
|
|
|
|
|
|
Object.assign(col.metadata, globalOpts);
|
2014-07-29 15:45:15 -07:00
|
|
|
});
|
|
|
|
|
2014-03-20 16:50:16 -07:00
|
|
|
/**
|
|
|
|
* Add them grouped together to the global metadata.
|
|
|
|
*/
|
|
|
|
|
|
|
|
metadata.collections = {};
|
2017-09-01 11:52:15 +02:00
|
|
|
keys.forEach(function(key) {
|
2014-03-20 16:50:16 -07:00
|
|
|
return metadata.collections[key] = metadata[key];
|
|
|
|
});
|
|
|
|
|
2014-03-06 14:18:23 -08:00
|
|
|
done();
|
2014-02-05 15:06:46 -08:00
|
|
|
};
|
2014-03-06 14:33:37 -08:00
|
|
|
}
|
|
|
|
|
2014-03-06 14:40:19 -08:00
|
|
|
/**
|
|
|
|
* Normalize an `options` dictionary.
|
|
|
|
*
|
|
|
|
* @param {Object} options
|
|
|
|
*/
|
|
|
|
|
2017-09-01 11:52:15 +02:00
|
|
|
function normalize(options) {
|
2014-03-06 14:40:19 -08:00
|
|
|
options = options || {};
|
|
|
|
|
|
|
|
for (var key in options) {
|
|
|
|
var val = options[key];
|
2017-09-01 11:52:15 +02:00
|
|
|
if ('string' == typeof val) options[key] = {
|
|
|
|
pattern: val
|
|
|
|
};
|
|
|
|
if (val instanceof Array) options[key] = {
|
|
|
|
pattern: val
|
|
|
|
};
|
2014-03-06 14:40:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
return options;
|
|
|
|
}
|
|
|
|
|
2014-03-06 14:33:37 -08:00
|
|
|
/**
|
|
|
|
* Generate a matching function for a given set of `collections`.
|
|
|
|
*
|
|
|
|
* @param {Object} collections
|
|
|
|
* @return {Function}
|
|
|
|
*/
|
|
|
|
|
2017-09-01 11:52:15 +02:00
|
|
|
function matcher(cols) {
|
2014-03-06 14:33:37 -08:00
|
|
|
var keys = Object.keys(cols);
|
|
|
|
var matchers = {};
|
|
|
|
|
2017-09-01 11:52:15 +02:00
|
|
|
keys.forEach(function(key) {
|
2014-03-06 14:33:37 -08:00
|
|
|
var opts = cols[key];
|
2016-10-29 19:22:50 -03:00
|
|
|
if (!opts.pattern) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
matchers[key] = {
|
|
|
|
match: function(file) {
|
2017-09-01 11:52:15 +02:00
|
|
|
return multimatch(file, opts.pattern);
|
2016-10-29 19:22:50 -03:00
|
|
|
}
|
|
|
|
};
|
2014-03-06 14:33:37 -08:00
|
|
|
});
|
|
|
|
|
2017-09-01 11:52:15 +02:00
|
|
|
return function(file, data) {
|
2014-03-06 14:33:37 -08:00
|
|
|
var matches = [];
|
2014-08-12 13:54:44 -07:00
|
|
|
|
2014-08-12 14:12:39 -07:00
|
|
|
if (data.collection) {
|
|
|
|
var collection = data.collection;
|
2017-09-01 11:52:15 +02:00
|
|
|
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 = {};
|
2016-10-29 19:22:50 -03:00
|
|
|
}
|
2017-09-01 11:52:15 +02:00
|
|
|
|
|
|
|
collection = data.collection = tmpCollection;
|
|
|
|
|
|
|
|
Object.keys(collection).forEach(function(key) {
|
2014-10-13 12:55:10 +02:00
|
|
|
matches.push(key);
|
|
|
|
|
2016-10-29 19:22:50 -03:00
|
|
|
if (key && keys.indexOf(key) < 0) {
|
2014-10-13 12:55:10 +02:00
|
|
|
debug('adding new collection through metadata: %s', key);
|
2016-10-29 19:22:50 -03:00
|
|
|
}
|
2014-08-12 14:12:39 -07:00
|
|
|
});
|
2014-03-06 14:33:37 -08:00
|
|
|
}
|
2014-11-06 10:30:52 -08:00
|
|
|
|
2017-09-01 11:52:15 +02:00
|
|
|
for (var key in matchers) {
|
2014-11-06 10:30:52 -08:00
|
|
|
var m = matchers[key];
|
2016-10-29 19:22:50 -03:00
|
|
|
if (m.match(file).length) {
|
|
|
|
matches.push(key);
|
|
|
|
}
|
2014-08-12 14:12:39 -07:00
|
|
|
}
|
2014-08-12 13:54:44 -07:00
|
|
|
|
2017-09-01 11:52:15 +02:00
|
|
|
return data.collection || {};
|
2014-03-06 14:33:37 -08:00
|
|
|
};
|
2014-05-05 14:27:31 -07:00
|
|
|
}
|