diff options
author | Rocky Meza <rocky@fusionbox.com> | 2014-02-03 17:54:27 -0700 |
---|---|---|
committer | Rocky Meza <rocky@fusionbox.com> | 2014-02-03 17:54:27 -0700 |
commit | 53d023e45357516840ff28fa30df98ce14639111 (patch) | |
tree | ef1b194c8dd1a3b15db2af71abacf6349a169ed6 | |
parent | ae411e0b9723c1d63d8326c492bfdd5833c14094 (diff) | |
download | django-pyscss-53d023e45357516840ff28fa30df98ce14639111.tar.gz |
Reworked the relative/absolute import system.
Now, it works more like HTML URL resolution, where any URL is relative
unless it starts with a /.
-rw-r--r-- | README.rst | 4 | ||||
-rw-r--r-- | django_pyscss/compressor.py | 13 | ||||
-rw-r--r-- | django_pyscss/scss.py | 69 | ||||
-rw-r--r-- | testproject/testapp2/static/css/app2.scss | 4 | ||||
-rw-r--r-- | testproject/testproject/static/css/bar.scss | 2 | ||||
-rw-r--r-- | tests/test_compressor.py | 15 | ||||
-rw-r--r-- | tests/test_scss.py | 31 |
7 files changed, 85 insertions, 53 deletions
@@ -41,9 +41,7 @@ You can render SCSS from a file like this:: compiler.compile(scss_file='css/styles.scss') The file needs to be able to be located by staticfiles finders in order to be -used. All imports are relative to the ``STATIC_ROOT``, but you can also have -relative imports from a file. If you prefix an import with ``./``, you can -import a sibling file without having to write out the whole import path. +used. .. class:: django_pyscss.scss.DjangoScss diff --git a/django_pyscss/compressor.py b/django_pyscss/compressor.py index ab13f77..6ec9e31 100644 --- a/django_pyscss/compressor.py +++ b/django_pyscss/compressor.py @@ -3,6 +3,7 @@ from __future__ import absolute_import import os from compressor.filters import FilterBase +from compressor.conf import settings from django_pyscss.scss import DjangoScss, config @@ -14,8 +15,18 @@ class DjangoScssFilter(FilterBase): # It looks like there is a bug in django-compressor because it expects # us to accept attrs. super(DjangoScssFilter, self).__init__(content, filter_type, filename) + try: + # this is a link tag which means there is an SCSS file being + # referenced. + href = attrs['href'] + except KeyError: + # this is a style tag which means this is inline SCSS. + self.relative_to = None + else: + self.relative_to = os.path.dirname(href.replace(settings.STATIC_URL, '')) def input(self, **kwargs): if not os.path.exists(config.ASSETS_ROOT): os.makedirs(config.ASSETS_ROOT) - return self.compiler.compile(self.content) + return self.compiler.compile(scss_string=self.content, + relative_to=self.relative_to) diff --git a/django_pyscss/scss.py b/django_pyscss/scss.py index 03e6c57..3423b2c 100644 --- a/django_pyscss/scss.py +++ b/django_pyscss/scss.py @@ -4,7 +4,6 @@ import os from django.contrib.staticfiles.storage import staticfiles_storage from django.conf import settings -from django.core.exceptions import SuspiciousFileOperation from scss import ( Scss, dequote, log, SourceFile, SassRule, config, @@ -48,26 +47,39 @@ class DjangoScss(Scss): else: return self.get_file_from_storage(filename) - def _find_source_file(self, name): - file_and_storage = self.get_file_and_storage(name) - if file_and_storage is None: - return None - else: - full_filename, storage = file_and_storage - if name not in self.source_files: - with storage.open(full_filename) as f: - source = f.read() - - source_file = SourceFile( - full_filename, - source, - ) - # SourceFile.__init__ calls os.path.realpath on this, we don't want - # that. - source_file.parent_dir = os.path.dirname(name) - self.source_files.append(source_file) - self.source_file_index[full_filename] = source_file - return self.source_file_index[full_filename] + def get_possible_import_paths(self, filename, relative_to=None): + """ + Returns an iterable of possible filenames for an import. + + relative_to is None in the case that the SCSS is being rendered from a + string or if it is the first file. + """ + if filename.startswith('/'): # absolute import + filename = filename[1:] + elif relative_to: # relative import + filename = os.path.join(relative_to, filename) + + return [filename] + + def _find_source_file(self, filename, relative_to=None): + for name in self.get_possible_import_paths(filename, relative_to): + file_and_storage = self.get_file_and_storage(name) + if file_and_storage: + full_filename, storage = file_and_storage + if name not in self.source_files: + with storage.open(full_filename) as f: + source = f.read() + + source_file = SourceFile( + full_filename, + source, + ) + # SourceFile.__init__ calls os.path.realpath on this, we don't want + # that, we want them to remain relative. + source_file.parent_dir = os.path.dirname(name) + self.source_files.append(source_file) + self.source_file_index[full_filename] = source_file + return self.source_file_index[full_filename] def _do_import(self, rule, scope, block): """ @@ -83,10 +95,8 @@ class DjangoScss(Scss): for name in names: name = dequote(name.strip()) - if name.startswith('./'): - name = rule.source_file.parent_dir + name[1:] - - source_file = self._find_source_file(name) + relative_to = rule.source_file.parent_dir + source_file = self._find_source_file(name, relative_to) if source_file is None: i_codestr = self._do_magic_import(rule, scope, block) @@ -121,10 +131,12 @@ class DjangoScss(Scss): rule.namespace.add_import(import_key, rule.import_key, rule.file_and_line) self.manage_children(_rule, scope) - def Compilation(self, scss_string=None, scss_file=None, super_selector=None, filename=None, is_sass=None, line_numbers=True): + def Compilation(self, scss_string=None, scss_file=None, super_selector=None, + filename=None, is_sass=None, line_numbers=True, + relative_to=None): """ Overwritten to call _find_source_file instead of - SourceFile.from_filename. + SourceFile.from_filename. Also added the relative_to option. """ if super_selector: self.super_selector = super_selector + ' ' @@ -133,6 +145,9 @@ class DjangoScss(Scss): source_file = None if scss_string is not None: source_file = SourceFile.from_string(scss_string, filename, is_sass, line_numbers) + # Set the parent_dir to be something meaningful instead of the + # current working directory, which is never correct for DjangoScss. + source_file.parent_dir = relative_to elif scss_file is not None: # Call _find_source_file instead of SourceFile.from_filename source_file = self._find_source_file(scss_file) diff --git a/testproject/testapp2/static/css/app2.scss b/testproject/testapp2/static/css/app2.scss index d19b910..a096082 100644 --- a/testproject/testapp2/static/css/app2.scss +++ b/testproject/testapp2/static/css/app2.scss @@ -1,2 +1,2 @@ -@import "css/foo.scss"; -@import "css/app1.scss"; +@import "foo.scss"; +@import "app1.scss"; diff --git a/testproject/testproject/static/css/bar.scss b/testproject/testproject/static/css/bar.scss index 448776a..08819e4 100644 --- a/testproject/testproject/static/css/bar.scss +++ b/testproject/testproject/static/css/bar.scss @@ -1 +1 @@ -@import "./foo.scss"; +@import "foo.scss"; diff --git a/tests/test_compressor.py b/tests/test_compressor.py index 4f8f64e..4ff6630 100644 --- a/tests/test_compressor.py +++ b/tests/test_compressor.py @@ -9,8 +9,23 @@ APP2_LINK_TAG = """ {% endcompress %} """ +IMPORT_APP2_STYLE_TAG = """ +{% load staticfiles compress %} +{% compress css %} +<style type="text/x-scss"> +@import "css/app2.scss"; +</style> +{% endcompress %} +""" + class CompressorTest(TestCase): def test_compressor_can_compile_scss(self): actual = Template(APP2_LINK_TAG).render(Context()) + # 4b368862ec8c is the cache key that compressor gives to the compiled + # version of app2.scss. + self.assertIn('4b368862ec8c.css', actual) + + def test_compressor_can_compile_scss_from_style_tag(self): + actual = Template(IMPORT_APP2_STYLE_TAG).render(Context()) self.assertIn('4b368862ec8c.css', actual) diff --git a/tests/test_scss.py b/tests/test_scss.py index 3178f6b..d538068 100644 --- a/tests/test_scss.py +++ b/tests/test_scss.py @@ -9,26 +9,12 @@ from django_pyscss.scss import DjangoScss from tests.utils import clean_css, CollectStaticTestCase -IMPORT_FOO = """ -@import "css/foo.scss"; -""" - with open(os.path.join(settings.BASE_DIR, 'testproject', 'static', 'css', 'foo.scss')) as f: FOO_CONTENTS = f.read() - -IMPORT_APP1 = """ -@import "css/app1.scss"; -""" - with open(os.path.join(settings.BASE_DIR, 'testapp1', 'static', 'css', 'app1.scss')) as f: APP1_CONTENTS = f.read() - -IMPORT_APP2 = """ -@import "css/app2.scss"; -""" - APP2_CONTENTS = FOO_CONTENTS + APP1_CONTENTS @@ -43,20 +29,27 @@ class CompilerTestMixin(object): class ImportTestMixin(CompilerTestMixin): def test_import_from_staticfiles_dirs(self): - actual = self.compiler.compile(scss_string=IMPORT_FOO) + actual = self.compiler.compile(scss_string='@import "/css/foo.scss";') + self.assertEqual(clean_css(actual), clean_css(FOO_CONTENTS)) + + def test_import_from_staticfiles_dirs_relative(self): + actual = self.compiler.compile(scss_string='@import "css/foo.scss";') self.assertEqual(clean_css(actual), clean_css(FOO_CONTENTS)) def test_import_from_app(self): - actual = self.compiler.compile(scss_string=IMPORT_APP1) + actual = self.compiler.compile(scss_string='@import "/css/app1.scss";') + self.assertEqual(clean_css(actual), clean_css(APP1_CONTENTS)) + + def test_import_from_app_relative(self): + actual = self.compiler.compile(scss_string='@import "css/app1.scss";') self.assertEqual(clean_css(actual), clean_css(APP1_CONTENTS)) def test_imports_within_file(self): - actual = self.compiler.compile(scss_string=IMPORT_APP2) + actual = self.compiler.compile(scss_string='@import "/css/app2.scss";') self.assertEqual(clean_css(actual), clean_css(APP2_CONTENTS)) def test_relative_import(self): - bar_scss = 'css/bar.scss' - actual = self.compiler.compile(scss_file=bar_scss) + actual = self.compiler.compile(scss_file='/css/bar.scss') self.assertEqual(clean_css(actual), clean_css(FOO_CONTENTS)) def test_bad_import(self): |