sass 3.3.0 → 3.4.25

Sign up to get free protection for your applications and to get access to all the features.
Files changed (208) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +3 -1
  3. data/CODE_OF_CONDUCT.md +10 -0
  4. data/CONTRIBUTING.md +148 -0
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +76 -62
  7. data/Rakefile +104 -24
  8. data/VERSION +1 -1
  9. data/VERSION_DATE +1 -1
  10. data/VERSION_NAME +1 -1
  11. data/bin/sass +1 -1
  12. data/bin/scss +1 -1
  13. data/extra/sass-spec-ref.sh +32 -0
  14. data/extra/update_watch.rb +1 -1
  15. data/lib/sass/cache_stores/filesystem.rb +9 -5
  16. data/lib/sass/cache_stores/memory.rb +4 -5
  17. data/lib/sass/callbacks.rb +2 -2
  18. data/lib/sass/css.rb +12 -13
  19. data/lib/sass/deprecation.rb +55 -0
  20. data/lib/sass/engine.rb +106 -70
  21. data/lib/sass/environment.rb +39 -19
  22. data/lib/sass/error.rb +17 -20
  23. data/lib/sass/exec/base.rb +199 -0
  24. data/lib/sass/exec/sass_convert.rb +283 -0
  25. data/lib/sass/exec/sass_scss.rb +440 -0
  26. data/lib/sass/exec.rb +5 -771
  27. data/lib/sass/features.rb +9 -2
  28. data/lib/sass/importers/base.rb +8 -3
  29. data/lib/sass/importers/filesystem.rb +30 -38
  30. data/lib/sass/logger/base.rb +8 -2
  31. data/lib/sass/logger/delayed.rb +50 -0
  32. data/lib/sass/logger.rb +8 -3
  33. data/lib/sass/media.rb +1 -4
  34. data/lib/sass/plugin/compiler.rb +224 -90
  35. data/lib/sass/plugin/configuration.rb +38 -22
  36. data/lib/sass/plugin/merb.rb +2 -2
  37. data/lib/sass/plugin/rack.rb +3 -3
  38. data/lib/sass/plugin/rails.rb +1 -1
  39. data/lib/sass/plugin/staleness_checker.rb +4 -4
  40. data/lib/sass/plugin.rb +6 -5
  41. data/lib/sass/script/css_lexer.rb +1 -1
  42. data/lib/sass/script/css_parser.rb +2 -3
  43. data/lib/sass/script/css_variable_warning.rb +52 -0
  44. data/lib/sass/script/functions.rb +739 -318
  45. data/lib/sass/script/lexer.rb +134 -54
  46. data/lib/sass/script/parser.rb +252 -56
  47. data/lib/sass/script/tree/funcall.rb +13 -6
  48. data/lib/sass/script/tree/interpolation.rb +127 -4
  49. data/lib/sass/script/tree/list_literal.rb +31 -4
  50. data/lib/sass/script/tree/literal.rb +4 -0
  51. data/lib/sass/script/tree/node.rb +21 -3
  52. data/lib/sass/script/tree/operation.rb +54 -1
  53. data/lib/sass/script/tree/selector.rb +26 -0
  54. data/lib/sass/script/tree/string_interpolation.rb +59 -38
  55. data/lib/sass/script/tree/variable.rb +1 -1
  56. data/lib/sass/script/tree.rb +1 -0
  57. data/lib/sass/script/value/base.rb +17 -14
  58. data/lib/sass/script/value/bool.rb +0 -5
  59. data/lib/sass/script/value/color.rb +78 -42
  60. data/lib/sass/script/value/helpers.rb +119 -2
  61. data/lib/sass/script/value/list.rb +0 -15
  62. data/lib/sass/script/value/map.rb +1 -1
  63. data/lib/sass/script/value/null.rb +0 -5
  64. data/lib/sass/script/value/number.rb +112 -31
  65. data/lib/sass/script/value/string.rb +102 -13
  66. data/lib/sass/script/value.rb +0 -1
  67. data/lib/sass/script.rb +3 -3
  68. data/lib/sass/scss/css_parser.rb +24 -4
  69. data/lib/sass/scss/parser.rb +290 -383
  70. data/lib/sass/scss/rx.rb +17 -9
  71. data/lib/sass/scss/static_parser.rb +306 -4
  72. data/lib/sass/scss.rb +0 -2
  73. data/lib/sass/selector/abstract_sequence.rb +35 -18
  74. data/lib/sass/selector/comma_sequence.rb +114 -19
  75. data/lib/sass/selector/pseudo.rb +266 -0
  76. data/lib/sass/selector/sequence.rb +146 -40
  77. data/lib/sass/selector/simple.rb +22 -33
  78. data/lib/sass/selector/simple_sequence.rb +122 -39
  79. data/lib/sass/selector.rb +57 -197
  80. data/lib/sass/shared.rb +2 -2
  81. data/lib/sass/source/map.rb +31 -14
  82. data/lib/sass/source/position.rb +4 -4
  83. data/lib/sass/stack.rb +2 -8
  84. data/lib/sass/supports.rb +10 -13
  85. data/lib/sass/tree/at_root_node.rb +1 -0
  86. data/lib/sass/tree/charset_node.rb +1 -1
  87. data/lib/sass/tree/comment_node.rb +1 -1
  88. data/lib/sass/tree/css_import_node.rb +9 -1
  89. data/lib/sass/tree/directive_node.rb +8 -2
  90. data/lib/sass/tree/error_node.rb +18 -0
  91. data/lib/sass/tree/extend_node.rb +1 -1
  92. data/lib/sass/tree/function_node.rb +9 -0
  93. data/lib/sass/tree/import_node.rb +6 -5
  94. data/lib/sass/tree/keyframe_rule_node.rb +15 -0
  95. data/lib/sass/tree/node.rb +5 -3
  96. data/lib/sass/tree/prop_node.rb +6 -7
  97. data/lib/sass/tree/rule_node.rb +26 -11
  98. data/lib/sass/tree/visitors/check_nesting.rb +56 -32
  99. data/lib/sass/tree/visitors/convert.rb +59 -44
  100. data/lib/sass/tree/visitors/cssize.rb +34 -30
  101. data/lib/sass/tree/visitors/deep_copy.rb +6 -1
  102. data/lib/sass/tree/visitors/extend.rb +15 -13
  103. data/lib/sass/tree/visitors/perform.rb +87 -50
  104. data/lib/sass/tree/visitors/set_options.rb +15 -1
  105. data/lib/sass/tree/visitors/to_css.rb +72 -43
  106. data/lib/sass/util/multibyte_string_scanner.rb +0 -2
  107. data/lib/sass/util/normalized_map.rb +0 -1
  108. data/lib/sass/util/subset_map.rb +2 -3
  109. data/lib/sass/util.rb +334 -154
  110. data/lib/sass/version.rb +7 -7
  111. data/lib/sass.rb +10 -8
  112. data/test/sass/cache_test.rb +62 -20
  113. data/test/sass/callbacks_test.rb +1 -1
  114. data/test/sass/compiler_test.rb +24 -11
  115. data/test/sass/conversion_test.rb +241 -50
  116. data/test/sass/css2sass_test.rb +73 -5
  117. data/test/sass/css_variable_test.rb +132 -0
  118. data/test/sass/encoding_test.rb +219 -0
  119. data/test/sass/engine_test.rb +343 -260
  120. data/test/sass/exec_test.rb +12 -2
  121. data/test/sass/extend_test.rb +333 -44
  122. data/test/sass/functions_test.rb +353 -260
  123. data/test/sass/importer_test.rb +40 -21
  124. data/test/sass/logger_test.rb +1 -1
  125. data/test/sass/more_results/more_import.css +1 -1
  126. data/test/sass/more_templates/more1.sass +10 -10
  127. data/test/sass/more_templates/more_import.sass +2 -2
  128. data/test/sass/plugin_test.rb +24 -21
  129. data/test/sass/results/compact.css +1 -1
  130. data/test/sass/results/complex.css +4 -4
  131. data/test/sass/results/expanded.css +1 -1
  132. data/test/sass/results/import.css +1 -1
  133. data/test/sass/results/import_charset_ibm866.css +2 -2
  134. data/test/sass/results/mixins.css +17 -17
  135. data/test/sass/results/nested.css +1 -1
  136. data/test/sass/results/parent_ref.css +2 -2
  137. data/test/sass/results/script.css +5 -5
  138. data/test/sass/results/scss_import.css +1 -1
  139. data/test/sass/script_conversion_test.rb +71 -39
  140. data/test/sass/script_test.rb +714 -123
  141. data/test/sass/scss/css_test.rb +213 -30
  142. data/test/sass/scss/rx_test.rb +8 -4
  143. data/test/sass/scss/scss_test.rb +766 -22
  144. data/test/sass/source_map_test.rb +263 -95
  145. data/test/sass/superselector_test.rb +210 -0
  146. data/test/sass/templates/_partial.sass +1 -1
  147. data/test/sass/templates/basic.sass +10 -10
  148. data/test/sass/templates/bork1.sass +1 -1
  149. data/test/sass/templates/bork5.sass +1 -1
  150. data/test/sass/templates/compact.sass +10 -10
  151. data/test/sass/templates/complex.sass +187 -187
  152. data/test/sass/templates/compressed.sass +10 -10
  153. data/test/sass/templates/expanded.sass +10 -10
  154. data/test/sass/templates/import.sass +2 -2
  155. data/test/sass/templates/importee.sass +3 -3
  156. data/test/sass/templates/mixins.sass +22 -22
  157. data/test/sass/templates/multiline.sass +4 -4
  158. data/test/sass/templates/nested.sass +13 -13
  159. data/test/sass/templates/parent_ref.sass +12 -12
  160. data/test/sass/templates/script.sass +70 -70
  161. data/test/sass/templates/scss_import.scss +2 -1
  162. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +1 -1
  163. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +2 -2
  164. data/test/sass/templates/subdir/subdir.sass +3 -3
  165. data/test/sass/templates/units.sass +10 -10
  166. data/test/sass/test_helper.rb +1 -1
  167. data/test/sass/util/multibyte_string_scanner_test.rb +11 -3
  168. data/test/sass/util/normalized_map_test.rb +1 -1
  169. data/test/sass/util/subset_map_test.rb +2 -2
  170. data/test/sass/util_test.rb +46 -45
  171. data/test/sass/value_helpers_test.rb +5 -7
  172. data/test/sass-spec.yml +3 -0
  173. data/test/test_helper.rb +7 -6
  174. data/vendor/listen/CHANGELOG.md +1 -228
  175. data/vendor/listen/Gemfile +5 -15
  176. data/vendor/listen/README.md +111 -77
  177. data/vendor/listen/Rakefile +0 -42
  178. data/vendor/listen/lib/listen/adapter.rb +195 -82
  179. data/vendor/listen/lib/listen/adapters/bsd.rb +27 -64
  180. data/vendor/listen/lib/listen/adapters/darwin.rb +21 -58
  181. data/vendor/listen/lib/listen/adapters/linux.rb +23 -55
  182. data/vendor/listen/lib/listen/adapters/polling.rb +25 -34
  183. data/vendor/listen/lib/listen/adapters/windows.rb +50 -46
  184. data/vendor/listen/lib/listen/directory_record.rb +96 -61
  185. data/vendor/listen/lib/listen/listener.rb +135 -37
  186. data/vendor/listen/lib/listen/turnstile.rb +9 -5
  187. data/vendor/listen/lib/listen/version.rb +1 -1
  188. data/vendor/listen/lib/listen.rb +33 -19
  189. data/vendor/listen/listen.gemspec +6 -0
  190. data/vendor/listen/spec/listen/adapter_spec.rb +43 -77
  191. data/vendor/listen/spec/listen/adapters/polling_spec.rb +8 -8
  192. data/vendor/listen/spec/listen/directory_record_spec.rb +81 -56
  193. data/vendor/listen/spec/listen/listener_spec.rb +128 -39
  194. data/vendor/listen/spec/listen_spec.rb +15 -21
  195. data/vendor/listen/spec/spec_helper.rb +4 -0
  196. data/vendor/listen/spec/support/adapter_helper.rb +52 -15
  197. data/vendor/listen/spec/support/directory_record_helper.rb +7 -5
  198. data/vendor/listen/spec/support/listeners_helper.rb +30 -7
  199. metadata +310 -300
  200. data/CONTRIBUTING +0 -3
  201. data/ext/mkrf_conf.rb +0 -27
  202. data/lib/sass/script/value/deprecated_false.rb +0 -55
  203. data/lib/sass/scss/script_lexer.rb +0 -15
  204. data/lib/sass/scss/script_parser.rb +0 -25
  205. data/vendor/listen/lib/listen/dependency_manager.rb +0 -126
  206. data/vendor/listen/lib/listen/multi_listener.rb +0 -143
  207. data/vendor/listen/spec/listen/dependency_manager_spec.rb +0 -107
  208. data/vendor/listen/spec/listen/multi_listener_spec.rb +0 -174
@@ -31,24 +31,36 @@ module Sass::Plugin
31
31
  # Creates a new compiler.
32
32
  #
33
33
  # @param opts [{Symbol => Object}]
34
- # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
34
+ # See {file:SASS_REFERENCE.md#Options the Sass options documentation}.
35
35
  def initialize(opts = {})
36
+ @watched_files = Set.new
36
37
  options.merge!(opts)
37
38
  end
38
39
 
39
- # Register a callback to be run after stylesheets are mass-updated.
40
+ # Register a callback to be run before stylesheets are mass-updated.
40
41
  # This is run whenever \{#update\_stylesheets} is called,
41
42
  # unless the \{file:SASS_REFERENCE.md#never_update-option `:never_update` option}
42
43
  # is enabled.
43
44
  #
44
- # @yield [individual_files]
45
- # @yieldparam individual_files [<(String, String)>]
46
- # Individual files to be updated, in addition to the directories
47
- # specified in the options.
45
+ # @yield [files]
46
+ # @yieldparam files [<(String, String, String)>]
47
+ # Individual files to be updated. Files in directories specified are included in this list.
48
48
  # The first element of each pair is the source file,
49
- # the second is the target CSS file.
49
+ # the second is the target CSS file,
50
+ # the third is the target sourcemap file.
50
51
  define_callback :updating_stylesheets
51
52
 
53
+ # Register a callback to be run after stylesheets are mass-updated.
54
+ # This is run whenever \{#update\_stylesheets} is called,
55
+ # unless the \{file:SASS_REFERENCE.md#never_update-option `:never_update` option}
56
+ # is enabled.
57
+ #
58
+ # @yield [updated_files]
59
+ # @yieldparam updated_files [<(String, String)>]
60
+ # Individual files that were updated.
61
+ # The first element of each pair is the source file, the second is the target CSS file.
62
+ define_callback :updated_stylesheets
63
+
52
64
  # Register a callback to be run after a single stylesheet is updated.
53
65
  # The callback is only run if the stylesheet is really updated;
54
66
  # if the CSS file is fresh, this won't be run.
@@ -67,6 +79,21 @@ module Sass::Plugin
67
79
  # The location of the sourcemap being generated, if any.
68
80
  define_callback :updated_stylesheet
69
81
 
82
+ # Register a callback to be run when compilation starts.
83
+ #
84
+ # In combination with on_updated_stylesheet, this could be used
85
+ # to collect compilation statistics like timing or to take a
86
+ # diff of the changes to the output file.
87
+ #
88
+ # @yield [template, css, sourcemap]
89
+ # @yieldparam template [String]
90
+ # The location of the Sass/SCSS file being updated.
91
+ # @yieldparam css [String]
92
+ # The location of the CSS file being generated.
93
+ # @yieldparam sourcemap [String]
94
+ # The location of the sourcemap being generated, if any.
95
+ define_callback :compilation_starting
96
+
70
97
  # Register a callback to be run when Sass decides not to update a stylesheet.
71
98
  # In particular, the callback is run when Sass finds that
72
99
  # the template file and none of its dependencies
@@ -139,7 +166,8 @@ module Sass::Plugin
139
166
  define_callback :template_deleted
140
167
 
141
168
  # Register a callback to be run when Sass deletes a CSS file.
142
- # This happens when the corresponding Sass/SCSS file has been deleted.
169
+ # This happens when the corresponding Sass/SCSS file has been deleted
170
+ # and when the compiler cleans the output files.
143
171
  #
144
172
  # @yield [filename]
145
173
  # @yieldparam filename [String]
@@ -147,7 +175,8 @@ module Sass::Plugin
147
175
  define_callback :deleting_css
148
176
 
149
177
  # Register a callback to be run when Sass deletes a sourcemap file.
150
- # This happens when the corresponding Sass/SCSS file has been deleted.
178
+ # This happens when the corresponding Sass/SCSS file has been deleted
179
+ # and when the compiler cleans the output files.
151
180
  #
152
181
  # @yield [filename]
153
182
  # @yieldparam filename [String]
@@ -162,35 +191,73 @@ module Sass::Plugin
162
191
  # in {file:SASS_REFERENCE.md#css_location-option `:css_location`}.
163
192
  # If it has, it updates the CSS file.
164
193
  #
165
- # @param individual_files [Array<(String, String)>]
194
+ # @param individual_files [Array<(String, String[, String])>]
166
195
  # A list of files to check for updates
167
196
  # **in addition to those specified by the
168
197
  # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
169
198
  # The first string in each pair is the location of the Sass/SCSS file,
170
199
  # the second is the location of the CSS file that it should be compiled to.
200
+ # The third string, if provided, is the location of the Sourcemap file.
171
201
  def update_stylesheets(individual_files = [])
172
- individual_files = individual_files.dup
173
202
  Sass::Plugin.checked_for_updates = true
174
203
  staleness_checker = StalenessChecker.new(engine_options)
175
204
 
176
- template_location_array.each do |template_location, css_location|
177
- Sass::Util.glob(File.join(template_location, "**", "[^_]*.s[ca]ss")).sort.each do |file|
178
- # Get the relative path to the file
179
- name = file.sub(template_location.to_s.sub(/\/*$/, '/'), "")
180
- css = css_filename(name, css_location)
181
- sourcemap = Sass::Util.sourcemap_name(css) if engine_options[:sourcemap]
182
- individual_files << [file, css, sourcemap]
183
- end
184
- end
205
+ files = file_list(individual_files)
206
+ run_updating_stylesheets(files)
185
207
 
186
- individual_files.each do |file, css, sourcemap|
208
+ updated_stylesheets = []
209
+ files.each do |file, css, sourcemap|
187
210
  # TODO: Does staleness_checker need to check the sourcemap file as well?
188
211
  if options[:always_update] || staleness_checker.stylesheet_needs_update?(css, file)
212
+ # XXX For consistency, this should return the sourcemap too, but it would
213
+ # XXX be an API change.
214
+ updated_stylesheets << [file, css]
189
215
  update_stylesheet(file, css, sourcemap)
190
216
  else
191
217
  run_not_updating_stylesheet(file, css, sourcemap)
192
218
  end
193
219
  end
220
+ run_updated_stylesheets(updated_stylesheets)
221
+ end
222
+
223
+ # Construct a list of files that might need to be compiled
224
+ # from the provided individual_files and the template_locations.
225
+ #
226
+ # Note: this method does not cache the results as they can change
227
+ # across invocations when sass files are added or removed.
228
+ #
229
+ # @param individual_files [Array<(String, String[, String])>]
230
+ # A list of files to check for updates
231
+ # **in addition to those specified by the
232
+ # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
233
+ # The first string in each pair is the location of the Sass/SCSS file,
234
+ # the second is the location of the CSS file that it should be compiled to.
235
+ # The third string, if provided, is the location of the Sourcemap file.
236
+ # @return [Array<(String, String, String)>]
237
+ # A list of [sass_file, css_file, sourcemap_file] tuples similar
238
+ # to what was passed in, but expanded to include the current state
239
+ # of the directories being updated.
240
+ def file_list(individual_files = [])
241
+ files = individual_files.map do |tuple|
242
+ if engine_options[:sourcemap] == :none
243
+ tuple[0..1]
244
+ elsif tuple.size < 3
245
+ [tuple[0], tuple[1], Sass::Util.sourcemap_name(tuple[1])]
246
+ else
247
+ tuple.dup
248
+ end
249
+ end
250
+
251
+ template_location_array.each do |template_location, css_location|
252
+ Sass::Util.glob(File.join(template_location, "**", "[^_]*.s[ca]ss")).sort.each do |file|
253
+ # Get the relative path to the file
254
+ name = Sass::Util.relative_path_from(file, template_location).to_s
255
+ css = css_filename(name, css_location)
256
+ sourcemap = Sass::Util.sourcemap_name(css) unless engine_options[:sourcemap] == :none
257
+ files << [file, css, sourcemap]
258
+ end
259
+ end
260
+ files
194
261
  end
195
262
 
196
263
  # Watches the template directory (or directories)
@@ -211,24 +278,45 @@ module Sass::Plugin
211
278
  # The version of Listen distributed with Sass is loaded by default,
212
279
  # but if another version has already been loaded that will be used instead.
213
280
  #
214
- # @param individual_files [Array<(String, String)>]
215
- # A list of files to watch for updates
281
+ # @param individual_files [Array<(String, String[, String])>]
282
+ # A list of files to check for updates
216
283
  # **in addition to those specified by the
217
284
  # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
218
285
  # The first string in each pair is the location of the Sass/SCSS file,
219
286
  # the second is the location of the CSS file that it should be compiled to.
220
- def watch(individual_files = [])
221
- update_stylesheets(individual_files)
287
+ # The third string, if provided, is the location of the Sourcemap file.
288
+ # @param options [Hash] The options that control how watching works.
289
+ # @option options [Boolean] :skip_initial_update
290
+ # Don't do an initial update when starting the watcher when true
291
+ def watch(individual_files = [], options = {})
292
+ @inferred_directories = []
293
+ options, individual_files = individual_files, [] if individual_files.is_a?(Hash)
294
+ update_stylesheets(individual_files) unless options[:skip_initial_update]
222
295
 
223
296
  directories = watched_paths
224
297
  individual_files.each do |(source, _, _)|
225
- directories << File.dirname(File.expand_path(source))
298
+ source = File.expand_path(source)
299
+ @watched_files << Sass::Util.realpath(source).to_s
300
+ @inferred_directories << File.dirname(source)
226
301
  end
302
+
303
+ directories += @inferred_directories
227
304
  directories = remove_redundant_directories(directories)
228
305
 
306
+ # A Listen version prior to 2.0 will write a test file to a directory to
307
+ # see if a watcher supports watching that directory. That breaks horribly
308
+ # on read-only directories, so we filter those out.
309
+ unless Sass::Util.listen_geq_2?
310
+ directories = directories.select {|d| File.directory?(d) && File.writable?(d)}
311
+ end
312
+
229
313
  # TODO: Keep better track of what depends on what
230
314
  # so we don't have to run a global update every time anything changes.
231
- listener_args = directories + [{:relative_paths => false}]
315
+ # XXX The :additional_watch_paths option exists for Compass to use until
316
+ # a deprecated feature is removed. It may be removed without warning.
317
+ listener_args = directories +
318
+ Array(options[:additional_watch_paths]) +
319
+ [{:relative_paths => false}]
232
320
 
233
321
  # The native windows listener is much slower than the polling option, according to
234
322
  # https://github.com/nex3/sass/commit/a3031856b22bc834a5417dedecb038b7be9b9e3e
@@ -240,49 +328,8 @@ module Sass::Plugin
240
328
  end
241
329
 
242
330
  listener = create_listener(*listener_args) do |modified, added, removed|
243
- recompile_required = false
244
-
245
- modified.uniq.each do |f|
246
- next unless watched_file?(f)
247
- recompile_required = true
248
- run_template_modified(relative_to_pwd(f))
249
- end
250
-
251
- added.uniq.each do |f|
252
- next unless watched_file?(f)
253
- recompile_required = true
254
- run_template_created(relative_to_pwd(f))
255
- end
256
-
257
- removed.uniq.each do |f|
258
- if (files = individual_files.find {|(source, _, _)| File.expand_path(source) == f})
259
- recompile_required = true
260
- # This was a file we were watching explicitly and compiling to a particular location.
261
- # Delete the corresponding file.
262
- try_delete_css files[1]
263
- else
264
- next unless watched_file?(f)
265
- recompile_required = true
266
- # Look for the sass directory that contained the sass file
267
- # And try to remove the css file that corresponds to it
268
- template_location_array.each do |(sass_dir, css_dir)|
269
- sass_dir = File.expand_path(sass_dir)
270
- if child_of_directory?(sass_dir, f)
271
- remainder = f[(sass_dir.size + 1)..-1]
272
- try_delete_css(css_filename(remainder, css_dir))
273
- break
274
- end
275
- end
276
- end
277
- run_template_deleted(relative_to_pwd(f))
278
- end
279
-
280
- if recompile_required
281
- # In case a file we're watching is removed and then recreated we
282
- # prune out the non-existant files here.
283
- watched_files_remaining = individual_files.select {|(source, _, _)| File.exists?(source)}
284
- update_stylesheets(watched_files_remaining)
285
- end
331
+ on_file_changed(individual_files, modified, added, removed)
332
+ yield(modified, added, removed) if block_given?
286
333
  end
287
334
 
288
335
  if poll && !Sass::Util.listen_geq_2?
@@ -302,6 +349,8 @@ module Sass::Plugin
302
349
  def engine_options(additional_options = {})
303
350
  opts = options.merge(additional_options)
304
351
  opts[:load_paths] = load_paths(opts)
352
+ options[:sourcemap] = :auto if options[:sourcemap] == true
353
+ options[:sourcemap] = :none if options[:sourcemap] == false
305
354
  opts
306
355
  end
307
356
 
@@ -310,12 +359,42 @@ module Sass::Plugin
310
359
  StalenessChecker.stylesheet_needs_update?(css_file, template_file)
311
360
  end
312
361
 
362
+ # Remove all output files that would be created by calling update_stylesheets, if they exist.
363
+ #
364
+ # This method runs the deleting_css and deleting_sourcemap callbacks for
365
+ # the files that are deleted.
366
+ #
367
+ # @param individual_files [Array<(String, String[, String])>]
368
+ # A list of files to check for updates
369
+ # **in addition to those specified by the
370
+ # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
371
+ # The first string in each pair is the location of the Sass/SCSS file,
372
+ # the second is the location of the CSS file that it should be compiled to.
373
+ # The third string, if provided, is the location of the Sourcemap file.
374
+ def clean(individual_files = [])
375
+ file_list(individual_files).each do |(_, css_file, sourcemap_file)|
376
+ if File.exist?(css_file)
377
+ run_deleting_css css_file
378
+ File.delete(css_file)
379
+ end
380
+ if sourcemap_file && File.exist?(sourcemap_file)
381
+ run_deleting_sourcemap sourcemap_file
382
+ File.delete(sourcemap_file)
383
+ end
384
+ end
385
+ nil
386
+ end
387
+
313
388
  private
314
389
 
315
390
  def create_listener(*args, &block)
316
- require 'listen'
391
+ Sass::Util.load_listen!
317
392
  if Sass::Util.listen_geq_2?
318
- Listen.to(*args, &block)
393
+ # Work around guard/listen#243.
394
+ options = args.pop if args.last.is_a?(Hash)
395
+ args.map do |dir|
396
+ Listen.to(dir, options, &block)
397
+ end
319
398
  else
320
399
  Listen::Listener.new(*args, &block)
321
400
  end
@@ -323,16 +402,13 @@ module Sass::Plugin
323
402
 
324
403
  def listen_to(listener)
325
404
  if Sass::Util.listen_geq_2?
326
- listener.start
327
- listener.thread.join
328
- listener.stop # Partially work around guard/listen#146
405
+ listener.map {|l| l.start}
406
+ sleep
329
407
  else
330
- begin
331
- listener.start!
332
- rescue Interrupt
333
- # Squelch Interrupt for clean exit from Listen::Listener
334
- end
408
+ listener.start!
335
409
  end
410
+ rescue Interrupt
411
+ # Squelch Interrupt for clean exit from Listen::Listener
336
412
  end
337
413
 
338
414
  def remove_redundant_directories(directories)
@@ -351,9 +427,55 @@ module Sass::Plugin
351
427
  dedupped
352
428
  end
353
429
 
430
+ def on_file_changed(individual_files, modified, added, removed)
431
+ recompile_required = false
432
+
433
+ modified.uniq.each do |f|
434
+ next unless watched_file?(f)
435
+ recompile_required = true
436
+ run_template_modified(relative_to_pwd(f))
437
+ end
438
+
439
+ added.uniq.each do |f|
440
+ next unless watched_file?(f)
441
+ recompile_required = true
442
+ run_template_created(relative_to_pwd(f))
443
+ end
444
+
445
+ removed.uniq.each do |f|
446
+ next unless watched_file?(f)
447
+ run_template_deleted(relative_to_pwd(f))
448
+ if (files = individual_files.find {|(source, _, _)| File.expand_path(source) == f})
449
+ recompile_required = true
450
+ # This was a file we were watching explicitly and compiling to a particular location.
451
+ # Delete the corresponding file.
452
+ try_delete_css files[1]
453
+ else
454
+ next unless watched_file?(f)
455
+ recompile_required = true
456
+ # Look for the sass directory that contained the sass file
457
+ # And try to remove the css file that corresponds to it
458
+ template_location_array.each do |(sass_dir, css_dir)|
459
+ sass_dir = File.expand_path(sass_dir)
460
+ next unless child_of_directory?(sass_dir, f)
461
+ remainder = f[(sass_dir.size + 1)..-1]
462
+ try_delete_css(css_filename(remainder, css_dir))
463
+ break
464
+ end
465
+ end
466
+ end
467
+
468
+ return unless recompile_required
469
+
470
+ # In case a file we're watching is removed and then recreated we
471
+ # prune out the non-existant files here.
472
+ watched_files_remaining = individual_files.select {|(source, _, _)| File.exist?(source)}
473
+ update_stylesheets(watched_files_remaining)
474
+ end
475
+
354
476
  def update_stylesheet(filename, css, sourcemap)
355
477
  dir = File.dirname(css)
356
- unless File.exists?(dir)
478
+ unless File.exist?(dir)
357
479
  run_creating_directory dir
358
480
  FileUtils.mkdir_p dir
359
481
  end
@@ -364,6 +486,7 @@ module Sass::Plugin
364
486
  :filename => filename,
365
487
  :sourcemap_filename => sourcemap)
366
488
  mapping = nil
489
+ run_compilation_starting(filename, css, sourcemap)
367
490
  engine = Sass::Engine.for_file(filename, engine_opts)
368
491
  if sourcemap
369
492
  rendered, mapping = engine.render_with_sourcemap(File.basename(sourcemap))
@@ -373,12 +496,16 @@ module Sass::Plugin
373
496
  rescue StandardError => e
374
497
  compilation_error_occured = true
375
498
  run_compilation_error e, filename, css, sourcemap
376
- rendered = Sass::SyntaxError.exception_to_css(e, options)
499
+ raise e unless options[:full_exception]
500
+ rendered = Sass::SyntaxError.exception_to_css(e, options[:line] || 1)
377
501
  end
378
502
 
379
503
  write_file(css, rendered)
380
504
  if mapping
381
- write_file(sourcemap, mapping.to_json(:css_path => css, :sourcemap_path => sourcemap))
505
+ write_file(
506
+ sourcemap,
507
+ mapping.to_json(
508
+ :css_path => css, :sourcemap_path => sourcemap, :type => options[:sourcemap]))
382
509
  end
383
510
  run_updated_stylesheet(filename, css, sourcemap) unless compilation_error_occured
384
511
  end
@@ -393,19 +520,26 @@ module Sass::Plugin
393
520
  end
394
521
 
395
522
  def try_delete_css(css)
396
- if File.exists?(css)
523
+ if File.exist?(css)
397
524
  run_deleting_css css
398
525
  File.delete css
399
526
  end
400
527
  map = Sass::Util.sourcemap_name(css)
401
- if File.exists?(map)
402
- run_deleting_sourcemap map
403
- File.delete map
404
- end
528
+
529
+ return unless File.exist?(map)
530
+
531
+ run_deleting_sourcemap map
532
+ File.delete map
405
533
  end
406
534
 
407
535
  def watched_file?(file)
408
- normalized_load_paths.find {|lp| lp.watched_file?(file)}
536
+ @watched_files.include?(file) ||
537
+ normalized_load_paths.any? {|lp| lp.watched_file?(file)} ||
538
+ @inferred_directories.any? {|d| sass_file_in_directory?(d, file)}
539
+ end
540
+
541
+ def sass_file_in_directory?(directory, filename)
542
+ filename =~ /\.s[ac]ss$/ && filename.start_with?(directory + File::SEPARATOR)
409
543
  end
410
544
 
411
545
  def watched_paths
@@ -435,7 +569,7 @@ module Sass::Plugin
435
569
  end
436
570
 
437
571
  def relative_to_pwd(f)
438
- Sass::Util.pathname(f).relative_path_from(Sass::Util.pathname(Dir.pwd)).to_s
572
+ Sass::Util.relative_path_from(f, Dir.pwd).to_s
439
573
  rescue ArgumentError # when a relative path cannot be computed
440
574
  f
441
575
  end
@@ -1,8 +1,10 @@
1
1
  module Sass
2
2
  module Plugin
3
- # We keep configuration in its own self-contained file
4
- # so that we can load it independently in Rails 3,
5
- # where the full plugin stuff is lazy-loaded.
3
+ # We keep configuration in its own self-contained file so that we can load
4
+ # it independently in Rails 3, where the full plugin stuff is lazy-loaded.
5
+ #
6
+ # Note that this is not guaranteed to be thread-safe. For guaranteed thread
7
+ # safety, use a separate {Sass::Plugin} for each thread.
6
8
  module Configuration
7
9
  # Returns the default options for a {Sass::Plugin::Compiler}.
8
10
  #
@@ -25,7 +27,7 @@ module Sass
25
27
  end
26
28
 
27
29
  # An options hash.
28
- # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
30
+ # See {file:SASS_REFERENCE.md#Options the Sass options documentation}.
29
31
  #
30
32
  # @return [{Symbol => Object}]
31
33
  def options
@@ -85,33 +87,47 @@ module Sass
85
87
  # See the {file:SASS_REFERENCE.md#template_location-option `:template_location` option}
86
88
  # for details.
87
89
  #
90
+ # Modifications to the returned array may not be persistent. Use {#add_template_location}
91
+ # and {#remove_template_location} instead.
92
+ #
88
93
  # @return [Array<(String, String)>]
89
94
  # An array of `[template_location, css_location]` pairs.
90
95
  def template_location_array
91
- old_template_location = options[:template_location]
92
- normalize_template_location!
93
- options[:template_location]
94
- ensure
95
- options[:template_location] = old_template_location
96
+ convert_template_location(options[:template_location], options[:css_location])
96
97
  end
97
98
 
98
99
  private
99
100
 
100
- def normalize_template_location!
101
- return if options[:template_location].is_a?(Array)
102
- options[:template_location] =
103
- case options[:template_location]
104
- when nil
105
- if options[:css_location]
106
- [[File.join(options[:css_location], 'sass'), options[:css_location]]]
107
- else
108
- []
109
- end
110
- when String
111
- [[options[:template_location], options[:css_location]]]
101
+ # Returns the given template location, as an array. If it's already an array,
102
+ # it is returned unmodified. Otherwise, a new array is created and returned.
103
+ #
104
+ # @param template_location [String, Array<(String, String)>]
105
+ # A single template location, or a pre-normalized array of template
106
+ # locations and CSS locations.
107
+ # @param css_location [String?]
108
+ # The location for compiled CSS files.
109
+ # @return [Array<(String, String)>]
110
+ # An array of `[template_location, css_location]` pairs.
111
+ def convert_template_location(template_location, css_location)
112
+ return template_location if template_location.is_a?(Array)
113
+
114
+ case template_location
115
+ when nil
116
+ if css_location
117
+ [[File.join(css_location, 'sass'), css_location]]
112
118
  else
113
- options[:template_location].to_a
119
+ []
114
120
  end
121
+ when String
122
+ [[template_location, css_location]]
123
+ else
124
+ template_location.to_a
125
+ end
126
+ end
127
+
128
+ def normalize_template_location!
129
+ options[:template_location] = convert_template_location(
130
+ options[:template_location], options[:css_location])
115
131
  end
116
132
  end
117
133
  end
@@ -2,7 +2,7 @@ unless defined?(Sass::MERB_LOADED)
2
2
  Sass::MERB_LOADED = true
3
3
 
4
4
  module Sass::Plugin::Configuration
5
- # Different default options in a m envirionment.
5
+ # Different default options in a m environment.
6
6
  def default_options
7
7
  @default_options ||= begin
8
8
  version = Merb::VERSION.split('.').map {|n| n.to_i}
@@ -15,7 +15,7 @@ unless defined?(Sass::MERB_LOADED)
15
15
  end
16
16
 
17
17
  {
18
- :always_update => false,
18
+ :always_update => false,
19
19
  :template_location => root + '/public/stylesheets/sass',
20
20
  :css_location => root + '/public/stylesheets',
21
21
  :cache_location => root + '/tmp/sass-cache',
@@ -9,19 +9,19 @@ module Sass
9
9
  #
10
10
  # ## Customize
11
11
  #
12
- # Sass::Plugin.options.merge(
12
+ # Sass::Plugin.options.merge!(
13
13
  # :cache_location => './tmp/sass-cache',
14
14
  # :never_update => environment != :production,
15
15
  # :full_exception => environment != :production)
16
16
  #
17
- # {file:SASS_REFERENCE.md#options See the Reference for more options}.
17
+ # {file:SASS_REFERENCE.md#Options See the Reference for more options}.
18
18
  #
19
19
  # ## Use
20
20
  #
21
21
  # Put your Sass files in `public/stylesheets/sass`.
22
22
  # Your CSS will be generated in `public/stylesheets`,
23
23
  # and regenerated every request if necessary.
24
- # The locations and frequency {file:SASS_REFERENCE.md#options can be customized}.
24
+ # The locations and frequency {file:SASS_REFERENCE.md#Options can be customized}.
25
25
  # That's all there is to it!
26
26
  class Rack
27
27
  # The delay, in seconds, between update checks.
@@ -2,7 +2,7 @@ unless defined?(Sass::RAILS_LOADED)
2
2
  Sass::RAILS_LOADED = true
3
3
 
4
4
  module Sass::Plugin::Configuration
5
- # Different default options in a rails envirionment.
5
+ # Different default options in a rails environment.
6
6
  def default_options
7
7
  return @default_options if @default_options
8
8
  opts = {
@@ -39,14 +39,14 @@ module Sass
39
39
  # for checking the staleness of several stylesheets at once.
40
40
  #
41
41
  # @param options [{Symbol => Object}]
42
- # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
42
+ # See {file:SASS_REFERENCE.md#Options the Sass options documentation}.
43
43
  def initialize(options)
44
44
  # URIs that are being actively checked for staleness. Protects against
45
45
  # import loops.
46
46
  @actively_checking = Set.new
47
47
 
48
48
  # Entries in the following instance-level caches are never explicitly expired.
49
- # Instead they are supposed to automaticaly go out of scope when a series of staleness
49
+ # Instead they are supposed to automatically go out of scope when a series of staleness
50
50
  # checks (this instance of StalenessChecker was created for) is finished.
51
51
  @mtimes, @dependencies_stale, @parse_trees = {}, {}, {}
52
52
  @options = Sass::Engine.normalize_options(options)
@@ -72,7 +72,7 @@ module Sass
72
72
  # Returns whether a Sass or SCSS stylesheet has been modified since a given time.
73
73
  #
74
74
  # @param template_file [String] The location of the Sass or SCSS template.
75
- # @param mtime [Fixnum] The modification time to check against.
75
+ # @param mtime [Time] The modification time to check against.
76
76
  # @param importer [Sass::Importers::Base] The importer used to locate the stylesheet.
77
77
  # Defaults to the filesystem importer.
78
78
  # @return [Boolean] Whether the stylesheet has been modified.
@@ -103,7 +103,7 @@ module Sass
103
103
  # so it's better to use when checking multiple stylesheets at once.
104
104
  #
105
105
  # @param template_file [String] The location of the Sass or SCSS template.
106
- # @param mtime [Fixnum] The modification time to check against.
106
+ # @param mtime [Time] The modification time to check against.
107
107
  # @param importer [Sass::Importers::Base] The importer used to locate the stylesheet.
108
108
  # Defaults to the filesystem importer.
109
109
  # @return [Boolean] Whether the stylesheet has been modified.
data/lib/sass/plugin.rb CHANGED
@@ -12,7 +12,7 @@ module Sass
12
12
  # when it's used as a plugin for various frameworks.
13
13
  # All Rack-enabled frameworks are supported out of the box.
14
14
  # The plugin is
15
- # {file:SASS_REFERENCE.md#rails_merb_plugin automatically activated for Rails and Merb}.
15
+ # {file:SASS_REFERENCE.md#Rack_Rails_Merb_Plugin automatically activated for Rails and Merb}.
16
16
  # Other frameworks must enable it explicitly; see {Sass::Plugin::Rack}.
17
17
  #
18
18
  # This module has a large set of callbacks available
@@ -93,7 +93,8 @@ module Sass
93
93
  # the second is the location of the CSS file that it should be compiled to.
94
94
  # @see #update_stylesheets
95
95
  def force_update_stylesheets(individual_files = [])
96
- Compiler.new(options.dup.merge(
96
+ Compiler.new(
97
+ options.dup.merge(
97
98
  :never_update => false,
98
99
  :always_update => true,
99
100
  :cache => false)).update_stylesheets(individual_files)
@@ -123,9 +124,9 @@ module Sass
123
124
  end
124
125
  end
125
126
 
126
- # On Rails 3+ the rails plugin is loaded at the right time in railtie.rb
127
- if defined?(ActionController) && !Sass::Util.ap_geq_3?
128
- require 'sass/plugin/rails'
127
+ if defined?(ActionController)
128
+ # On Rails 3+ the rails plugin is loaded at the right time in railtie.rb
129
+ require 'sass/plugin/rails' unless Sass::Util.ap_geq_3?
129
130
  elsif defined?(Merb::Plugins)
130
131
  require 'sass/plugin/merb'
131
132
  else