Fixes cjs bugs.
authordsc <david.schoonover@gmail.com>
Sun, 28 Nov 2010 01:01:26 +0000 (17:01 -0800)
committerdsc <david.schoonover@gmail.com>
Sun, 28 Nov 2010 01:01:26 +0000 (17:01 -0800)
bin/cjs.py
lib/functional/functional.js [moved from lib/functional.js with 100% similarity]
lib/functional/to-function.js [moved from src/Y/tofunction.cjs with 100% similarity]
src/Y/index.cjs

index 95706e3..250e42d 100755 (executable)
@@ -18,19 +18,22 @@ from path import path
 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
 
 
@@ -69,7 +72,7 @@ def canonicalise(query, base=None):
     
     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()
@@ -79,10 +82,22 @@ def canonicalise(query, base=None):
     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 = {
@@ -97,7 +112,7 @@ class Module(Bunch):
     }
     
     
-    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
@@ -109,12 +124,13 @@ class Module(Bunch):
         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
@@ -144,10 +160,17 @@ class Module(Bunch):
     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):
@@ -172,17 +195,17 @@ class CommonJS(object):
     """ 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)
@@ -194,10 +217,14 @@ class CommonJS(object):
     _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)
@@ -206,7 +233,7 @@ class CommonJS(object):
     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]
@@ -223,6 +250,10 @@ class CommonJS(object):
         
         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:
@@ -259,12 +290,17 @@ def main():
     
     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()
     
@@ -276,15 +312,19 @@ def main():
     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
 
index 4b3e16b..bc8097e 100644 (file)
@@ -1,7 +1,7 @@
 
 /// Import our external deps first ///
-require('lessly/future');
-require('functional/to-function');
+// require('lessly/future');
+// require('functional/to-function');
 
 /// Set up core and utilities ///
 var core = require('Y/core')