UNPKG

closure-builder

Version:

Simple Closure, Soy and JavaScript Build system

402 lines (353 loc) 10.7 kB
/** * @fileoverview Closure Builder - Closure online compiler * * @license Copyright 2016 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @author mbordihn@google.com (Markus Bordihn) */ var fs = require('fs-extra'); var https = require('https'); var querystring = require('querystring'); var javaTools = require('../../tools/java.js'); var pathTools = require('../../tools/path.js'); /** * ClosureCompiler. * @constructor * @struct * @final */ var ClosureCompiler = function() {}; /** * @param {!string} files * @param {Object=} opt_options * @param {string=} opt_target_file */ ClosureCompiler.compile = function(files, opt_options, opt_target_file) { }; /** * @param {!string} files * @param {Object=} opt_options * @param {string=} opt_target_file * @param {function=} opt_callback */ ClosureCompiler.localCompile = function(files, opt_options, opt_target_file, opt_callback) { if (!files) { return; } var i = 0; var compiler = pathTools.getClosureCompilerJar(); var compilerOptions = []; var options = opt_options || {}; // Compilation level if (!options.compilation_level) { options.compilation_level = 'SIMPLE_OPTIMIZATIONS'; } // Compiler warnings if (!options.jscomp_warning) { options.jscomp_warning = ['checkVars', 'conformanceViolations', 'deprecated', 'externsValidation', 'fileoverviewTags', 'globalThis', 'misplacedTypeAnnotation', 'missingProvide', 'missingRequire', 'missingReturn', 'nonStandardJsDocs', 'typeInvalidation', 'uselessCode']; } // Handling compiler error if (options.jscomp_error) { for (i = 0; i < options.jscomp_error.length; i++) { compilerOptions.push('--jscomp_error', options.jscomp_error[i]); } delete options.jscomp_warning; } // Handling compiler off if (options.jscomp_off) { for (i = 0; i < options.jscomp_off.length; i++) { compilerOptions.push('--jscomp_off', options.jscomp_off[i]); } delete options.jscomp_warning; } // Handling compiler warnings if (options.jscomp_warning) { for (i = 0; i < options.jscomp_warning.length; i++) { compilerOptions.push('--jscomp_warning', options.jscomp_warning[i]); } delete options.jscomp_warning; } // Handling files var dupFile = {}; for (i = 0; i < files.length; i++) { if (!dupFile[files[i]]) { compilerOptions.push('--js', files[i]); } dupFile[files[i]] = true; } // Handling externs files if (options.externs) { for (i = 0; i < options.externs.length; i++) { compilerOptions.push('--externs', options.externs[i]); } delete options.externs; } // Handling generate_exports if (options.generate_exports && !options.use_closure_basefile) { options.use_closure_basefile = true; } // Closure templates if (options.use_closure_templates) { compilerOptions.push('--js=' + pathTools.getClosureSoyUtilsFile()); if (!options.use_closure_library) { options.use_closure_library = true; } delete options.use_closure_templates; } // Closure base file if (options.use_closure_basefile) { if (!options.use_closure_library) { var baseFile = pathTools.getClosureBaseFile(); if (baseFile) { compilerOptions.push('--js', baseFile); } } delete options.use_closure_basefile; } // Closure library if (options.use_closure_library) { var closureLibraryFiles = pathTools.getClosureLibraryFiles(); for (i = 0; i < closureLibraryFiles.length; i++) { compilerOptions.push('--js=' + closureLibraryFiles[i]); } delete options.use_closure_library; } // Handling options for (var option in options) { compilerOptions.push('--' + option, options[option]); } var compilerEvent = function(error, stdout, stderr) { var code = stdout; var errorMsg = stderr || error; var errors = null; var warnings = null; var numErrors = 0; var numWarnings = 0; if (errorMsg) { var parsedErrorMessage = ClosureCompiler.parseErrorMessage(errorMsg); numErrors = parsedErrorMessage.errors; numWarnings = parsedErrorMessage.warnings; } if (numErrors == 0 && numWarnings > 0) { warnings = errorMsg; ClosureCompiler.warn(warnings); } else if (numErrors > 0) { errors = errorMsg; ClosureCompiler.error(errors); code = null; } if (opt_callback) { opt_callback(errors, warnings, opt_target_file, code); } }; javaTools.execJavaJar(compiler, compilerOptions, compilerEvent); }; /** * @param {!string} files * @param {Object=} opt_options * @param {string=} opt_target_file * @param {function=} opt_callback */ ClosureCompiler.remoteCompile = function(files, opt_options, opt_target_file, opt_callback) { // Handling options var unsupportedOptions = { 'entry_point': true, 'generate_exports': true }; var option; for (option in opt_options) { if (option in unsupportedOptions) { var errorMsg = 'ERROR - ' + option + ' is unsupported by the ' + 'closure-compiler webservice!'; ClosureCompiler.error(errorMsg); if (opt_callback) { opt_callback(errorMsg); } return; } } var options = opt_options || {}; var data = { 'compilation_level' : 'SIMPLE_OPTIMIZATIONS', 'output_format': 'json', 'output_info': ['compiled_code', 'warnings', 'errors', 'statistics'], 'js_code': [] }; // Closure templates if (options.use_closure_templates) { var closureSoyUtilsFile = pathTools.getClosureSoyUtilsFile(); if (closureSoyUtilsFile) { data['js_code'].push(fs.readFileSync(closureSoyUtilsFile).toString()); if (!options.use_closure_library) { options.use_closure_library = true; } } delete options.use_closure_templates; } // Handling files for (var i = 0; i < files.length; i++) { var fileContent = fs.readFileSync(files[i]).toString(); if (fileContent) { data['js_code'].push(fileContent); } } // Handling externs files if (options.externs) { var externsCode = ''; for (i = 0; i < options.externs.length; i++) { externsCode += fs.readFileSync(options.externs[i]).toString(); } if (externsCode) { data['js_externs'] = externsCode; } delete options.externs; } // Handling options for (option in options) { data[option] = options[option]; } var dataString = querystring.stringify(data); var httpOptions = { host: 'closure-compiler.appspot.com', path: '/compile', method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': dataString.length } }; var request = https.request(httpOptions, function(response) { var data = ''; response.setEncoding('utf8'); response.on('data', function(chunk) { data += chunk; }); response.on('end', function() { var result = JSON.parse(data); var code = result.compiledCode; var errorMsg = result.errors; var warningMsg = result.warnings; var serverErrorMsg = result.serverErrors; var errors = null; var warnings = null; if (serverErrorMsg) { errors = ClosureCompiler.parseJsonError(serverErrorMsg); ClosureCompiler.error(errors || errorMsg); code = ''; } else if (errorMsg) { errors = ClosureCompiler.parseJsonError(errorMsg); ClosureCompiler.error(errors); code = ''; } else if (warningMsg) { warnings = ClosureCompiler.parseJsonError(warningMsg); ClosureCompiler.warn(warnings); } if (code) { code += '\n'; } if (opt_callback) { opt_callback(errors, warnings, opt_target_file, code); } }); }); request.on('error', function(e) { ClosureCompiler.error('HTTP request error:', e.message); if (opt_callback) { opt_callback(e.message); } }); request.write(dataString); request.end(); }; /** * @param {string} data * @return {!string} */ ClosureCompiler.parseJsonError = function(data) { var message = ''; for (var i=0; i<data.length; i++) { var msg = data[i].error || data[i].warning; var type = (data[i].error) ? 'ERROR' : 'WARNING'; if (data.file && data.file !== 'Input_0') { message += data.file + ':' + data.lineno + ': ' + type + ' - ' + msg + '\n'; } else if (data[i].line) { message += type + ' - ' + msg + ' : ' + data[i].line + '\n'; } else { message += type + ' - ' + msg + '\n'; } } return message; }; /** * @param {string} message * @return {Object} with number of detected errors and warnings */ ClosureCompiler.parseErrorMessage = function(message) { var errors = 0; var warnings = 0; if (message && message.match) { var message_reg = /([0-9]+) error\(s\), ([0-9]+) warning\(s\)/; var messageInfo = message.match(message_reg); if (messageInfo) { errors = messageInfo[1]; warnings = messageInfo[2]; } else if (message.indexOf('INTERNAL COMPILER ERROR') !== -1 || message.indexOf('NullPointerException') !== -1) { errors = 1; } else if (message.toLowerCase().indexOf('error') !== -1) { errors = message.toLowerCase().split('error').length - 1; } else if (message.toLowerCase().indexOf('warning') !== -1) { if (message.indexOf('Java HotSpot\(TM\) Client VM warning') === -1 || message.toLowerCase().split('warning').length > 2) { warnings = message.toLowerCase().split('warning').length - 1; } else { warnings = 0; } } } else if (message) { errors = 1; } return { errors: errors, warnings: warnings }; }; /** * @param {string} msg */ ClosureCompiler.info = function(msg) { if (msg) { console.info('[Closure Compiler]', msg); } }; /** * @param {string} msg */ ClosureCompiler.warn = function(msg) { console.error('[Closure Compiler Warn]', msg); }; /** * @param {string} msg */ ClosureCompiler.error = function(msg) { console.error('[Closure Compiler Error]', msg); }; module.exports = ClosureCompiler;