xass 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +11 -0
  3. data/CONTRIBUTING +3 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +201 -0
  6. data/Rakefile +349 -0
  7. data/VERSION +1 -0
  8. data/VERSION_NAME +1 -0
  9. data/bin/push +13 -0
  10. data/bin/sass +13 -0
  11. data/bin/sass-convert +12 -0
  12. data/bin/scss +13 -0
  13. data/extra/update_watch.rb +13 -0
  14. data/init.rb +18 -0
  15. data/lib/sass/cache_stores/base.rb +88 -0
  16. data/lib/sass/cache_stores/chain.rb +33 -0
  17. data/lib/sass/cache_stores/filesystem.rb +64 -0
  18. data/lib/sass/cache_stores/memory.rb +47 -0
  19. data/lib/sass/cache_stores/null.rb +25 -0
  20. data/lib/sass/cache_stores.rb +15 -0
  21. data/lib/sass/callbacks.rb +66 -0
  22. data/lib/sass/css.rb +409 -0
  23. data/lib/sass/engine.rb +930 -0
  24. data/lib/sass/environment.rb +101 -0
  25. data/lib/sass/error.rb +201 -0
  26. data/lib/sass/exec.rb +707 -0
  27. data/lib/sass/importers/base.rb +139 -0
  28. data/lib/sass/importers/filesystem.rb +186 -0
  29. data/lib/sass/importers.rb +22 -0
  30. data/lib/sass/logger/base.rb +32 -0
  31. data/lib/sass/logger/log_level.rb +49 -0
  32. data/lib/sass/logger.rb +15 -0
  33. data/lib/sass/media.rb +213 -0
  34. data/lib/sass/plugin/compiler.rb +406 -0
  35. data/lib/sass/plugin/configuration.rb +123 -0
  36. data/lib/sass/plugin/generic.rb +15 -0
  37. data/lib/sass/plugin/merb.rb +48 -0
  38. data/lib/sass/plugin/rack.rb +60 -0
  39. data/lib/sass/plugin/rails.rb +47 -0
  40. data/lib/sass/plugin/staleness_checker.rb +199 -0
  41. data/lib/sass/plugin.rb +133 -0
  42. data/lib/sass/railtie.rb +10 -0
  43. data/lib/sass/repl.rb +57 -0
  44. data/lib/sass/root.rb +7 -0
  45. data/lib/sass/script/arg_list.rb +52 -0
  46. data/lib/sass/script/bool.rb +18 -0
  47. data/lib/sass/script/color.rb +606 -0
  48. data/lib/sass/script/css_lexer.rb +29 -0
  49. data/lib/sass/script/css_parser.rb +31 -0
  50. data/lib/sass/script/funcall.rb +245 -0
  51. data/lib/sass/script/functions.rb +1543 -0
  52. data/lib/sass/script/interpolation.rb +79 -0
  53. data/lib/sass/script/lexer.rb +345 -0
  54. data/lib/sass/script/list.rb +85 -0
  55. data/lib/sass/script/literal.rb +221 -0
  56. data/lib/sass/script/node.rb +99 -0
  57. data/lib/sass/script/null.rb +37 -0
  58. data/lib/sass/script/number.rb +453 -0
  59. data/lib/sass/script/operation.rb +110 -0
  60. data/lib/sass/script/parser.rb +502 -0
  61. data/lib/sass/script/string.rb +51 -0
  62. data/lib/sass/script/string_interpolation.rb +103 -0
  63. data/lib/sass/script/unary_operation.rb +69 -0
  64. data/lib/sass/script/variable.rb +58 -0
  65. data/lib/sass/script.rb +39 -0
  66. data/lib/sass/scss/css_parser.rb +36 -0
  67. data/lib/sass/scss/parser.rb +1180 -0
  68. data/lib/sass/scss/rx.rb +133 -0
  69. data/lib/sass/scss/script_lexer.rb +15 -0
  70. data/lib/sass/scss/script_parser.rb +25 -0
  71. data/lib/sass/scss/static_parser.rb +54 -0
  72. data/lib/sass/scss.rb +16 -0
  73. data/lib/sass/selector/abstract_sequence.rb +94 -0
  74. data/lib/sass/selector/comma_sequence.rb +92 -0
  75. data/lib/sass/selector/sequence.rb +507 -0
  76. data/lib/sass/selector/simple.rb +119 -0
  77. data/lib/sass/selector/simple_sequence.rb +215 -0
  78. data/lib/sass/selector.rb +452 -0
  79. data/lib/sass/shared.rb +76 -0
  80. data/lib/sass/supports.rb +229 -0
  81. data/lib/sass/tree/charset_node.rb +22 -0
  82. data/lib/sass/tree/comment_node.rb +82 -0
  83. data/lib/sass/tree/content_node.rb +9 -0
  84. data/lib/sass/tree/css_import_node.rb +60 -0
  85. data/lib/sass/tree/debug_node.rb +18 -0
  86. data/lib/sass/tree/directive_node.rb +42 -0
  87. data/lib/sass/tree/each_node.rb +24 -0
  88. data/lib/sass/tree/extend_node.rb +36 -0
  89. data/lib/sass/tree/for_node.rb +36 -0
  90. data/lib/sass/tree/function_node.rb +34 -0
  91. data/lib/sass/tree/if_node.rb +52 -0
  92. data/lib/sass/tree/import_node.rb +75 -0
  93. data/lib/sass/tree/media_node.rb +58 -0
  94. data/lib/sass/tree/mixin_def_node.rb +38 -0
  95. data/lib/sass/tree/mixin_node.rb +39 -0
  96. data/lib/sass/tree/node.rb +196 -0
  97. data/lib/sass/tree/prop_node.rb +152 -0
  98. data/lib/sass/tree/return_node.rb +18 -0
  99. data/lib/sass/tree/root_node.rb +78 -0
  100. data/lib/sass/tree/rule_node.rb +132 -0
  101. data/lib/sass/tree/supports_node.rb +51 -0
  102. data/lib/sass/tree/trace_node.rb +32 -0
  103. data/lib/sass/tree/variable_node.rb +30 -0
  104. data/lib/sass/tree/visitors/base.rb +75 -0
  105. data/lib/sass/tree/visitors/check_nesting.rb +147 -0
  106. data/lib/sass/tree/visitors/convert.rb +316 -0
  107. data/lib/sass/tree/visitors/cssize.rb +241 -0
  108. data/lib/sass/tree/visitors/deep_copy.rb +102 -0
  109. data/lib/sass/tree/visitors/extend.rb +68 -0
  110. data/lib/sass/tree/visitors/perform.rb +446 -0
  111. data/lib/sass/tree/visitors/set_options.rb +125 -0
  112. data/lib/sass/tree/visitors/to_css.rb +228 -0
  113. data/lib/sass/tree/warn_node.rb +18 -0
  114. data/lib/sass/tree/while_node.rb +18 -0
  115. data/lib/sass/util/multibyte_string_scanner.rb +155 -0
  116. data/lib/sass/util/subset_map.rb +109 -0
  117. data/lib/sass/util/test.rb +10 -0
  118. data/lib/sass/util.rb +948 -0
  119. data/lib/sass/version.rb +126 -0
  120. data/lib/sass.rb +95 -0
  121. data/rails/init.rb +1 -0
  122. data/test/Gemfile +3 -0
  123. data/test/Gemfile.lock +10 -0
  124. data/test/sass/cache_test.rb +89 -0
  125. data/test/sass/callbacks_test.rb +61 -0
  126. data/test/sass/conversion_test.rb +1760 -0
  127. data/test/sass/css2sass_test.rb +458 -0
  128. data/test/sass/data/hsl-rgb.txt +319 -0
  129. data/test/sass/engine_test.rb +3244 -0
  130. data/test/sass/exec_test.rb +86 -0
  131. data/test/sass/extend_test.rb +1482 -0
  132. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  133. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  134. data/test/sass/functions_test.rb +1139 -0
  135. data/test/sass/importer_test.rb +192 -0
  136. data/test/sass/logger_test.rb +58 -0
  137. data/test/sass/mock_importer.rb +49 -0
  138. data/test/sass/more_results/more1.css +9 -0
  139. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  140. data/test/sass/more_results/more_import.css +29 -0
  141. data/test/sass/more_templates/_more_partial.sass +2 -0
  142. data/test/sass/more_templates/more1.sass +23 -0
  143. data/test/sass/more_templates/more_import.sass +11 -0
  144. data/test/sass/plugin_test.rb +564 -0
  145. data/test/sass/results/alt.css +4 -0
  146. data/test/sass/results/basic.css +9 -0
  147. data/test/sass/results/cached_import_option.css +3 -0
  148. data/test/sass/results/compact.css +5 -0
  149. data/test/sass/results/complex.css +86 -0
  150. data/test/sass/results/compressed.css +1 -0
  151. data/test/sass/results/expanded.css +19 -0
  152. data/test/sass/results/filename_fn.css +3 -0
  153. data/test/sass/results/if.css +3 -0
  154. data/test/sass/results/import.css +31 -0
  155. data/test/sass/results/import_charset.css +5 -0
  156. data/test/sass/results/import_charset_1_8.css +5 -0
  157. data/test/sass/results/import_charset_ibm866.css +5 -0
  158. data/test/sass/results/import_content.css +1 -0
  159. data/test/sass/results/line_numbers.css +49 -0
  160. data/test/sass/results/mixins.css +95 -0
  161. data/test/sass/results/multiline.css +24 -0
  162. data/test/sass/results/nested.css +22 -0
  163. data/test/sass/results/options.css +1 -0
  164. data/test/sass/results/parent_ref.css +13 -0
  165. data/test/sass/results/script.css +16 -0
  166. data/test/sass/results/scss_import.css +31 -0
  167. data/test/sass/results/scss_importee.css +2 -0
  168. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  169. data/test/sass/results/subdir/subdir.css +3 -0
  170. data/test/sass/results/units.css +11 -0
  171. data/test/sass/results/warn.css +0 -0
  172. data/test/sass/results/warn_imported.css +0 -0
  173. data/test/sass/script_conversion_test.rb +299 -0
  174. data/test/sass/script_test.rb +622 -0
  175. data/test/sass/scss/css_test.rb +1100 -0
  176. data/test/sass/scss/rx_test.rb +156 -0
  177. data/test/sass/scss/scss_test.rb +2106 -0
  178. data/test/sass/scss/test_helper.rb +37 -0
  179. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  180. data/test/sass/templates/_double_import_loop2.sass +1 -0
  181. data/test/sass/templates/_filename_fn_import.scss +11 -0
  182. data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
  183. data/test/sass/templates/_imported_charset_utf8.sass +4 -0
  184. data/test/sass/templates/_imported_content.sass +3 -0
  185. data/test/sass/templates/_partial.sass +2 -0
  186. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  187. data/test/sass/templates/alt.sass +16 -0
  188. data/test/sass/templates/basic.sass +23 -0
  189. data/test/sass/templates/bork1.sass +2 -0
  190. data/test/sass/templates/bork2.sass +2 -0
  191. data/test/sass/templates/bork3.sass +2 -0
  192. data/test/sass/templates/bork4.sass +2 -0
  193. data/test/sass/templates/bork5.sass +3 -0
  194. data/test/sass/templates/cached_import_option.scss +3 -0
  195. data/test/sass/templates/compact.sass +17 -0
  196. data/test/sass/templates/complex.sass +305 -0
  197. data/test/sass/templates/compressed.sass +15 -0
  198. data/test/sass/templates/double_import_loop1.sass +1 -0
  199. data/test/sass/templates/expanded.sass +17 -0
  200. data/test/sass/templates/filename_fn.scss +18 -0
  201. data/test/sass/templates/if.sass +11 -0
  202. data/test/sass/templates/import.sass +12 -0
  203. data/test/sass/templates/import_charset.sass +9 -0
  204. data/test/sass/templates/import_charset_1_8.sass +6 -0
  205. data/test/sass/templates/import_charset_ibm866.sass +11 -0
  206. data/test/sass/templates/import_content.sass +4 -0
  207. data/test/sass/templates/importee.less +2 -0
  208. data/test/sass/templates/importee.sass +19 -0
  209. data/test/sass/templates/line_numbers.sass +13 -0
  210. data/test/sass/templates/mixin_bork.sass +5 -0
  211. data/test/sass/templates/mixins.sass +76 -0
  212. data/test/sass/templates/multiline.sass +20 -0
  213. data/test/sass/templates/nested.sass +25 -0
  214. data/test/sass/templates/nested_bork1.sass +2 -0
  215. data/test/sass/templates/nested_bork2.sass +2 -0
  216. data/test/sass/templates/nested_bork3.sass +2 -0
  217. data/test/sass/templates/nested_bork4.sass +2 -0
  218. data/test/sass/templates/nested_import.sass +2 -0
  219. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  220. data/test/sass/templates/options.sass +2 -0
  221. data/test/sass/templates/parent_ref.sass +25 -0
  222. data/test/sass/templates/same_name_different_ext.sass +2 -0
  223. data/test/sass/templates/same_name_different_ext.scss +1 -0
  224. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  225. data/test/sass/templates/script.sass +101 -0
  226. data/test/sass/templates/scss_import.scss +11 -0
  227. data/test/sass/templates/scss_importee.scss +1 -0
  228. data/test/sass/templates/single_import_loop.sass +1 -0
  229. data/test/sass/templates/subdir/import_up1.scss +1 -0
  230. data/test/sass/templates/subdir/import_up2.scss +1 -0
  231. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  232. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  233. data/test/sass/templates/subdir/subdir.sass +6 -0
  234. data/test/sass/templates/units.sass +11 -0
  235. data/test/sass/templates/warn.sass +3 -0
  236. data/test/sass/templates/warn_imported.sass +4 -0
  237. data/test/sass/test_helper.rb +8 -0
  238. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  239. data/test/sass/util/subset_map_test.rb +91 -0
  240. data/test/sass/util_test.rb +382 -0
  241. data/test/test_helper.rb +80 -0
  242. metadata +354 -0
@@ -0,0 +1,406 @@
1
+ require 'fileutils'
2
+
3
+ require 'sass'
4
+ # XXX CE: is this still necessary now that we have the compiler class?
5
+ require 'sass/callbacks'
6
+ require 'sass/plugin/configuration'
7
+ require 'sass/plugin/staleness_checker'
8
+
9
+ module Sass::Plugin
10
+
11
+ # The Compiler class handles compilation of multiple files and/or directories,
12
+ # including checking which CSS files are out-of-date and need to be updated
13
+ # and calling Sass to perform the compilation on those files.
14
+ #
15
+ # {Sass::Plugin} uses this class to update stylesheets for a single application.
16
+ # Unlike {Sass::Plugin}, though, the Compiler class has no global state,
17
+ # and so multiple instances may be created and used independently.
18
+ #
19
+ # If you need to compile a Sass string into CSS,
20
+ # please see the {Sass::Engine} class.
21
+ #
22
+ # Unlike {Sass::Plugin}, this class doesn't keep track of
23
+ # whether or how many times a stylesheet should be updated.
24
+ # Therefore, the following `Sass::Plugin` options are ignored by the Compiler:
25
+ #
26
+ # * `:never_update`
27
+ # * `:always_check`
28
+ class Compiler
29
+ include Sass::Util
30
+ include Configuration
31
+ extend Sass::Callbacks
32
+
33
+ # Creates a new compiler.
34
+ #
35
+ # @param options [{Symbol => Object}]
36
+ # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
37
+ def initialize(options = {})
38
+ self.options.merge!(options)
39
+ end
40
+
41
+ # Register a callback to be run after stylesheets are mass-updated.
42
+ # This is run whenever \{#update\_stylesheets} is called,
43
+ # unless the \{file:SASS_REFERENCE.md#never_update-option `:never_update` option}
44
+ # is enabled.
45
+ #
46
+ # @yield [individual_files]
47
+ # @yieldparam individual_files [<(String, String)>]
48
+ # Individual files to be updated, in addition to the directories
49
+ # specified in the options.
50
+ # The first element of each pair is the source file,
51
+ # the second is the target CSS file.
52
+ define_callback :updating_stylesheets
53
+
54
+ # Register a callback to be run after a single stylesheet is updated.
55
+ # The callback is only run if the stylesheet is really updated;
56
+ # if the CSS file is fresh, this won't be run.
57
+ #
58
+ # Even if the \{file:SASS_REFERENCE.md#full_exception-option `:full_exception` option}
59
+ # is enabled, this callback won't be run
60
+ # when an exception CSS file is being written.
61
+ # To run an action for those files, use \{#on\_compilation\_error}.
62
+ #
63
+ # @yield [template, css]
64
+ # @yieldparam template [String]
65
+ # The location of the Sass/SCSS file being updated.
66
+ # @yieldparam css [String]
67
+ # The location of the CSS file being generated.
68
+ define_callback :updated_stylesheet
69
+
70
+ # Register a callback to be run before a single stylesheet is updated.
71
+ # The callback is only run if the stylesheet is guaranteed to be updated;
72
+ # if the CSS file is fresh, this won't be run.
73
+ #
74
+ # Even if the \{file:SASS_REFERENCE.md#full_exception-option `:full_exception` option}
75
+ # is enabled, this callback won't be run
76
+ # when an exception CSS file is being written.
77
+ # To run an action for those files, use \{#on\_compilation\_error}.
78
+ #
79
+ # @yield [template, css]
80
+ # @yieldparam template [String]
81
+ # The location of the Sass/SCSS file being updated.
82
+ # @yieldparam css [String]
83
+ # The location of the CSS file being generated.
84
+ define_callback :updating_stylesheet
85
+
86
+ def on_updating_stylesheet_with_deprecation_warning(&block)
87
+ Sass::Util.sass_warn("Sass::Compiler#on_updating_stylesheet callback is deprecated and will be removed in a future release. Use Sass::Compiler#on_updated_stylesheet instead, which is run after stylesheet compilation.")
88
+ on_updating_stylesheet_without_deprecation_warning(&block)
89
+ end
90
+ alias_method :on_updating_stylesheet_without_deprecation_warning, :on_updating_stylesheet
91
+ alias_method :on_updating_stylesheet, :on_updating_stylesheet_with_deprecation_warning
92
+
93
+ # Register a callback to be run when Sass decides not to update a stylesheet.
94
+ # In particular, the callback is run when Sass finds that
95
+ # the template file and none of its dependencies
96
+ # have been modified since the last compilation.
97
+ #
98
+ # Note that this is **not** run when the
99
+ # \{file:SASS_REFERENCE.md#never-update_option `:never_update` option} is set,
100
+ # nor when Sass decides not to compile a partial.
101
+ #
102
+ # @yield [template, css]
103
+ # @yieldparam template [String]
104
+ # The location of the Sass/SCSS file not being updated.
105
+ # @yieldparam css [String]
106
+ # The location of the CSS file not being generated.
107
+ define_callback :not_updating_stylesheet
108
+
109
+ # Register a callback to be run when there's an error
110
+ # compiling a Sass file.
111
+ # This could include not only errors in the Sass document,
112
+ # but also errors accessing the file at all.
113
+ #
114
+ # @yield [error, template, css]
115
+ # @yieldparam error [Exception] The exception that was raised.
116
+ # @yieldparam template [String]
117
+ # The location of the Sass/SCSS file being updated.
118
+ # @yieldparam css [String]
119
+ # The location of the CSS file being generated.
120
+ define_callback :compilation_error
121
+
122
+ # Register a callback to be run when Sass creates a directory
123
+ # into which to put CSS files.
124
+ #
125
+ # Note that even if multiple levels of directories need to be created,
126
+ # the callback may only be run once.
127
+ # For example, if "foo/" exists and "foo/bar/baz/" needs to be created,
128
+ # this may only be run for "foo/bar/baz/".
129
+ # This is not a guarantee, however;
130
+ # it may also be run for "foo/bar/".
131
+ #
132
+ # @yield [dirname]
133
+ # @yieldparam dirname [String]
134
+ # The location of the directory that was created.
135
+ define_callback :creating_directory
136
+
137
+ # Register a callback to be run when Sass detects
138
+ # that a template has been modified.
139
+ # This is only run when using \{#watch}.
140
+ #
141
+ # @yield [template]
142
+ # @yieldparam template [String]
143
+ # The location of the template that was modified.
144
+ define_callback :template_modified
145
+
146
+ # Register a callback to be run when Sass detects
147
+ # that a new template has been created.
148
+ # This is only run when using \{#watch}.
149
+ #
150
+ # @yield [template]
151
+ # @yieldparam template [String]
152
+ # The location of the template that was created.
153
+ define_callback :template_created
154
+
155
+ # Register a callback to be run when Sass detects
156
+ # that a template has been deleted.
157
+ # This is only run when using \{#watch}.
158
+ #
159
+ # @yield [template]
160
+ # @yieldparam template [String]
161
+ # The location of the template that was deleted.
162
+ define_callback :template_deleted
163
+
164
+ # Register a callback to be run when Sass deletes a CSS file.
165
+ # This happens when the corresponding Sass/SCSS file has been deleted.
166
+ #
167
+ # @yield [filename]
168
+ # @yieldparam filename [String]
169
+ # The location of the CSS file that was deleted.
170
+ define_callback :deleting_css
171
+
172
+ # Updates out-of-date stylesheets.
173
+ #
174
+ # Checks each Sass/SCSS file in {file:SASS_REFERENCE.md#template_location-option `:template_location`}
175
+ # to see if it's been modified more recently than the corresponding CSS file
176
+ # in {file:SASS_REFERENCE.md#css_location-option `:css_location`}.
177
+ # If it has, it updates the CSS file.
178
+ #
179
+ # @param individual_files [Array<(String, String)>]
180
+ # A list of files to check for updates
181
+ # **in addition to those specified by the
182
+ # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
183
+ # The first string in each pair is the location of the Sass/SCSS file,
184
+ # the second is the location of the CSS file that it should be compiled to.
185
+ def update_stylesheets(individual_files = [])
186
+ individual_files = individual_files.dup
187
+ Sass::Plugin.checked_for_updates = true
188
+ staleness_checker = StalenessChecker.new(engine_options)
189
+
190
+ template_location_array.each do |template_location, css_location|
191
+ Sass::Util.glob(File.join(template_location, "**", "[^_]*.s[ca]ss")).sort.each do |file|
192
+ # Get the relative path to the file
193
+ name = file.sub(template_location.to_s.sub(/\/*$/, '/'), "")
194
+ css = css_filename(name, css_location)
195
+ individual_files << [file, css]
196
+ end
197
+ end
198
+
199
+ run_updating_stylesheets individual_files
200
+
201
+ individual_files.each do |file, css|
202
+ if options[:always_update] || staleness_checker.stylesheet_needs_update?(css, file)
203
+ update_stylesheet(file, css)
204
+ else
205
+ run_not_updating_stylesheet(file, css)
206
+ end
207
+ end
208
+ end
209
+
210
+ # Watches the template directory (or directories)
211
+ # and updates the CSS files whenever the related Sass/SCSS files change.
212
+ # `watch` never returns.
213
+ #
214
+ # Whenever a change is detected to a Sass/SCSS file in
215
+ # {file:SASS_REFERENCE.md#template_location-option `:template_location`},
216
+ # the corresponding CSS file in {file:SASS_REFERENCE.md#css_location-option `:css_location`}
217
+ # will be recompiled.
218
+ # The CSS files of any Sass/SCSS files that import the changed file will also be recompiled.
219
+ #
220
+ # Before the watching starts in earnest, `watch` calls \{#update\_stylesheets}.
221
+ #
222
+ # Note that `watch` uses the [Listen](http://github.com/guard/listen) library
223
+ # to monitor the filesystem for changes.
224
+ # Listen isn't loaded until `watch` is run.
225
+ # The version of Listen distributed with Sass is loaded by default,
226
+ # but if another version has already been loaded that will be used instead.
227
+ #
228
+ # @param individual_files [Array<(String, String)>]
229
+ # A list of files to watch for updates
230
+ # **in addition to those specified by the
231
+ # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
232
+ # The first string in each pair is the location of the Sass/SCSS file,
233
+ # the second is the location of the CSS file that it should be compiled to.
234
+ def watch(individual_files = [])
235
+ update_stylesheets(individual_files)
236
+
237
+ load_listen!
238
+
239
+ template_paths = template_locations # cache the locations
240
+ individual_files_hash = individual_files.inject({}) do |h, files|
241
+ parent = File.dirname(files.first)
242
+ (h[parent] ||= []) << files unless template_paths.include?(parent)
243
+ h
244
+ end
245
+ directories = template_paths + individual_files_hash.keys +
246
+ [{:relative_paths => true}]
247
+
248
+ # TODO: Keep better track of what depends on what
249
+ # so we don't have to run a global update every time anything changes.
250
+ listener = Listen::MultiListener.new(*directories) do |modified, added, removed|
251
+ modified.each do |f|
252
+ parent = File.dirname(f)
253
+ if files = individual_files_hash[parent]
254
+ next unless files.first == f
255
+ else
256
+ next unless f =~ /\.s[ac]ss$/
257
+ end
258
+ run_template_modified(f)
259
+ end
260
+
261
+ added.each do |f|
262
+ parent = File.dirname(f)
263
+ if files = individual_files_hash[parent]
264
+ next unless files.first == f
265
+ else
266
+ next unless f =~ /\.s[ac]ss$/
267
+ end
268
+ run_template_created(f)
269
+ end
270
+
271
+ removed.each do |f|
272
+ parent = File.dirname(f)
273
+ if files = individual_files_hash[parent]
274
+ next unless files.first == f
275
+ try_delete_css files[1]
276
+ else
277
+ next unless f =~ /\.s[ac]ss$/
278
+ try_delete_css f.gsub(/\.s[ac]ss$/, '.css')
279
+ end
280
+ run_template_deleted(f)
281
+ end
282
+
283
+ update_stylesheets(individual_files)
284
+ end
285
+
286
+ # The native windows listener is much slower than the polling
287
+ # option, according to https://github.com/nex3/sass/commit/a3031856b22bc834a5417dedecb038b7be9b9e3e#commitcomment-1295118
288
+ listener.force_polling(true) if @options[:poll] || Sass::Util.windows?
289
+
290
+ begin
291
+ listener.start
292
+ rescue Exception => e
293
+ raise e unless e.is_a?(Interrupt)
294
+ end
295
+ end
296
+
297
+ # Non-destructively modifies \{#options} so that default values are properly set,
298
+ # and returns the result.
299
+ #
300
+ # @param additional_options [{Symbol => Object}] An options hash with which to merge \{#options}
301
+ # @return [{Symbol => Object}] The modified options hash
302
+ def engine_options(additional_options = {})
303
+ opts = options.merge(additional_options)
304
+ opts[:load_paths] = load_paths(opts)
305
+ opts
306
+ end
307
+
308
+ # Compass expects this to exist
309
+ def stylesheet_needs_update?(css_file, template_file)
310
+ StalenessChecker.stylesheet_needs_update?(css_file, template_file)
311
+ end
312
+
313
+ private
314
+
315
+ def load_listen!
316
+ if defined?(gem)
317
+ begin
318
+ gem 'listen', '~> 0.7'
319
+ require 'listen'
320
+ rescue Gem::LoadError
321
+ dir = Sass::Util.scope("vendor/listen/lib")
322
+ $LOAD_PATH.unshift dir
323
+ begin
324
+ require 'listen'
325
+ rescue LoadError => e
326
+ e.message << "\n" <<
327
+ if File.exists?(scope(".git"))
328
+ 'Run "git submodule update --init" to get the recommended version.'
329
+ else
330
+ 'Run "gem install listen" to get it.'
331
+ end
332
+ raise e
333
+ end
334
+ end
335
+ else
336
+ begin
337
+ require 'listen'
338
+ rescue LoadError => e
339
+ dir = Sass::Util.scope("vendor/listen/lib")
340
+ if $LOAD_PATH.include?(dir)
341
+ raise e unless File.exists?(scope(".git"))
342
+ e.message << "\n" <<
343
+ 'Run "git submodule update --init" to get the recommended version.'
344
+ else
345
+ $LOAD_PATH.unshift dir
346
+ retry
347
+ end
348
+ end
349
+ end
350
+ end
351
+
352
+ def update_stylesheet(filename, css)
353
+ dir = File.dirname(css)
354
+ unless File.exists?(dir)
355
+ run_creating_directory dir
356
+ FileUtils.mkdir_p dir
357
+ end
358
+
359
+ begin
360
+ File.read(filename) unless File.readable?(filename) # triggers an error for handling
361
+ engine_opts = engine_options(:css_filename => css, :filename => filename)
362
+ result = Sass::Engine.for_file(filename, engine_opts).render
363
+ rescue Exception => e
364
+ compilation_error_occured = true
365
+ run_compilation_error e, filename, css
366
+ result = Sass::SyntaxError.exception_to_css(e, options)
367
+ else
368
+ run_updating_stylesheet filename, css
369
+ end
370
+
371
+ write_file(css, result)
372
+ run_updated_stylesheet(filename, css) unless compilation_error_occured
373
+ end
374
+
375
+ def write_file(css, content)
376
+ flag = 'w'
377
+ flag = 'wb' if Sass::Util.windows? && options[:unix_newlines]
378
+ File.open(css, flag) do |file|
379
+ file.set_encoding(content.encoding) unless Sass::Util.ruby1_8?
380
+ file.print(content)
381
+ end
382
+ end
383
+
384
+ def try_delete_css(css)
385
+ return unless File.exists?(css)
386
+ run_deleting_css css
387
+ File.delete css
388
+ end
389
+
390
+ def load_paths(opts = options)
391
+ (opts[:load_paths] || []) + template_locations
392
+ end
393
+
394
+ def template_locations
395
+ template_location_array.to_a.map {|l| l.first}
396
+ end
397
+
398
+ def css_locations
399
+ template_location_array.to_a.map {|l| l.last}
400
+ end
401
+
402
+ def css_filename(name, path)
403
+ "#{path}/#{name}".gsub(/\.s[ac]ss$/, '.css')
404
+ end
405
+ end
406
+ end
@@ -0,0 +1,123 @@
1
+ # We keep configuration in its own self-contained file
2
+ # so that we can load it independently in Rails 3,
3
+ # where the full plugin stuff is lazy-loaded.
4
+
5
+ module Sass
6
+ module Plugin
7
+ module Configuration
8
+
9
+ # Returns the default options for a {Sass::Plugin::Compiler}.
10
+ #
11
+ # @return [{Symbol => Object}]
12
+ def default_options
13
+ @default_options ||= {
14
+ :css_location => './public/stylesheets',
15
+ :always_update => false,
16
+ :always_check => true,
17
+ :full_exception => true,
18
+ :cache_location => ".sass-cache"
19
+ }.freeze
20
+ end
21
+
22
+ # Resets the options and {Sass::Callbacks::InstanceMethods#clear_callbacks! clears all callbacks}.
23
+ def reset!
24
+ @options = nil
25
+ clear_callbacks!
26
+ end
27
+
28
+ # An options hash.
29
+ # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
30
+ #
31
+ # @return [{Symbol => Object}]
32
+ def options
33
+ @options ||= default_options.dup
34
+ end
35
+
36
+ # Sets the options hash.
37
+ # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
38
+ # See {Sass::Plugin::Configuration#reset!}
39
+ # @deprecated Instead, modify the options hash in-place.
40
+ # @param value [{Symbol => Object}] The options hash
41
+ def options=(value)
42
+ Sass::Util.sass_warn("Setting Sass::Plugin.options is deprecated " +
43
+ "and will be removed in a future release.")
44
+ options.merge!(value)
45
+ end
46
+
47
+ # Adds a new template-location/css-location mapping.
48
+ # This means that Sass/SCSS files in `template_location`
49
+ # will be compiled to CSS files in `css_location`.
50
+ #
51
+ # This is preferred over manually manipulating the {file:SASS_REFERENCE.md#template_location-option `:template_location` option}
52
+ # since the option can be in multiple formats.
53
+ #
54
+ # Note that this method will change `options[:template_location]`
55
+ # to be in the Array format.
56
+ # This means that even if `options[:template_location]`
57
+ # had previously been a Hash or a String,
58
+ # it will now be an Array.
59
+ #
60
+ # @param template_location [String] The location where Sass/SCSS files will be.
61
+ # @param css_location [String] The location where compiled CSS files will go.
62
+ def add_template_location(template_location, css_location = options[:css_location])
63
+ normalize_template_location!
64
+ template_location_array << [template_location, css_location]
65
+ end
66
+
67
+ # Removes a template-location/css-location mapping.
68
+ # This means that Sass/SCSS files in `template_location`
69
+ # will no longer be compiled to CSS files in `css_location`.
70
+ #
71
+ # This is preferred over manually manipulating the {file:SASS_REFERENCE.md#template_location-option `:template_location` option}
72
+ # since the option can be in multiple formats.
73
+ #
74
+ # Note that this method will change `options[:template_location]`
75
+ # to be in the Array format.
76
+ # This means that even if `options[:template_location]`
77
+ # had previously been a Hash or a String,
78
+ # it will now be an Array.
79
+ #
80
+ # @param template_location [String]
81
+ # The location where Sass/SCSS files were,
82
+ # which is now going to be ignored.
83
+ # @param css_location [String]
84
+ # The location where compiled CSS files went, but will no longer go.
85
+ # @return [Boolean]
86
+ # Non-`nil` if the given mapping already existed and was removed,
87
+ # or `nil` if nothing was changed.
88
+ def remove_template_location(template_location, css_location = options[:css_location])
89
+ normalize_template_location!
90
+ template_location_array.delete([template_location, css_location])
91
+ end
92
+
93
+ # Returns the template locations configured for Sass
94
+ # as an array of `[template_location, css_location]` pairs.
95
+ # See the {file:SASS_REFERENCE.md#template_location-option `:template_location` option}
96
+ # for details.
97
+ #
98
+ # @return [Array<(String, String)>]
99
+ # An array of `[template_location, css_location]` pairs.
100
+ def template_location_array
101
+ old_template_location = options[:template_location]
102
+ normalize_template_location!
103
+ options[:template_location]
104
+ ensure
105
+ options[:template_location] = old_template_location
106
+ end
107
+
108
+ private
109
+
110
+ def normalize_template_location!
111
+ return if options[:template_location].is_a?(Array)
112
+ options[:template_location] =
113
+ case options[:template_location]
114
+ when nil
115
+ options[:css_location] ?
116
+ [[File.join(options[:css_location], 'sass'), options[:css_location]]] : []
117
+ when String; [[options[:template_location], options[:css_location]]]
118
+ else; options[:template_location].to_a
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,15 @@
1
+ # The reason some options are declared here rather than in sass/plugin/configuration.rb
2
+ # is that otherwise they'd clobber the Rails-specific options.
3
+ # Since Rails' options are lazy-loaded in Rails 3,
4
+ # they're reverse-merged with the default options
5
+ # so that user configuration is preserved.
6
+ # This means that defaults that differ from Rails'
7
+ # must be declared here.
8
+
9
+ unless defined?(Sass::GENERIC_LOADED)
10
+ Sass::GENERIC_LOADED = true
11
+
12
+ Sass::Plugin.options.merge!(:css_location => './public/stylesheets',
13
+ :always_update => false,
14
+ :always_check => true)
15
+ end
@@ -0,0 +1,48 @@
1
+ unless defined?(Sass::MERB_LOADED)
2
+ Sass::MERB_LOADED = true
3
+
4
+ module Sass::Plugin::Configuration
5
+ # Different default options in a m envirionment.
6
+ def default_options
7
+ @default_options ||= begin
8
+ version = Merb::VERSION.split('.').map { |n| n.to_i }
9
+ if version[0] <= 0 && version[1] < 5
10
+ root = MERB_ROOT
11
+ env = MERB_ENV
12
+ else
13
+ root = Merb.root.to_s
14
+ env = Merb.environment
15
+ end
16
+
17
+ {
18
+ :always_update => false,
19
+ :template_location => root + '/public/stylesheets/sass',
20
+ :css_location => root + '/public/stylesheets',
21
+ :cache_location => root + '/tmp/sass-cache',
22
+ :always_check => env != "production",
23
+ :quiet => env != "production",
24
+ :full_exception => env != "production"
25
+ }.freeze
26
+ end
27
+ end
28
+ end
29
+
30
+ config = Merb::Plugins.config[:sass] || Merb::Plugins.config["sass"] || {}
31
+
32
+ if defined? config.symbolize_keys!
33
+ config.symbolize_keys!
34
+ end
35
+
36
+ Sass::Plugin.options.merge!(config)
37
+
38
+ require 'sass/plugin/rack'
39
+ class Sass::Plugin::MerbBootLoader < Merb::BootLoader
40
+ after Merb::BootLoader::RackUpApplication
41
+
42
+ def self.run
43
+ # Apparently there's no better way than this to add Sass
44
+ # to Merb's Rack stack.
45
+ Merb::Config[:app] = Sass::Plugin::Rack.new(Merb::Config[:app])
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,60 @@
1
+ module Sass
2
+ module Plugin
3
+ # Rack middleware for compiling Sass code.
4
+ #
5
+ # ## Activate
6
+ #
7
+ # require 'sass/plugin/rack'
8
+ # use Sass::Plugin::Rack
9
+ #
10
+ # ## Customize
11
+ #
12
+ # Sass::Plugin.options.merge(
13
+ # :cache_location => './tmp/sass-cache',
14
+ # :never_update => environment != :production,
15
+ # :full_exception => environment != :production)
16
+ #
17
+ # {file:SASS_REFERENCE.md#options See the Reference for more options}.
18
+ #
19
+ # ## Use
20
+ #
21
+ # Put your Sass files in `public/stylesheets/sass`.
22
+ # Your CSS will be generated in `public/stylesheets`,
23
+ # and regenerated every request if necessary.
24
+ # The locations and frequency {file:SASS_REFERENCE.md#options can be customized}.
25
+ # That's all there is to it!
26
+ class Rack
27
+ # The delay, in seconds, between update checks.
28
+ # Useful when many resources are requested for a single page.
29
+ # `nil` means no delay at all.
30
+ #
31
+ # @return [Float]
32
+ attr_accessor :dwell
33
+
34
+ # Initialize the middleware.
35
+ #
36
+ # @param app [#call] The Rack application
37
+ # @param dwell [Float] See \{#dwell}
38
+ def initialize(app, dwell = 1.0)
39
+ @app = app
40
+ @dwell = dwell
41
+ @check_after = Time.now.to_f
42
+ end
43
+
44
+ # Process a request, checking the Sass stylesheets for changes
45
+ # and updating them if necessary.
46
+ #
47
+ # @param env The Rack request environment
48
+ # @return [(#to_i, {String => String}, Object)] The Rack response
49
+ def call(env)
50
+ if @dwell.nil? || Time.now.to_f > @check_after
51
+ Sass::Plugin.check_for_updates
52
+ @check_after = Time.now.to_f + @dwell if @dwell
53
+ end
54
+ @app.call(env)
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ require 'sass/plugin'