12da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Copyright (c) 2012 The Chromium Authors. All rights reserved. 22da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Use of this source code is governed by a BSD-style license that can be 32da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# found in the LICENSE file. 42da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport sys 52da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport os 62da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport re 72da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 82da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass DepsException(Exception): 92da489cd246702bee5938545b18a6f710ed214bcJamie Gennis pass 102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis""" 122da489cd246702bee5938545b18a6f710ed214bcJamie GennisThe core of this script is the calc_load_sequence function. In total, this 132da489cd246702bee5938545b18a6f710ed214bcJamie Genniswalks over the provided javascript files and figures out their dependencies 142da489cd246702bee5938545b18a6f710ed214bcJamie Gennisusing the module definitions provided in each file. This allows us to, for 152da489cd246702bee5938545b18a6f710ed214bcJamie Gennisexample, have a trio of modules: 162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 172da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfoo.js: 182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis base.require('bar'); 192da489cd246702bee5938545b18a6f710ed214bcJamie Gennisand bar.js: 202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis base.require('baz'); 212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2288448d9ae4dfff1805045790ef5f32495d62abccJeff Browncalc_load_sequence(['foo'], '.') will yield: 232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis [Module('baz'), Module('bar'), Module('foo')] 242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 252da489cd246702bee5938545b18a6f710ed214bcJamie Genniswhich is, based on the dependencies, the correct sequence in which to load 262da489cd246702bee5938545b18a6f710ed214bcJamie Gennisthose modules. 272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis""" 282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 292da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass ResourceFinder(object): 302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Helper code for finding a module given a name and current module. 312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis The dependency resolution code in Module.resolve will find bits of code in the 332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis actual javascript that says things require('bar'). This 342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis code is responsible for figuring out what filename corresponds to 'bar' given 352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis a Module('foo'). 362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ 372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def __init__(self, root_dir): 382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._root_dir = root_dir 392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis pass 402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4188448d9ae4dfff1805045790ef5f32495d62abccJeff Brown @property 4288448d9ae4dfff1805045790ef5f32495d62abccJeff Brown def root_dir(self): 4388448d9ae4dfff1805045790ef5f32495d62abccJeff Brown return self._root_dir 442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4588448d9ae4dfff1805045790ef5f32495d62abccJeff Brown def _find_and_load_filename(self, absolute_path): 462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not os.path.exists(absolute_path): 472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return None, None 482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis f = open(absolute_path, 'r') 502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis contents = f.read() 512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis f.close() 522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return absolute_path, contents 542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 5588448d9ae4dfff1805045790ef5f32495d62abccJeff Brown def _find_and_load(self, current_module, requested_name, extension): 5688448d9ae4dfff1805045790ef5f32495d62abccJeff Brown assert current_module.filename 5788448d9ae4dfff1805045790ef5f32495d62abccJeff Brown pathy_name = requested_name.replace(".", os.sep) 5888448d9ae4dfff1805045790ef5f32495d62abccJeff Brown filename = pathy_name + extension 5988448d9ae4dfff1805045790ef5f32495d62abccJeff Brown absolute_path = os.path.join(self._root_dir, filename) 6088448d9ae4dfff1805045790ef5f32495d62abccJeff Brown return self._find_and_load_filename(absolute_path) 6188448d9ae4dfff1805045790ef5f32495d62abccJeff Brown 622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def find_and_load_module(self, current_module, requested_module_name): 632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return self._find_and_load(current_module, requested_module_name, ".js") 642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 6588448d9ae4dfff1805045790ef5f32495d62abccJeff Brown def find_and_load_raw_script(self, current_module, filename): 6688448d9ae4dfff1805045790ef5f32495d62abccJeff Brown absolute_path = os.path.join(self._root_dir, filename) 6788448d9ae4dfff1805045790ef5f32495d62abccJeff Brown return self._find_and_load_filename(absolute_path) 6888448d9ae4dfff1805045790ef5f32495d62abccJeff Brown 692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def find_and_load_style_sheet(self, 702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis current_module, requested_style_sheet_name): 712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return self._find_and_load( 722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis current_module, requested_style_sheet_name, ".css") 732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 752da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass StyleSheet(object): 762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Represents a stylesheet resource referenced by a module via the 772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis base.requireStylesheet(xxx) directive.""" 782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def __init__(self, name, filename, contents): 792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self.name = name 802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self.filename = filename 812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self.contents = contents 822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def __repr__(self): 842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return "StyleSheet(%s)" % self.name 852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 8688448d9ae4dfff1805045790ef5f32495d62abccJeff Brownclass RawScript(object): 8788448d9ae4dfff1805045790ef5f32495d62abccJeff Brown """Represents a raw script resource referenced by a module via the 8888448d9ae4dfff1805045790ef5f32495d62abccJeff Brown base.requireRawScript(xxx) directive.""" 8988448d9ae4dfff1805045790ef5f32495d62abccJeff Brown def __init__(self, name, filename, contents): 9088448d9ae4dfff1805045790ef5f32495d62abccJeff Brown self.name = name 9188448d9ae4dfff1805045790ef5f32495d62abccJeff Brown self.filename = filename 9288448d9ae4dfff1805045790ef5f32495d62abccJeff Brown self.contents = contents 9388448d9ae4dfff1805045790ef5f32495d62abccJeff Brown 9488448d9ae4dfff1805045790ef5f32495d62abccJeff Brown def __repr__(self): 9588448d9ae4dfff1805045790ef5f32495d62abccJeff Brown return "RawScript(%s)" % self.name 9688448d9ae4dfff1805045790ef5f32495d62abccJeff Brown 972da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef _tokenize_js(text): 982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis rest = text 992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis tokens = ["//", "/*", "*/", "\n"] 1002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis while len(rest): 1012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis indices = [rest.find(token) for token in tokens] 1022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis found_indices = [index for index in indices if index >= 0] 1032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if len(found_indices) == 0: 1052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # end of string 1062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis yield rest 1072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return 1082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis min_index = min(found_indices) 1102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token_with_min = tokens[indices.index(min_index)] 1112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if min_index > 0: 1132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis yield rest[:min_index] 1142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis yield rest[min_index:min_index + len(token_with_min)] 1162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis rest = rest[min_index + len(token_with_min):] 1172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1182da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef _strip_js_comments(text): 1192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis result_tokens = [] 1202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token_stream = _tokenize_js(text).__iter__() 1212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis while True: 1222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis try: 1232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis t = token_stream.next() 1242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis except StopIteration: 1252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis break 1262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if t == "//": 1282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis while True: 1292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis try: 1302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis t2 = token_stream.next() 1312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if t2 == "\n": 1322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis break 1332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis except StopIteration: 1342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis break 1352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif t == '/*': 1362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis nesting = 1 1372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis while True: 1382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis try: 1392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis t2 = token_stream.next() 1402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if t2 == "/*": 1412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis nesting += 1 1422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif t2 == "*/": 1432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis nesting -= 1 1442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if nesting == 0: 1452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis break 1462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis except StopIteration: 1472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis break 1482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis else: 1492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis result_tokens.append(t) 1502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return "".join(result_tokens) 1512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 15288448d9ae4dfff1805045790ef5f32495d62abccJeff Browndef _MangleRawScriptFilenameToModuleName(filename): 15388448d9ae4dfff1805045790ef5f32495d62abccJeff Brown name = filename 15488448d9ae4dfff1805045790ef5f32495d62abccJeff Brown name = name.replace(os.sep, ':') 15588448d9ae4dfff1805045790ef5f32495d62abccJeff Brown name = name.replace('..', '!!') 15688448d9ae4dfff1805045790ef5f32495d62abccJeff Brown return name 15788448d9ae4dfff1805045790ef5f32495d62abccJeff Brown 1582da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass Module(object): 1592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Represents a javascript module. It can either be directly requested, e.g. 1602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis passed in by name to calc_load_sequence, or created by being referenced a 1612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis module via the base.require(xxx) directive. 1622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Interesting properties on this object are: 1642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis - filename: the file of the actual module 1662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis - contents: the actual text contents of the module 1672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis - style_sheets: StyleSheet objects that this module relies on for styling 1682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis information. 1692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis - dependent_modules: other modules that this module needs in order to run 1702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ 1712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def __init__(self, name = None): 1722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self.name = name 1732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self.filename = None 1742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self.contents = None 1752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self.dependent_module_names = [] 1772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self.dependent_modules = [] 17888448d9ae4dfff1805045790ef5f32495d62abccJeff Brown self.dependent_raw_script_names = [] 17988448d9ae4dfff1805045790ef5f32495d62abccJeff Brown self.dependent_raw_scripts = [] 1802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self.style_sheet_names = [] 1812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self.style_sheets = [] 1822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def __repr__(self): 1842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return "Module(%s)" % self.name 1852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def load_and_parse(self, module_filename, 1872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis module_contents = None, 1882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis decl_required = True): 1892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not module_contents: 1902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis f = open(module_filename, 'r') 1912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self.contents = f.read() 1922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis f.close() 1932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis else: 1942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self.contents = module_contents 1952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self.filename = module_filename 19666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis stripped_text = _strip_js_comments(self.contents) 19766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis self.validate_uses_strict_mode_(stripped_text) 19866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis self.parse_definition_(stripped_text, decl_required) 1992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def resolve(self, all_resources, resource_finder): 2012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if "scripts" not in all_resources: 2022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis all_resources["scripts"] = {} 2032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if "style_sheets" not in all_resources: 2042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis all_resources["style_sheets"] = {} 20588448d9ae4dfff1805045790ef5f32495d62abccJeff Brown if "raw_scripts" not in all_resources: 20688448d9ae4dfff1805045790ef5f32495d62abccJeff Brown all_resources["raw_scripts"] = {} 2072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis assert self.filename 2092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis for name in self.dependent_module_names: 2112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if name in all_resources["scripts"]: 2122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis assert all_resources["scripts"][name].contents 2132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self.dependent_modules.append(all_resources["scripts"][name]) 2142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis continue 2152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis filename, contents = resource_finder.find_and_load_module(self, name) 2172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not filename: 21866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis raise DepsException("No file for module %(name)s needed by %(dep)s" % 21966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis {"name": name, "dep": self.filename}) 2202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis module = Module(name) 2222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis all_resources["scripts"][name] = module 2232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self.dependent_modules.append(module) 22466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis try: 22566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis module.load_and_parse(filename, contents) 22666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis except Exception, e: 22766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis raise Exception('While processing ' + filename + ': ' + e.message) 2282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis module.resolve(all_resources, resource_finder) 2292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 23088448d9ae4dfff1805045790ef5f32495d62abccJeff Brown for name in self.dependent_raw_script_names: 23188448d9ae4dfff1805045790ef5f32495d62abccJeff Brown filename, contents = resource_finder.find_and_load_raw_script(self, name) 23288448d9ae4dfff1805045790ef5f32495d62abccJeff Brown if not filename: 23366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis raise DepsException("Could not find a file for raw script %s" % name) 23488448d9ae4dfff1805045790ef5f32495d62abccJeff Brown 23588448d9ae4dfff1805045790ef5f32495d62abccJeff Brown if name in all_resources["raw_scripts"]: 23688448d9ae4dfff1805045790ef5f32495d62abccJeff Brown assert all_resources["raw_scripts"][name].contents 23788448d9ae4dfff1805045790ef5f32495d62abccJeff Brown self.dependent_raw_scripts.append(all_resources["raw_scripts"][name]) 23888448d9ae4dfff1805045790ef5f32495d62abccJeff Brown continue 23988448d9ae4dfff1805045790ef5f32495d62abccJeff Brown 24088448d9ae4dfff1805045790ef5f32495d62abccJeff Brown raw_script = RawScript(name, filename, contents) 24188448d9ae4dfff1805045790ef5f32495d62abccJeff Brown all_resources["raw_scripts"][name] = raw_script 24288448d9ae4dfff1805045790ef5f32495d62abccJeff Brown self.dependent_raw_scripts.append(raw_script) 24388448d9ae4dfff1805045790ef5f32495d62abccJeff Brown 2442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis for name in self.style_sheet_names: 2452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if name in all_resources["style_sheets"]: 2462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis assert all_resources["style_sheets"][name].contents 24766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis self.style_sheets.append(all_resources["style_sheets"][name]) 2482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis continue 2492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis filename, contents = resource_finder.find_and_load_style_sheet(self, name) 2512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not filename: 2522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis raise DepsException("Could not find a file for stylesheet %s" % name) 2532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis style_sheet = StyleSheet(name, filename, contents) 2552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis all_resources["style_sheets"][name] = style_sheet 2562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self.style_sheets.append(style_sheet) 2572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def compute_load_sequence_recursive(self, load_sequence, already_loaded_set): 2592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis for dependent_module in self.dependent_modules: 2602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis dependent_module.compute_load_sequence_recursive(load_sequence, 2612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis already_loaded_set) 2622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if self.name not in already_loaded_set: 2632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis already_loaded_set.add(self.name) 2642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis load_sequence.append(self) 2652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 26666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis def validate_uses_strict_mode_(self, stripped_text): 26766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis lines = stripped_text.split('\n') 26866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis for line in lines: 26966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis line = line.strip() 27066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis if len(line.strip()) == 0: 27166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis continue 27266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis if line.strip() == """'use strict';""": 27366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis break 27466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis raise DepsException('%s must use strict mode' % self.name) 27566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis 27666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis def parse_definition_(self, stripped_text, decl_required = True): 2772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not decl_required and not self.name: 2782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis raise Exception("Module.name must be set for decl_required to be false.") 2792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis rest = stripped_text 2812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis while True: 2822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Things to search for. 2832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis m_r = re.search("""base\s*\.\s*require\((["'])(.+?)\\1\)""", 2842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis rest, re.DOTALL) 2852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis m_s = re.search("""base\s*\.\s*requireStylesheet\((["'])(.+?)\\1\)""", 2862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis rest, re.DOTALL) 28788448d9ae4dfff1805045790ef5f32495d62abccJeff Brown m_irs = re.search("""base\s*\.\s*requireRawScript\((["'])(.+?)\\1\)""", 28888448d9ae4dfff1805045790ef5f32495d62abccJeff Brown rest, re.DOTALL) 28988448d9ae4dfff1805045790ef5f32495d62abccJeff Brown matches = [m for m in [m_r, m_s, m_irs] if m] 2902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Figure out which was first. 29288448d9ae4dfff1805045790ef5f32495d62abccJeff Brown matches.sort(key=lambda x: x.start()) 29388448d9ae4dfff1805045790ef5f32495d62abccJeff Brown if len(matches): 29488448d9ae4dfff1805045790ef5f32495d62abccJeff Brown m = matches[0] 2952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis else: 2962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis break 2972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if m == m_r: 2992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis dependent_module_name = m.group(2) 3002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if '/' in dependent_module_name: 3012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis raise DepsException("Slashes are not allowed in module names. " 3022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis "Use '.' instead: %s" % dependent_module_name) 30388448d9ae4dfff1805045790ef5f32495d62abccJeff Brown if dependent_module_name.endswith('js'): 30488448d9ae4dfff1805045790ef5f32495d62abccJeff Brown raise DepsException("module names shouldn't end with .js" 30588448d9ae4dfff1805045790ef5f32495d62abccJeff Brown "The module system will append that for you: %s" % 30688448d9ae4dfff1805045790ef5f32495d62abccJeff Brown dependent_module_name) 3072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self.dependent_module_names.append(dependent_module_name) 3082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif m == m_s: 30988448d9ae4dfff1805045790ef5f32495d62abccJeff Brown style_sheet_name = m.group(2) 31088448d9ae4dfff1805045790ef5f32495d62abccJeff Brown if '/' in style_sheet_name: 31188448d9ae4dfff1805045790ef5f32495d62abccJeff Brown raise DepsException("Slashes are not allowed in style sheet names. " 31288448d9ae4dfff1805045790ef5f32495d62abccJeff Brown "Use '.' instead: %s" % style_sheet_name) 31388448d9ae4dfff1805045790ef5f32495d62abccJeff Brown if style_sheet_name.endswith('.css'): 31488448d9ae4dfff1805045790ef5f32495d62abccJeff Brown raise DepsException("Style sheets should not end in .css. " 31588448d9ae4dfff1805045790ef5f32495d62abccJeff Brown "The module system will append that for you" % 31688448d9ae4dfff1805045790ef5f32495d62abccJeff Brown style_sheet_name) 31788448d9ae4dfff1805045790ef5f32495d62abccJeff Brown self.style_sheet_names.append(style_sheet_name) 31888448d9ae4dfff1805045790ef5f32495d62abccJeff Brown elif m == m_irs: 31988448d9ae4dfff1805045790ef5f32495d62abccJeff Brown name = m.group(2) 32088448d9ae4dfff1805045790ef5f32495d62abccJeff Brown self.dependent_raw_script_names.append(name) 3212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis rest = rest[m.end():] 3232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 32588448d9ae4dfff1805045790ef5f32495d62abccJeff Browndef calc_load_sequence(filenames, toplevel_dir): 3262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Given a list of starting javascript files, figure out all the Module 3272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis objects that need to be loaded to satisfiy their dependencies. 3282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis The javascript files shoud specify their dependencies in a format that is 3302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis textually equivalent to base.js' require syntax, namely: 3312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis base.require(module1); 3332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis base.require(module2); 3342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis base.requireStylesheet(stylesheet); 3352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis The output of this function is an array of Module objects ordered by 3372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis dependency. 3382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ 3392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis all_resources = {} 3402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis all_resources["scripts"] = {} 34166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis resource_finder = ResourceFinder(os.path.abspath(toplevel_dir)) 34266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis initial_module_name_indices = {} 3432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis for filename in filenames: 3442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not os.path.exists(filename): 3452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis raise Exception("Could not find %s" % filename) 34688448d9ae4dfff1805045790ef5f32495d62abccJeff Brown 34788448d9ae4dfff1805045790ef5f32495d62abccJeff Brown rel_filename = os.path.relpath(filename, toplevel_dir) 34888448d9ae4dfff1805045790ef5f32495d62abccJeff Brown dirname = os.path.dirname(rel_filename) 34988448d9ae4dfff1805045790ef5f32495d62abccJeff Brown modname = os.path.splitext(os.path.basename(rel_filename))[0] 3502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if len(dirname): 3512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis name = dirname.replace('/', '.') + '.' + modname 3522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis else: 3532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis name = modname 3542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if name in all_resources["scripts"]: 3562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis continue 3572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis module = Module(name) 35966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis initial_module_name_indices[module.name] = len(initial_module_name_indices) 3602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis module.load_and_parse(filename, decl_required = False) 3612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis all_resources["scripts"][module.name] = module 3622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis module.resolve(all_resources, resource_finder) 3632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 36466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis # Find the root modules: ones that have no dependencies. While doing that, 36566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis # sort the dependent module list so that the computed load order is stable. 3662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis module_ref_counts = {} 3672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis for module in all_resources["scripts"].values(): 36866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis module.dependent_modules.sort(lambda x, y: cmp(x.name, y.name)) 3692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis module_ref_counts[module.name] = 0 3702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def inc_ref_count(name): 3722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis module_ref_counts[name] = module_ref_counts[name] + 1 3732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis for module in all_resources["scripts"].values(): 3742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis for dependent_module in module.dependent_modules: 3752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis inc_ref_count(dependent_module.name) 3762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis root_modules = [all_resources["scripts"][name] 3782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis for name, ref_count in module_ref_counts.items() 3792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if ref_count == 0] 3802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 38166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis # Sort root_modules by the order they were originally requested, 38266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis # then sort everything else by name. 38366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis def compare_root_module(x, y): 38466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis n = len(initial_module_name_indices); 38566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis iX = initial_module_name_indices.get(x.name, n) 38666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis iY = initial_module_name_indices.get(y.name, n) 38766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis if cmp(iX, iY) != 0: 38866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis return cmp(iX, iY) 38966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis return cmp(x.name, y.name) 39066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis root_modules.sort(compare_root_module) 3912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis already_loaded_set = set() 3932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis load_sequence = [] 3942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis for module in root_modules: 3952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis module.compute_load_sequence_recursive(load_sequence, already_loaded_set) 3962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return load_sequence 397