lodash: Cleanup build scripts and add @kitcambridge as a contributor. [jddalton]

Former-commit-id: 8e37a98f155b8b2bd8ed35a993d83022ac610620
This commit is contained in:
John-David Dalton
2012-04-23 00:47:55 -04:00
parent 588c6b978b
commit 9169039974
4 changed files with 321 additions and 266 deletions

View File

@@ -84,3 +84,8 @@ Feel free to fork and send pull requests if you see improvements!
* [John-David Dalton](http://allyoucanleet.com/) * [John-David Dalton](http://allyoucanleet.com/)
[![twitter/jdalton](http://gravatar.com/avatar/299a3d891ff1920b69c364d061007043?s=70)](https://twitter.com/jdalton "Follow @jdalton on Twitter") [![twitter/jdalton](http://gravatar.com/avatar/299a3d891ff1920b69c364d061007043?s=70)](https://twitter.com/jdalton "Follow @jdalton on Twitter")
## Contributors
* [Kit Cambridge](http://kitcambridge.github.com/)
[![twitter/kitcambridge](http://gravatar.com/avatar/6662a1d02f351b5ef2f8b4d815804661?s=70)](https://twitter.com/kitcambridge "Follow @kitcambridge on Twitter")

264
build.js
View File

@@ -1,136 +1,178 @@
#!/usr/bin/env node #!/usr/bin/env node
;(function() {
;(function () {
'use strict'; 'use strict';
/* Load the Node file system, path, and child process modules. */ /** The Node filesystem, path, and child process modules */
var fs = require('fs'), path = require('path'), spawn = require('child_process').spawn, var fs = require('fs'),
path = require('path'),
spawn = require('child_process').spawn;
/* Load the UglifyJS compressor. */ /** The build directory containing the build scripts */
uglifyJS = require(path.join(__dirname, 'vendor', 'uglifyjs', 'uglify-js')), var buildPath = path.join(__dirname, 'build');
/* The `build` directory, containing the build scripts. */
buildPath = path.join(__dirname, 'build'),
/* The distribution directory. */
distPath = path.join(__dirname, 'dist'),
/* Load the pre- and post-processors. */ /** The distribution directory */
preprocess = require(path.join(buildPath, 'pre-compile')), var distPath = path.join(__dirname, 'dist');
postprocess = require(path.join(buildPath, 'post-compile')),
/* The pre-processed Lo-Dash source. */ /** Load the pre- and post-processors */
source = preprocess(fs.readFileSync(path.join(__dirname, 'lodash.js'), 'utf8')); var preprocess = require(path.join(buildPath, 'pre-compile')),
postprocess = require(path.join(buildPath, 'post-compile'));
/* Create the destination directory if it doesn't exist. */ /** The pre-processed Lo-Dash source */
if (!path.existsSync(distPath)) { var source = preprocess(fs.readFileSync(path.join(__dirname, 'lodash.js'), 'utf8'));
fs.mkdirSync(distPath);
/** Load the UglifyJS compressor */
var uglifyJS = require(path.join(__dirname, 'vendor', 'uglifyjs', 'uglify-js'));
/*--------------------------------------------------------------------------*/
/**
* Compresses a `source` string using the Closure Compiler. Yields the
* minified result, and any exceptions encountered, to a `callback` function.
*
* @private
* @param {String} source The JavaScript source to minify.
* @param {Function} callback The function called when minifying is complete.
*/
function compile(source, callback) {
var stderr = '',
stdout = '';
var compiler = spawn('java', [
// load the Closure Compiler and set the compression options
'-jar', path.join(__dirname, 'vendor', 'closure-compiler', 'compiler.jar'),
'--compilation_level=ADVANCED_OPTIMIZATIONS',
'--language_in=ECMASCRIPT5_STRICT',
'--warning_level=QUIET'
]);
// explicitly set the encoding of the output and error streams
compiler.stdout.setEncoding('utf8');
compiler.stderr.setEncoding('utf8');
compiler.stdout.on('data', function(data) {
stdout += data;
});
compiler.stderr.on('data', function(data) {
stderr += data;
});
compiler.on('exit', function(status) {
var exception = null;
if (status) {
exception = new Error(stderr);
exception.status = status;
}
callback(exception, stdout);
});
// proxy the source string to Closure Compiler
compiler.stdin.end(source);
} }
/* Compress and `gzip` Lo-Dash using the Closure Compiler. */ /**
compile(source, function (exception, results) { * Compresses a `source` string using the Unix `gzip` commands. Yields the
if (exception) { * result, and any exceptions encountered, to a `callback` function.
throw exception; *
} * @private
// Post-process the minified source. * @param {String} source The JavaScript source to gzip.
var source = postprocess(results); * @param {Function} callback The function called when gzipping is complete.
// Save the final minified version. */
fs.writeFileSync(path.join(distPath, 'lodash.compiler.js'), source); function gzip(source, callback) {
gzip(source, function (exception, results) { var compressor = spawn('gzip', ['-9f', '-c']),
if (exception) { stderr = '',
throw exception; stdout = '';
}
// Save the `gzip`-ed version. The explicit `binary` encoding is
// necessary to ensure that the stream is written correctly.
fs.writeFileSync(path.join(distPath, 'lodash.compiler.js.gz'), results, 'binary');
});
});
/* Compress and `gzip` Lo-Dash using UglifyJS. */ compressor.stdout.setEncoding('binary');
uglify(source, function (results) { compressor.stderr.setEncoding('utf8');
var source = postprocess(results);
fs.writeFileSync(path.join(distPath, 'lodash.uglify.js'), source);
gzip(source, function (exception, results) {
if (exception) {
throw exception;
}
fs.writeFileSync(path.join(distPath, 'lodash.uglify.js.gz'), results, 'binary');
});
});
/* Compresses a `source` string using UglifyJS. Yields the result to a compressor.stdout.on('data', function(data) {
stdout += data;
});
compressor.stderr.on('data', function(data) {
stderr += data;
});
compressor.on('exit', function(status) {
var exception = null;
if (status) {
exception = new Error(stderr);
exception.status = status;
}
callback(exception, stdout);
});
// proxy the source string to the `gzip` executable
compressor.stdin.end(source);
}
/**
* Compresses a `source` string using UglifyJS. Yields the result to a
* `callback` function. This function is synchronous; the `callback` is used * `callback` function. This function is synchronous; the `callback` is used
* for symmetry. * for symmetry.
*/ *
* @private
* @param {String} source The JavaScript source to minify.
* @param {Function} callback The function called when minifying is complete.
*/
function uglify(source, callback) { function uglify(source, callback) {
var results = uglifyJS.uglify.gen_code( var ugly = uglifyJS.uglify;
// Enable unsafe transformations.
uglifyJS.uglify.ast_squeeze_more( var result = ugly.gen_code(
uglifyJS.uglify.ast_squeeze( // enable unsafe transformations.
// Munge variable and function names, excluding the special `define` ugly.ast_squeeze_more(
// function exposed by asynchronous module loaders. ugly.ast_squeeze(
uglifyJS.uglify.ast_mangle(uglifyJS.parser.parse(source), { // munge variable and function names, excluding the special `define`
// function exposed by AMD loaders.
ugly.ast_mangle(uglifyJS.parser.parse(source), {
'except': ['define'] 'except': ['define']
} }
))), { ))), {
'ascii_only': true 'ascii_only': true
}); });
callback(uglifyJS.uglify.split_lines(results, 500));
// split lines at 500 characters to be consistent with Closure Compiler
callback(ugly.split_lines(result, 500));
} }
/* Compresses a `source` string using the Closure Compiler. Yields the /*--------------------------------------------------------------------------*/
* minified result to a `callback` function.
*/ // create the destination directory if it doesn't exist
function compile(source, callback) { if (!path.existsSync(distPath)) {
var compiler = spawn('java', [ fs.mkdirSync(distPath);
// Load the Closure Compiler and set the compression options.
'-jar', path.join(__dirname, 'vendor', 'closure-compiler', 'compiler.jar'),
'--compilation_level=ADVANCED_OPTIMIZATIONS',
'--language_in=ECMASCRIPT5_STRICT',
'--warning_level=QUIET'
]), stdout = '', stderr = '';
// Explicitly set the encoding of the output and error streams.
compiler.stdout.setEncoding('utf8');
compiler.stderr.setEncoding('utf8');
compiler.stdout.on('data', function (data) {
stdout += data;
});
compiler.stderr.on('data', function (data) {
stderr += data;
});
compiler.on('exit', function (status) {
var exception = null;
if (status) {
exception = new Error(stderr);
exception.status = status;
}
callback(exception, stdout);
});
compiler.stdin.end(source);
} }
/* Compresses a `source` string using the Unix `gzip` commands. Yields the // compress and `gzip` Lo-Dash using the Closure Compiler
* result, and any exceptions encountered, to a `callback` function. compile(source, function(exception, result) {
*/ if (exception) {
function gzip(source, callback) { throw exception;
var compressor = spawn('gzip', ['-9f', '-c']), stdout = '', stderr = ''; }
compressor.stdout.setEncoding('binary'); // post-process the minified source
compressor.stderr.setEncoding('utf8'); var source = postprocess(result);
compressor.stdout.on('data', function (data) {
stdout += data; // save the final minified version
}); fs.writeFileSync(path.join(distPath, 'lodash.compiler.js'), source);
compressor.stderr.on('data', function (data) {
stderr += data; // save the `gzip`-ed version
}); gzip(source, function(exception, result) {
compressor.on('exit', function (status) { if (exception) {
var exception = null; throw exception;
if (status) {
exception = new Error(stderr);
exception.status = status;
} }
callback(exception, stdout); // explicit `binary` encoding is necessary to ensure that the stream is written correctly
fs.writeFileSync(path.join(distPath, 'lodash.compiler.js.gz'), result, 'binary');
}); });
// Proxy the source string to the `gzip` executable. });
compressor.stdin.end(source);
} // compress and `gzip` Lo-Dash using UglifyJS
}()); uglify(source, function(result) {
var source = postprocess(result);
fs.writeFileSync(path.join(distPath, 'lodash.uglify.js'), source);
gzip(source, function(exception, result) {
if (exception) {
throw exception;
}
fs.writeFileSync(path.join(distPath, 'lodash.uglify.js.gz'), result, 'binary');
});
});
}());

View File

@@ -2,41 +2,49 @@
;(function() { ;(function() {
'use strict'; 'use strict';
/* Post-processes a compressed `src` string. */ /** The Node filesystem module */
var postprocess = module.exports = function postprocess(src) { var fs = require('fs');
/** The minimal license/copyright header */
var license =
'/*!\n' +
' Lo-Dash @VERSION github.com/bestiejs/lodash/blob/master/LICENSE.txt\n' +
' Underscore.js 1.3.3 github.com/documentcloud/underscore/blob/master/LICENSE\n' +
'*/';
/*--------------------------------------------------------------------------*/ /** The minimal license/copyright template */
var licenseTemplate =
// set the version '/*!\n' +
license = license.replace('@VERSION', (/VERSION:([\'"])(.*?)\1/).exec(src).pop()); ' Lo-Dash @VERSION github.com/bestiejs/lodash/blob/master/LICENSE.txt\n' +
' Underscore.js 1.3.3 github.com/documentcloud/underscore/blob/master/LICENSE\n' +
// move vars exposed by Closure Compiler into the IIFE '*/';
src = src.replace(/^([^(\n]+)\s*(\(function[^)]+\){)/, '$2$1');
// use double quotes consistently
src = src.replace(/'use strict'/, '"use strict"');
// add license
return license + '\n;' + src;
};
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
/** The filesystem module */ /**
var fs = require('fs'), src; * Post-process a given minified JavaScript `source`, preparing it for
* deployment.
if (module == require.main) { *
// read the JavaScript source file from the first argument if the script * @private
// was invoked directly (i.e., `node post-compile.js source.js`) * @param {String} source The source to process.
src = fs.readFileSync(process.argv[2], 'utf8'); * @returns {String} Returns the processed source.
*/
// write to the same file function postprocess(source) {
fs.writeFileSync(process.argv[2], postprocess(src), 'utf8'); // set the version
var license = licenseTemplate.replace('@VERSION', (/VERSION:([\'"])(.*?)\1/).exec(source).pop());
// move vars exposed by Closure Compiler into the IIFE
source = source.replace(/^([^(\n]+)\s*(\(function[^)]+\){)/, '$2$1');
// use double quotes consistently
source = source.replace(/'use strict'/, '"use strict"');
// add license
return license + '\n;' + source;
} }
}());
/*--------------------------------------------------------------------------*/
// expose `postprocess`
if (module != require.main) {
module.exports = postprocess;
} else {
// Read the JavaScript source file from the first argument if the script
// was invoked directly (e.g. `node post-compile.js source.js`) and write to
// the same file.
(function() {
var source = fs.readFileSync(process.argv[2], 'utf8');
fs.writeFileSync(process.argv[2], postprocess(source), 'utf8');
}());
}
}());

View File

@@ -2,117 +2,119 @@
;(function() { ;(function() {
'use strict'; 'use strict';
var preprocess = module.exports = function preprocess(src) { /** The Node filesystem module */
/** Used to minify variables embedded in compiled strings */ var fs = require('fs');
var compiledVars = [
'accumulator',
'array',
'arrayClass',
'bind',
'callback',
'className',
'collection',
'computed',
'concat',
'current',
'false',
'funcClass',
'hasOwnProperty',
'identity',
'index',
'indexOf',
'Infinity',
'initial',
'isArray',
'isEmpty',
'length',
'object',
'Math',
'property',
'result',
'slice',
'source',
'stringClass',
'target',
'thisArg',
'toString',
'true',
'undefined',
'value',
'values'
];
/** Used to minify string values embedded in compiled strings */ /** Used to minify string values embedded in compiled strings */
var compiledValues = [ var compiledValues = [
'arrays', 'arrays',
'objects' 'objects'
]; ];
/** Used to minify `iterationFactory` option properties */ /** Used to minify variables embedded in compiled strings */
var iterationFactoryOptions = [ var compiledVars = [
'afterLoop', 'accumulator',
'args', 'array',
'array', 'arrayClass',
'beforeLoop', 'bind',
'bottom', 'callback',
'exits', 'className',
'inLoop', 'collection',
'init', 'computed',
'iterate', 'concat',
'loopExp', 'current',
'object', 'false',
'returns', 'funcClass',
'top', 'hasOwnProperty',
'useHas' 'identity',
]; 'index',
'indexOf',
'Infinity',
'initial',
'isArray',
'isEmpty',
'length',
'object',
'Math',
'property',
'result',
'slice',
'source',
'stringClass',
'target',
'thisArg',
'toString',
'true',
'undefined',
'value',
'values'
];
/** Used to minify variables and string values to a single character */ /** Used to minify `iterationFactory` option properties */
var minNames = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''); var iterationFactoryOptions = [
'afterLoop',
'args',
'array',
'beforeLoop',
'bottom',
'exits',
'inLoop',
'init',
'iterate',
'loopExp',
'object',
'returns',
'top',
'useHas'
];
/** Used protect the specified properties from getting minified */ /** Used to minify variables and string values to a single character */
var propWhitelist = [ var minNames = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
'_',
'amd',
'chain',
'clearInterval',
'criteria',
'escape',
'evaluate',
'interpolate',
'isEqual',
'isFinite',
'lodash',
'setTimeout',
'templateSettings',
'toArray',
'value',
'variable'
];
/*--------------------------------------------------------------------------*/ /** Used protect the specified properties from getting minified */
var propWhitelist = [
'_',
'amd',
'chain',
'clearInterval',
'criteria',
'escape',
'evaluate',
'interpolate',
'isEqual',
'isFinite',
'lodash',
'setTimeout',
'templateSettings',
'toArray',
'value',
'variable'
];
/** /*--------------------------------------------------------------------------*/
* Remove copyright to add later in post-compile.js
*/
src = src.replace(/\/\*![\s\S]+?\*\//, '');
/** /**
* Correct JSDoc tags for Closure Compiler. * Pre-process a given JavaScript `source`, preparing it for minification.
*/ *
src = src.replace(/@(?:alias|category)[^\n]*/g, ''); * @private
* @param {String} source The source to process.
* @returns {String} Returns the processed source.
*/
function preprocess(source) {
// remove copyright to add later in post-compile.js
source = source.replace(/\/\*![\s\S]+?\*\//, '');
/** // correct JSDoc tags for Closure Compiler
* Add brackets to whitelisted properties so Closure Compiler won't mung them. source = source.replace(/@(?:alias|category)[^\n]*/g, '');
* http://code.google.com/closure/compiler/docs/api-tutorial3.html#export
*/
src = src.replace(RegExp('\\.(' + iterationFactoryOptions.concat(propWhitelist).join('|') + ')\\b', 'g'), "['$1']");
/** // add brackets to whitelisted properties so Closure Compiler won't mung them.
* Minify `sortBy` and `template` methods. // http://code.google.com/closure/compiler/docs/api-tutorial3.html#export
*/ source = source.replace(RegExp('\\.(' + iterationFactoryOptions.concat(propWhitelist).join('|') + ')\\b', 'g'), "['$1']");
// minify `sortBy` and `template` methods
['sortBy', 'template'].forEach(function(methodName) { ['sortBy', 'template'].forEach(function(methodName) {
var properties = ['criteria', 'value'], var properties = ['criteria', 'value'],
snippet = src.match(RegExp('(\\n\\s*)function ' + methodName + '[\\s\\S]+?\\1}'))[0], snippet = source.match(RegExp('(\\n\\s*)function ' + methodName + '[\\s\\S]+?\\1}'))[0],
result = snippet; result = snippet;
// minify property strings // minify property strings
@@ -124,15 +126,11 @@
result = result.replace(/\\n/g, ''); result = result.replace(/\\n/g, '');
// replace with modified snippet // replace with modified snippet
src = src.replace(snippet, result); source = source.replace(snippet, result);
}); });
/*--------------------------------------------------------------------------*/ // minify all `iterationFactory` related snippets
source.match(
/**
* Minify all `iterationFactory` related snippets.
*/
src.match(
RegExp([ RegExp([
// match variables storing `iterationFactory` options // match variables storing `iterationFactory` options
'var [a-zA-Z]+FactoryOptions\\s*=\\s*\\{[\\s\\S]+?};\\n', 'var [a-zA-Z]+FactoryOptions\\s*=\\s*\\{[\\s\\S]+?};\\n',
@@ -152,7 +150,7 @@
.replace(/\)\([^)]+/, '$&,true,false'); .replace(/\)\([^)]+/, '$&,true,false');
// replace with modified snippet early and clip snippet // replace with modified snippet early and clip snippet
src = src.replace(snippet, result); source = source.replace(snippet, result);
snippet = result = result.replace(/\)\([\s\S]+$/, ''); snippet = result = result.replace(/\)\([\s\S]+$/, '');
} }
@@ -189,22 +187,24 @@
result = result.replace(/\\n/g, ''); result = result.replace(/\\n/g, '');
// replace with modified snippet // replace with modified snippet
src = src.replace(snippet, result); source = source.replace(snippet, result);
}); });
return src;
}; return source;
}
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
/** The filesystem module */ // expose `preprocess`
var fs = require('fs'), src; if (module != require.main) {
module.exports = preprocess;
if (module == require.main) { } else {
// read the JavaScript source file from the first argument if the script // Read the JavaScript source file from the first argument if the script
// was invoked directly (i.e., `node pre-compile.js source.js`) // was invoked directly (e.g. `node pre-compile.js source.js`) and write to
src = fs.readFileSync(process.argv[2], 'utf8'); // the same file.
(function() {
// write to the same file var source = fs.readFileSync(process.argv[2], 'utf8');
fs.writeFileSync(process.argv[2], preprocess(src), 'utf8'); fs.writeFileSync(process.argv[2], preprocess(source), 'utf8');
}());
} }
}()); }());