| @@ -0,0 +1,102 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| """ | |||||
| Less css plugin | |||||
| """ | |||||
| from hyde.plugin import CLTransformer | |||||
| from hyde.fs import File | |||||
| import re | |||||
| import subprocess | |||||
| class StylusPlugin(CLTransformer): | |||||
| """ | |||||
| The plugin class for less css | |||||
| """ | |||||
| def __init__(self, site): | |||||
| super(StylusPlugin, self).__init__(site) | |||||
| def begin_text_resource(self, resource, text): | |||||
| """ | |||||
| Replace @import statements with {% include %} statements. | |||||
| """ | |||||
| if not resource.source_file.kind == 'styl': | |||||
| return | |||||
| import_finder = re.compile( | |||||
| '^\\s*@import\s+(?:\'|\")([^\'\"]*)(?:\'|\")\s*\;?\s*$', | |||||
| re.MULTILINE) | |||||
| def import_to_include(match): | |||||
| """ | |||||
| Converts a css import statement to include statemnt. | |||||
| """ | |||||
| if not match.lastindex: | |||||
| return '' | |||||
| path = match.groups(1)[0] | |||||
| afile = File(resource.source_file.parent.child(path)) | |||||
| if len(afile.kind.strip()) == 0: | |||||
| afile = File(afile.path + '.styl') | |||||
| ref = self.site.content.resource_from_path(afile.path) | |||||
| if not ref: | |||||
| raise self.template.exception_class( | |||||
| "Cannot import from path [%s]" % afile.path) | |||||
| ref.is_processable = False | |||||
| return "\n" + \ | |||||
| self.template.get_include_statement(ref.relative_path) + \ | |||||
| "\n" | |||||
| text = import_finder.sub(import_to_include, text) | |||||
| return text | |||||
| @property | |||||
| def defaults(self): | |||||
| """ | |||||
| Returns `compress` if not in development mode. | |||||
| """ | |||||
| try: | |||||
| mode = self.site.config.mode | |||||
| except AttributeError: | |||||
| mode = "production" | |||||
| defaults = {"compress":""} | |||||
| if mode.startswith('dev'): | |||||
| defaults = {} | |||||
| return defaults | |||||
| @property | |||||
| def plugin_name(self): | |||||
| """ | |||||
| The name of the plugin. | |||||
| """ | |||||
| return "stylus" | |||||
| def text_resource_complete(self, resource, text): | |||||
| """ | |||||
| Save the file to a temporary place and run stylus compiler. | |||||
| Read the generated file and return the text as output. | |||||
| Set the target path to have a css extension. | |||||
| """ | |||||
| if not resource.source_file.kind == 'styl': | |||||
| return | |||||
| stylus = self.app | |||||
| source = File.make_temp(text) | |||||
| target = source | |||||
| supported = [("compress", "C")] | |||||
| args = [str(stylus)] | |||||
| args.extend(self.process_args(supported)) | |||||
| args.append(str(source)) | |||||
| try: | |||||
| self.call_app(args) | |||||
| except subprocess.CalledProcessError: | |||||
| raise self.template.exception_class( | |||||
| "Cannot process %s. Error occurred when " | |||||
| "processing [%s]" % (stylus.name, resource.source_file)) | |||||
| out = target.read_all() | |||||
| new_name = resource.source_file.name_without_extension + ".css" | |||||
| target_folder = File(resource.relative_path).parent | |||||
| resource.relative_deploy_path = target_folder.child(new_name) | |||||
| return out | |||||
| @@ -209,6 +209,7 @@ class CLTransformer(Plugin): | |||||
| return self.__class__.__name__.replace('Plugin', '').lower() | return self.__class__.__name__.replace('Plugin', '').lower() | ||||
| @property | |||||
| def defaults(self): | def defaults(self): | ||||
| """ | """ | ||||
| Default command line options. Can be overridden | Default command line options. Can be overridden | ||||
| @@ -266,10 +267,12 @@ class CLTransformer(Plugin): | |||||
| Given a list of supported arguments, consutructs an argument | Given a list of supported arguments, consutructs an argument | ||||
| list that could be passed on to the call_app function. | list that could be passed on to the call_app function. | ||||
| """ | """ | ||||
| args = {} | |||||
| args.update(self.defaults) | |||||
| try: | try: | ||||
| args = getattr(self.settings, 'args').to_dict() | |||||
| args.update(getattr(self.settings, 'args').to_dict()) | |||||
| except AttributeError: | except AttributeError: | ||||
| args = {} | |||||
| pass | |||||
| result = [] | result = [] | ||||
| for arg in supported: | for arg in supported: | ||||
| @@ -0,0 +1,18 @@ | |||||
| * { | |||||
| border: 0; | |||||
| padding: 0; | |||||
| margin: 0; | |||||
| } | |||||
| #header { | |||||
| color: #333; | |||||
| border-left: 1px; | |||||
| border-right: 2px; | |||||
| } | |||||
| #footer { | |||||
| color: #444; | |||||
| } | |||||
| #content { | |||||
| -webkit-border-radius: 10px; | |||||
| -moz-border-radius: 10px; | |||||
| border-radius: 10px; | |||||
| } | |||||
| @@ -0,0 +1,4 @@ | |||||
| rounded(radius = 5px) | |||||
| -webkit-border-radius radius | |||||
| -moz-border-radius radius | |||||
| border-radius radius | |||||
| @@ -0,0 +1,5 @@ | |||||
| * { | |||||
| border: 0; | |||||
| padding: 0; | |||||
| margin: 0; | |||||
| } | |||||
| @@ -0,0 +1,2 @@ | |||||
| the-border = 1px | |||||
| base-color = #111 | |||||
| @@ -0,0 +1,14 @@ | |||||
| @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 | |||||
| rounded 10px | |||||
| @@ -29,20 +29,20 @@ class TestLess(object): | |||||
| def test_can_execute_less(self): | def test_can_execute_less(self): | ||||
| s = Site(TEST_SITE) | s = Site(TEST_SITE) | ||||
| s.config.plugins = ['hyde.ext.plugins.less.LessCSSPlugin'] | s.config.plugins = ['hyde.ext.plugins.less.LessCSSPlugin'] | ||||
| less_paths = ['/usr/local/share/npm/bin/lessc', '~/local/bin/lessc'] | |||||
| for path in less_paths: | |||||
| if File(path).exists: | |||||
| s.config.less = Expando(dict(app=path)) | |||||
| source = TEST_SITE.child('content/media/css/site.less') | |||||
| target = File(Folder(s.config.deploy_root_path).child('media/css/site.css')) | |||||
| gen = Generator(s) | |||||
| gen.generate_resource_at_path(source) | |||||
| assert target.exists | |||||
| text = target.read_all() | |||||
| expected_text = File(LESS_SOURCE.child('expected-site.css')).read_all() | |||||
| assert text == expected_text | |||||
| return | |||||
| assert "Cannot find the less css executable" | |||||
| paths = ['/usr/local/share/npm/bin/lessc', '~/local/bin/lessc'] | |||||
| less = [path for path in paths if File(path).exists] | |||||
| if not less: | |||||
| assert False, "Cannot find the uglify executable" | |||||
| less = less[0] | |||||
| s.config.less = Expando(dict(app=less)) | |||||
| source = TEST_SITE.child('content/media/css/site.less') | |||||
| target = File(Folder(s.config.deploy_root_path).child('media/css/site.css')) | |||||
| gen = Generator(s) | |||||
| gen.generate_resource_at_path(source) | |||||
| assert target.exists | |||||
| text = target.read_all() | |||||
| expected_text = File(LESS_SOURCE.child('expected-site.css')).read_all() | |||||
| assert text == expected_text | |||||
| return | |||||
| @@ -0,0 +1,47 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| """ | |||||
| 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 | |||||
| STYLUS_SOURCE = File(__file__).parent.child_folder('stylus') | |||||
| TEST_SITE = File(__file__).parent.parent.child_folder('_test') | |||||
| class TestLess(object): | |||||
| def setUp(self): | |||||
| TEST_SITE.make() | |||||
| TEST_SITE.parent.child_folder( | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| STYLUS_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_can_execute_stylus(self): | |||||
| s = Site(TEST_SITE) | |||||
| s.config.plugins = ['hyde.ext.plugins.stylus.StylusPlugin'] | |||||
| paths = ['/usr/local/share/npm/bin/stylus', '~/local/bin/stylus'] | |||||
| stylus = [path for path in paths if File(path).exists] | |||||
| if not stylus: | |||||
| assert False, "Cannot find the stylus executable" | |||||
| stylus = stylus[0] | |||||
| s.config.stylus = Expando(dict(app=stylus)) | |||||
| source = TEST_SITE.child('content/media/css/site.styl') | |||||
| target = File(Folder(s.config.deploy_root_path).child('media/css/site.css')) | |||||
| gen = Generator(s) | |||||
| gen.generate_resource_at_path(source) | |||||
| assert target.exists | |||||
| text = target.read_all() | |||||
| expected_text = File(STYLUS_SOURCE.child('expected-site.css')).read_all() | |||||
| assert text.strip() == expected_text.strip() | |||||
| @@ -22,6 +22,7 @@ class TestLess(object): | |||||
| JS = TEST_SITE.child_folder('content/media/js') | JS = TEST_SITE.child_folder('content/media/js') | ||||
| JS.make() | JS.make() | ||||
| UGLIFY_SOURCE.copy_contents_to(JS) | UGLIFY_SOURCE.copy_contents_to(JS) | ||||
| def tearDown(self): | def tearDown(self): | ||||
| TEST_SITE.delete() | TEST_SITE.delete() | ||||
| @@ -36,7 +37,7 @@ class TestLess(object): | |||||
| assert False, "Cannot find the uglify executable" | assert False, "Cannot find the uglify executable" | ||||
| uglify = uglify[0] | uglify = uglify[0] | ||||
| s.config.uglify = Expando(dict(app=path)) | |||||
| s.config.uglify = Expando(dict(app=uglify)) | |||||
| source = TEST_SITE.child('content/media/js/jquery.js') | source = TEST_SITE.child('content/media/js/jquery.js') | ||||
| target = File(Folder(s.config.deploy_root_path).child('media/js/jquery.js')) | target = File(Folder(s.config.deploy_root_path).child('media/js/jquery.js')) | ||||
| gen = Generator(s) | gen = Generator(s) | ||||
| @@ -57,7 +58,7 @@ class TestLess(object): | |||||
| assert False, "Cannot find the uglify executable" | assert False, "Cannot find the uglify executable" | ||||
| uglify = uglify[0] | uglify = uglify[0] | ||||
| s.config.uglify = Expando(dict(app=path, args={"nc":""})) | |||||
| s.config.uglify = Expando(dict(app=uglify, args={"nc":""})) | |||||
| source = TEST_SITE.child('content/media/js/jquery.js') | source = TEST_SITE.child('content/media/js/jquery.js') | ||||
| target = File(Folder(s.config.deploy_root_path).child('media/js/jquery.js')) | target = File(Folder(s.config.deploy_root_path).child('media/js/jquery.js')) | ||||
| gen = Generator(s) | gen = Generator(s) | ||||