diff options
Diffstat (limited to 'catapult/common/py_vulcanize/py_vulcanize/resource_loader.py')
-rw-r--r-- | catapult/common/py_vulcanize/py_vulcanize/resource_loader.py | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/catapult/common/py_vulcanize/py_vulcanize/resource_loader.py b/catapult/common/py_vulcanize/py_vulcanize/resource_loader.py new file mode 100644 index 00000000..015adaa6 --- /dev/null +++ b/catapult/common/py_vulcanize/py_vulcanize/resource_loader.py @@ -0,0 +1,228 @@ +# Copyright (c) 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. + +"""ResourceFinder is a helper class for finding resources given their name.""" + +import codecs +import os + +from py_vulcanize import module +from py_vulcanize import style_sheet as style_sheet_module +from py_vulcanize import resource as resource_module +from py_vulcanize import html_module +from py_vulcanize import strip_js_comments + + +class ResourceLoader(object): + """Manges loading modules and their dependencies from files. + + Modules handle parsing and the construction of their individual dependency + pointers. The loader deals with bookkeeping of what has been loaded, and + mapping names to file resources. + """ + + def __init__(self, project): + self.project = project + self.stripped_js_by_filename = {} + self.loaded_modules = {} + self.loaded_raw_scripts = {} + self.loaded_style_sheets = {} + self.loaded_images = {} + + @property + def source_paths(self): + """A list of base directories to search for modules under.""" + return self.project.source_paths + + def FindResource(self, some_path, binary=False): + """Finds a Resource for the given path. + + Args: + some_path: A relative or absolute path to a file. + + Returns: + A Resource or None. + """ + if os.path.isabs(some_path): + return self.FindResourceGivenAbsolutePath(some_path, binary) + else: + return self.FindResourceGivenRelativePath(some_path, binary) + + def FindResourceGivenAbsolutePath(self, absolute_path, binary=False): + """Returns a Resource for the given absolute path.""" + candidate_paths = [] + for source_path in self.source_paths: + if absolute_path.startswith(source_path): + candidate_paths.append(source_path) + if len(candidate_paths) == 0: + return None + + # Sort by length. Longest match wins. + candidate_paths.sort(lambda x, y: len(x) - len(y)) + longest_candidate = candidate_paths[-1] + return resource_module.Resource(longest_candidate, absolute_path, binary) + + def FindResourceGivenRelativePath(self, relative_path, binary=False): + """Returns a Resource for the given relative path.""" + absolute_path = None + for script_path in self.source_paths: + absolute_path = os.path.join(script_path, relative_path) + if os.path.exists(absolute_path): + return resource_module.Resource(script_path, absolute_path, binary) + return None + + def _FindResourceGivenNameAndSuffix( + self, requested_name, extension, return_resource=False): + """Searches for a file and reads its contents. + + Args: + requested_name: The name of the resource that was requested. + extension: The extension for this requested resource. + + Returns: + A (path, contents) pair. + """ + pathy_name = requested_name.replace('.', os.sep) + filename = pathy_name + extension + + resource = self.FindResourceGivenRelativePath(filename) + if return_resource: + return resource + if not resource: + return None, None + return _read_file(resource.absolute_path) + + def FindModuleResource(self, requested_module_name): + """Finds a module javascript file and returns a Resource, or none.""" + js_resource = self._FindResourceGivenNameAndSuffix( + requested_module_name, '.js', return_resource=True) + html_resource = self._FindResourceGivenNameAndSuffix( + requested_module_name, '.html', return_resource=True) + if js_resource and html_resource: + if html_module.IsHTMLResourceTheModuleGivenConflictingResourceNames( + js_resource, html_resource): + return html_resource + return js_resource + elif js_resource: + return js_resource + return html_resource + + def LoadModule(self, module_name=None, module_filename=None, + excluded_scripts=None): + assert bool(module_name) ^ bool(module_filename), ( + 'Must provide either module_name or module_filename.') + if module_filename: + resource = self.FindResource(module_filename) + if not resource: + raise Exception('Could not find %s in %s' % ( + module_filename, repr(self.source_paths))) + module_name = resource.name + else: + resource = None # Will be set if we end up needing to load. + + if module_name in self.loaded_modules: + assert self.loaded_modules[module_name].contents + return self.loaded_modules[module_name] + + if not resource: # happens when module_name was given + resource = self.FindModuleResource(module_name) + if not resource: + raise module.DepsException('No resource for module "%s"' % module_name) + + m = html_module.HTMLModule(self, module_name, resource) + self.loaded_modules[module_name] = m + + # Fake it, this is probably either polymer.min.js or platform.js which are + # actually .js files.... + if resource.absolute_path.endswith('.js'): + return m + + m.Parse(excluded_scripts) + m.Load(excluded_scripts) + return m + + def LoadRawScript(self, relative_raw_script_path): + resource = None + for source_path in self.source_paths: + possible_absolute_path = os.path.join( + source_path, os.path.normpath(relative_raw_script_path)) + if os.path.exists(possible_absolute_path): + resource = resource_module.Resource( + source_path, possible_absolute_path) + break + if not resource: + raise module.DepsException( + 'Could not find a file for raw script %s in %s' % + (relative_raw_script_path, self.source_paths)) + assert relative_raw_script_path == resource.unix_style_relative_path, ( + 'Expected %s == %s' % (relative_raw_script_path, + resource.unix_style_relative_path)) + + if resource.absolute_path in self.loaded_raw_scripts: + return self.loaded_raw_scripts[resource.absolute_path] + + raw_script = module.RawScript(resource) + self.loaded_raw_scripts[resource.absolute_path] = raw_script + return raw_script + + def LoadStyleSheet(self, name): + if name in self.loaded_style_sheets: + return self.loaded_style_sheets[name] + + resource = self._FindResourceGivenNameAndSuffix( + name, '.css', return_resource=True) + if not resource: + raise module.DepsException( + 'Could not find a file for stylesheet %s' % name) + + style_sheet = style_sheet_module.StyleSheet(self, name, resource) + style_sheet.load() + self.loaded_style_sheets[name] = style_sheet + return style_sheet + + def LoadImage(self, abs_path): + if abs_path in self.loaded_images: + return self.loaded_images[abs_path] + + if not os.path.exists(abs_path): + raise module.DepsException("url('%s') did not exist" % abs_path) + + res = self.FindResourceGivenAbsolutePath(abs_path, binary=True) + if res is None: + raise module.DepsException("url('%s') was not in search path" % abs_path) + + image = style_sheet_module.Image(res) + self.loaded_images[abs_path] = image + return image + + def GetStrippedJSForFilename(self, filename, early_out_if_no_py_vulcanize): + if filename in self.stripped_js_by_filename: + return self.stripped_js_by_filename[filename] + + with open(filename, 'r') as f: + contents = f.read(4096) + if early_out_if_no_py_vulcanize and ('py_vulcanize' not in contents): + return None + + s = strip_js_comments.StripJSComments(contents) + self.stripped_js_by_filename[filename] = s + return s + + +def _read_file(absolute_path): + """Reads a file and returns a (path, contents) pair. + + Args: + absolute_path: Absolute path to a file. + + Raises: + Exception: The given file doesn't exist. + IOError: There was a problem opening or reading the file. + """ + if not os.path.exists(absolute_path): + raise Exception('%s not found.' % absolute_path) + f = codecs.open(absolute_path, mode='r', encoding='utf-8') + contents = f.read() + f.close() + return absolute_path, contents |