A fork of hyde, the static site generation. Some patches will be pushed upstream.
 
 
 

222 lines
6.9 KiB

  1. # -*- coding: utf-8 -*-
  2. """
  3. CSS plugins
  4. """
  5. from hyde.plugin import CLTransformer
  6. import re
  7. import subprocess
  8. from fswrap import File
  9. class LessCSSPlugin(CLTransformer):
  10. """
  11. The plugin class for less css
  12. """
  13. def __init__(self, site):
  14. super(LessCSSPlugin, self).__init__(site)
  15. @property
  16. def executable_name(self):
  17. return "lessc"
  18. def _should_parse_resource(self, resource):
  19. """
  20. Check user defined
  21. """
  22. return resource.source_file.kind == 'less' and \
  23. getattr(resource, 'meta', {}).get('parse', True)
  24. def _should_replace_imports(self, resource):
  25. return getattr(resource, 'meta', {}).get('uses_template', True)
  26. def begin_site(self):
  27. """
  28. Find all the less css files and set their relative deploy path.
  29. """
  30. for resource in self.site.content.walk_resources():
  31. if self._should_parse_resource(resource):
  32. new_name = resource.source_file.name_without_extension + ".css"
  33. target_folder = File(resource.relative_deploy_path).parent
  34. resource.relative_deploy_path = target_folder.child(new_name)
  35. def begin_text_resource(self, resource, text):
  36. """
  37. Replace @import statements with {% include %} statements.
  38. """
  39. if not self._should_parse_resource(resource) or \
  40. not self._should_replace_imports(resource):
  41. return text
  42. import_finder = re.compile(
  43. '^\\s*@import\s+(?:\'|\")([^\'\"]*)(?:\'|\")\s*\;\s*$',
  44. re.MULTILINE)
  45. def import_to_include(match):
  46. if not match.lastindex:
  47. return ''
  48. path = match.groups(1)[0]
  49. afile = File(resource.source_file.parent.child(path))
  50. if len(afile.kind.strip()) == 0:
  51. afile = File(afile.path + '.less')
  52. ref = self.site.content.resource_from_path(afile.path)
  53. if not ref:
  54. raise self.template.exception_class(
  55. "Cannot import from path [%s]" % afile.path)
  56. ref.is_processable = False
  57. return self.template.get_include_statement(ref.relative_path)
  58. text = import_finder.sub(import_to_include, text)
  59. return text
  60. @property
  61. def plugin_name(self):
  62. """
  63. The name of the plugin.
  64. """
  65. return "less"
  66. def text_resource_complete(self, resource, text):
  67. """
  68. Save the file to a temporary place and run less compiler.
  69. Read the generated file and return the text as output.
  70. Set the target path to have a css extension.
  71. """
  72. if not self._should_parse_resource(resource):
  73. return
  74. supported = [
  75. "verbose",
  76. ("silent", "s"),
  77. ("compress", "x"),
  78. "O0",
  79. "O1",
  80. "O2",
  81. "include-path="
  82. ]
  83. less = self.app
  84. source = File.make_temp(text)
  85. target = File.make_temp('')
  86. args = [unicode(less)]
  87. args.extend(self.process_args(supported))
  88. args.extend([unicode(source), unicode(target)])
  89. try:
  90. self.call_app(args)
  91. except subprocess.CalledProcessError:
  92. raise self.template.exception_class(
  93. "Cannot process %s. Error occurred when "
  94. "processing [%s]" % (self.app.name, resource.source_file))
  95. return target.read_all()
  96. class StylusPlugin(CLTransformer):
  97. """
  98. The plugin class for stylus css
  99. """
  100. def __init__(self, site):
  101. super(StylusPlugin, self).__init__(site)
  102. def begin_site(self):
  103. """
  104. Find all the styl files and set their relative deploy path.
  105. """
  106. for resource in self.site.content.walk_resources():
  107. if resource.source_file.kind == 'styl':
  108. new_name = resource.source_file.name_without_extension + ".css"
  109. target_folder = File(resource.relative_deploy_path).parent
  110. resource.relative_deploy_path = target_folder.child(new_name)
  111. def begin_text_resource(self, resource, text):
  112. """
  113. Replace @import statements with {% include %} statements.
  114. """
  115. if not resource.source_file.kind == 'styl':
  116. return
  117. import_finder = re.compile(
  118. '^\\s*@import\s+(?:\'|\")([^\'\"]*)(?:\'|\")\s*\;?\s*$',
  119. re.MULTILINE)
  120. def import_to_include(match):
  121. """
  122. Converts a css import statement to include statemnt.
  123. """
  124. if not match.lastindex:
  125. return ''
  126. path = match.groups(1)[0]
  127. afile = File(File(resource.source_file.parent.child(path)).fully_expanded_path)
  128. if len(afile.kind.strip()) == 0:
  129. afile = File(afile.path + '.styl')
  130. ref = self.site.content.resource_from_path(afile.path)
  131. if not ref:
  132. try:
  133. include = self.settings.args.include
  134. except AttributeError:
  135. include = False
  136. if not include:
  137. raise self.template.exception_class(
  138. "Cannot import from path [%s]" % afile.path)
  139. else:
  140. ref.is_processable = False
  141. return "\n" + \
  142. self.template.get_include_statement(ref.relative_path) + \
  143. "\n"
  144. return '@import "' + path + '"\n'
  145. text = import_finder.sub(import_to_include, text)
  146. return text
  147. @property
  148. def defaults(self):
  149. """
  150. Returns `compress` if not in development mode.
  151. """
  152. try:
  153. mode = self.site.config.mode
  154. except AttributeError:
  155. mode = "production"
  156. defaults = {"compress":""}
  157. if mode.startswith('dev'):
  158. defaults = {}
  159. return defaults
  160. @property
  161. def plugin_name(self):
  162. """
  163. The name of the plugin.
  164. """
  165. return "stylus"
  166. def text_resource_complete(self, resource, text):
  167. """
  168. Save the file to a temporary place and run stylus compiler.
  169. Read the generated file and return the text as output.
  170. Set the target path to have a css extension.
  171. """
  172. if not resource.source_file.kind == 'styl':
  173. return
  174. stylus = self.app
  175. source = File.make_temp(text.strip())
  176. target = source
  177. supported = [("compress", "c"), ("include", "I")]
  178. args = [unicode(stylus)]
  179. args.extend(self.process_args(supported))
  180. args.append(unicode(source))
  181. try:
  182. self.call_app(args)
  183. except subprocess.CalledProcessError, e:
  184. raise self.template.exception_class(
  185. "Cannot process %s. Error occurred when "
  186. "processing [%s]" % (stylus.name, resource.source_file))
  187. return target.read_all()