1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
|
#!/usr/bin/env python
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import argparse
from checker import Checker as Checker
import os
import sys
try:
import json
except:
import simplejson as json
class Module(object):
def __init__(self, name, sources, depends=[], externs=[]):
self.name = name
self.sources = sources
# TODO(dbeam): support depending on other modules/dependency flattening.
self.depends = depends
self.externs = externs
@staticmethod
def from_dict(d):
keys = d.keys()
required = ["name", "sources"]
assert all(r in keys for r in required), "Module missing name or sources"
allowed = required + ["depends", "externs"]
assert all(k in allowed for k in keys), "Module has unknown key"
depends = d["depends"] if "depends" in d else []
externs = d["externs"] if "externs" in d else []
return Module(d["name"], d["sources"], depends=depends, externs=externs)
# TODO(dbeam): should ModuleParser be internal to ModuleCompiler or should we
# pass Modules into ModuleCompiler.compile()? Maybe this is fine?
class ModuleParser(object):
_cache = {}
def __init__(self, verbose=False):
self._verbose = verbose
def parse(self, file_path):
if file_path in self._cache:
print "(INFO) Found module file %s in the cache" % file_path
return self._cache[file_path]
file = open(file_path, "r")
data = json.load(file)
file.close()
if self._verbose:
pretty_json = json.dumps(data, indent=2, separators=(',', ': ')).strip()
print "(INFO) Layout: " + os.linesep + pretty_json + os.linesep
self._cache[file_path] = [Module.from_dict(m) for m in data]
return self._cache[file_path]
class ModuleCompiler(object):
_checker = None
_parser = None
def __init__(self, verbose=False):
self._verbose = verbose
def _debug(self, msg, prefix="(INFO) ", suffix=""):
if self._verbose:
print prefix + msg.strip() + suffix
def compile(self, module_file):
self._debug("MODULE FILE: " + module_file, prefix="")
# NOTE: It's possible but unlikely that |_checker| or |_parser|'s verbosity
# isn't the same as |self._verbose| due to this class being called with
# verbose=False then verbose=True in the same program.
self._parser = self._parser or ModuleParser(verbose=self._verbose)
self._checker = self._checker or Checker(verbose=self._verbose)
current_dir = os.getcwd()
module_dir = os.path.dirname(module_file)
rel_path = lambda f: f
if current_dir and module_dir:
here_to_module_dir = os.path.relpath(module_dir, current_dir)
if here_to_module_dir:
rel_path = lambda f: os.path.join(here_to_module_dir, f)
modules = self._parser.parse(module_file)
for m in modules:
self._debug("MODULE: " + m.name, prefix="", suffix=os.linesep)
for s in m.sources:
depends = [rel_path(d) for d in m.depends]
externs = [rel_path(e) for e in m.externs]
exit_code, _ = self._checker.check(rel_path(s), depends=depends,
externs=externs)
if exit_code:
sys.exit(exit_code)
if s != m.sources[-1]:
self._debug(os.linesep, prefix="")
if m != modules[-1]:
self._debug(os.linesep, prefix="")
def main(opts):
module_compiler = ModuleCompiler(verbose=opts.verbose)
for module_file in opts.module_file:
module_compiler.compile(module_file)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Typecheck JavaScript using Closure compiler")
parser.add_argument("-v", "--verbose", action="store_true",
help="Show more information as this script runs")
parser.add_argument("module_file", nargs=argparse.ONE_OR_MORE,
help="Path to a modules file to check")
main(parser.parse_args())
|