Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from bitovi/builder
Download builder functionality
- Loading branch information
Showing
18 changed files
with
470 additions
and
195 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
var _ = require('underscore'); | ||
var async = require('async'); | ||
var path = require('path'); | ||
var fs = require('fs'); | ||
var clone = require('clone'); | ||
|
||
var opener = require('./open'); | ||
var pluginify = require('./pluginify'); | ||
var steal = require('../steal'); | ||
var utilities = require('./utilities'); | ||
|
||
/** | ||
* Load all Steal dependencies for each configuration and add them to the | ||
* download builder information object. | ||
* | ||
* @param {Object} info The download builder information object | ||
* @param {String} root The root path to use | ||
* @param {Function} callback Callback to call when done loading | ||
*/ | ||
function loadSteals(info, root, callback) { | ||
var configurations = info.configurations; | ||
var modules = _.keys(info.modules); | ||
var stealers = _.map(configurations, function(configuration, configName) { | ||
return function(callback) { | ||
opener(modules, _.extend({ | ||
root: root | ||
}, configuration.steal), function(error, configSteals) { | ||
callback(error, configName, clone(_.uniq(configSteals, function(stl) { | ||
return stl.options && stl.options.id.toString(); | ||
}))); | ||
}); | ||
} | ||
}); | ||
|
||
async.series(stealers, function(error, args) { | ||
if(error) { | ||
return callback(error); | ||
} | ||
|
||
var steals = {}; | ||
_.each(args, function(rets) { | ||
var name = rets[0]; | ||
var configSteals = rets[1]; | ||
steals[name] = configSteals; | ||
}); | ||
callback(null, steals); | ||
}); | ||
} | ||
|
||
/** | ||
* Build with a given configuration. | ||
* | ||
* @param {{}} options The options to use | ||
* @option {Array} ids An array of ids to build (`isDefault` modules will be used if empty) | ||
* @option {Boolean} minify Whether to minify the output using UglifyJS 2 | ||
* @option {String} url The source URL to put in the banner | ||
* @option {String} configuration The configuration name to use (uses default configuration if not available) | ||
* @param {Object} info The builder configuration information as returned by `loadInfo` | ||
* @param {Function} callback The callback to call with the source output | ||
* @returns {*} | ||
*/ | ||
function builder(options, info, callback) { | ||
// Grab our ids from the `modules` query parameter if possible | ||
var ids = builder.getIds(options.ids, info.modules); | ||
var configuration = options.configuration ? | ||
info.configurations[options.configuration] : | ||
_.find(info.configurations, function(item) { | ||
return item.isDefault; | ||
}); | ||
|
||
if(!configuration) { | ||
return callback(new Error('No configuration for: '+ options.configuration)); | ||
} | ||
|
||
// From the list of ids get all our Steals | ||
var buildSteals = builder.getSteals(configuration.steals, ids); | ||
var pluginifyOptions = _.extend({}, info.pluginify, configuration.pluginify); | ||
|
||
pluginify.pluginifySteals(buildSteals, pluginifyOptions, function (error, str) { | ||
if(error) { | ||
return callback(error); | ||
} | ||
|
||
var banner = info.banner ? utilities.banner(info.banner, { | ||
url: options.url || '', | ||
info: info | ||
}) : ''; | ||
var content = options.minify ? utilities.uglify(str) : utilities.prettify(str, info.beautify); | ||
|
||
callback(null, banner + content); | ||
}); | ||
} | ||
|
||
/** | ||
* From a list of Steal objects return every object matching the | ||
* id from the given list. | ||
* | ||
* @param {Array} steals The list of Steals | ||
* @param {Array<String>} ids The list of ids | ||
* @returns {Array} The list of Steal objects matching the id | ||
*/ | ||
builder.getSteals = function (steals, ids) { | ||
var result = []; | ||
ids.forEach(function (name) { | ||
var id = steal.id(name).toString(); | ||
var stl = _.find(steals, function (current) { | ||
return current.options.id.toString() === id; | ||
}); | ||
if (!stl) { | ||
throw new Error('Module ' + name + ' is not available.'); | ||
} | ||
result.push(stl); | ||
}); | ||
return result; | ||
} | ||
|
||
/** | ||
* Converts a single module name or a list of modules. | ||
* Returns a list of the `builder.json` default module names | ||
* if no modules have been given. | ||
* | ||
* @param {Object|String|null} modules The single module | ||
* name, a list of modules names or nothing. | ||
* @param {Object} builderModules The module list from the | ||
* `builder.json` definition | ||
* @returns {Array} The list of module ids | ||
*/ | ||
builder.getIds = function (modules, builderModules) { | ||
if (!modules || !modules.length) { | ||
var ids = []; | ||
_.each(builderModules, function (value, name) { | ||
if (value.isDefault) { | ||
ids.push(name); | ||
} | ||
}); | ||
return ids; | ||
} | ||
return _.isArray(modules) ? modules : [modules]; | ||
} | ||
|
||
/** | ||
* Get all dependencies for a given Steal id. | ||
* Only include visible modules. | ||
* | ||
* @param {String} id The Steal id string to check for. | ||
* @param {Array} steals A list of Steal dependencies to check in | ||
* @param {Array} [visibleModules] A list of visible module ids. | ||
* If given only these modules will be included in the dependencies. | ||
* @returns {Array} A list of ids. | ||
*/ | ||
builder.getDependencies = function (id, steals, visibleModules) { | ||
id = steal.id(id).toString(); | ||
|
||
var result = []; | ||
var stl = _.find(steals, function (curStl) { | ||
return id === curStl.options.id.toString(); | ||
}); | ||
|
||
if (stl) { | ||
var visibles = visibleModules ? _.map(visibleModules, function (mod) { | ||
return steal.id(mod).toString(); | ||
}) : null; | ||
opener.visit([stl], function (stl) { | ||
var currentId = stl.options.id.toString(); | ||
if (steal.id(id).toString() !== currentId && | ||
(!visibles || visibles.indexOf(currentId) !== -1)) { | ||
result.push(currentId); | ||
} | ||
}); | ||
} | ||
|
||
return result; | ||
} | ||
|
||
/** | ||
* Load the download builder information which will contain: | ||
* | ||
* - `package.json` | ||
* - `builder.json` | ||
* - All Steal dependencies (and file contents) for every available download builder configuration | ||
* | ||
* This will be used throughout most of the download builder. | ||
* The callback data will be `undefined` if any of the JSON configuration files | ||
* isn't available. | ||
* | ||
* @param {String} filePath The path to check for. | ||
* @param {Function} callback Callback with the information object | ||
* @returns {*} | ||
*/ | ||
builder.loadInfo = function (filePath, callback) { | ||
var builder = path.join(filePath, '/builder.json'); | ||
var pkg = path.join(filePath, '/package.json'); | ||
|
||
if (!(fs.existsSync(builder) && fs.existsSync(pkg))) { | ||
return callback(null, null); | ||
} | ||
|
||
var info = _.extend({ | ||
path: filePath, | ||
configurations: {} | ||
}, require(builder), require(pkg)); | ||
|
||
// Load Steals with parent folder of the current path as the root | ||
loadSteals(info, path.dirname(filePath), function(error, steals) { | ||
if(error) { | ||
return callback(error); | ||
} | ||
|
||
_.each(steals, function(stls, name) { | ||
info.configurations[name].steals = stls; | ||
}); | ||
|
||
callback(null, info); | ||
}); | ||
} | ||
|
||
module.exports = builder; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
var _ = require('underscore'); | ||
var beautify = require('js-beautify').js_beautify; | ||
var UglifyJS = require('uglify-js'); | ||
|
||
/** | ||
* Creates a banner from a template. | ||
* | ||
* @param {String} banner The banner template | ||
* @param {Object} options The options to pass to the banner template | ||
*/ | ||
exports.banner = function (banner, options) { | ||
return banner ? _.template(banner, options) : ''; | ||
} | ||
|
||
/** | ||
* Prettify a source code string using the optional | ||
* options. Removes all multiline comments and | ||
* multi-newlines and semicolons. | ||
* Beautifies the code with the `beautify` settings. | ||
* | ||
* @param {String} str The source code to beautify | ||
* @param {Object} options The options containing the | ||
* optional `banner` template and `beautify` settings. | ||
* @returns {String} The prettified source code. | ||
*/ | ||
exports.prettify = function (str, options) { | ||
options = options || {}; | ||
|
||
// Strip out multiline comments and clean up some other things | ||
// And run jsBeautifier | ||
var content = beautify(str.replace(/\/\*([\s\S]*?)\*\//gim, '') | ||
.replace(/\/\/(\s*)\n/gim, '') | ||
.replace(/;[\s]*;/gim, '') | ||
.replace(/(\/\/.*)\n[\s]*;/gi, '') | ||
.replace(/(\n){3,}/gim, '\n\n'), options); | ||
|
||
return content; | ||
} | ||
|
||
/** | ||
* Minify JavaScript source code using UglifyJS 2. | ||
* | ||
* @param {String} code The JavaScript source code to minify. | ||
* @returns {String} The minified source code | ||
*/ | ||
exports.uglify = function (code) { | ||
var toplevel = UglifyJS.parse(code); | ||
|
||
toplevel.figure_out_scope(); | ||
|
||
var compressor = UglifyJS.Compressor({ | ||
warnings: false | ||
}); | ||
var compressed = toplevel.transform(compressor); | ||
|
||
compressed.figure_out_scope(); | ||
compressed.compute_char_frequency(); | ||
compressed.mangle_names(); | ||
|
||
return compressed.print_to_string(); | ||
} |
Oops, something went wrong.