From 916903997447c7519049107d9b6b4d22adab0640 Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Mon, 23 Apr 2012 00:47:55 -0400 Subject: [PATCH] lodash: Cleanup build scripts and add @kitcambridge as a contributor. [jddalton] Former-commit-id: 8e37a98f155b8b2bd8ed35a993d83022ac610620 --- README.md | 5 + build.js | 264 ++++++++++++++++++++++++------------------ build/post-compile.js | 74 ++++++------ build/pre-compile.js | 244 +++++++++++++++++++------------------- 4 files changed, 321 insertions(+), 266 deletions(-) diff --git a/README.md b/README.md index ce121a35c..4f9e043a7 100644 --- a/README.md +++ b/README.md @@ -84,3 +84,8 @@ Feel free to fork and send pull requests if you see improvements! * [John-David Dalton](http://allyoucanleet.com/) [![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") diff --git a/build.js b/build.js index 51b49482c..5b8430707 100755 --- a/build.js +++ b/build.js @@ -1,136 +1,178 @@ #!/usr/bin/env node - -;(function () { +;(function() { 'use strict'; - /* Load the Node file system, path, and child process modules. */ - var fs = require('fs'), path = require('path'), spawn = require('child_process').spawn, + /** The Node filesystem, path, and child process modules */ + var fs = require('fs'), + path = require('path'), + spawn = require('child_process').spawn; - /* Load the UglifyJS compressor. */ - uglifyJS = require(path.join(__dirname, 'vendor', 'uglifyjs', 'uglify-js')), - - /* The `build` directory, containing the build scripts. */ - buildPath = path.join(__dirname, 'build'), - - /* The distribution directory. */ - distPath = path.join(__dirname, 'dist'), + /** The build directory containing the build scripts */ + var buildPath = path.join(__dirname, 'build'); - /* Load the pre- and post-processors. */ - preprocess = require(path.join(buildPath, 'pre-compile')), - postprocess = require(path.join(buildPath, 'post-compile')), + /** The distribution directory */ + var distPath = path.join(__dirname, 'dist'); - /* The pre-processed Lo-Dash source. */ - source = preprocess(fs.readFileSync(path.join(__dirname, 'lodash.js'), 'utf8')); + /** Load the pre- and post-processors */ + var preprocess = require(path.join(buildPath, 'pre-compile')), + postprocess = require(path.join(buildPath, 'post-compile')); - /* Create the destination directory if it doesn't exist. */ - if (!path.existsSync(distPath)) { - fs.mkdirSync(distPath); + /** The pre-processed Lo-Dash source */ + var source = preprocess(fs.readFileSync(path.join(__dirname, 'lodash.js'), 'utf8')); + + /** 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) { - if (exception) { - throw exception; - } - // Post-process the minified source. - var source = postprocess(results); - // Save the final minified version. - fs.writeFileSync(path.join(distPath, 'lodash.compiler.js'), source); - gzip(source, function (exception, results) { - if (exception) { - throw exception; - } - // 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'); - }); - }); + /** + * Compresses a `source` string using the Unix `gzip` commands. Yields the + * result, and any exceptions encountered, to a `callback` function. + * + * @private + * @param {String} source The JavaScript source to gzip. + * @param {Function} callback The function called when gzipping is complete. + */ + function gzip(source, callback) { + var compressor = spawn('gzip', ['-9f', '-c']), + stderr = '', + stdout = ''; - /* Compress and `gzip` Lo-Dash using UglifyJS. */ - uglify(source, function (results) { - 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'); - }); - }); + compressor.stdout.setEncoding('binary'); + compressor.stderr.setEncoding('utf8'); - /* 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 * 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) { - var results = uglifyJS.uglify.gen_code( - // Enable unsafe transformations. - uglifyJS.uglify.ast_squeeze_more( - uglifyJS.uglify.ast_squeeze( - // Munge variable and function names, excluding the special `define` - // function exposed by asynchronous module loaders. - uglifyJS.uglify.ast_mangle(uglifyJS.parser.parse(source), { + var ugly = uglifyJS.uglify; + + var result = ugly.gen_code( + // enable unsafe transformations. + ugly.ast_squeeze_more( + ugly.ast_squeeze( + // munge variable and function names, excluding the special `define` + // function exposed by AMD loaders. + ugly.ast_mangle(uglifyJS.parser.parse(source), { 'except': ['define'] } ))), { '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. - */ - function compile(source, callback) { - 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' - ]), 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); + /*--------------------------------------------------------------------------*/ + + // create the destination directory if it doesn't exist + if (!path.existsSync(distPath)) { + fs.mkdirSync(distPath); } - /* Compresses a `source` string using the Unix `gzip` commands. Yields the - * result, and any exceptions encountered, to a `callback` function. - */ - function gzip(source, callback) { - var compressor = spawn('gzip', ['-9f', '-c']), stdout = '', stderr = ''; - compressor.stdout.setEncoding('binary'); - compressor.stderr.setEncoding('utf8'); - 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; + // compress and `gzip` Lo-Dash using the Closure Compiler + compile(source, function(exception, result) { + if (exception) { + throw exception; + } + // post-process the minified source + var source = postprocess(result); + + // save the final minified version + fs.writeFileSync(path.join(distPath, 'lodash.compiler.js'), source); + + // save the `gzip`-ed version + gzip(source, function(exception, result) { + if (exception) { + throw exception; } - 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); - } -}()); \ No newline at end of file + }); + + // 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'); + }); + }); +}()); diff --git a/build/post-compile.js b/build/post-compile.js index cebe661d9..2b8829421 100644 --- a/build/post-compile.js +++ b/build/post-compile.js @@ -2,41 +2,49 @@ ;(function() { 'use strict'; - /* Post-processes a compressed `src` string. */ - var postprocess = module.exports = function postprocess(src) { - /** 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 Node filesystem module */ + var fs = require('fs'); - /*--------------------------------------------------------------------------*/ - - // set the version - license = license.replace('@VERSION', (/VERSION:([\'"])(.*?)\1/).exec(src).pop()); - - // 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 minimal license/copyright template */ + var licenseTemplate = + '/*!\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 filesystem module */ - var fs = require('fs'), src; - - if (module == require.main) { - // read the JavaScript source file from the first argument if the script - // was invoked directly (i.e., `node post-compile.js source.js`) - src = fs.readFileSync(process.argv[2], 'utf8'); - - // write to the same file - fs.writeFileSync(process.argv[2], postprocess(src), 'utf8'); + /** + * Post-process a given minified JavaScript `source`, preparing it for + * deployment. + * + * @private + * @param {String} source The source to process. + * @returns {String} Returns the processed source. + */ + function postprocess(source) { + // 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; } -}()); \ No newline at end of file + + /*--------------------------------------------------------------------------*/ + + // 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'); + }()); + } +}()); diff --git a/build/pre-compile.js b/build/pre-compile.js index 4ef7007c6..abe1380f8 100644 --- a/build/pre-compile.js +++ b/build/pre-compile.js @@ -2,117 +2,119 @@ ;(function() { 'use strict'; - var preprocess = module.exports = function preprocess(src) { - /** Used to minify variables embedded in compiled strings */ - 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' - ]; + /** The Node filesystem module */ + var fs = require('fs'); - /** Used to minify string values embedded in compiled strings */ - var compiledValues = [ - 'arrays', - 'objects' - ]; + /** Used to minify string values embedded in compiled strings */ + var compiledValues = [ + 'arrays', + 'objects' + ]; - /** Used to minify `iterationFactory` option properties */ - var iterationFactoryOptions = [ - 'afterLoop', - 'args', - 'array', - 'beforeLoop', - 'bottom', - 'exits', - 'inLoop', - 'init', - 'iterate', - 'loopExp', - 'object', - 'returns', - 'top', - 'useHas' - ]; + /** Used to minify variables embedded in compiled strings */ + 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 variables and string values to a single character */ - var minNames = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''); + /** Used to minify `iterationFactory` option properties */ + var iterationFactoryOptions = [ + 'afterLoop', + 'args', + 'array', + 'beforeLoop', + 'bottom', + 'exits', + 'inLoop', + 'init', + 'iterate', + 'loopExp', + 'object', + 'returns', + 'top', + 'useHas' + ]; - /** 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' - ]; + /** Used to minify variables and string values to a single character */ + var minNames = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''); - /*--------------------------------------------------------------------------*/ + /** 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. - */ - src = src.replace(/@(?:alias|category)[^\n]*/g, ''); + /** + * Pre-process a given JavaScript `source`, preparing it for minification. + * + * @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]+?\*\//, ''); - /** - * Add brackets to whitelisted properties so Closure Compiler won't mung them. - * http://code.google.com/closure/compiler/docs/api-tutorial3.html#export - */ - src = src.replace(RegExp('\\.(' + iterationFactoryOptions.concat(propWhitelist).join('|') + ')\\b', 'g'), "['$1']"); + // correct JSDoc tags for Closure Compiler + source = source.replace(/@(?:alias|category)[^\n]*/g, ''); - /** - * Minify `sortBy` and `template` methods. - */ + // add brackets to whitelisted properties so Closure Compiler won't mung them. + // 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) { 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; // minify property strings @@ -124,15 +126,11 @@ result = result.replace(/\\n/g, ''); // replace with modified snippet - src = src.replace(snippet, result); + source = source.replace(snippet, result); }); - /*--------------------------------------------------------------------------*/ - - /** - * Minify all `iterationFactory` related snippets. - */ - src.match( + // minify all `iterationFactory` related snippets + source.match( RegExp([ // match variables storing `iterationFactory` options 'var [a-zA-Z]+FactoryOptions\\s*=\\s*\\{[\\s\\S]+?};\\n', @@ -152,7 +150,7 @@ .replace(/\)\([^)]+/, '$&,true,false'); // replace with modified snippet early and clip snippet - src = src.replace(snippet, result); + source = source.replace(snippet, result); snippet = result = result.replace(/\)\([\s\S]+$/, ''); } @@ -189,22 +187,24 @@ result = result.replace(/\\n/g, ''); // replace with modified snippet - src = src.replace(snippet, result); + source = source.replace(snippet, result); }); - return src; - }; + + return source; + } /*--------------------------------------------------------------------------*/ - /** The filesystem module */ - var fs = require('fs'), src; - - if (module == require.main) { - // read the JavaScript source file from the first argument if the script - // was invoked directly (i.e., `node pre-compile.js source.js`) - src = fs.readFileSync(process.argv[2], 'utf8'); - - // write to the same file - fs.writeFileSync(process.argv[2], preprocess(src), 'utf8'); + // expose `preprocess` + if (module != require.main) { + module.exports = preprocess; + } else { + // Read the JavaScript source file from the first argument if the script + // was invoked directly (e.g. `node pre-compile.js source.js`) and write to + // the same file. + (function() { + var source = fs.readFileSync(process.argv[2], 'utf8'); + fs.writeFileSync(process.argv[2], preprocess(source), 'utf8'); + }()); } -}()); \ No newline at end of file +}());