File-Manifest
Require all the files in a directory into a single object
Installation
npm install file-manifest --save
Usage
File-manifest recursively requires everything in a given directory (optionally filtered with globstar patterns) and packages it into a single object where the keys are (by default) camel-cased file names. Thus if you had a directory called foo
, whose structure looked like this:
bar
baz
quux
some-long-name
You'd end up with an object that looked like this:
{
fooBar: 'foo/bar exports',
fooBazQuux: 'foo/baz/quux exports',
fooBazSomeLongName: 'foo/baz/some-long-name exports'
}
This is useful (for example) in an express app to create a route manifest:
var routes = require('file-manifest').generate('routes');
app.get('/', routes.home);
app.get('/users/:id', routes.profile);
or a middleware manifest:
var middleware = require('file-manifest').generate('middleware');
app.use(middleware.setOriginPolicy);
app.use(middleware.defaultLogger);
or in a mongoose app to load all models:
var models = require('file-manifest').generate('models');
module.exports = function(req, res, next) {
req.models = models;
next();
};
API
.generate(directory[, options][, callback])
The main entry point for the library. If a callback is passed (signature function(error, manifest)
), file-manifest will treat this is as an asynchronous call and return the results in the callback. The possible options are:
match
A string or array of string globstar patterns to filter which files to parse. (See minimatch for pattern syntax.)
memo
The starting value for the manifest reduction. By default, this is {}
(except in a couple cases defined below), but you can use a different starting value, so long as it is compatible with the reduction (that is, 'string'.push()
is going to blow up).
name
A function that takes a file object and returns the name of the key to be used for this file. Or a string that points to a built-in namer function (see the list of transformers defiled exposed). By default, camel casing is used for key names. For example, setting name to "dash" will return dash delimited file names like foo-bar-baz
instead of camel cased ones.
load
A function that loads the value of the key defined above. The default is require
for files that can be required (.js
and .json
) and readFile
for other files. You can pass any function that accepts a file path (absolute) and an optional callback. For instance, you could pass require('yamljs').load
to parse yaml files. Load can also be a string pointing at a built-in loader: one of "readFile"
or "require"
. You probably won't need this, since the correct one is chosen dynamically, but if you want to read javascript without loading it as source, or if you want to load a coffeescript file and you've previously called required coffee-script/register
, then you may want to do this.
reduce
A custom reduce function for creating the manifest. In asynchronous implementations, this uses async.js's reduce, and in sync implementations, it uses lodash's reduce. That is, you can pass an async reducer with the signature function(manifest, file, next)
where next
is a callback with the signature function(err, reduction)
or a sync reducer with the signature function(manifest, file[, index][, collection])
that returns the reduction. In both cases, the file
parameter is a file object. The reduce function will be called with the options object as the (this
) context, so you can either calculate the key and value yourself or call this.name.call(this, file)
and this.load.call(this, file[, callback])
to get them.
Additionally, reduce
can be a string pointing at a custom reducer. The default is "flat"
, which returns a single-level object of properties with their exports, but you can also pass "nested"
, "list"
, or "objectList"
. Given the example "foo" directory above, the results would be as follows:
{
fooBar: 'foo/bar exports',
fooBazQuux: 'foo/baz/quux exports',
fooBazSomeLongName: 'foo/baz/some-long-name exports'
}
{
foo: {
bar: 'foo/bar exports'
baz: {
quux: 'foo/baz/quux exports',
someLongName: 'foo/baz/some-long-name exports'
}
}
}
[
'foo/bar exports',
'foo/baz/quux exports',
'foo/baz/some-long-name exports'
]
[
{
name: 'foo/bar',
contents: 'foo/bar exports'
},
{
name: 'foo/baz/quux',
contents: 'foo/baz/quux exports'
},
{
name: 'foo/baz/some-long-name',
contents: 'foo/baz/some-long-name exports'
}
]
Some of this is handled smartly for you. For instance, if you set memo to []
in the options hash, reduce will automatically be set to "list." Similarly, if you set reduce to "list" or "objectList," memo will be set to []
.
.generateSync(directory[, options])
This is just syntactic sugar for calling .generate
without a function. It doesn't do anything that .generate
doesn't; it's provided only for completeness and for those who like having "sync" in their synchronous function names.
.generatePromise(directory[, options])
An asynchronous implementation that returns a promise instead.
.generateEvent(directory[, options])
An asynchronous implementation that returns an event emitter instead.
Examples
Given var fm = require('file-manifest');
:
Get all routes synchronously:
var routes = fm.generate('routes');
Get all member routes:
var routes = fm.generate('routes', { match: '**/*member*.js' });
Get all routes except index.js:
var routes = fm.generate('routes', { match: ['**/*.js', '!index.js'] }
Get all routes with dashed names:
var routes = fm.generate('routes', { name: 'dash' });
Get all routes with a random name:
var routes = fm.generate('routes', { name: require('uuid').v4 });
var routes = fm.generate('routes', { name: require('randomstring').generate });
Get all markdown files in the current directory (as strings using "readFile")
var routes = fm.generate('./', { match: '**/*.md', load: "readFile" });
Get all markdown files in the current directory as markdown:
var routes = fm.generate('./', { match: '**/*.md', load: require('marky-mark').parseFileSync });
Get a list of routes:
var routes = fm.generate('routes', { reduce: 'list' });
var routes = fm.generate('routes', { memo: [] });
Get a list of routes that includes the file names:
var routes = fm.generate('routes', { reduce: 'objectList' });
Get a nested object of routes:
var routes = fm.generate('routes', { reduce: 'nested' });
Get all files in lib grouped by extension
var routes = fm.generate('lib', {
reduce: function(manifest, file) {
var key = file.ext().replace('.', '');
manifest[key] = manifest[key] || [];
manifest[key].push(file.relative({ ext: false }));
return manifest;
}
});
Get all routes asynchronously:
fm.generate('routes', function(err, routes) {
});
Get all routes with a promise:
var promise = fm.generatePromise('routes');
promise.then(function(routes) {
}, function(error) {
});
Get all routes via event:
var emitter = fm.generateEvent('routes');
emitter.on('manifest', function(routes) {
}).on('error', function(error) {
});
Contributing
Please see the contribution guidelines.