sass 3.1.0 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (260) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING +1 -1
  3. data/MIT-LICENSE +2 -2
  4. data/README.md +29 -17
  5. data/Rakefile +43 -9
  6. data/VERSION +1 -1
  7. data/VERSION_DATE +1 -0
  8. data/VERSION_NAME +1 -1
  9. data/bin/sass +6 -1
  10. data/bin/sass-convert +6 -1
  11. data/bin/scss +6 -1
  12. data/ext/mkrf_conf.rb +27 -0
  13. data/lib/sass/cache_stores/base.rb +7 -3
  14. data/lib/sass/cache_stores/chain.rb +3 -2
  15. data/lib/sass/cache_stores/filesystem.rb +5 -7
  16. data/lib/sass/cache_stores/memory.rb +1 -1
  17. data/lib/sass/cache_stores/null.rb +2 -2
  18. data/lib/sass/callbacks.rb +2 -1
  19. data/lib/sass/css.rb +168 -53
  20. data/lib/sass/engine.rb +502 -174
  21. data/lib/sass/environment.rb +151 -111
  22. data/lib/sass/error.rb +7 -7
  23. data/lib/sass/exec.rb +176 -60
  24. data/lib/sass/features.rb +40 -0
  25. data/lib/sass/importers/base.rb +46 -7
  26. data/lib/sass/importers/deprecated_path.rb +51 -0
  27. data/lib/sass/importers/filesystem.rb +113 -30
  28. data/lib/sass/importers.rb +1 -0
  29. data/lib/sass/logger/base.rb +30 -0
  30. data/lib/sass/logger/log_level.rb +45 -0
  31. data/lib/sass/logger.rb +12 -0
  32. data/lib/sass/media.rb +213 -0
  33. data/lib/sass/plugin/compiler.rb +194 -104
  34. data/lib/sass/plugin/configuration.rb +18 -25
  35. data/lib/sass/plugin/merb.rb +1 -1
  36. data/lib/sass/plugin/staleness_checker.rb +37 -11
  37. data/lib/sass/plugin.rb +10 -13
  38. data/lib/sass/railtie.rb +2 -1
  39. data/lib/sass/repl.rb +5 -6
  40. data/lib/sass/script/css_lexer.rb +8 -4
  41. data/lib/sass/script/css_parser.rb +5 -2
  42. data/lib/sass/script/functions.rb +1547 -618
  43. data/lib/sass/script/lexer.rb +122 -72
  44. data/lib/sass/script/parser.rb +304 -135
  45. data/lib/sass/script/tree/funcall.rb +306 -0
  46. data/lib/sass/script/{interpolation.rb → tree/interpolation.rb} +43 -13
  47. data/lib/sass/script/tree/list_literal.rb +77 -0
  48. data/lib/sass/script/tree/literal.rb +45 -0
  49. data/lib/sass/script/tree/map_literal.rb +64 -0
  50. data/lib/sass/script/{node.rb → tree/node.rb} +30 -12
  51. data/lib/sass/script/{operation.rb → tree/operation.rb} +33 -21
  52. data/lib/sass/script/{string_interpolation.rb → tree/string_interpolation.rb} +14 -4
  53. data/lib/sass/script/{unary_operation.rb → tree/unary_operation.rb} +21 -9
  54. data/lib/sass/script/tree/variable.rb +57 -0
  55. data/lib/sass/script/tree.rb +15 -0
  56. data/lib/sass/script/value/arg_list.rb +36 -0
  57. data/lib/sass/script/value/base.rb +238 -0
  58. data/lib/sass/script/value/bool.rb +40 -0
  59. data/lib/sass/script/{color.rb → value/color.rb} +256 -74
  60. data/lib/sass/script/value/deprecated_false.rb +55 -0
  61. data/lib/sass/script/value/helpers.rb +155 -0
  62. data/lib/sass/script/value/list.rb +128 -0
  63. data/lib/sass/script/value/map.rb +70 -0
  64. data/lib/sass/script/value/null.rb +49 -0
  65. data/lib/sass/script/{number.rb → value/number.rb} +115 -62
  66. data/lib/sass/script/{string.rb → value/string.rb} +9 -11
  67. data/lib/sass/script/value.rb +12 -0
  68. data/lib/sass/script.rb +35 -9
  69. data/lib/sass/scss/css_parser.rb +2 -12
  70. data/lib/sass/scss/parser.rb +657 -230
  71. data/lib/sass/scss/rx.rb +17 -12
  72. data/lib/sass/scss/static_parser.rb +37 -6
  73. data/lib/sass/scss.rb +0 -1
  74. data/lib/sass/selector/abstract_sequence.rb +35 -3
  75. data/lib/sass/selector/comma_sequence.rb +29 -14
  76. data/lib/sass/selector/sequence.rb +371 -74
  77. data/lib/sass/selector/simple.rb +28 -13
  78. data/lib/sass/selector/simple_sequence.rb +163 -36
  79. data/lib/sass/selector.rb +138 -36
  80. data/lib/sass/shared.rb +3 -5
  81. data/lib/sass/source/map.rb +196 -0
  82. data/lib/sass/source/position.rb +39 -0
  83. data/lib/sass/source/range.rb +41 -0
  84. data/lib/sass/stack.rb +126 -0
  85. data/lib/sass/supports.rb +228 -0
  86. data/lib/sass/tree/at_root_node.rb +82 -0
  87. data/lib/sass/tree/comment_node.rb +34 -29
  88. data/lib/sass/tree/content_node.rb +9 -0
  89. data/lib/sass/tree/css_import_node.rb +60 -0
  90. data/lib/sass/tree/debug_node.rb +3 -3
  91. data/lib/sass/tree/directive_node.rb +33 -3
  92. data/lib/sass/tree/each_node.rb +9 -9
  93. data/lib/sass/tree/extend_node.rb +20 -6
  94. data/lib/sass/tree/for_node.rb +6 -6
  95. data/lib/sass/tree/function_node.rb +12 -4
  96. data/lib/sass/tree/if_node.rb +2 -15
  97. data/lib/sass/tree/import_node.rb +11 -5
  98. data/lib/sass/tree/media_node.rb +27 -11
  99. data/lib/sass/tree/mixin_def_node.rb +15 -4
  100. data/lib/sass/tree/mixin_node.rb +27 -7
  101. data/lib/sass/tree/node.rb +69 -35
  102. data/lib/sass/tree/prop_node.rb +47 -31
  103. data/lib/sass/tree/return_node.rb +4 -3
  104. data/lib/sass/tree/root_node.rb +20 -4
  105. data/lib/sass/tree/rule_node.rb +37 -26
  106. data/lib/sass/tree/supports_node.rb +38 -0
  107. data/lib/sass/tree/trace_node.rb +33 -0
  108. data/lib/sass/tree/variable_node.rb +10 -4
  109. data/lib/sass/tree/visitors/base.rb +5 -8
  110. data/lib/sass/tree/visitors/check_nesting.rb +67 -52
  111. data/lib/sass/tree/visitors/convert.rb +134 -53
  112. data/lib/sass/tree/visitors/cssize.rb +245 -51
  113. data/lib/sass/tree/visitors/deep_copy.rb +102 -0
  114. data/lib/sass/tree/visitors/extend.rb +68 -0
  115. data/lib/sass/tree/visitors/perform.rb +331 -105
  116. data/lib/sass/tree/visitors/set_options.rb +125 -0
  117. data/lib/sass/tree/visitors/to_css.rb +259 -95
  118. data/lib/sass/tree/warn_node.rb +3 -3
  119. data/lib/sass/tree/while_node.rb +3 -3
  120. data/lib/sass/util/cross_platform_random.rb +19 -0
  121. data/lib/sass/util/multibyte_string_scanner.rb +157 -0
  122. data/lib/sass/util/normalized_map.rb +130 -0
  123. data/lib/sass/util/ordered_hash.rb +192 -0
  124. data/lib/sass/util/subset_map.rb +11 -2
  125. data/lib/sass/util/test.rb +9 -0
  126. data/lib/sass/util.rb +565 -39
  127. data/lib/sass/version.rb +27 -15
  128. data/lib/sass.rb +39 -4
  129. data/test/sass/cache_test.rb +15 -0
  130. data/test/sass/compiler_test.rb +223 -0
  131. data/test/sass/conversion_test.rb +901 -107
  132. data/test/sass/css2sass_test.rb +94 -0
  133. data/test/sass/engine_test.rb +1059 -164
  134. data/test/sass/exec_test.rb +86 -0
  135. data/test/sass/extend_test.rb +933 -837
  136. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  137. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  138. data/test/sass/functions_test.rb +995 -136
  139. data/test/sass/importer_test.rb +338 -18
  140. data/test/sass/logger_test.rb +58 -0
  141. data/test/sass/more_results/more_import.css +2 -2
  142. data/test/sass/plugin_test.rb +114 -30
  143. data/test/sass/results/cached_import_option.css +3 -0
  144. data/test/sass/results/filename_fn.css +3 -0
  145. data/test/sass/results/import.css +2 -2
  146. data/test/sass/results/import_charset.css +1 -0
  147. data/test/sass/results/import_charset_1_8.css +1 -0
  148. data/test/sass/results/import_charset_ibm866.css +1 -0
  149. data/test/sass/results/import_content.css +1 -0
  150. data/test/sass/results/script.css +1 -1
  151. data/test/sass/results/scss_import.css +2 -2
  152. data/test/sass/results/units.css +2 -2
  153. data/test/sass/script_conversion_test.rb +43 -1
  154. data/test/sass/script_test.rb +380 -36
  155. data/test/sass/scss/css_test.rb +257 -75
  156. data/test/sass/scss/scss_test.rb +2322 -110
  157. data/test/sass/source_map_test.rb +887 -0
  158. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  159. data/test/sass/templates/_double_import_loop2.sass +1 -0
  160. data/test/sass/templates/_filename_fn_import.scss +11 -0
  161. data/test/sass/templates/_imported_content.sass +3 -0
  162. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  163. data/test/sass/templates/bork5.sass +3 -0
  164. data/test/sass/templates/cached_import_option.scss +3 -0
  165. data/test/sass/templates/double_import_loop1.sass +1 -0
  166. data/test/sass/templates/filename_fn.scss +18 -0
  167. data/test/sass/templates/import_charset.sass +2 -0
  168. data/test/sass/templates/import_charset_1_8.sass +2 -0
  169. data/test/sass/templates/import_charset_ibm866.sass +2 -0
  170. data/test/sass/templates/import_content.sass +4 -0
  171. data/test/sass/templates/same_name_different_ext.sass +2 -0
  172. data/test/sass/templates/same_name_different_ext.scss +1 -0
  173. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  174. data/test/sass/templates/single_import_loop.sass +1 -0
  175. data/test/sass/templates/subdir/import_up1.scss +1 -0
  176. data/test/sass/templates/subdir/import_up2.scss +1 -0
  177. data/test/sass/test_helper.rb +1 -1
  178. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  179. data/test/sass/util/normalized_map_test.rb +51 -0
  180. data/test/sass/util_test.rb +183 -0
  181. data/test/sass/value_helpers_test.rb +181 -0
  182. data/test/test_helper.rb +45 -5
  183. data/vendor/listen/CHANGELOG.md +228 -0
  184. data/vendor/listen/CONTRIBUTING.md +38 -0
  185. data/vendor/listen/Gemfile +30 -0
  186. data/vendor/listen/Guardfile +8 -0
  187. data/vendor/{fssm → listen}/LICENSE +1 -1
  188. data/vendor/listen/README.md +315 -0
  189. data/vendor/listen/Rakefile +47 -0
  190. data/vendor/listen/Vagrantfile +96 -0
  191. data/vendor/listen/lib/listen/adapter.rb +214 -0
  192. data/vendor/listen/lib/listen/adapters/bsd.rb +112 -0
  193. data/vendor/listen/lib/listen/adapters/darwin.rb +85 -0
  194. data/vendor/listen/lib/listen/adapters/linux.rb +113 -0
  195. data/vendor/listen/lib/listen/adapters/polling.rb +67 -0
  196. data/vendor/listen/lib/listen/adapters/windows.rb +87 -0
  197. data/vendor/listen/lib/listen/dependency_manager.rb +126 -0
  198. data/vendor/listen/lib/listen/directory_record.rb +371 -0
  199. data/vendor/listen/lib/listen/listener.rb +225 -0
  200. data/vendor/listen/lib/listen/multi_listener.rb +143 -0
  201. data/vendor/listen/lib/listen/turnstile.rb +28 -0
  202. data/vendor/listen/lib/listen/version.rb +3 -0
  203. data/vendor/listen/lib/listen.rb +40 -0
  204. data/vendor/listen/listen.gemspec +22 -0
  205. data/vendor/listen/spec/listen/adapter_spec.rb +183 -0
  206. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +36 -0
  207. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +37 -0
  208. data/vendor/listen/spec/listen/adapters/linux_spec.rb +47 -0
  209. data/vendor/listen/spec/listen/adapters/polling_spec.rb +68 -0
  210. data/vendor/listen/spec/listen/adapters/windows_spec.rb +30 -0
  211. data/vendor/listen/spec/listen/dependency_manager_spec.rb +107 -0
  212. data/vendor/listen/spec/listen/directory_record_spec.rb +1225 -0
  213. data/vendor/listen/spec/listen/listener_spec.rb +169 -0
  214. data/vendor/listen/spec/listen/multi_listener_spec.rb +174 -0
  215. data/vendor/listen/spec/listen/turnstile_spec.rb +56 -0
  216. data/vendor/listen/spec/listen_spec.rb +73 -0
  217. data/vendor/listen/spec/spec_helper.rb +21 -0
  218. data/vendor/listen/spec/support/adapter_helper.rb +629 -0
  219. data/vendor/listen/spec/support/directory_record_helper.rb +55 -0
  220. data/vendor/listen/spec/support/fixtures_helper.rb +29 -0
  221. data/vendor/listen/spec/support/listeners_helper.rb +156 -0
  222. data/vendor/listen/spec/support/platform_helper.rb +15 -0
  223. metadata +344 -271
  224. data/lib/sass/less.rb +0 -382
  225. data/lib/sass/script/bool.rb +0 -18
  226. data/lib/sass/script/funcall.rb +0 -162
  227. data/lib/sass/script/list.rb +0 -76
  228. data/lib/sass/script/literal.rb +0 -245
  229. data/lib/sass/script/variable.rb +0 -54
  230. data/lib/sass/scss/sass_parser.rb +0 -11
  231. data/test/sass/less_conversion_test.rb +0 -653
  232. data/vendor/fssm/README.markdown +0 -55
  233. data/vendor/fssm/Rakefile +0 -59
  234. data/vendor/fssm/VERSION.yml +0 -5
  235. data/vendor/fssm/example.rb +0 -9
  236. data/vendor/fssm/fssm.gemspec +0 -77
  237. data/vendor/fssm/lib/fssm/backends/fsevents.rb +0 -36
  238. data/vendor/fssm/lib/fssm/backends/inotify.rb +0 -26
  239. data/vendor/fssm/lib/fssm/backends/polling.rb +0 -25
  240. data/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +0 -131
  241. data/vendor/fssm/lib/fssm/monitor.rb +0 -26
  242. data/vendor/fssm/lib/fssm/path.rb +0 -91
  243. data/vendor/fssm/lib/fssm/pathname.rb +0 -502
  244. data/vendor/fssm/lib/fssm/state/directory.rb +0 -57
  245. data/vendor/fssm/lib/fssm/state/file.rb +0 -24
  246. data/vendor/fssm/lib/fssm/support.rb +0 -63
  247. data/vendor/fssm/lib/fssm/tree.rb +0 -176
  248. data/vendor/fssm/lib/fssm.rb +0 -33
  249. data/vendor/fssm/profile/prof-cache.rb +0 -40
  250. data/vendor/fssm/profile/prof-fssm-pathname.html +0 -1231
  251. data/vendor/fssm/profile/prof-pathname.rb +0 -68
  252. data/vendor/fssm/profile/prof-plain-pathname.html +0 -988
  253. data/vendor/fssm/profile/prof.html +0 -2379
  254. data/vendor/fssm/spec/path_spec.rb +0 -75
  255. data/vendor/fssm/spec/root/duck/quack.txt +0 -0
  256. data/vendor/fssm/spec/root/file.css +0 -0
  257. data/vendor/fssm/spec/root/file.rb +0 -0
  258. data/vendor/fssm/spec/root/file.yml +0 -0
  259. data/vendor/fssm/spec/root/moo/cow.txt +0 -0
  260. data/vendor/fssm/spec/spec_helper.rb +0 -14
data/lib/sass/exec.rb CHANGED
@@ -17,6 +17,7 @@ module Sass
17
17
  #
18
18
  # @see #parse
19
19
  def parse!
20
+ # rubocop:disable RescueException
20
21
  begin
21
22
  parse
22
23
  rescue Exception => e
@@ -28,6 +29,7 @@ module Sass
28
29
  exit 1
29
30
  end
30
31
  exit 0
32
+ # rubocop:enable RescueException
31
33
  end
32
34
 
33
35
  # Parses the command-line arguments and runs the executable.
@@ -58,7 +60,9 @@ module Sass
58
60
  def get_line(exception)
59
61
  # SyntaxErrors have weird line reporting
60
62
  # when there's trailing whitespace
61
- return (exception.message.scan(/:(\d+)/).first || ["??"]).first if exception.is_a?(::SyntaxError)
63
+ if exception.is_a?(::SyntaxError)
64
+ return (exception.message.scan(/:(\d+)/).first || ["??"]).first
65
+ end
62
66
  (exception.backtrace[0].scan(/:(\d+)/).first || ["??"]).first
63
67
  end
64
68
 
@@ -70,7 +74,8 @@ module Sass
70
74
  #
71
75
  # @param opts [OptionParser]
72
76
  def set_opts(opts)
73
- opts.on('-s', '--stdin', :NONE, 'Read input from standard input instead of an input file') do
77
+ opts.on('-s', '--stdin', :NONE,
78
+ 'Read input from standard input instead of an input file') do
74
79
  @options[:input] = $stdin
75
80
  end
76
81
 
@@ -95,6 +100,7 @@ module Sass
95
100
 
96
101
  # Processes the options set by the command-line arguments.
97
102
  # In particular, sets `@options[:input]` and `@options[:output]`
103
+ # (and `@options[:sourcemap]` if one has been specified)
98
104
  # to appropriate IO streams.
99
105
  #
100
106
  # This is meant to be overridden by subclasses
@@ -108,12 +114,17 @@ module Sass
108
114
  @options[:filename] = filename
109
115
  open_file(filename) || $stdin
110
116
  end
111
- output ||= open_file(args.shift, 'w') || $stdout
117
+ @options[:output_filename] = args.shift
118
+ output ||= @options[:output_filename] || $stdout
119
+
120
+ if @options[:sourcemap] && @options[:output_filename]
121
+ @options[:sourcemap_filename] = Util.sourcemap_name(@options[:output_filename])
122
+ end
112
123
 
113
124
  @options[:input], @options[:output] = input, output
114
125
  end
115
126
 
116
- COLORS = { :red => 31, :green => 32, :yellow => 33 }
127
+ COLORS = {:red => 31, :green => 32, :yellow => 33}
117
128
 
118
129
  # Prints a status message about performing the given action,
119
130
  # colored using the given color (via terminal escapes) if possible.
@@ -125,11 +136,12 @@ module Sass
125
136
  def puts_action(name, color, arg)
126
137
  return if @options[:for_engine][:quiet]
127
138
  printf color(color, "%11s %s\n"), name, arg
139
+ STDOUT.flush
128
140
  end
129
141
 
130
- # Same as \{Kernel.puts}, but doesn't print anything if the `--quiet` option is set.
142
+ # Same as `Kernel.puts`, but doesn't print anything if the `--quiet` option is set.
131
143
  #
132
- # @param args [Array] Passed on to \{Kernel.puts}
144
+ # @param args [Array] Passed on to `Kernel.puts`
133
145
  def puts(*args)
134
146
  return if @options[:for_engine][:quiet]
135
147
  Kernel.puts(*args)
@@ -151,7 +163,15 @@ module Sass
151
163
  # so we just filter for Windows terms (which don't set TERM)
152
164
  # and not-real terminals, which aren't ttys.
153
165
  return str if ENV["TERM"].nil? || ENV["TERM"].empty? || !STDOUT.tty?
154
- return "\e[#{COLORS[color]}m#{str}\e[0m"
166
+ "\e[#{COLORS[color]}m#{str}\e[0m"
167
+ end
168
+
169
+ def write_output(text, destination)
170
+ if destination.is_a?(String)
171
+ open_file(destination, 'w') {|file| file.write(text)}
172
+ else
173
+ destination.write(text)
174
+ end
155
175
  end
156
176
 
157
177
  private
@@ -159,7 +179,10 @@ module Sass
159
179
  def open_file(filename, flag = 'r')
160
180
  return if filename.nil?
161
181
  flag = 'wb' if @options[:unix_newlines] && flag == 'w'
162
- File.open(filename, flag)
182
+ file = File.open(filename, flag)
183
+ return file unless block_given?
184
+ yield file
185
+ file.close
163
186
  end
164
187
 
165
188
  def handle_load_error(err)
@@ -182,7 +205,7 @@ MESSAGE
182
205
  def initialize(args)
183
206
  super
184
207
  @options[:for_engine] = {
185
- :load_paths => ['.'] + (ENV['SASSPATH'] || '').split(File::PATH_SEPARATOR)
208
+ :load_paths => default_sass_path
186
209
  }
187
210
  @default_syntax = :sass
188
211
  end
@@ -192,6 +215,8 @@ MESSAGE
192
215
  # Tells optparse how to parse the arguments.
193
216
  #
194
217
  # @param opts [OptionParser]
218
+ # @comment
219
+ # rubocop:disable MethodLength
195
220
  def set_opts(opts)
196
221
  super
197
222
 
@@ -229,15 +254,28 @@ END
229
254
  'Only meaningful for --watch and --update.') do
230
255
  @options[:stop_on_error] = true
231
256
  end
257
+ opts.on('--poll', 'Check for file changes manually, rather than relying on the OS.',
258
+ 'Only meaningful for --watch.') do
259
+ @options[:poll] = true
260
+ end
261
+ opts.on('-f', '--force', 'Recompile all Sass files, even if the CSS file is newer.',
262
+ 'Only meaningful for --update.') do
263
+ @options[:force] = true
264
+ end
232
265
  opts.on('-c', '--check', "Just check syntax, don't evaluate.") do
233
266
  require 'stringio'
234
267
  @options[:check_syntax] = true
235
268
  @options[:output] = StringIO.new
236
269
  end
237
- opts.on('-t', '--style NAME',
238
- 'Output style. Can be nested (default), compact, compressed, or expanded.') do |name|
270
+ style_desc = 'Output style. Can be nested (default), compact, compressed, or expanded.'
271
+ opts.on('-t', '--style NAME', style_desc) do |name|
239
272
  @options[:for_engine][:style] = name.to_sym
240
273
  end
274
+ opts.on('--precision NUMBER_OF_DIGITS', Integer,
275
+ "How many digits of precision to use when outputting decimal numbers." +
276
+ "Defaults to #{::Sass::Script::Value::Number.precision}.") do |precision|
277
+ ::Sass::Script::Value::Number.precision = precision
278
+ end
241
279
  opts.on('-q', '--quiet', 'Silence warnings and status messages during compilation.') do
242
280
  @options[:for_engine][:quiet] = true
243
281
  end
@@ -245,7 +283,7 @@ END
245
283
  @options[:compass] = true
246
284
  end
247
285
  opts.on('-g', '--debug-info',
248
- 'Emit extra information in the generated CSS that can be used by the FireSass Firebug plugin.') do
286
+ 'Emit output that can be used by the FireSass Firebug plugin.') do
249
287
  @options[:for_engine][:debug_info] = true
250
288
  end
251
289
  opts.on('-l', '--line-numbers', '--line-comments',
@@ -262,19 +300,33 @@ END
262
300
  opts.on('-r', '--require LIB', 'Require a Ruby library before running Sass.') do |lib|
263
301
  require lib
264
302
  end
265
- opts.on('--cache-location PATH', 'The path to put cached Sass files. Defaults to .sass-cache.') do |loc|
303
+ opts.on('--cache-location PATH',
304
+ 'The path to put cached Sass files. Defaults to .sass-cache.') do |loc|
266
305
  @options[:for_engine][:cache_location] = loc
267
306
  end
268
307
  opts.on('-C', '--no-cache', "Don't cache to sassc files.") do
269
308
  @options[:for_engine][:cache] = false
270
309
  end
310
+ opts.on('--sourcemap', 'Create sourcemap files next to the generated CSS files.') do
311
+ @options[:sourcemap] = true
312
+ end
271
313
 
272
- unless ::Sass::Util.ruby1_8?
273
- opts.on('-E encoding', 'Specify the default encoding for Sass files.') do |encoding|
314
+ encoding_desc = if ::Sass::Util.ruby1_8?
315
+ 'Does not work in ruby 1.8.'
316
+ else
317
+ 'Specify the default encoding for Sass files.'
318
+ end
319
+ opts.on('-E', '--default-encoding ENCODING', encoding_desc) do |encoding|
320
+ if ::Sass::Util.ruby1_8?
321
+ $stderr.puts "Specifying the encoding is not supported in ruby 1.8."
322
+ exit 1
323
+ else
274
324
  Encoding.default_external = encoding
275
325
  end
276
326
  end
277
327
  end
328
+ # @comment
329
+ # rubocop:enable MethodLength
278
330
 
279
331
  # Processes the options set by the command-line arguments,
280
332
  # and runs the Sass compiler appropriately.
@@ -294,6 +346,8 @@ END
294
346
  return watch_or_update if @options[:watch] || @options[:update]
295
347
  super
296
348
  @options[:for_engine][:filename] = @options[:filename]
349
+ @options[:for_engine][:css_filename] = @options[:output] if @options[:output].is_a?(String)
350
+ @options[:for_engine][:sourcemap_filename] = @options[:sourcemap_filename]
297
351
 
298
352
  begin
299
353
  input = @options[:input]
@@ -308,16 +362,32 @@ END
308
362
  # We don't need to do any special handling of @options[:check_syntax] here,
309
363
  # because the Sass syntax checking happens alongside evaluation
310
364
  # and evaluation doesn't actually evaluate any code anyway.
311
- ::Sass::Engine.new(input.read(), @options[:for_engine])
365
+ ::Sass::Engine.new(input.read, @options[:for_engine])
312
366
  end
313
367
 
314
- input.close() if input.is_a?(File)
368
+ input.close if input.is_a?(File)
369
+
370
+ if @options[:sourcemap]
371
+ unless @options[:sourcemap_filename]
372
+ raise "Can't generate a sourcemap for an input without a path."
373
+ end
315
374
 
316
- output.write(engine.render)
317
- output.close() if output.is_a? File
375
+ relative_sourcemap_path = ::Sass::Util.pathname(@options[:sourcemap_filename]).
376
+ relative_path_from(::Sass::Util.pathname(@options[:output_filename]).dirname)
377
+ rendered, mapping = engine.render_with_sourcemap(relative_sourcemap_path.to_s)
378
+ write_output(rendered, output)
379
+ write_output(mapping.to_json(
380
+ :css_path => @options[:output_filename],
381
+ :sourcemap_path => @options[:sourcemap_filename]) + "\n",
382
+ @options[:sourcemap_filename])
383
+ else
384
+ write_output(engine.render, output)
385
+ end
318
386
  rescue ::Sass::SyntaxError => e
319
387
  raise e if @options[:trace]
320
388
  raise e.sass_backtrace_str("standard input")
389
+ ensure
390
+ output.close if output.is_a? File
321
391
  end
322
392
  end
323
393
 
@@ -345,10 +415,19 @@ END
345
415
  ::Sass::Repl.new(@options).run
346
416
  end
347
417
 
418
+ # @comment
419
+ # rubocop:disable MethodLength
348
420
  def watch_or_update
349
421
  require 'sass/plugin'
350
422
  ::Sass::Plugin.options.merge! @options[:for_engine]
351
423
  ::Sass::Plugin.options[:unix_newlines] = @options[:unix_newlines]
424
+ ::Sass::Plugin.options[:poll] = @options[:poll]
425
+ ::Sass::Plugin.options[:sourcemap] = @options[:sourcemap]
426
+
427
+ if @options[:force]
428
+ raise "The --force flag may only be used with --update." unless @options[:update]
429
+ ::Sass::Plugin.options[:always_update] = true
430
+ end
352
431
 
353
432
  raise <<MSG if @args.empty?
354
433
  What files should I watch? Did you mean something like:
@@ -372,25 +451,38 @@ MSG
372
451
 
373
452
  dirs, files = @args.map {|name| split_colon_path(name)}.
374
453
  partition {|i, _| File.directory? i}
375
- files.map! {|from, to| [from, to || from.gsub(/\..*?$/, '.css')]}
454
+ files.map! do |from, to|
455
+ to ||= from.gsub(/\.[^.]*?$/, '.css')
456
+ sourcemap = Util.sourcemap_name(to) if @options[:sourcemap]
457
+ [from, to, sourcemap]
458
+ end
376
459
  dirs.map! {|from, to| [from, to || from]}
377
460
  ::Sass::Plugin.options[:template_location] = dirs
378
461
 
379
- ::Sass::Plugin.on_updating_stylesheet do |_, css|
380
- if File.exists? css
381
- puts_action :overwrite, :yellow, css
382
- else
383
- puts_action :create, :green, css
462
+ ::Sass::Plugin.on_updated_stylesheet do |_, css, sourcemap|
463
+ [css, sourcemap].each do |file|
464
+ next unless file
465
+ puts_action :write, :green, file
384
466
  end
385
467
  end
386
468
 
387
469
  had_error = false
388
470
  ::Sass::Plugin.on_creating_directory {|dirname| puts_action :directory, :green, dirname}
389
471
  ::Sass::Plugin.on_deleting_css {|filename| puts_action :delete, :yellow, filename}
472
+ ::Sass::Plugin.on_deleting_sourcemap {|filename| puts_action :delete, :yellow, filename}
390
473
  ::Sass::Plugin.on_compilation_error do |error, _, _|
474
+ if error.is_a?(SystemCallError) && !@options[:stop_on_error]
475
+ had_error = true
476
+ puts_action :error, :red, error.message
477
+ STDOUT.flush
478
+ next
479
+ end
480
+
391
481
  raise error unless error.is_a?(::Sass::SyntaxError) && !@options[:stop_on_error]
392
482
  had_error = true
393
- puts_action :error, :red, "#{error.sass_filename} (Line #{error.sass_line}: #{error.message})"
483
+ puts_action :error, :red,
484
+ "#{error.sass_filename} (Line #{error.sass_line}: #{error.message})"
485
+ STDOUT.flush
394
486
  end
395
487
 
396
488
  if @options[:update]
@@ -401,12 +493,23 @@ MSG
401
493
 
402
494
  puts ">>> Sass is watching for changes. Press Ctrl-C to stop."
403
495
 
404
- ::Sass::Plugin.on_template_modified {|template| puts ">>> Change detected to: #{template}"}
405
- ::Sass::Plugin.on_template_created {|template| puts ">>> New template detected: #{template}"}
406
- ::Sass::Plugin.on_template_deleted {|template| puts ">>> Deleted template detected: #{template}"}
496
+ ::Sass::Plugin.on_template_modified do |template|
497
+ puts ">>> Change detected to: #{template}"
498
+ STDOUT.flush
499
+ end
500
+ ::Sass::Plugin.on_template_created do |template|
501
+ puts ">>> New template detected: #{template}"
502
+ STDOUT.flush
503
+ end
504
+ ::Sass::Plugin.on_template_deleted do |template|
505
+ puts ">>> Deleted template detected: #{template}"
506
+ STDOUT.flush
507
+ end
407
508
 
408
509
  ::Sass::Plugin.watch(files)
409
510
  end
511
+ # @comment
512
+ # rubocop:enable MethodLength
410
513
 
411
514
  def colon_path?(path)
412
515
  !split_colon_path(path)[1].nil?
@@ -429,7 +532,17 @@ MSG
429
532
  def probably_dest_dir?(path)
430
533
  return false unless path
431
534
  return false if colon_path?(path)
432
- return Dir.glob(File.join(path, "*.s[ca]ss")).empty?
535
+ ::Sass::Util.glob(File.join(path, "*.s[ca]ss")).empty?
536
+ end
537
+
538
+ def default_sass_path
539
+ if ENV['SASS_PATH']
540
+ # The select here prevents errors when the environment's
541
+ # load paths specified do not exist.
542
+ ENV['SASS_PATH'].split(File::PATH_SEPARATOR).select {|d| File.directory?(d)}
543
+ else
544
+ [::Sass::Importers::DeprecatedPath.new(".")]
545
+ end
433
546
  end
434
547
  end
435
548
 
@@ -454,6 +567,8 @@ MSG
454
567
  # Tells optparse how to parse the arguments.
455
568
  #
456
569
  # @param opts [OptionParser]
570
+ # @comment
571
+ # rubocop:disable MethodLength
457
572
  def set_opts(opts)
458
573
  opts.banner = <<END
459
574
  Usage: sass-convert [options] [INPUT] [OUTPUT]
@@ -467,14 +582,14 @@ Options:
467
582
  END
468
583
 
469
584
  opts.on('-F', '--from FORMAT',
470
- 'The format to convert from. Can be css, scss, sass, less.',
585
+ 'The format to convert from. Can be css, scss, sass.',
471
586
  'By default, this is inferred from the input filename.',
472
587
  'If there is none, defaults to css.') do |name|
473
588
  @options[:from] = name.downcase.to_sym
474
- unless [:css, :scss, :sass, :less].include?(@options[:from])
589
+ raise "sass-convert no longer supports LessCSS." if @options[:from] == :less
590
+ unless [:css, :scss, :sass].include?(@options[:from])
475
591
  raise "Unknown format for sass-convert --from: #{name}"
476
592
  end
477
- try_less_note if @options[:from] == :less
478
593
  end
479
594
 
480
595
  opts.on('-T', '--to FORMAT',
@@ -502,6 +617,17 @@ END
502
617
  @options[:for_tree][:dasherize] = true
503
618
  end
504
619
 
620
+ opts.on('--indent NUM',
621
+ 'How many spaces to use for each level of indentation. Defaults to 2.',
622
+ '"t" means use hard tabs.') do |indent|
623
+
624
+ if indent == 't'
625
+ @options[:for_tree][:indent] = "\t"
626
+ else
627
+ @options[:for_tree][:indent] = " " * indent.to_i
628
+ end
629
+ end
630
+
505
631
  opts.on('--old', 'Output the old-style ":prop val" property syntax.',
506
632
  'Only meaningful when generating Sass.') do
507
633
  @options[:for_tree][:old] = true
@@ -512,13 +638,16 @@ END
512
638
  end
513
639
 
514
640
  unless ::Sass::Util.ruby1_8?
515
- opts.on('-E encoding', 'Specify the default encoding for Sass and CSS files.') do |encoding|
641
+ opts.on('-E encoding',
642
+ 'Specify the default encoding for Sass and CSS files.') do |encoding|
516
643
  Encoding.default_external = encoding
517
644
  end
518
645
  end
519
646
 
520
647
  super
521
648
  end
649
+ # @comment
650
+ # rubocop:enable MethodLength
522
651
 
523
652
  # Processes the options set by the command-line arguments,
524
653
  # and runs the CSS compiler appropriately.
@@ -532,7 +661,9 @@ END
532
661
 
533
662
  super
534
663
  input = @options[:input]
535
- raise "Error: '#{input.path}' is a directory (did you mean to use --recursive?)" if File.directory?(input)
664
+ if File.directory?(input)
665
+ raise "Error: '#{input.path}' is a directory (did you mean to use --recursive?)"
666
+ end
536
667
  output = @options[:output]
537
668
  output = input if @options[:in_place]
538
669
  process_file(input, output)
@@ -541,27 +672,29 @@ END
541
672
  private
542
673
 
543
674
  def process_directory
544
- unless input = @options[:input] = @args.shift
675
+ unless (input = @options[:input] = @args.shift)
545
676
  raise "Error: directory required when using --recursive."
546
677
  end
547
678
 
548
679
  output = @options[:output] = @args.shift
549
680
  raise "Error: --from required when using --recursive." unless @options[:from]
550
681
  raise "Error: --to required when using --recursive." unless @options[:to]
551
- raise "Error: '#{@options[:input]}' is not a directory" unless File.directory?(@options[:input])
552
- if @options[:output] && File.exists?(@options[:output]) && !File.directory?(@options[:output])
682
+ unless File.directory?(@options[:input])
683
+ raise "Error: '#{@options[:input]}' is not a directory"
684
+ end
685
+ if @options[:output] && File.exists?(@options[:output]) &&
686
+ !File.directory?(@options[:output])
553
687
  raise "Error: '#{@options[:output]}' is not a directory"
554
688
  end
555
689
  @options[:output] ||= @options[:input]
556
690
 
557
- from = @options[:from]
558
691
  if @options[:to] == @options[:from] && !@options[:in_place]
559
692
  fmt = @options[:from]
560
693
  raise "Error: converting from #{fmt} to #{fmt} without --in-place"
561
694
  end
562
695
 
563
696
  ext = @options[:from]
564
- Dir.glob("#{@options[:input]}/**/*.#{ext}") do |f|
697
+ ::Sass::Util.glob("#{@options[:input]}/**/*.#{ext}") do |f|
565
698
  output =
566
699
  if @options[:in_place]
567
700
  f
@@ -585,7 +718,6 @@ END
585
718
  end
586
719
 
587
720
  input = open_file(f)
588
- output = @options[:in_place] ? input : open_file(output, "w")
589
721
  process_file(input, output)
590
722
  end
591
723
  end
@@ -596,7 +728,7 @@ END
596
728
  case input.path
597
729
  when /\.scss$/; :scss
598
730
  when /\.sass$/; :sass
599
- when /\.less$/; :less
731
+ when /\.less$/; raise "sass-convert no longer supports LessCSS."
600
732
  when /\.css$/; :css
601
733
  end
602
734
  elsif @options[:in_place]
@@ -620,11 +752,6 @@ END
620
752
  if @options[:from] == :css
621
753
  require 'sass/css'
622
754
  ::Sass::CSS.new(input.read, @options[:for_tree]).render(@options[:to])
623
- elsif @options[:from] == :less
624
- require 'sass/less'
625
- try_less_note
626
- input = input.read if input.is_a?(IO) && !input.is_a?(File) # Less is dumb
627
- Less::Engine.new(input).to_tree.to_sass_tree.send("to_#{@options[:to]}", @options[:for_tree])
628
755
  else
629
756
  if input.is_a?(File)
630
757
  ::Sass::Engine.for_file(input.path, @options[:for_engine])
@@ -634,8 +761,8 @@ END
634
761
  end
635
762
  end
636
763
 
637
- output = File.open(input.path, 'w') if @options[:in_place]
638
- output.write(out)
764
+ output = input.path if @options[:in_place]
765
+ write_output(out, output)
639
766
  rescue ::Sass::SyntaxError => e
640
767
  raise e if @options[:trace]
641
768
  file = " of #{e.sass_filename}" if e.sass_filename
@@ -643,17 +770,6 @@ END
643
770
  rescue LoadError => err
644
771
  handle_load_error(err)
645
772
  end
646
-
647
- @@less_note_printed = false
648
- def try_less_note
649
- return if @@less_note_printed
650
- @@less_note_printed = true
651
- warn <<NOTE
652
- * NOTE: Sass and Less are different languages, and they work differently.
653
- * I'll do my best to translate, but some features -- especially mixins --
654
- * should be checked by hand.
655
- NOTE
656
- end
657
773
  end
658
774
  end
659
775
  end
@@ -0,0 +1,40 @@
1
+ require 'set'
2
+ module Sass
3
+ # Provides `Sass.has_feature?` which allows for simple feature detection
4
+ # by providing a feature name.
5
+ module Features
6
+ # This is the set of features that can be detected.
7
+ KNOWN_FEATURES = Set[*%w{
8
+ }]
9
+
10
+ # Check if a feature exists by name. This is used to implement
11
+ # the Sass function `feature-exists($feature)`
12
+ #
13
+ # @param feature_name [String] The case sensitive name of the feature to
14
+ # check if it exists in this version of Sass.
15
+ # @return [Boolean] whether the feature of that name exists.
16
+ def has_feature?(feature_name)
17
+ KNOWN_FEATURES.include?(feature_name)
18
+ end
19
+
20
+ # Add a feature to Sass. Plugins can use this to easily expose their
21
+ # availability to end users. Plugins must prefix their feature
22
+ # names with a dash to distinguish them from official features.
23
+ #
24
+ # @example
25
+ # Sass.add_feature("-import-globbing")
26
+ # Sass.add_feature("-math-cos")
27
+ #
28
+ #
29
+ # @param feature_name [String] The case sensitive name of the feature to
30
+ # to add to Sass. Must begin with a dash.
31
+ def add_feature(feature_name)
32
+ unless feature_name[0] == ?-
33
+ raise ArgumentError.new("Plugin feature names must begin with a dash")
34
+ end
35
+ KNOWN_FEATURES << feature_name
36
+ end
37
+ end
38
+
39
+ extend Features
40
+ end
@@ -15,13 +15,9 @@ module Sass
15
15
  # They should also implement the \{#find\_relative} method.
16
16
  #
17
17
  # Importers should be serializable via `Marshal.dump`.
18
- # In addition to the standard `_dump` and `_load` methods,
19
- # importers can define `_before_dump`, `_after_dump`, `_around_dump`,
20
- # and `_after_load` methods as per {Sass::Util#dump} and {Sass::Util#load}.
21
18
  #
22
19
  # @abstract
23
20
  class Base
24
-
25
21
  # Find a Sass file relative to another file.
26
22
  # Importers without a notion of "relative paths"
27
23
  # should just return nil here.
@@ -72,7 +68,8 @@ module Sass
72
68
  # If no such files exist, it should return nil.
73
69
  #
74
70
  # The {Sass::Engine} to be returned should be passed `options`,
75
- # with a few modifications. `:filename` and `:syntax` should be set appropriately,
71
+ # with a few modifications. `:syntax` should be set appropriately,
72
+ # `:filename` should be set to `uri`,
76
73
  # and `:importer` should be set to this importer.
77
74
  #
78
75
  # @param uri [String] The URI to import.
@@ -121,6 +118,29 @@ module Sass
121
118
  Sass::Util.abstract(self)
122
119
  end
123
120
 
121
+ # Get the publicly-visible URL for an imported file. This URL is used by
122
+ # source maps to link to the source stylesheet. This may return `nil` to
123
+ # indicate that no public URL is available; however, this will cause
124
+ # sourcemap generation to fail if any CSS is generated from files imported
125
+ # from this importer.
126
+ #
127
+ # @param uri [String] A URI known to be valid for this importer.
128
+ # @param sourcemap_directory [String, NilClass] The absolute path to a
129
+ # directory on disk where the sourcemap will be saved. If uri refers to
130
+ # a file on disk that's accessible relative to sourcemap_directory, this
131
+ # may return a relative URL. This may be `nil` if the sourcemap's
132
+ # eventual location is unknown.
133
+ # @return [String?] The publicly-visible URL for this file, or `nil`
134
+ # indicating that no publicly-visible URL exists.
135
+ def public_url(uri, sourcemap_directory = nil)
136
+ return if @public_url_warning_issued
137
+ @public_url_warning_issued = true
138
+ Sass::Util.sass_warn <<WARNING
139
+ WARNING: #{self.class.name} should define the #public_url method.
140
+ WARNING
141
+ nil
142
+ end
143
+
124
144
  # A string representation of the importer.
125
145
  # Should be overridden by subclasses.
126
146
  #
@@ -131,8 +151,27 @@ module Sass
131
151
  def to_s
132
152
  Sass::Util.abstract(self)
133
153
  end
154
+
155
+ # If the importer is based on files on the local filesystem
156
+ # this method should return folders which should be watched
157
+ # for changes.
158
+ #
159
+ # @return [Array<String>] List of absolute paths of directories to watch
160
+ def directories_to_watch
161
+ []
162
+ end
163
+
164
+ # If this importer is based on files on the local filesystem This method
165
+ # should return true if the file, when changed, should trigger a
166
+ # recompile.
167
+ #
168
+ # It is acceptable for non-sass files to be watched and trigger a recompile.
169
+ #
170
+ # @param filename [String] The absolute filename for a file that has changed.
171
+ # @return [Boolean] When the file changed should cause a recompile.
172
+ def watched_file?(filename)
173
+ false
174
+ end
134
175
  end
135
176
  end
136
177
  end
137
-
138
-