| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # Copyright 2014 The Chromium Authors. All rights reserved. | 2 # Copyright 2014 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Runs Closure compiler on JavaScript files to check for errors and produce | 6 """Runs Closure compiler on JavaScript files to check for errors and produce |
| 7 minified output.""" | 7 minified output.""" |
| 8 | 8 |
| 9 import argparse | 9 import argparse |
| 10 import os | 10 import os |
| 11 import re | 11 import re |
| 12 import subprocess | 12 import subprocess |
| 13 import sys | 13 import sys |
| 14 import tempfile | 14 import tempfile |
| 15 | 15 |
| 16 import build.inputs | 16 import build.inputs |
| 17 import processor | 17 import processor |
| 18 import error_filter | 18 import error_filter |
| 19 | 19 |
| 20 | 20 |
| 21 _CURRENT_DIR = os.path.join(os.path.dirname(__file__)) |
| 22 |
| 23 |
| 21 class Checker(object): | 24 class Checker(object): |
| 22 """Runs the Closure compiler on given source files to typecheck them | 25 """Runs the Closure compiler on given source files to typecheck them |
| 23 and produce minified output.""" | 26 and produce minified output.""" |
| 24 | 27 |
| 28 _COMMON_JSCOMP_ERRORS = [ |
| 29 "accessControls", |
| 30 "ambiguousFunctionDecl", |
| 31 "checkStructDictInheritance", |
| 32 "checkTypes", |
| 33 "checkVars", |
| 34 "constantProperty", |
| 35 "deprecated", |
| 36 "externsValidation", |
| 37 "globalThis", |
| 38 "invalidCasts", |
| 39 "missingProperties", |
| 40 "missingReturn", |
| 41 "nonStandardJsDocs", |
| 42 "suspiciousCode", |
| 43 "undefinedNames", |
| 44 "undefinedVars", |
| 45 "unknownDefines", |
| 46 "uselessCode", |
| 47 "visibility", |
| 48 ] |
| 49 |
| 50 # Extra @jsDocAnnotations used when compiling polymer code. |
| 51 _POLYMER_EXTRA_ANNOTATIONS = [ |
| 52 "attribute", |
| 53 "status", |
| 54 "element", |
| 55 "homepage", |
| 56 "submodule", |
| 57 "group", |
| 58 ] |
| 59 |
| 25 _COMMON_CLOSURE_ARGS = [ | 60 _COMMON_CLOSURE_ARGS = [ |
| 26 "--accept_const_keyword", | 61 "--accept_const_keyword", |
| 27 "--jscomp_error=accessControls", | |
| 28 "--jscomp_error=ambiguousFunctionDecl", | |
| 29 "--jscomp_error=checkStructDictInheritance", | |
| 30 "--jscomp_error=checkTypes", | |
| 31 "--jscomp_error=checkVars", | |
| 32 "--jscomp_error=constantProperty", | |
| 33 "--jscomp_error=deprecated", | |
| 34 "--jscomp_error=externsValidation", | |
| 35 "--jscomp_error=globalThis", | |
| 36 "--jscomp_error=invalidCasts", | |
| 37 "--jscomp_error=missingProperties", | |
| 38 "--jscomp_error=missingReturn", | |
| 39 "--jscomp_error=nonStandardJsDocs", | |
| 40 "--jscomp_error=suspiciousCode", | |
| 41 "--jscomp_error=undefinedNames", | |
| 42 "--jscomp_error=undefinedVars", | |
| 43 "--jscomp_error=unknownDefines", | |
| 44 "--jscomp_error=uselessCode", | |
| 45 "--jscomp_error=visibility", | |
| 46 "--language_in=ECMASCRIPT5_STRICT", | 62 "--language_in=ECMASCRIPT5_STRICT", |
| 47 "--summary_detail_level=3", | 63 "--summary_detail_level=3", |
| 48 "--compilation_level=SIMPLE_OPTIMIZATIONS", | 64 "--compilation_level=SIMPLE_OPTIMIZATIONS", |
| 49 "--source_map_format=V3", | 65 "--source_map_format=V3", |
| 66 "--polymer_pass", |
| 67 ] + [ |
| 68 "--jscomp_error=%s" % err for err in _COMMON_JSCOMP_ERRORS |
| 69 ] + [ |
| 70 "--extra_annotation_name=%s" % a for a in _POLYMER_EXTRA_ANNOTATIONS |
| 50 ] | 71 ] |
| 51 | 72 |
| 52 # These are the extra flags used when compiling in strict mode. | 73 # These are the extra flags used when compiling in strict mode. |
| 53 # Flags that are normally disabled are turned on for strict mode. | 74 # Flags that are normally disabled are turned on for strict mode. |
| 54 _STRICT_CLOSURE_ARGS = [ | 75 _STRICT_CLOSURE_ARGS = [ |
| 55 "--jscomp_error=reportUnknownTypes", | 76 "--jscomp_error=reportUnknownTypes", |
| 56 "--jscomp_error=duplicate", | 77 "--jscomp_error=duplicate", |
| 57 "--jscomp_error=misplacedTypeAnnotation", | 78 "--jscomp_error=misplacedTypeAnnotation", |
| 58 ] | 79 ] |
| 59 | 80 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 74 ] | 95 ] |
| 75 | 96 |
| 76 _MAP_FILE_FORMAT = "%s.map" | 97 _MAP_FILE_FORMAT = "%s.map" |
| 77 | 98 |
| 78 def __init__(self, verbose=False, strict=False): | 99 def __init__(self, verbose=False, strict=False): |
| 79 """ | 100 """ |
| 80 Args: | 101 Args: |
| 81 verbose: Whether this class should output diagnostic messages. | 102 verbose: Whether this class should output diagnostic messages. |
| 82 strict: Whether the Closure Compiler should be invoked more strictly. | 103 strict: Whether the Closure Compiler should be invoked more strictly. |
| 83 """ | 104 """ |
| 84 current_dir = os.path.join(os.path.dirname(__file__)) | 105 self._runner_jar = os.path.join(_CURRENT_DIR, "runner", "runner.jar") |
| 85 self._runner_jar = os.path.join(current_dir, "runner", "runner.jar") | |
| 86 self._temp_files = [] | 106 self._temp_files = [] |
| 87 self._verbose = verbose | 107 self._verbose = verbose |
| 88 self._strict = strict | 108 self._strict = strict |
| 89 self._error_filter = error_filter.PromiseErrorFilter() | 109 self._error_filter = error_filter.PromiseErrorFilter() |
| 90 | 110 |
| 91 def _nuke_temp_files(self): | 111 def _nuke_temp_files(self): |
| 92 """Deletes any temp files this class knows about.""" | 112 """Deletes any temp files this class knows about.""" |
| 93 if not self._temp_files: | 113 if not self._temp_files: |
| 94 return | 114 return |
| 95 | 115 |
| (...skipping 237 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 333 | 353 |
| 334 if cleaned_errors: | 354 if cleaned_errors: |
| 335 prefix = "\n" if output else "" | 355 prefix = "\n" if output else "" |
| 336 self._log_error("Error in: %s%s%s" % (source_file, prefix, output)) | 356 self._log_error("Error in: %s%s%s" % (source_file, prefix, output)) |
| 337 elif output: | 357 elif output: |
| 338 self._log_debug("Output: %s" % output) | 358 self._log_debug("Output: %s" % output) |
| 339 | 359 |
| 340 self._nuke_temp_files() | 360 self._nuke_temp_files() |
| 341 return bool(cleaned_errors), stderr | 361 return bool(cleaned_errors), stderr |
| 342 | 362 |
| 343 def check_multiple(self, sources, out_file=None, output_wrapper=None): | 363 def check_multiple(self, sources, out_file=None, output_wrapper=None, |
| 364 externs=None): |
| 344 """Closure compile a set of files and check for errors. | 365 """Closure compile a set of files and check for errors. |
| 345 | 366 |
| 346 Args: | 367 Args: |
| 347 sources: An array of files to check. | 368 sources: An array of files to check. |
| 348 out_file: A file where the compiled output is written to. | 369 out_file: A file where the compiled output is written to. |
| 349 output_wrapper: Wraps output into this string at the place denoted by the | 370 output_wrapper: Wraps output into this string at the place denoted by the |
| 350 marker token %output%. | 371 marker token %output%. |
| 372 externs: @extern files that inform the compiler about custom globals. |
| 351 | 373 |
| 352 Returns: | 374 Returns: |
| 353 (found_errors, stderr) A boolean indicating whether errors were found and | 375 (found_errors, stderr) A boolean indicating whether errors were found and |
| 354 the raw Closure Compiler stderr (as a string). | 376 the raw Closure Compiler stderr (as a string). |
| 355 """ | 377 """ |
| 356 errors, stderr = self._run_js_check(sources, out_file=out_file, | 378 errors, stderr = self._run_js_check(sources, out_file=out_file, |
| 357 output_wrapper=output_wrapper) | 379 output_wrapper=output_wrapper, |
| 380 externs=externs) |
| 358 self._nuke_temp_files() | 381 self._nuke_temp_files() |
| 359 return bool(errors), stderr | 382 return bool(errors), stderr |
| 360 | 383 |
| 361 | 384 |
| 362 if __name__ == "__main__": | 385 if __name__ == "__main__": |
| 363 parser = argparse.ArgumentParser( | 386 parser = argparse.ArgumentParser( |
| 364 description="Typecheck JavaScript using Closure compiler") | 387 description="Typecheck JavaScript using Closure compiler") |
| 365 parser.add_argument("sources", nargs=argparse.ONE_OR_MORE, | 388 parser.add_argument("sources", nargs=argparse.ONE_OR_MORE, |
| 366 help="Path to a source file to typecheck") | 389 help="Path to a source file to typecheck") |
| 367 single_file_group = parser.add_mutually_exclusive_group() | 390 single_file_group = parser.add_mutually_exclusive_group() |
| (...skipping 14 matching lines...) Expand all Loading... |
| 382 help="Show more information as this script runs") | 405 help="Show more information as this script runs") |
| 383 parser.add_argument("--strict", action="store_true", | 406 parser.add_argument("--strict", action="store_true", |
| 384 help="Enable strict type checking") | 407 help="Enable strict type checking") |
| 385 parser.add_argument("--success-stamp", | 408 parser.add_argument("--success-stamp", |
| 386 help="Timestamp file to update upon success") | 409 help="Timestamp file to update upon success") |
| 387 | 410 |
| 388 parser.set_defaults(single_file=True, strict=False) | 411 parser.set_defaults(single_file=True, strict=False) |
| 389 opts = parser.parse_args() | 412 opts = parser.parse_args() |
| 390 | 413 |
| 391 depends = opts.depends or [] | 414 depends = opts.depends or [] |
| 392 externs = opts.externs or set() | 415 externs = set(opts.externs or []) |
| 416 |
| 417 polymer_externs = os.path.join(os.path.dirname(_CURRENT_DIR), 'polymer', |
| 418 'v0_8', 'components-chromium', |
| 419 'polymer-externs', 'polymer.externs.js') |
| 420 externs.add(polymer_externs) |
| 393 | 421 |
| 394 if opts.out_file: | 422 if opts.out_file: |
| 395 out_dir = os.path.dirname(opts.out_file) | 423 out_dir = os.path.dirname(opts.out_file) |
| 396 if not os.path.exists(out_dir): | 424 if not os.path.exists(out_dir): |
| 397 os.makedirs(out_dir) | 425 os.makedirs(out_dir) |
| 398 | 426 |
| 399 checker = Checker(verbose=opts.verbose, strict=opts.strict) | 427 checker = Checker(verbose=opts.verbose, strict=opts.strict) |
| 400 if opts.single_file: | 428 if opts.single_file: |
| 401 for source in opts.sources: | 429 for source in opts.sources: |
| 402 depends, externs = build.inputs.resolve_recursive_dependencies( | 430 depends, externs = build.inputs.resolve_recursive_dependencies( |
| 403 source, depends, externs) | 431 source, depends, externs) |
| 404 found_errors, _ = checker.check(source, out_file=opts.out_file, | 432 found_errors, _ = checker.check(source, out_file=opts.out_file, |
| 405 depends=depends, externs=externs, | 433 depends=depends, externs=externs, |
| 406 output_wrapper=opts.output_wrapper) | 434 output_wrapper=opts.output_wrapper) |
| 407 if found_errors: | 435 if found_errors: |
| 408 sys.exit(1) | 436 sys.exit(1) |
| 409 else: | 437 else: |
| 410 found_errors, stderr = checker.check_multiple( | 438 found_errors, stderr = checker.check_multiple( |
| 411 opts.sources, | 439 opts.sources, |
| 412 out_file=opts.out_file, | 440 out_file=opts.out_file, |
| 413 output_wrapper=opts.output_wrapper) | 441 output_wrapper=opts.output_wrapper, |
| 442 externs=externs) |
| 414 if found_errors: | 443 if found_errors: |
| 415 print stderr | 444 print stderr |
| 416 sys.exit(1) | 445 sys.exit(1) |
| 417 | 446 |
| 418 if opts.success_stamp: | 447 if opts.success_stamp: |
| 419 with open(opts.success_stamp, "w"): | 448 with open(opts.success_stamp, "w"): |
| 420 os.utime(opts.success_stamp, None) | 449 os.utime(opts.success_stamp, None) |
| OLD | NEW |