| #!/usr/bin/env python | |
| import json | |
| import sys | |
| try: # Py3 | |
| from urllib.parse import urlencode | |
| from urllib.request import urlopen | |
| from urllib.request import Request | |
| except ImportError: # Py2 | |
| from urllib import urlencode | |
| from urllib2 import urlopen | |
| from urllib2 import Request | |
| URL = 'https://closure-compiler.appspot.com/compile' | |
| def main(): | |
| import argparse | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument('files', type=str, nargs='+', | |
| help='.js files to cat and compile together') | |
| parser.add_argument('--advanced', '-a', action='store_true', | |
| help='whether to use advanced compilation') | |
| parser.add_argument('--strict', '-s', action='store_true', | |
| help='whether to run in strict mode') | |
| parser.add_argument('--closure', '-c', action='store_true', | |
| help='whether to include closure library') | |
| parser.add_argument('--externs', '-e', type=str, nargs='*', | |
| default=[], help='externs .js files') | |
| parser.add_argument('--ignore', '-i', type=str, action='append', | |
| help='warning/error to ignore') | |
| parser.add_argument('--exports', '-x', action='store_true', | |
| help='whether to generate exports') | |
| parser.add_argument('--named', '-n', action='store_true', | |
| help='send named files to compiler') | |
| args = parser.parse_args() | |
| # Read content of all files. | |
| def read(path): | |
| with open(path) as f: | |
| return f.read() | |
| content = [(x, read(x)) for x in args.files] | |
| externs = [read(x) for x in args.externs] | |
| # Build CompilerOptions, for compile() call. | |
| options = CompilerOptions() | |
| options.advancedLevel = args.advanced | |
| options.closureLibrary = args.closure | |
| options.strictMode = args.strict | |
| options.generateExports = args.exports | |
| options.namedFiles = args.named | |
| # Run compile step, and render output code! | |
| out = compile(content, externs, options) | |
| if 'compiledCode' in out: | |
| print(out['compiledCode']) | |
| # Render each valid response message to stderr. | |
| modes = (('errors', bcolors.FAIL), ('warnings', bcolors.WARNING)) | |
| for category, color in modes: | |
| for msg in out.get(category, []): | |
| if msg['file']: | |
| msg['file'] = filepath(args.files, msg['file']) | |
| if args.ignore and msg['type'] in args.ignore: | |
| continue | |
| writemsg(sys.stderr, msg, color) | |
| # Look for server errors. | |
| if 'serverErrors' in out: | |
| # TODO: Format this so it doesn't look like Java has failed locally (!). | |
| for err in out['serverErrors']: | |
| sys.stderr.write(err['error']) | |
| # Return non-zero only if there is no code. | |
| if 'compiledCode' not in out: | |
| sys.exit(1) | |
| class bcolors: | |
| HEADER = '\033[95m' | |
| OKBLUE = '\033[94m' | |
| OKGREEN = '\033[92m' | |
| WARNING = '\033[93m' | |
| FAIL = '\033[91m' | |
| ENDC = '\033[0m' | |
| def writemsg(out, msg, color=bcolors.WARNING): | |
| if not msg['file'] and msg['lineno'] < 0: | |
| out.write('{}\n'.format(msg['type'])) | |
| else: | |
| out.write('{}:{} ({})\n'.format(msg['file'], msg['lineno'], msg['type'])) | |
| if 'warning' in msg: | |
| out.write('{}\n'.format(msg['warning'])) | |
| if 'error' in msg: | |
| out.write('{}\n'.format(msg['error'])) | |
| if msg['line']: | |
| out.write('{}{}\n'.format(color, msg['line'])) | |
| out.write('{}{}^\n'.format(bcolors.OKGREEN, msg['charno'] * ' ')) | |
| out.write('{}\n'.format(bcolors.ENDC)) | |
| out.flush() | |
| def filepath(files, response): | |
| """Finds the filepath from an array of files and a response. | |
| Args: | |
| files: contains a list of paths | |
| response: contains either 'Input_<n>' or other text | |
| Returns: name of file | |
| """ | |
| if not response: | |
| return '' | |
| PREFIX = 'Input_' | |
| if response.startswith(PREFIX): | |
| index = int(response[len(PREFIX):]) | |
| return files[index] | |
| return response | |
| class CompilerOptions: | |
| language = 6 | |
| languageOut = 5 | |
| advancedLevel = False | |
| closureLibrary = False | |
| strictMode = False | |
| generateExports = False | |
| namedFiles = False | |
| def compile(content, externs, options): | |
| level = (options.advancedLevel and 'ADVANCED' or 'SIMPLE') + '_OPTIMIZATIONS' | |
| params = [ | |
| ('compilation_level', level), | |
| ('output_format', 'json'), | |
| ('output_info', 'compiled_code'), | |
| ('output_info', 'warnings'), | |
| ('output_info', 'errors'), | |
| ('warning_level', 'verbose'), | |
| ('generate_exports', options.generateExports), | |
| ('use_closure_library', options.closureLibrary and 'true' or 'false'), | |
| ] | |
| for name, data in content: | |
| if options.namedFiles and len(name): | |
| params.append(('js_code:' + name, data)) | |
| else: | |
| params.append(('js_code', data)) | |
| for key, lang in [('language', options.language), ('language_out', options.languageOut)]: | |
| s = 'ES{}'.format(lang) | |
| if options.strictMode: | |
| s += '_STRICT' | |
| params.append((key, s)) | |
| if externs: | |
| externs = '\n'.join(externs) | |
| params.append(('js_externs', externs)) | |
| request = Request(URL) | |
| request.add_header('Content-Type', | |
| 'application/x-www-form-urlencoded;charset=utf-8') | |
| data = urlencode(params).encode('utf-8') | |
| response = urlopen(request, data) | |
| if response.getcode() != 200: | |
| raise Exception('response status was: ' + response.status) | |
| raw = response.read().decode('utf-8') | |
| return json.loads(raw) | |
| if __name__ == '__main__': | |
| main() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment