javascript module loader/build tool for briding the gap between server and client side
api
defining modules
module dependencies
path resolution
compatability
configuration
plugins
environment
dependencies
create a new instace of built
var bilt = require('bilt');
var myProject = bilt();
bilt.require(paths, callback)
require paths and callback in-order as arguments.
myProject.require(['mymod.js'], function(mymod){
});
bilt.build(paths, init, callback)
create a javascript file from required paths, and call back with source. init is called at runtime
myProject.build(['mymod.js'], function(mymod){
mymod();
}, function(e, source){
console.log('build complete', e, source);
})
each module is a javascript file with a call to define
passing any javascript value you like: functions, literals, etc. unlike AMD, functions are not factory functions, and will not be evaluated as a function, unless you want to...
//define a function
define(function(a){
return a.toUpperCase();
});
//object
define({
myProp : 9923,
myFn : function(a){
return a+3;
}
});
//not sure why you would do this
define('im a string');
modules can make calls to require
to load eachother. all references to each module are shallow copies: a change to one reference to the module manifests across all its refernces. if this is not desired, consider using a function as a constructor to return new copies of the module.
define(function(){
var mystring = require('./mystring.js');
var myfn = require('./myfn.js');
console.log(myfn(mystring)); /* log: IM A STRING */
});
you can optionally include a config parameter in the define function, as an array of dependencies to include:
define(['some_dep.js'], 'my module');
or as a config object:
define({
paths : {
string : {
source : './mystring.js',
}
},
deps : ['./mymod.js']
}function(path){
var fs = require('string');
return string+'!';
});
all paths are relative to the script calling require
or build
. a path to a module is resolved with the following process:
paths rewrite
if the path has an entry in the configuration options rewrite the path accordingly.platform check
if anodePath
is specified and the module is to be executed in node, use it.try open localy
check to see if the path exists locally.try open remotely
check to see if it can be loaded from a remote server;catch resolve again
if path is not found start at step2
and try to re-resolve it. (this is required if references are multiple levels deep. not that you should do that). continue re-resolving untill it creates a circular reference.look in node_modules
if all other resolution has failed, look for the file as if it were the path to a node module.resoultion failed
path was not found, throw that error
bilt can include non standard modules:
raw javascript
using theexport
config option, built will evaluate the script in a closure and return the global variable as defined.node modules
in any bilt module, calls tonodeRequire
will load the node module relative to the parent asrequire
would in a commonJS module.
passed as an object with any of the following properties:
-
deps
array of paths the module is dependent on. -
export
specify what variable to export for standard javascript files. -
minify
bool if to minify just this module -
factory
the module must be a function, called once on first require. defines returned value -
paths
object of mapping paths to their sources and/or extra options: -
source
path from which to load module. -
nodePath
specify separate path to require instead when executing in the node environment -
examples
//source only 'module-alias' : './module.js'
//with options 'jquery' : { source : 'https://code.jquery.com/jquery-1.11.3.min.js', export : '$' }
plugins filter the module being required. plugins are prepended to a path in a require call like so: path_to_plugin.js!my_module.js
. plugins are simply modules (an object) with any of the three optional properties defined as in this contrived example:
//text.js
define({
normalize : function(path){
return path+'.txt';
},
transform : function(raw, callback){
callback(null, 'define('+JSON.stringify(raw)+')');
},
init : function(value){
return 'the plugin:'+value;
}
})
transform
run always in node, takes in raw string from the loaded file and returns the javascript to be evaluated by bilt.init
run every time the module is required, modifies its value. runs in the environment of the child module.
example usage:
//mytext.txt
i'm the contents of a text file!
define(function(){
return require('text.js!mytext'); // "the plugin: i'm the contents of a text file!"
})
bilt offers two functions that allow the expression within them to only be executed in node or the browser.
browser(expression)
returns expression on client, null on servernode(expression)
visa versa this is especially useful in plugins with an init step that will only be run on the client to prevent client-only dependencies from being loaded during plugin normalization/transformation in the build step
async
asyncronous control flow / functional libraryunderscore
synchrouous functional libraryrequest
for downloading external files with http(s)uglify-js2
javascript minificationesprima
javascript parseresprima-walk
iterates over expressions in an esprima objectescodegen
generates source from esprima objectparent-require
nodrequires up the dependency treerequire-resolve
finds the absolute path from a node module name