import pystache
-REQUIRE_PAT = re.compile(r'require\(\s*([\'"])(.*?)\1\s*\)')
-ROOT = path(__file__).abspath().dirname().dirname()
-LIB = ROOT/'lib/cjs'
-CWD = path('.')
-BLANK = path('')
-MODULE_TEMPLATE = ''
+LINE_COMMENT_PAT = re.compile(r'(.*?)(//.*?)(?:\n|$)')
+PAIR_COMMENT_PAT = re.compile(r'(.*?)(/\*.*?\*/)')
+REQUIRE_PAT = re.compile(r'\brequire\(\s*([\'"])(.*?)\1\s*\)')
+ROOT = path(__file__).abspath().dirname().dirname()
+LIB = ROOT/'lib/cjs'
+CWD = path('.')
+BLANK = path('')
+MODULE_TEMPLATE = ''
try:
with (LIB/'module.js').open('rU') as f :
MODULE_TEMPLATE = f.read()
except Exception as ex:
print
- print 'ROOT={ROOT}, LIB={LIB}, module.js={}'.format(LIB/'module.js', **locals())
+ print 'Error reading module template file!'
+ print ' ROOT={ROOT}, LIB={LIB}, module.js={}'.format(LIB/'module.js', **locals())
raise
query = trim(query, '/index.cjs', '.cjs', '/index.js', '.js')
basedir = path(base or '').dirname()
- print "canonicalise(query={query}, basedir={basedir})".format(**locals())
+ # print "canonicalise(query={query}, basedir={basedir})".format(**locals())
if query.startswith('.'):
id = ( basedir / query ).normpath()
if id.startswith('..'):
raise ResolutionError('Impossible to resolve {} from {}!'.format(query, base))
- print " -->", str(id)
+ # print " -->", str(id)
return str(id)
+class Repo(object):
+ def __init__(self, filepath, url):
+ self.path = filepath
+ self.url = url
+
+ def __repr__(self):
+ return 'Repo({}, url={})'.format(self.path, self.url)
+
+ def __str__(self):
+ return repr(self)
+
+
class Module(Bunch):
DEFAULTS = {
}
- def __init__(self, id, file, out=None, compile=True):
+ def __init__(self, id, file, uri='', out=None, compile=True):
self.update(Module.DEFAULTS)
self.id = id
else:
out = self.file.replace('.cjs', '.js')
self.outpath = path(out)
+ self.uri = uri
if compile: self.compile()
- print "new!", repr(self)
+ # print "new!", repr(self)
- def compile(self, uri=''):
- if uri: self.uri = uri.format(**self) # TODO: calc uri
+ def compile(self):
+ # if uri: self.uri = uri.format(**self) # TODO: calc uri
if not self.file.endswith('.cjs'):
return self
def contents(self):
return self.read('contents')
+ def findRequires(self, text):
+ text = LINE_COMMENT_PAT.subn(r'\1 ', text)[0]
+ text = PAIR_COMMENT_PAT.subn(r'\1 ', text)[0]
+ # print " findRequires() -> %r" % line
+ for mreq in REQUIRE_PAT.finditer(text):
+ yield canonicalise(mreq.group(2), self)
+
@property
def requires(self):
if self._requires is None:
- self._requires = [ canonicalise(m.group(2), self) for m in REQUIRE_PAT.finditer(self.text) ]
+ self._requires = list(self.findRequires(self.text))
return self._requires
def __hash__(self):
""" Compiles JS modules into browser-safe JS files. """
@staticmethod
- def discover(files, repos, out=None):
+ def discover(files, repos, **options):
"Discover listed modules and their dependencies."
- cjs = CommonJS(repos, out)
+ cjs = CommonJS(repos, **options)
queue = [ cjs.lookup(f) for f in files ]
seen = set()
while queue:
mod = queue.pop(0)
seen.add(mod)
- print mod, "requirements:", mod.requires
+ # print mod, "requirements:", mod.requires
for query in mod.requires:
- print mod, "requires", query
+ # print mod, "requires", query
req = cjs.lookup(query, mod)
if req not in seen:
queue.append(req)
_graph = None # id -> set(dependants)
- def __init__(self, repos, out=None):
+ def __init__(self, repos, out='build', clean=True):
self.modules = {}
self.repos = set( path(path(p).abspath()+'/') for p in repos)
- self.out = out
+ self.out = None if out is '' else out
+
+ if self.out is not None and clean:
+ out = path(self.out)
+ if out.exists(): out.rmtree()
def register(self, id, file):
mod = self.modules[id] = Module(id=id, file=file, out=self.out)
def lookup(self, query, base=None):
absquery = path(query).abspath()
id = canonicalise(query, base)
- print "lookup(query={query}, base={base}) -> id={id}".format(**locals())
+ # print "lookup(query={query}, base={base}) -> id={id}".format(**locals())
if id in self.modules:
return self.modules[id]
raise ResolutionError('Unable to find file for (query={query}, id={id}, base={base}) in repos={}!'.format(map(str, self.repos), **locals()))
+ def __iter__(self):
+ for k, mod in self.modules.iteritems():
+ yield k, mod
+
@property
def graph(self):
if self._graph is None:
parser = OptionParser(
usage = 'usage: %prog [options] file[...] [-- [repo_path[...]]]',
- description = 'Resolves imports in JS files',
+ description = 'Compiles CommonJS modules.',
version = '%prog'+" %i.%i.%i" % __version__)
parser.add_option("-p", "--repo-paths", dest='repos', default='',
- help="Comma-seperated paths to search for unqualified modules. [default: .]")
- parser.add_option("-o", "--out", default=None,
- help="Root directory to write compiled JS files. [default: Module's directory]")
+ help="Comma-seperated paths to search for unqualified modules. "
+ "If a path contains :, the portion afterward will be taken as the repo URL. [default: .]")
+ parser.add_option("-o", "--out", default='build',
+ help="Root directory to write compiled JS files. Specify '' to write to module's dir. [default: build]")
+ parser.add_option("-C", "--no-clean", dest='clean', default=True, action="store_false",
+ help="Do not clean the out-dir of compiled files. [default: False if -o, True otherwise]")
+ parser.add_option("-d", "--print-deps", default=False, action="store_true",
+ help="Prints module dependencies after compiling. [default: %default]")
(options, args) = parser.parse_args()
repos = filter(lambda f: f in args, repos)
repos.extend( filter(None, options.repos.split(',')) )
- print 'files:', files, 'repos:', (repos or ['.'])
- js = CommonJS.discover(files=files, repos=repos or ['.'], out=options.out)
+ # print 'files:', files, 'repos:', (repos or ['.'])
+ js = CommonJS.discover(files=files, repos=repos or ['.'], **options.__dict__)
- print 'deplist:'
- print js.deplist
- print
- print 'dependencies:'
- print '\n'.join(js.dependencies)
- print
+ if options.print_deps:
+ column = Popen(['column', '-s', '\t', '-t'], stderr=sys.stderr, stdin=PIPE, stdout=PIPE)
+ mods = js.modules.values()
+ for mod in sorted(mods, key=lambda m: len(m.requires), reverse=True):
+ column.stdin.write('%s\t->\t%r\n' % (mod, sorted(mod.requires)))
+ # print '%s\t->\t%r' % (mod, sorted(mod.requires))
+
+ column.stdin.close()
+ if column.wait() != 0: print 'Some sort of error has occurred!'
+ print column.stdout.read()
return 0