need is a script loader for node.js. It acts as both a replacement and companion to the built-in require function. It takes the fundamental ideas of require and adapts them to have a tighter integration with your node.js application.
require is a great tool for including external node modules in to an application. However it falls short on internal node modules. need aims to fix this without forcing major structural changes within an application.
To use need, simply run the following command in the root of your application:
npm install need
Then include the following line in your node.js application:
var need = require('need');
need has its own require function that acts independently to the built-in function. The following line demonstrates how at first glance need is almost identical to require. The function returns the exports from the required file just as require does.
var exported = need('./a');
This function will first attempt to look for ./a.js and if a matching file is not found then it will attempt to find a file at ./a/index.js. It is possible to override this behavior by specifying an alternate fall-back path however should only be used if absolutely required.
need can detect the path of the caller file, and the related path is based on the current location of the caller file.
# /path/to/the/example.js
module.exports = function() { console.log('loaded') }
# /path/to/app.js
need('./the/example')()
# console:
loaded
The root is the actual root on the file system.
# /path/to/example.js
module.exports = function() { console.log('loaded') }
# app.js
need('/path/to/example')()
# console:
loaded
Just like require, need can load modules in the same behaviour.
# app.js
console.log( need('os').platform() );
# console:
linux
You should not include the file extension when requiring a file. need will always search for the file name before attempting the file extension and so including a file extension will first search for a file with a double-extension. For example:
# example.js
module.exports = function() { console.log('Not this one') }
# example.js.js
module.exports = function() { console.log('But this one') }
# app.js
need('example.js')()
# console:
But this one
If the file example.js.js did not exist then it would fall back to example.js.
need offers the ability to require multiple files at the same time. For example:
need(['./a', './b']);
The output here is a key-value pair of paths and exports. Multiple keys may point to the same exports. For example:
# models/index.js
module.exports = function() { console.log('Models') }
# controllers.js
module.exports = function() { console.log('Controllers') }
# app.js
var needs = need(['./models', './controllers']);
needs.get('./models')();
needs.get('./models/index')();
needs.get('./controllers')();
needs.get('./controllers/index')();
# console:
Models
Models
Controllers
[ Error ]
need allows the use of wildcards to require multiple files without being specific. For example:
need([
'a/b/', // require b/index.js
'a/b', // require b.js, fall back to b/index.js
'a/*/, // recursive require in sub-directories of a
'a/*', // recursive require in a
'*' // recursive require in current directory
]);
If you require a single path that includes a wildcard then the output will always be a key-value pair. If there is any uncertainty in whether a path fill include a wildcard, for example when the path is generated from another function, then simply enclose the input as though it was an array.
If need encounters a function then it will evaluate the function and add the results to be required. For example:
need(function() { return ['a/b', 'd/e'] });
if need encounters an object instead of a string or array then it will loop through the object and add the keys to be required. For example:
need({
'a/b': function(){},
'd/e': function(){}
});
Note that the functions here do not do anything. The purpose of these are explained below.
need allows you to nest the various different inputs that it accepts. For example:
need([
['a/b', 'c/d'],
function() {
return 'e/f'
},
{
'g/h': function(){},
'i/j': function(){}
}
]);
need allows you to define directory namespaces to split code up in to a more logical format. For example:
# example.js
var models = need.set('models', './models');
This namespace can then be accessed from anywhere in an application. The best use of this functionality is when trying to require a file that is several levels away or when requiring multiple files in the same directory. The idea is to make the code cleaner and easier to understand.
# a/b/c/d/e/example.js
var models = need.get('models');
The result of getting or setting will be a subclass of the need function with the root set to the specified directory in the set function. You can then use the returned function to require files. For example:
models(['a/b', 'c/d']);
This code may not be very clear as there is no longer any mention of need. While the above still works it may be beneficial to use the following syntax:
models.need(['a/b', 'c/d']);
Or alternatively:
models.require(['a/b', 'c/d']);
Both functions work identically to that of the subclassed need function. If you wish, these functions are also exposed on the root need function so you can use the following syntax:
need.need(['a/b', 'c/d']);
need.require(['a/b', 'c/d']);
These namespaces can go as deep as you like and can only be accessed from the parent namespace. For example:
# file1.js
var models = need.set('models', './models'),
people = models.set('people', 'people');
# file2.js
var people = need.get('people'); // Error
need accepts a callback function as a second parameter. This function is passed three parameters.
need(['a/b', 'c/d'], function(send, each, pair) {
// ...
});
This is one of the core features of need and is the reason why it was developed. The idea was to make it easy to send app and other variables to multiple files. send is a function that can be called to evaluate the exports from each required file. This only applies to exports that are functions:
# a/b.js
exports.something = function(input) {
console.log('a/b says: ' + input);
}
# c/d.js
module.exports = function(input) {
console.log('c/d says: ' + input);
}
# app.js
need(['a/b', 'c/d'], function(send, each, pair) {
send('Hello World!');
});
# console:
c/d says: Hello World!
In the above example only c/d.js actually exported a function via module.exports. send then made it easy to pass values on to other files.
Of course there may be situations where you do not wish all required files to be evaluated and sent parameters. In these cases it would be advised to use a seperate need function or alternatively use each. send is also exposed in a more general form on the need variable.
each is a function that allows you to perform actions on each file individually. each is also exposed in a more general form on the need variable.
# a/b.js
module.exports = function(input) {
console.log('a/b says: ' + input);
}
# c/d.js
module.exports = function(input) {
console.log('c/d says: ' + input);
}
# app.js
need(['a/b', 'c/d'], function(s, each, p) {
each(function(send, match, key, val) {
send(key);
});
});
# console:
a/b says: a/b
c/d says: c/d
In the above example each of the required functions is sent its own path.
match is only available within each. It evaluates a given path to see if it matches another path. match is also exposed on the need variable.
# a/b.js
module.exports = function(input) {
console.log('a/b says: ' + input);
}
# c/d.js
module.exports = function(input) {
console.log('c/d says: ' + input);
}
# app.js
need(['a/b', 'c/d'], function(s, each, p) {
each(function(send, match, key, val) {
if (match(key, 'a/*')) {
send('Matched!');
} else {
send('Not matched!');
}
});
});
# console:
a/b says: Matched!
c/d says: Not matched!
In the above example, match is used to check whether the required file is within the specified directory and sends different commands depending on whether the result is true or false.
This is the key-value object that would normally be returned from the need function.
The callback function is allowed to return a value. By default if nothing is returned then need will return the key-value pair. If the callback returns a value then this value will be returned from need.
need accepts a config object as a second parameter.
need(['a/b', 'c/d'], {
ordered: false, // default true. Set to false if required files do not need to be synchronously required in a specific order.
logging: true, // default false. Set to true to log warnings, errors
recurse: false, // default true. Set to false to stop recursive require
pattern: ['*Controller'], // only require files matching pattern
ignores: ['a/b/*'], // ignore files matching pattern/path
success: function(){} // The callback function
});
.
…
modules/ index.js only, not models.js
var application = need.set('application', './'),
controllers = need.set('controllers', './controllers'),
models = need.set('models', './models'),
routes = need.set('routes', './routes'),
config = need.set('config', './config');
need('a/{dir}/*'); // a/b/c -> c: b
need('a/{dir}/x'); // a/b/c/x -> x: b/c
sending
need.send need.each need.match
future improvements: support loading modules support loading json watch directory for moving files warning levels 0 == false == off, 1 == error, 2 == warn+error, 3 == warn+error+log