Commits

Alessandro Molina committed 0e86956

Should provide nested imports support

  • Participants
  • Parent commits 4078253

Comments (0)

Files changed (2)

File tgext/scss/compiler.py

 
 
 class Scss(object):
+    import_re = re.compile(r'^\s*@import +url\(\s*["\']?([^\)\'\"]+)["\']?\s*\)\s*;?\s*$', re.MULTILINE)
+
     # configuration:
     construct = 'self'
 
             elif c_property == '@prototype ':  # Remove '@prototype '
                 c_property = c_property[11:]
                 ####################################################################
+
             if c_property.startswith('@'):
                 code, name = (c_property.split(None, 1) + [''])[:2]
                 code = code.lower()
         else:
             log.error("Required mixin not found: %s:%d (%s)", funct, num_args, rule[INDEX][rule[LINENO]])
 
+    def _do_import(self, rule, p_selectors, p_parents, p_children, scope, media, c_lineno, c_property, c_codestr, code, name):
+        """
+        Implements @import
+        Load and import mixins and functions and rules
+        """
+        i_codestr = None
+
+        #This is here for backward compatibility with tgext.scss version 2.
+        if 'url("/' in name or "url('/" in name:
+            match = self.import_re.match('@import ' + name)
+            if match:
+                name = match.group(1)
+
+        # Protect against going to prohibited places...
+        if '..' not in name and '://' not in name and 'url(' not in name:
+            names = name.split(',')
+            for name in names:
+                name = dequote(name.strip())
+
+                #Avoid making possible to import files from root
+                if name[0] == '/':
+                    name = name[1:]
+
+                if '@import ' + name not in rule[OPTIONS]:  # If already imported in this scope, skip...
+                    filename = os.path.basename(name)
+                    dirname = os.path.dirname(name)
+                    load_paths = []
+                    i_codestr = None
+                    for path in ['./'] + self._scss_opts['root_path'].split(','):
+                        for basepath in ['./', os.path.dirname(rule[PATH])]:
+                            i_codestr = None
+                            full_path = os.path.realpath(os.path.join(path, basepath, dirname))
+                            if full_path not in load_paths:
+                                try:
+                                    full_filename = os.path.join(full_path, '_' + filename + '.scss')
+                                    i_codestr = open(full_filename).read()
+                                except:
+                                    try:
+                                        full_filename = os.path.join(full_path, filename + '.scss')
+                                        i_codestr = open(full_filename).read()
+                                    except:
+                                        try:
+                                            full_filename = os.path.join(full_path, '_' + filename)
+                                            i_codestr = open(full_filename).read()
+                                        except:
+                                            try:
+                                                full_filename = os.path.join(full_path, filename)
+                                                i_codestr = open(full_filename).read()
+                                            except:
+                                                pass
+                                if i_codestr is not None:
+                                    break
+                                else:
+                                    load_paths.append(full_path)
+                        if i_codestr is not None:
+                            break
+                    i_codestr = i_codestr and self.load_string(i_codestr, full_filename)
+                    if i_codestr is None:
+                        log.warn("File to import not found or unreadable: '%s'\nLoad paths:\n\t%s", filename, "\n\t".join(load_paths))
+                    else:
+                        _rule = spawn_rule(rule, codestr=i_codestr, path=full_filename, lineno=c_lineno)
+                        self.manage_children(_rule, p_selectors, p_parents, p_children, scope, media)
+                        rule[OPTIONS]['@import ' + name] = True
+        else:
+            rule[PROPERTIES].append((c_lineno, c_property, None))
+
     @print_timing(10)
     def _do_if(self, rule, p_selectors, p_parents, p_children, scope, media, c_lineno, c_property, c_codestr, code, name):
         """

File tgext/scss/middleware.py

-import os, os.path, re
+import os, os.path
 from webob import Request, Response
 from webob.exc import status_map
 from compiler import Scss
 from tg import config
 
 class SCSSMiddleware(object):
-    import_re = re.compile(r'^\s*@import +url\(\s*["\']?([^\)\'\"]+)["\']?\s*\)\s*;?\s*$', re.MULTILINE)
-
     def __init__(self, app):
         self.app = app
         self.cache = {}
         except KeyError:
             self.root_directory = os.path.normcase(os.path.abspath((config['pylons.paths']['static_files'])))            
 
-    def convert(self, text, imports):
-        text = self.execute_imports(text, imports)
-
-        p = Scss(scss_opts=dict(compress=True))
+    def convert(self, text):
+        p = Scss(scss_opts=dict(compress=True, root_path=self.root_directory))
         return p.compile(text)
 
-    def parse_imports(self, src):
+    def get_scss_content(self, path):
+        f = open(path)
+        try:
+            return f.read()
+        finally:
+            f.close()
+
+    def parse_imports(self, file_path):
         result = []
         def child(obj):
-            result.append(self.full_path(obj.group(1)))
-        src = self.import_re.sub(child, src)
-        return src, result
-
-    def execute_imports(self, src, imports):
-        output = []
-        for path in imports:
-            f = open(path)
-            output.append(f.read())
-            f.close()
-        output.append(src)
-
-        return '\n'.join(output)
+            imported_path = self.full_path(obj.group(1))
+            result.extend(self.parse_imports(imported_path))
+            result.append(imported_path)
+        src = self.get_scss_content(file_path)
+        Scss.import_re.sub(child, src)
+        return result
 
     def full_path(self, path):
         if path[0] == '/':
         if not files:
             #We still don't know which files are imported, at least the first
             #time we must parse it.
-            content = open(full)
-            text, imports = self.parse_imports(content.read())
-            content.close()
+            imports = self.parse_imports(full)
             files = imports + [full]
         mtime = max([os.stat(f).st_mtime for f in files])
 
 
         cached_data = self.cache.get(requested_path)
         if not cached_data or cached_data['etag_key'] != etag_key:
-            content = open(full)
-            text, imports = self.parse_imports(content.read())
-            cached_data = {'content':self.convert(text, imports),
+            imports = self.parse_imports(full)
+            self.includes_cache[requested_path] = imports + [full]
+
+            cached_data = {'content':self.convert(self.get_scss_content(full)),
                            'etag_key':etag_key}
             self.cache[requested_path] = cached_data
-            self.includes_cache[requested_path] = imports + [full]
-            content.close()
 
         response = Response()
         response.content_type = 'text/css'