From: dsc Date: Sun, 5 Dec 2010 03:38:10 +0000 (-0800) Subject: Refactoring for build system. X-Git-Url: http://git.less.ly:3516/?a=commitdiff_plain;h=7f61fad47202c70086c5e983fceddd2c9d9a46f0;p=tanks.git Refactoring for build system. --- diff --git a/.gitignore b/.gitignore index ec39ced..080d845 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.md.html tmp/ build/ +.commonjs diff --git a/bin/cjs.py b/bin/cjs.py deleted file mode 100755 index 1bd5429..0000000 --- a/bin/cjs.py +++ /dev/null @@ -1,434 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 -__author__ = 'David Schoonover ' -__date__ = '2010-11-24' -__version__ = (0, 0, 1) - -__all__ = ('ResolutionError', 'Module', 'JSResolver',) - - -import sys, re, jsond as json -from itertools import chain, repeat -from collections import defaultdict -from subprocess import Popen, check_output, STDOUT, PIPE -from glob import glob -from pprint import pprint, pformat - -from bunch import * -from path import path -import pystache - - -AT_REQUIRE_PAT = re.compile(r'//@require\(\s*([\'"])(.*?)\1\s*\)') -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 = '' -DUMMY_TEMPLATE = '' -DEPS_TEMPLATE = '' - -try: - with (LIB/'module.js').open('rU') as f: - MODULE_TEMPLATE = f.read() - with (LIB/'dummy.js').open('rU') as f: - DUMMY_TEMPLATE = f.read() - with (LIB/'deps.js').open('rU') as f: - DEPS_TEMPLATE = f.read() -except Exception as ex: - print - print 'Error reading template file!' - print ' ROOT={ROOT}, LIB={LIB}, module.js={}'.format(LIB/'module.js', **locals()) - raise - - -class ResolutionError(Exception): pass - - -def trim(s='', *chars): - for chs in chars: - if chs and s.endswith(chs): - s = s[:-len(chs)] - return s - -def partition(it, sep): - """ Partitions an iterable at the first occurrence of sep, and return a 3-tuple - containing the list of items before the separator, the separator itself, - and after the seperator. If the separator is not found, return a 3-tuple of - (the exhausted iterable as a list, None, []). - """ - before = [] - it = iter(it) - for val in it: - if val == sep: - break - else: - before.append(val) - else: - return (before, None, []) - return (before, sep, list(it)) - - - -def canonicalise(query, base=None): - """ query: A module ID (relative or absolute) or filepath - base: Optional. The referring module ID. - """ - if isinstance(base, Module): - base = base.id - - query = trim(query, '/index.cjs', '.cjs', '/index.js', '.js') - basedir = path(base or '').dirname() - # print "canonicalise(query={query}, basedir={basedir})".format(**locals()) - - if query.startswith('.'): - id = ( basedir / query ).normpath() - else: - id = query - - if id.startswith('..'): - raise ResolutionError('Impossible to resolve {} from {}!'.format(query, base)) - - # print " -->", str(id) - return str(id) - - - -class Module(Bunch): - DEFAULTS = { - 'id' : '', # Canonical Module ID - 'file' : None, # path - 'name' : '', # Module Name - 'uri' : '', # Module URI (TODO) - 'text' : None, # Unmodified module text - 'contents' : None, # Compiled module text - 'outpath' : None, # Build directory - '_requires' : None, - '_at_requires' : None, - } - - - def __init__(self, id, file, uri=BLANK, out=None, compile=True): - self.update(Module.DEFAULTS) - - self.id = id - self.file = path(file) - self.name = re.subn(r'\W', '', self.id.split('/').pop().capitalize())[0] - - if out: - out = path(out) - outpath = (out / self.id) + '.js' - else: - out = BLANK - outpath = self.file.replace('.cjs', '.js') - self.outpath = path(outpath) - self.uri = str(out/uri/self.id)+'.js' - - self.compile() - # print "new!", repr(self) - - def compile(self): - outdir = self.outpath.dirname() - if not outdir.exists(): - outdir.makedirs() - - with self.outpath.open('w') as out: - out.write(self.contents) - - return self - - def read(self, attr='contents'): - if self['text'] is None: - with self.file.open('rU') as f: - txt = f.read() - self['text'] = txt - template = MODULE_TEMPLATE if self.file.endswith('.cjs') else DUMMY_TEMPLATE - self['contents'] = pystache.render(template, self) - return self[attr] - - @property - def text(self): - return self.read('text') - - @property - def contents(self): - return self.read('contents') - - def findDirectives(self, text): - for areq in AT_REQUIRE_PAT.finditer(text): - yield canonicalise(areq.group(2), self) - - 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) - - def calcRequires(self, attr): - if self._requires is None or self._at_requires is None: - self._at_requires = list(self.findDirectives(self.text)) - self._requires = list(self.findRequires(self.text)) - return getattr(self, attr) - - @property - def atRequires(self): - return self.calcRequires('_at_requires') - - @property - def nonAtRequires(self): - return self.calcRequires('_requires') - - @property - def requires(self): - return self.calcRequires('_at_requires') + self._requires - - def __json_default__(self): - return dict( (k,self[k]) for k in 'id file name _requires _at_requires'.split(' ') ) - - def __hash__(self): - return hash(self.id) - - def __cmp__(self, other): - return cmp(self.id, other.id) - - def __eq__(self, other): - return self.id == other.id - - def __str__(self): - return str(self.id) - - def __repr__(self): - return '{self.__class__.__name__}(id={self.id}, file={self.file!s})'.format(**locals()) - - - - -class CommonJS(object): - """ Compiles JS modules into browser-safe JS files. """ - - repos = [] - modules = {} # id -> Module - _graph = None # id -> set(dependants) - _at_graph = None # id -> set(dependants) - - def __init__(self, repos, out='build', save='build', deps_name=None, clean=True, **options): - self.modules = {} - self.options = options - self.deps_name = deps_name - self.repos = set( path(path(p).abspath()+'/') for p in repos) - self.out = None if out is '' else out - - if self.out is not None and clean: - out = path(self.out) - for f in out.glob('*'): - if f.isdir(): - f.rmtree() - elif f.isfile(): - f.remove() - - def register(self, id, file): - mod = self.modules[id] = Module(id=id, file=file, out=self.out) - return mod - - def lookup(self, query, base=None): - absquery = path(query).abspath() - id = canonicalise(query, base) - # print "lookup(query={query}, base={base}) -> id={id}".format(**locals()) - - if id in self.modules: - return self.modules[id] - - for repo in self.repos: - if absquery.startswith(repo) and absquery.isfile(): - id = canonicalise(absquery[len(repo):]) - return self.register(id, absquery) - - p = (repo / id).abspath() - for f in (p+'.cjs', p/'index.cjs', p+'.js', p/'index.js'): - if f.isfile(): - return self.register(id, f) - - 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 - - def reset(self): - self._graph = None - self._at_graph = None - return self - - def calcGraph(self, attr): - if self._graph is None: - graph = self._graph = defaultdict(set) - atgraph = self._at_graph = defaultdict(set) - for mod in self.modules.values(): - for req in mod.nonAtRequires: - graph[req].add(mod) - for req in mod.atRequires: - atgraph[req].add(mod) - return getattr(self, attr) - - @property - def graph(self): - return self.calcGraph('_graph') - - @property - def atGraph(self): - return self.calcGraph('_at_graph') - - def tsort(self, graph): - p = Popen(['tsort'], stderr=sys.stderr, stdin=PIPE, stdout=PIPE) - deps = '\n'.join( '%s %s' % (id, dep.id) for (id, deps) in graph.iteritems() for dep in deps ) - p.stdin.write(deps) - p.stdin.close() - if p.wait() != 0: - raise ResolutionError('Cannot resolve dependencies! Requirements must be acyclic!') - return p.stdout.read().strip().split('\n') - - @property - def dependencies(self): - atg = self.tsort(self.atGraph) - nog = self.tsort(self.graph) - atdeps = self.atGraph.keys() - - for dep in atg[:]: - if dep not in atdeps: - try: atg.remove(dep) - except ValueError: pass - else: - try: nog.remove(dep) - except ValueError: pass - - # print '\nAtKeys:\n', pformat(atdeps), '\n' - # print '\nAtDeps:\n', pformat(atg), '\n' - # print '\nNoKeys:\n', pformat(nog), '\n' - return atg+nog - - def dumpDependencies(self): - column = Popen(['column', '-s', '\t', '-t'], stderr=sys.stderr, stdin=PIPE, stdout=PIPE) - # for mod in sorted(self.modules.values(), key=lambda m: len(m.requires), reverse=True): - for mod in sorted(self.modules.values()): - column.stdin.write('%s\t->\t%r\n' % (mod, sorted(mod.requires))) - column.stdin.close() - if column.wait() != 0: - print >> sys.stderr, 'Some sort of error has occurred!' - return column.stdout.read() - - def genDepLoader(self): - with (path(self.out)/self.deps_name).open('w') as deps: - deps.write( pystache.render(DEPS_TEMPLATE, uris=json.dumps(self.uris)) ) - - @property - def uris(self): - return [ self.modules[d].uri for d in self.dependencies ] - - @property - def scriptTags(self): - return '\n'.join( ''.format(uri) for uri in self.uris ) - - def save(self, dir): - with (path(dir)/'.modules.json').open('w') as f: - json.dump({ - 'modules' : self.modules, - 'ensure' : dict( (id, list(mod.id for mod in mods)) for id,mods in self.atGraph.iteritems() ), - 'graph' : dict( (id, list(mod.id for mod in mods)) for id,mods in self.graph.iteritems() ), - }, f, indent=4) - return self - - @staticmethod - def discover(files, repos, **options): - "Discover listed modules and their dependencies." - cjs = CommonJS(repos, **options) - - # print >> sys.stderr, '\n[ %s ]' % f - - queue = [ cjs.reset().lookup(f) for f in files ] - seen = set() - while queue: - mod = queue.pop(0) - seen.add(mod) - # print mod, "requirements:", mod.requires - for query in mod.requires: - # print mod, "requires", query - req = cjs.lookup(query, mod) - if req not in seen: - queue.append(req) - - if options.get('deps_name', None): - cjs.genDepLoader() - - if options.get('print_deps', None): - print >> sys.stderr, '\n[ %s ]' % f - print >> sys.stderr, 'All Dependencies:' - print >> sys.stderr, cjs.dumpDependencies() - print >> sys.stderr, '' - print >> sys.stderr, 'Resolution:' - print >> sys.stderr, '\n'.join(cjs.dependencies) - - if options.get('script_tags', None): - print cjs.scriptTags - - if options.get('state', None): - cjs.save(options['state']) - - return cjs - - - - - - -def main(): - from optparse import OptionParser - - parser = OptionParser( - usage = 'usage: %prog [options] file[...] [-- [repo_path[...]]]', - description = 'Compiles CommonJS modules.', - version = '%prog'+" %i.%i.%i" % __version__) - parser.add_option("-p", "--repo-paths", default='', - 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("--print-deps", default=False, action="store_true", - help="Prints module dependencies after compiling. [default: %default]") - parser.add_option("-g", "--gen-deps-script", dest='deps_name', default=None, - help="Generates a JS script with the given name which doc-writes tags for this set of modules.") - parser.add_option("-s", "--script-tags", default=False, action="store_true", - help="Emits script-tags for this set of modules in dependency order. [default: %default]") - parser.add_option("-S", "--state", default='build', - help="Directory to save build state. [default: %default]") - - - (options, args) = parser.parse_args() - - if not args: - parser.error("You must specify at least one JS file to resolve!") - - (files, sep, repos) = partition(sys.argv, '--') - files = filter(lambda f: f in args, files) - repos = filter(lambda f: f in args, repos) - repos.extend( filter(None, options.repo_paths.split(',')) ) - - # print 'files:', files, 'repos:', (repos or ['.']) - try: - cjs = CommonJS.discover(files=files, repos=repos or ['.'], **options.__dict__) - except ResolutionError as ex: - print >> sys.stderr, str(ex) - return 1 - - return 0 - -if __name__ == '__main__': - sys.exit(main()) - diff --git a/lib/cjs/deps.js b/lib/cjs/deps.js deleted file mode 100644 index 3735e59..0000000 --- a/lib/cjs/deps.js +++ /dev/null @@ -1,3 +0,0 @@ -{{{uris}}}.forEach(function(uri){ - document.write(''); -}); diff --git a/lib/cjs/dummy.js b/lib/cjs/dummy.js deleted file mode 100644 index 71c5a6b..0000000 --- a/lib/cjs/dummy.js +++ /dev/null @@ -1,14 +0,0 @@ -require.install( -// Module ID -'{{{id}}}', - -// Module metadata -{ - 'name' : '{{{name}}}', - 'id' : '{{{id}}}', - 'uri' : '{{{uri}}}' -}, -// Dummy module: setup does nothing -function setup{{{name}}}(require, exports, module){}); - -{{{text}}} diff --git a/lib/cjs/module.js b/lib/cjs/module.js deleted file mode 100644 index 167f51a..0000000 --- a/lib/cjs/module.js +++ /dev/null @@ -1,17 +0,0 @@ -require.install( -// Module ID -'{{{id}}}', - -// Module metadata -{ - 'name' : '{{{name}}}', - 'id' : '{{{id}}}', - 'uri' : '{{{uri}}}' -}, - -// Module Setup function -function setup{{{name}}}(require, exports, module){ - -{{{text}}} - -}); \ No newline at end of file diff --git a/lib/cjs/require.js b/lib/cjs/require.js deleted file mode 100644 index 586ad3e..0000000 --- a/lib/cjs/require.js +++ /dev/null @@ -1,50 +0,0 @@ -;require = (function(){ - -var modules = require.modules = {} -, cache = require.cache = {}; - -function require(id){ - if ( cache.hasOwnProperty(id) ) - return cache[id]; - - if ( !modules.hasOwnProperty(id) ) - throw new Error('No such module "'+id+'"!'); - - var module = modules[id] - , exports = module.exports = {}; - - // TODO: load the module - // if (module.hasOwnProperty('load')) { - // /* load needs to load the file synchronously using XHR - // we should probably supply a lib function, like require.load(). - // */ - // module.load.call(exports, require, exports, module); - // } - - // setup the module - module.setup.call(exports, require, exports, module); - - return exports; -}; - -require.install = function(id, module, setup){ - if ( !id ) - throw new Error('No module ID specified: id='+id+'!'); - - if ( modules.hasOwnProperty(id) ) - throw new Error('Module "'+id+'" already exists!'); - - if ( module instanceof Function ) { - setup = module; - module = { 'id':id }; - } - - module.id = id; - module.setup = setup; - modules[id] = module; - - return module; -}; - -return require; -})(); diff --git a/lib/jquery.hotkeys.js b/lib/jquery.hotkeys.js index 315014f..fbd71c7 100644 --- a/lib/jquery.hotkeys.js +++ b/lib/jquery.hotkeys.js @@ -1,4 +1,3 @@ -//@require('jquery') /* * jQuery Hotkeys Plugin * Copyright 2010, John Resig diff --git a/lib/jquery.hotkeys.min.js b/lib/jquery.hotkeys.min.js index 90a601f..6689e7a 100644 --- a/lib/jquery.hotkeys.min.js +++ b/lib/jquery.hotkeys.min.js @@ -1,2 +1 @@ -//@require('jquery') (function(b){b.fn.__bind__=b.fn.bind;b.fn.__unbind__=b.fn.unbind;b.fn.__find__=b.fn.find;var a={version:"0.7.9",override:/keypress|keydown|keyup/g,triggersMap:{},specialKeys:{27:"esc",9:"tab",32:"space",13:"return",8:"backspace",145:"scroll",20:"capslock",144:"numlock",19:"pause",45:"insert",36:"home",46:"del",35:"end",33:"pageup",34:"pagedown",37:"left",38:"up",39:"right",40:"down",109:"-",112:"f1",113:"f2",114:"f3",115:"f4",116:"f5",117:"f6",118:"f7",119:"f8",120:"f9",121:"f10",122:"f11",123:"f12",191:"/"},shiftNums:{"`":"~","1":"!","2":"@","3":"#","4":"$","5":"%","6":"^","7":"&","8":"*","9":"(","0":")","-":"_","=":"+",";":":","'":'"',",":"<",".":">","/":"?","\\":"|"},newTrigger:function(e,d,f){var c={};c[e]={};c[e][d]={cb:f,disableInInput:false};return c}};a.specialKeys=b.extend(a.specialKeys,{96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9",106:"*",107:"+",109:"-",110:".",111:"/"});b.fn.find=function(c){this.query=c;return b.fn.__find__.apply(this,arguments)};b.fn.unbind=function(h,e,g){if(b.isFunction(e)){g=e;e=null}if(e&&typeof e==="string"){var f=((this.prevObject&&this.prevObject.query)||(this[0].id&&this[0].id)||this[0]).toString();var d=h.split(" ");for(var c=0;c + - - - - + - - + + - - + + + + - - + + - - + - - - - - - + - - + + + + + + + + + + + diff --git a/tanks.php b/tanks.php index da77d84..3858af2 100644 --- a/tanks.php +++ b/tanks.php @@ -60,7 +60,7 @@ class Tanks { // "lib/uki/uki-more.dev.js", // "lib/uki/uki-theme/aristo.js", - "src/lessly/future.js", + "src/future.js", "lib/cjs/require.js", "src/Y/y.js.php",