From 786c434a29a487fd076245c66cebd2c63d7573be Mon Sep 17 00:00:00 2001 From: Kit Cambridge Date: Sun, 22 Apr 2012 21:21:32 -0600 Subject: [PATCH] Extract the `preprocess` and `postprocess` functions into `build/pre-compile.js` and `build/post-compile.js`, respectively. Clean up `build.js`. Former-commit-id: 18c47305b304cca41f00d994138f5b067def1b07 --- build.js | 290 ++++++------------------------------------ build/post-compile.js | 42 ++++++ build/pre-compile.js | 210 ++++++++++++++++++++++++++++++ 3 files changed, 291 insertions(+), 251 deletions(-) create mode 100644 build/post-compile.js create mode 100644 build/pre-compile.js diff --git a/build.js b/build.js index df17919d1..55e506b8e 100755 --- a/build.js +++ b/build.js @@ -1,21 +1,30 @@ #!/usr/bin/env node ;(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, + 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")), - + uglifyJS = require(path.join(__dirname, 'vendor', 'uglifyjs', 'uglify-js')), + + /* The `build` directory, containing the build scripts. */ + buildPath = path.join(__dirname, 'build'), + /* The distribution directory. */ - distribution = path.join(__dirname, "dist"), + distPath = path.join(__dirname, 'dist'), + + /* Load the pre- and post-processors. */ + preprocess = require(path.join(buildPath, 'pre-compile')), + postprocess = require(path.join(buildPath, 'post-compile')), /* The pre-processed Lodash source. */ - source = preprocess(fs.readFileSync(path.join(__dirname, "lodash.js"), "utf8")); + source = preprocess(fs.readFileSync(path.join(__dirname, 'lodash.js'), 'utf8')); /* Create the destination directory if it doesn't exist. */ - if (!path.existsSync(distribution)) { - fs.mkdirSync(distribution); + if (!path.existsSync(distPath)) { + fs.mkdirSync(distPath); } /* Compress and `gzip` Lodash using the Closure Compiler. */ @@ -26,26 +35,26 @@ // Post-process the minified source. var source = postprocess(results); // Save the final minified version. - fs.writeFileSync(path.join(distribution, "lodash.compiler.js"), source); + 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(distribution, "lodash.compiler.js.gz"), results, "binary"); + fs.writeFileSync(path.join(distPath, 'lodash.compiler.js.gz'), results, 'binary'); }); }); /* Compress and `gzip` Lodash using UglifyJS. */ uglify(source, function (results) { var source = postprocess(results); - fs.writeFileSync(path.join(distribution, "lodash.uglify.js"), source); + fs.writeFileSync(path.join(distPath, 'lodash.uglify.js'), source); gzip(source, function (exception, results) { if (exception) { throw exception; } - fs.writeFileSync(path.join(distribution, "lodash.uglify.js.gz"), results, "binary"); + fs.writeFileSync(path.join(distPath, 'lodash.uglify.js.gz'), results, 'binary'); }); }); @@ -61,10 +70,10 @@ // Munge variable and function names, excluding the special `define` // function exposed by asynchronous module loaders. uglifyJS.uglify.ast_mangle(uglifyJS.parser.parse(source), { - "except": ["define"] + 'except': ['define'] } ))), { - "ascii_only": true + 'ascii_only': true }); callback(uglifyJS.uglify.split_lines(results, 500)); } @@ -73,23 +82,23 @@ * minified result to a `callback` function. */ function compile(source, callback) { - var compiler = spawn("java", [ + 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 = ""; + '-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) { + compiler.stdout.setEncoding('utf8'); + compiler.stderr.setEncoding('utf8'); + compiler.stdout.on('data', function (data) { stdout += data; }); - compiler.stderr.on("data", function (data) { + compiler.stderr.on('data', function (data) { stderr += data; }); - compiler.on("exit", function (status) { + compiler.on('exit', function (status) { var exception = null; if (status) { exception = new Error(stderr); @@ -104,16 +113,16 @@ * 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) { + 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) { + compressor.stderr.on('data', function (data) { stderr += data; }); - compressor.on("exit", function (status) { + compressor.on('exit', function (status) { var exception = null; if (status) { exception = new Error(stderr); @@ -124,225 +133,4 @@ // Proxy the source string to the `gzip` executable. compressor.stdin.end(source); } - - /* Post-processes a compressed `source` string. */ - function postprocess(source) { - /** 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' + - '*/'; - - /*--------------------------------------------------------------------------*/ - - // set the version - license = license.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; - } - - /* Pre-processes an uncompressed `source` string. */ - function preprocess(source) { - /** 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 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 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 - */ - source = source.replace(/\/\*![\s\S]+?\*\//, ''); - - /** - * Correct JSDoc tags for Closure Compiler. - */ - source = source.replace(/@(?:alias|category)[^\n]*/g, ''); - - /** - * 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 = source.match(RegExp('(\\n\\s*)function ' + methodName + '[\\s\\S]+?\\1}'))[0], - result = snippet; - - // minify property strings - properties.forEach(function(property, index) { - result = result.replace(RegExp("'" + property + "'", 'g'), "'" + minNames[index] + "'"); - }); - - // remove escaped newlines in strings - result = result.replace(/\\n/g, ''); - - // replace with modified snippet - source = source.replace(snippet, result); - }); - - /*--------------------------------------------------------------------------*/ - - /** - * Minify all `iterationFactory` related snippets. - */ - source.match( - RegExp([ - // match variables storing `iterationFactory` options - 'var [a-zA-Z]+FactoryOptions\\s*=\\s*\\{[\\s\\S]+?};\\n', - // match the the `iterationFactory` function - '(\\n\\s*)function iterationFactory[\\s\\S]+?\\1}', - // match methods created by `iterationFactor` calls - 'iterationFactory\\((?:[\'{]|[a-zA-Z]+,)[\\s\\S]+?\\);\\n' - ].join('|'), 'g') - ) - .forEach(function(snippet, index) { - var result = snippet; - - // add `true` and `false` arguments to be minified - if (/function iterationFactory/.test(snippet)) { - result = result - .replace(/(Function\('[\s\S]+?)undefined/, '$1true,false,undefined') - .replace(/\)\([^)]+/, '$&,true,false'); - - // replace with modified snippet early and clip snippet - source = source.replace(snippet, result); - snippet = result = result.replace(/\)\([\s\S]+$/, ''); - } - - // minify snippet variables/arguments - compiledVars.forEach(function(variable, index) { - result = result.replace(RegExp('([^.]\\b|\\\\n)' + variable + '\\b(?!\'\\s*[\\]:])', 'g'), '$1' + minNames[index]); - // correct `typeof x == 'object'` - if (variable == 'object') { - result = result.replace(RegExp("(typeof [^']+')" + minNames[index] + "'", 'g'), "$1object'"); - } - // correct boolean literals - if (variable == 'true' || variable == 'false') { - result = result - .replace(RegExp(':\\s*' + minNames[index] + '\\s*,', 'g'), ':' + variable + ',') - .replace(RegExp('\\s*' + minNames[index] + '\\s*;', 'g'), variable + ';'); - } - }); - - // minify snippet values - compiledValues.forEach(function(value, index) { - result = result.replace(RegExp("'" + value + "'", 'g'), "'" + minNames[index] + "'"); - }); - - // minify iterationFactory option property strings - iterationFactoryOptions.forEach(function(property, index) { - if (property == 'array' || property == 'object') { - result = result.replace(RegExp("'" + property + "'(\\s*[\\]:])", 'g'), "'" + minNames[index] + "'$1"); - } else { - result = result.replace(RegExp("'" + property + "'", 'g'), "'" + minNames[index] + "'"); - } - }); - - // remove escaped newlines in strings - result = result.replace(/\\n/g, ''); - - // replace with modified snippet - source = source.replace(snippet, result); - }); - - /*--------------------------------------------------------------------------*/ - - // write to the same file - return source; - } }()); \ No newline at end of file diff --git a/build/post-compile.js b/build/post-compile.js new file mode 100644 index 000000000..cebe661d9 --- /dev/null +++ b/build/post-compile.js @@ -0,0 +1,42 @@ +#!/usr/bin/env node +;(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' + + '*/'; + + /*--------------------------------------------------------------------------*/ + + // 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 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'); + } +}()); \ No newline at end of file diff --git a/build/pre-compile.js b/build/pre-compile.js new file mode 100644 index 000000000..4ef7007c6 --- /dev/null +++ b/build/pre-compile.js @@ -0,0 +1,210 @@ +#!/usr/bin/env node +;(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' + ]; + + /** 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 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, ''); + + /** + * 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']"); + + /** + * 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], + result = snippet; + + // minify property strings + properties.forEach(function(property, index) { + result = result.replace(RegExp("'" + property + "'", 'g'), "'" + minNames[index] + "'"); + }); + + // remove escaped newlines in strings + result = result.replace(/\\n/g, ''); + + // replace with modified snippet + src = src.replace(snippet, result); + }); + + /*--------------------------------------------------------------------------*/ + + /** + * Minify all `iterationFactory` related snippets. + */ + src.match( + RegExp([ + // match variables storing `iterationFactory` options + 'var [a-zA-Z]+FactoryOptions\\s*=\\s*\\{[\\s\\S]+?};\\n', + // match the the `iterationFactory` function + '(\\n\\s*)function iterationFactory[\\s\\S]+?\\1}', + // match methods created by `iterationFactor` calls + 'iterationFactory\\((?:[\'{]|[a-zA-Z]+,)[\\s\\S]+?\\);\\n' + ].join('|'), 'g') + ) + .forEach(function(snippet, index) { + var result = snippet; + + // add `true` and `false` arguments to be minified + if (/function iterationFactory/.test(snippet)) { + result = result + .replace(/(Function\('[\s\S]+?)undefined/, '$1true,false,undefined') + .replace(/\)\([^)]+/, '$&,true,false'); + + // replace with modified snippet early and clip snippet + src = src.replace(snippet, result); + snippet = result = result.replace(/\)\([\s\S]+$/, ''); + } + + // minify snippet variables/arguments + compiledVars.forEach(function(variable, index) { + result = result.replace(RegExp('([^.]\\b|\\\\n)' + variable + '\\b(?!\'\\s*[\\]:])', 'g'), '$1' + minNames[index]); + // correct `typeof x == 'object'` + if (variable == 'object') { + result = result.replace(RegExp("(typeof [^']+')" + minNames[index] + "'", 'g'), "$1object'"); + } + // correct boolean literals + if (variable == 'true' || variable == 'false') { + result = result + .replace(RegExp(':\\s*' + minNames[index] + '\\s*,', 'g'), ':' + variable + ',') + .replace(RegExp('\\s*' + minNames[index] + '\\s*;', 'g'), variable + ';'); + } + }); + + // minify snippet values + compiledValues.forEach(function(value, index) { + result = result.replace(RegExp("'" + value + "'", 'g'), "'" + minNames[index] + "'"); + }); + + // minify iterationFactory option property strings + iterationFactoryOptions.forEach(function(property, index) { + if (property == 'array' || property == 'object') { + result = result.replace(RegExp("'" + property + "'(\\s*[\\]:])", 'g'), "'" + minNames[index] + "'$1"); + } else { + result = result.replace(RegExp("'" + property + "'", 'g'), "'" + minNames[index] + "'"); + } + }); + + // remove escaped newlines in strings + result = result.replace(/\\n/g, ''); + + // replace with modified snippet + src = src.replace(snippet, result); + }); + return src; + }; + + /*--------------------------------------------------------------------------*/ + + /** 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'); + } +}()); \ No newline at end of file