sass 3.3.0 → 3.4.25

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 (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