closure-compiler-webpack-plugin
Version:
Google Closure Compiler plugin for webpack built on the Nailgun JVM.
254 lines (216 loc) • 6.87 kB
JavaScript
var nailgun = require('node-nailgun');
var gcc = require('google-closure-compiler').compiler.COMPILER_PATH;
var ModuleFilenameHelpers = require('webpack/lib/ModuleFilenameHelpers');
var SourceMapConsumer = require('webpack-core/lib/source-map').SourceMapConsumer;
var SourceMapSource = require('webpack-core/lib/SourceMapSource');
var RawSource = require('webpack-core/lib/RawSource');
var RequestShortener = require('webpack/lib/RequestShortener');
function ClosureCompilerPlugin(options) {
if (typeof options !== 'object') {
options = {};
} else if (options.create_source_map !== false) {
options['json_streams'] = 'OUT';
} else {
delete options.json_streams;
}
this.options = options;
}
ClosureCompilerPlugin.prototype.apply = function(compiler) {
var options = this.options;
var jvm = nailgun.createServer();
var jvmStarted = false;
options.test = options.test || /\.js($|\?)/i;
compiler.plugin('compilation', function(compilation) {
if (options.sourceMap !== false) {
compilation.plugin('build-module', function(module) {
module.useSourceMap = true;
});
}
compilation.plugin('optimize-chunk-assets', function(chunks, callback) {
var compilationPromise = new Promise(function(resolve) {
var files = [];
var processedFiles = [];
chunks.forEach(function(chunk) {
chunk.files.forEach(function(file) {
files.push(file);
});
});
compilation.additionalChunkAssets.forEach(function(file) {
files.push(file);
});
files = files.filter(ModuleFilenameHelpers.matchObject.bind(undefined, options));
var numberOfFilesToProcess = files.length;
if (files.length) {
new Promise(function(resolve, reject) {
if (jvmStarted) {
resolve();
} else {
jvm.spawnJar(gcc, ['--help'], function(err) {
if (err) {
reject(err);
} else {
jvmStarted = true;
resolve();
}
});
}
})
.then(function() {
files.forEach(function(file) {
try {
var asset = compilation.assets[file];
if (asset.__ClosureCompilerPlugin) {
compilation.assets[file] = asset.__ClosureCompilerPlugin;
numberOfFilesToProcess--;
if (numberOfFilesToProcess > 0) {
return;
}
}
var gccArgs = JSON.parse(JSON.stringify(options));
var gccProcessArgs = [];
if (options.create_source_map !== false) {
if (asset.sourceAndMap) {
var sourceAndMap = asset.sourceAndMap();
var inputSourceMap = sourceAndMap.map;
var input = sourceAndMap.source;
} else {
var inputSourceMap = asset.map();
var input = asset.source();
}
var sourceMap = new SourceMapConsumer(inputSourceMap);
} else {
var input = asset.source();
delete gccArgs.create_source_map;
}
delete gccArgs.test;
for (var key in gccArgs) {
gccProcessArgs.push('--' + key);
gccProcessArgs.push(gccArgs[key]);
}
jvm.spawnJar(gcc, gccProcessArgs, function(err, closureCompilerProcess) {
if (err) {
compilation.errors.push(new Error(file + ' from Nailgun JVM\n' + err));
resolve();
} else {
var ender;
var enderTick = false;
var outputChunks = '';
var output;
var map;
closureCompilerProcess.stdout.on('data', function(data) {
outputChunks += data;
enderTick = false;
if (!ender) {
ender = setInterval(function() {
if (enderTick) {
clearInterval(ender);
try {
var parsedJSON = JSON.parse(outputChunks);
} catch(err) {}
if (parsedJSON) {
output = parsedJSON[0].src;
map = JSON.parse(parsedJSON[0].source_map);
} else {
output = outputChunks;
}
if (map) {
map.sources = [];
map.sources.push(file);
asset.__ClosureCompilerPlugin
= compilation.assets[file]
= new SourceMapSource(output, file, map, input, inputSourceMap);
} else {
asset.__ClosureCompilerPlugin
= compilation.assets[file]
= new RawSource(output);
}
if (processedFiles.length == (numberOfFilesToProcess - 1)
|| numberOfFilesToProcess == 0) {
resolve();
} else {
processedFiles.push(1);
}
}
enderTick = true;
});
}
});
var warnings;
closureCompilerProcess.stderr
.on('data', function(data) {
warnings += data.toString();
})
.on('end', function() {
if (warnings) {
compilation.warnings.push(new Error(
file + ' from Closure Compiler\n' + warnings
));
resolve();
}
});
closureCompilerProcess.stdin.write(input);
closureCompilerProcess.stdin.end();
}
});
} catch(err) {
if (err.line) {
if (sourceMap) {
var original = sourceMap.originalPositionFor({
line: err.line,
column: err.col
});
}
if (original && original.source) {
compilation.errors.push(new Error(
file
+ ' from Closure Compiler\n'
+ err.message
+ ' ['
+ new RequestShortener(compiler.context).shorten(original.source)
+ ':'
+ original.line
+ ','
+ original.column
+ ']'
));
} else {
compilation.errors.push(new Error(
file
+ ' from Closure Compiler\n'
+ err.message
+ ' ['
+ file
+ ':'
+ err.line
+ ','
+ err.col
+ ']'
));
}
} else if (err.msg) {
compilation.errors.push(new Error(file + ' from Closure Compiler\n' + err.msg));
} else {
compilation.errors.push(new Error(file + ' from Closure Compiler\n' + err.stack));
}
resolve();
}
});
})
.catch(function(reason) {
compilation.errors.push(new Error('Failed to jump start Nailgun JVM\n' + reason));
resolve();
});
} else {
resolve();
}
});
compilationPromise.then(function() {
callback();
});
});
compilation.plugin('normal-module-loader', function(context) {
context.minimize = true;
});
});
};
module.exports = ClosureCompilerPlugin;