From 8c3d5852f3a20dae8ee8402b4363ddc2c51b8ec7 Mon Sep 17 00:00:00 2001 From: Jordi Llonch Date: Mon, 24 Aug 2015 14:31:34 +1000 Subject: [PATCH] initial libsass support --- dev-req.txt | 1 + hyde/ext/plugins/css.py | 90 +++++++++++++++++++++++++++ hyde/tests/ext/scss/expected-sass.css | 1 + hyde/tests/ext/scss/sass.scss | 18 ++++++ hyde/tests/ext/test_sass.py | 47 ++++++++++++++ 5 files changed, 157 insertions(+) create mode 100644 hyde/tests/ext/scss/expected-sass.css create mode 100644 hyde/tests/ext/scss/sass.scss create mode 100644 hyde/tests/ext/test_sass.py diff --git a/dev-req.txt b/dev-req.txt index c1df4d1..9c8dbd3 100644 --- a/dev-req.txt +++ b/dev-req.txt @@ -5,4 +5,5 @@ mock==1.0.1 nose==1.3.6 Pillow==2.7.0 pyScss==1.3.4 +libsass==0.8.2 flake8==2.4.1 \ No newline at end of file diff --git a/hyde/ext/plugins/css.py b/hyde/ext/plugins/css.py index a5a6585..74cf0f9 100644 --- a/hyde/ext/plugins/css.py +++ b/hyde/ext/plugins/css.py @@ -402,3 +402,93 @@ class SassyCSSPlugin(Plugin): options['load_paths'].extend(includes) scss = self.scss.Scss(scss_opts=options, scss_vars=self.vars) return scss.compile(text) + + +# +# Sass CSS +# + + +class SassPlugin(Plugin): + + """ + The plugin class for LibSass + """ + + def __init__(self, site): + super(SassPlugin, self).__init__(site) + try: + import sass + except ImportError, e: + raise HydeException('Unable to import libsass: ' + e.message) + else: + self.sass = sass + self.resources = [] + + def _should_parse_resource(self, resource): + """ + Check user defined + """ + files = self.site.config.get("sass", {}).get("files", []) + return resource.source_file.kind == 'scss' and \ + resource.relative_path in files + + @property + def options(self): + """ + Returns options depending on development mode + """ + try: + mode = self.site.config.mode + except AttributeError: + mode = "production" + + if 'sass' in self.site.config and \ + 'output_style' in self.site.config.sass: + output_style = self.site.config.sass.output_style + else: + debug = mode.startswith('dev') + output_style = 'compressed' if not debug else 'nested' + + opts = {'output_style': output_style} + site_opts = self.settings.get('options', {}) + opts.update(site_opts) + return opts + + @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. + """ + 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) + self.resources.append(resource.relative_path) + + def text_resource_complete(self, resource, text): + """ + Run sassycss compiler on text. + """ + if resource.relative_path not in self.resources: + return + + includes = [resource.node.path] + self.includes + includes = [path.rstrip(os.sep) + os.sep for path in includes] + options = self.options + if 'include_paths' not in options: + options['include_paths'] = [] + options['include_paths'].extend(includes) + self.logger.error(resource) + try: + return self.sass.compile(string=text, **options) + except Exception, exc: + self.logger.error(exc) + raise diff --git a/hyde/tests/ext/scss/expected-sass.css b/hyde/tests/ext/scss/expected-sass.css new file mode 100644 index 0000000..26ae492 --- /dev/null +++ b/hyde/tests/ext/scss/expected-sass.css @@ -0,0 +1 @@ +@import url(inc/reset.css);#header{color:#333;border-left:1px;border-right:2px}#footer{color:#333}#content{-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px} diff --git a/hyde/tests/ext/scss/sass.scss b/hyde/tests/ext/scss/sass.scss new file mode 100644 index 0000000..b7f53f7 --- /dev/null +++ b/hyde/tests/ext/scss/sass.scss @@ -0,0 +1,18 @@ +@import "inc/mixin"; +@import "inc/vars"; +@import "inc/reset.css"; + +#header { + color: $base-color * 3; + border-left: $the-border; + border-right: $the-border * 2; +} + +#footer { + color: ($base-color + #111) * 1.5; +} + +#content { + @include rounded(10px); +} + diff --git a/hyde/tests/ext/test_sass.py b/hyde/tests/ext/test_sass.py new file mode 100644 index 0000000..e748c57 --- /dev/null +++ b/hyde/tests/ext/test_sass.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +""" +Use nose +`$ pip install nose` +`$ nosetests` +""" +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') + + +class TestSass(object): + + def setUp(self): + TEST_SITE.make() + TEST_SITE.parent.child_folder( + 'sites/test_jinja').copy_contents_to(TEST_SITE) + SCSS_SOURCE.copy_contents_to(TEST_SITE.child('content/media/css')) + File(TEST_SITE.child('content/media/css/site.css')).delete() + + 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.SassPlugin'] + s.config.sass = {'files': ['media/css/sass.scss']} + source = TEST_SITE.child('content/media/css/sass.scss') + target = File( + Folder(s.config.deploy_root_path).child('media/css/sass.css')) + gen = Generator(s) + gen.generate_resource_at_path(source) + + assert target.exists + text = target.read_all() + expected_text = File(SCSS_SOURCE.child('expected-sass.css')).read_all() + print "TEXT" + "-" * 80 + print text + print "-" * 80 + print expected_text + assert_no_diff(expected_text, text)