@@ -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() | |||
@property | |||
def defaults(self): | |||
""" | |||
Default command line options. Can be overridden | |||
@@ -266,10 +267,12 @@ class CLTransformer(Plugin): | |||
Given a list of supported arguments, consutructs an argument | |||
list that could be passed on to the call_app function. | |||
""" | |||
args = {} | |||
args.update(self.defaults) | |||
try: | |||
args = getattr(self.settings, 'args').to_dict() | |||
args.update(getattr(self.settings, 'args').to_dict()) | |||
except AttributeError: | |||
args = {} | |||
pass | |||
result = [] | |||
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): | |||
s = Site(TEST_SITE) | |||
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.make() | |||
UGLIFY_SOURCE.copy_contents_to(JS) | |||
def tearDown(self): | |||
TEST_SITE.delete() | |||
@@ -36,7 +37,7 @@ class TestLess(object): | |||
assert False, "Cannot find the uglify executable" | |||
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') | |||
target = File(Folder(s.config.deploy_root_path).child('media/js/jquery.js')) | |||
gen = Generator(s) | |||
@@ -57,7 +58,7 @@ class TestLess(object): | |||
assert False, "Cannot find the uglify executable" | |||
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') | |||
target = File(Folder(s.config.deploy_root_path).child('media/js/jquery.js')) | |||
gen = Generator(s) | |||