diff --git a/dev-req.txt b/dev-req.txt index 4e1c3aa..43f27b4 100644 --- a/dev-req.txt +++ b/dev-req.txt @@ -4,3 +4,4 @@ docutils mock nose pillow +pyscss \ No newline at end of file diff --git a/hyde/ext/plugins/css.py b/hyde/ext/plugins/css.py index ae06e8e..10c4c01 100644 --- a/hyde/ext/plugins/css.py +++ b/hyde/ext/plugins/css.py @@ -7,6 +7,7 @@ CSS plugins from hyde.plugin import CLTransformer, Plugin from hyde.exceptions import HydeException +import os import re import subprocess @@ -306,3 +307,90 @@ class CleverCSSPlugin(Plugin): return self.clevercss.convert(text, self.settings) +# +# Sassy CSS +# + +class SassyCSSPlugin(Plugin): + """ + The plugin class for SassyCSS + """ + + def __init__(self, site): + super(SassyCSSPlugin, self).__init__(site) + try: + import scss + except ImportError, e: + raise HydeException('Unable to import pyScss: ' + e.message) + else: + self.scss = scss + + def _should_parse_resource(self, resource): + """ + Check user defined + """ + return resource.source_file.kind == 'scss' and \ + getattr(resource, 'meta', {}).get('parse', True) + + @property + def options(self): + """ + Returns options depending on development mode + """ + try: + mode = self.site.config.mode + except AttributeError: + mode = "production" + + debug = mode.startswith('dev') + opts = {'compress': not debug, 'debug_info': debug} + site_opts = self.settings.get('options', {}) + opts.update(site_opts) + return opts + + @property + def vars(self): + """ + Returns scss variables. + """ + return self.settings.get('vars', {}) + + @property + def includes(self): + """ + Returns scss load paths. + """ + return self.settings.get('includes', []) + + + def begin_site(self): + """ + Find all the sassycss files and set their relative deploy path. + """ + self.scss.STATIC_URL = self.site.content_url('/') + self.scss.STATIC_ROOT = self.site.config.content_root_path.path + self.scss.ASSETS_URL = self.site.media_url('/') + self.scss.ASSETS_ROOT = self.site.config.deploy_root_path.child( + self.site.config.media_root) + + for resource in self.site.content.walk_resources(): + if self._should_parse_resource(resource): + new_name = resource.source_file.name_without_extension + ".css" + target_folder = File(resource.relative_deploy_path).parent + resource.relative_deploy_path = target_folder.child(new_name) + + def text_resource_complete(self, resource, text): + """ + Run sassycss compiler on text. + """ + if not self._should_parse_resource(resource): + return + + includes = [resource.node.path] + self.includes + includes = [path.rstrip(os.sep) + os.sep for path in includes] + options = self.options + if not 'load_paths' in options: + options['load_paths'] = [] + options['load_paths'].extend(includes) + scss = self.scss.Scss(scss_opts=options, scss_vars=self.vars ) + return scss.compile(text) diff --git a/hyde/tests/ext/scss/expected-site.css b/hyde/tests/ext/scss/expected-site.css index 28c7b3c..63edbe8 100644 --- a/hyde/tests/ext/scss/expected-site.css +++ b/hyde/tests/ext/scss/expected-site.css @@ -16,4 +16,3 @@ -moz-border-radius: 10px; border-radius: 10px; } - diff --git a/hyde/tests/ext/test_scss.py b/hyde/tests/ext/test_scss.py index 5cdb06f..b3dae5b 100644 --- a/hyde/tests/ext/test_scss.py +++ b/hyde/tests/ext/test_scss.py @@ -4,10 +4,11 @@ Use nose `$ pip install nose` `$ nosetests` """ -from hyde.fs import File, Folder -from hyde.model import Expando from hyde.generator import Generator from hyde.site import Site +from hyde.tests.util import assert_no_diff + +from fswrap import File, Folder SCSS_SOURCE = File(__file__).parent.child_folder('scss') TEST_SITE = File(__file__).parent.parent.child_folder('_test') @@ -26,8 +27,10 @@ class TestSassyCSS(object): def tearDown(self): TEST_SITE.delete() + def test_scss(self): s = Site(TEST_SITE) + s.config.mode = 'prod' s.config.plugins = ['hyde.ext.plugins.css.SassyCSSPlugin'] source = TEST_SITE.child('content/media/css/site.scss') target = File(Folder(s.config.deploy_root_path).child('media/css/site.css')) @@ -37,7 +40,5 @@ class TestSassyCSS(object): assert target.exists text = target.read_all() expected_text = File(SCSS_SOURCE.child('expected-site.css')).read_all() - - assert text == expected_text - return + assert_no_diff(expected_text, text) diff --git a/hyde/tests/util.py b/hyde/tests/util.py index 03a8f2c..563babf 100644 --- a/hyde/tests/util.py +++ b/hyde/tests/util.py @@ -1,4 +1,5 @@ import re +import difflib def strip_spaces_between_tags(value): """ @@ -7,6 +8,13 @@ def strip_spaces_between_tags(value): """ return re.sub(r'>\s+<', '><', unicode(value)) +def assert_no_diff(expected, out): + diff = [l for l in difflib.unified_diff(expected.splitlines(True), + out.splitlines(True), + n=3)] + assert not diff, ''.join(diff) + + def assert_html_equals(expected, actual, sanitize=None): expected = strip_spaces_between_tags(expected.strip()) actual = strip_spaces_between_tags(actual.strip())