@@ -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) | ||||