Preliminary Closure Compiler simple optimizations support.

Former-commit-id: 515b8e47b6fa08e1f36d0e735f8a2bf188286d12
This commit is contained in:
Kit Cambridge
2012-11-08 16:29:25 -08:00
parent f478fa8029
commit 3db6841305

View File

@@ -20,10 +20,13 @@
uglifyJS = require('../vendor/uglifyjs/uglify-js.js'); uglifyJS = require('../vendor/uglifyjs/uglify-js.js');
/** The Closure Compiler command-line options */ /** The Closure Compiler command-line options */
var closureOptions = [ var closureOptions = ['--warning_level=QUIET'];
'--compilation_level=ADVANCED_OPTIMIZATIONS',
'--warning_level=QUIET' /** The Closure Compiler optimization modes */
]; var OPTIMIZATION_MODES = {
'simple': 'SIMPLE_OPTIMIZATIONS',
'advanced': 'ADVANCED_OPTIMIZATIONS'
};
/** Reassign `existsSync` for older versions of Node */ /** Reassign `existsSync` for older versions of Node */
fs.existsSync || (fs.existsSync = path.existsSync); fs.existsSync || (fs.existsSync = path.existsSync);
@@ -97,8 +100,14 @@
options = source || options; options = source || options;
source = options.source || ''; source = options.source || '';
} }
this.compiled = {}; this.compiled = {
this.hybrid = {}; 'simple': {},
'advanced': {}
};
this.hybrid = {
'simple': {},
'advanced': {}
};
this.uglified = {}; this.uglified = {};
this.isSilent = !!options.isSilent; this.isSilent = !!options.isSilent;
this.isTemplate = !!options.isTemplate; this.isTemplate = !!options.isTemplate;
@@ -112,7 +121,7 @@
}; };
// begin the minification process // begin the minification process
closureCompile.call(this, source, onClosureCompile.bind(this)); closureCompile.call(this, source, 'simple', onClosureSimpleCompile.bind(this));
} }
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
@@ -124,30 +133,33 @@
* @private * @private
* @param {String} source The JavaScript source to minify. * @param {String} source The JavaScript source to minify.
* @param {String} [message] The message to log. * @param {String} [message] The message to log.
* @param {String} [mode] The optimization mode.
* @param {Function} callback The function called once the process has completed. * @param {Function} callback The function called once the process has completed.
*/ */
function closureCompile(source, message, callback) { function closureCompile(source, mode, message, callback) {
var options = closureOptions.slice(); var options = closureOptions.slice();
// use simple optimizations when minifying template files // juggle arguments
if (this.isTemplate) { if (typeof mode == 'function') {
options = options.map(function(value) { callback = mode;
return value.replace(/^(--compilation_level)=.+$/, '$1=SIMPLE_OPTIMIZATIONS'); mode = null;
}); } else if (typeof message == 'function') {
callback = message;
message = null;
} }
// use simple optimizations by default when minifying template files
mode = OPTIMIZATION_MODES[mode] || OPTIMIZATION_MODES[this.isTemplate ? 'simple' : 'advanced'];
options.push('--compilation_level=' + mode);
// the standard error stream, standard output stream, and the Closure Compiler process // the standard error stream, standard output stream, and the Closure Compiler process
var error = '', var error = '',
output = '', output = '',
compiler = spawn('java', ['-jar', closurePath].concat(options)); compiler = spawn('java', ['-jar', closurePath].concat(options));
// juggle arguments
if (typeof message == 'function') {
callback = message;
message = null;
}
if (!this.isSilent) { if (!this.isSilent) {
console.log(message == null console.log(message == null
? 'Compressing ' + path.basename(this.outputPath, '.js') + ' using the Closure Compiler...' ? 'Compressing ' + path.basename(this.outputPath, '.js') + ' using the Closure Compiler, `' + mode + '`...'
: message : message
); );
} }
@@ -231,13 +243,13 @@
* @param {Object|Undefined} exception The error object. * @param {Object|Undefined} exception The error object.
* @param {String} result The resulting minified source. * @param {String} result The resulting minified source.
*/ */
function onClosureCompile(exception, result) { function onClosureSimpleCompile(exception, result) {
if (exception) { if (exception) {
throw exception; throw exception;
} }
// store the post-processed Closure Compiler result and gzip it // store the post-processed Closure Compiler result and gzip it
this.compiled.source = result = postprocess(result); this.compiled.simple.source = result = postprocess(result);
gzip(result, onClosureGzip.bind(this)); gzip(result, onClosureSimpleGzip.bind(this));
} }
/** /**
@@ -247,7 +259,7 @@
* @param {Object|Undefined} exception The error object. * @param {Object|Undefined} exception The error object.
* @param {Buffer} result The resulting gzipped source. * @param {Buffer} result The resulting gzipped source.
*/ */
function onClosureGzip(exception, result) { function onClosureSimpleGzip(exception, result) {
if (exception) { if (exception) {
throw exception; throw exception;
} }
@@ -255,7 +267,29 @@
console.log('Done. Size: %d bytes.', result.length); console.log('Done. Size: %d bytes.', result.length);
} }
// store the gzipped result and report the size // store the gzipped result and report the size
this.compiled.gzip = result; this.compiled.simple.gzip = result;
// next, compile the source using advanced optimizations
closureCompile.call(this, this.source, 'advanced', onClosureAdvancedCompile.bind(this));
}
function onClosureAdvancedCompile(exception, result) {
if (exception) {
throw exception;
}
this.compiled.advanced.source = result = postprocess(result);
gzip(result, onClosureAdvancedGzip.bind(this));
}
function onClosureAdvancedGzip(exception, result) {
if (exception) {
throw exception;
}
if (!this.isSilent) {
console.log('Done. Size: %d bytes.', result.length);
}
// store the gzipped result and report the size
this.compiled.advanced.gzip = result;
// next, minify the source using only UglifyJS // next, minify the source using only UglifyJS
uglify.call(this, this.source, onUglify.bind(this)); uglify.call(this, this.source, onUglify.bind(this));
@@ -291,13 +325,13 @@
if (!this.isSilent) { if (!this.isSilent) {
console.log('Done. Size: %d bytes.', result.length); console.log('Done. Size: %d bytes.', result.length);
} }
var message = 'Compressing ' + path.basename(this.outputPath, '.js') + ' using hybrid minification...'; var message = 'Compressing ' + path.basename(this.outputPath, '.js') + ' using hybrid minification; `SIMPLE_OPTIMIZATIONS`...';
// store the gzipped result and report the size // store the gzipped result and report the size
this.uglified.gzip = result; this.uglified.gzip = result;
// next, minify the Closure Compiler minified source using UglifyJS // next, minify the Closure Compiler simple minified source using UglifyJS
uglify.call(this, this.compiled.source, message, onHybrid.bind(this)); uglify.call(this, this.compiled.simple.source, message, onSimpleHybrid.bind(this));
} }
/** /**
@@ -307,13 +341,34 @@
* @param {Object|Undefined} exception The error object. * @param {Object|Undefined} exception The error object.
* @param {String} result The resulting minified source. * @param {String} result The resulting minified source.
*/ */
function onHybrid(exception, result) { function onSimpleHybrid(exception, result) {
if (exception) { if (exception) {
throw exception; throw exception;
} }
// store the post-processed Uglified result and gzip it // store the post-processed Uglified result and gzip it
this.hybrid.source = result = postprocess(result); this.hybrid.simple.source = result = postprocess(result);
gzip(result, onHybridGzip.bind(this)); gzip(result, onSimpleHybridGzip.bind(this));
}
function onAdvancedHybrid(exception, result) {
if (exception) {
throw exception;
}
this.hybrid.advanced.source = result = postprocess(result);
gzip(result, onAdvancedHybridGzip.bind(this));
}
function onAdvancedHybridGzip(exception, result) {
if (exception) {
throw exception;
}
if (!this.isSilent) {
console.log('Done. Size: %d bytes.', result.length);
}
this.hybrid.advanced.gzip = result;
// finish by choosing the smallest compressed file
onComplete.call(this);
} }
/** /**
@@ -323,7 +378,7 @@
* @param {Object|Undefined} exception The error object. * @param {Object|Undefined} exception The error object.
* @param {Buffer} result The resulting gzipped source. * @param {Buffer} result The resulting gzipped source.
*/ */
function onHybridGzip(exception, result) { function onSimpleHybridGzip(exception, result) {
if (exception) { if (exception) {
throw exception; throw exception;
} }
@@ -331,10 +386,10 @@
console.log('Done. Size: %d bytes.', result.length); console.log('Done. Size: %d bytes.', result.length);
} }
// store the gzipped result and report the size // store the gzipped result and report the size
this.hybrid.gzip = result; this.hybrid.simple.gzip = result;
// finish by choosing the smallest compressed file var message = 'Compressing ' + path.basename(this.outputPath, '.js') + ' using hybrid minification; `ADVANCED_OPTIMIZATIONS`...';
onComplete.call(this); uglify.call(this, this.compiled.advanced.source, message, onAdvancedHybrid.bind(this));
} }
/** /**
@@ -343,21 +398,33 @@
* @private * @private
*/ */
function onComplete() { function onComplete() {
var compiled = this.compiled, var compiledSimple = this.compiled.simple,
hybrid = this.hybrid, compiledAdvanced = this.compiled.advanced,
uglified = this.uglified; uglified = this.uglified,
hybridSimple = this.hybrid.simple,
hybridAdvanced = this.hybrid.advanced;
// select the smallest gzipped file and use its minified counterpart as the // select the smallest gzipped file and use its minified counterpart as the
// official minified release (ties go to the Closure Compiler) // official minified release (ties go to the Closure Compiler)
var min = Math.min(compiled.gzip.length, hybrid.gzip.length, uglified.gzip.length); var min = Math.min(
compiledSimple.gzip.length,
compiledAdvanced.gzip.length,
uglified.gzip.length,
hybridSimple.gzip.length,
hybridAdvanced.gzip.length
);
// pass the minified source to the minify instances "onComplete" callback // pass the minified source to the minify instances "onComplete" callback
this.onComplete( this.onComplete(
compiled.gzip.length == min compiledSimple.gzip.length == min
? compiled.source ? compiledSimple.source
: uglified.gzip.length == min : compiledAdvanced.gzip.length == min
? uglified.source ? compiledAdvanced.source
: hybrid.source : uglified.gzip.length == min
? uglified.source
: hybridSimple.gzip.length == min
? hybridSimple.source
: hybridAdvanced.source
); );
} }