sass 3.3.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +58 -50
  4. data/Rakefile +1 -4
  5. data/VERSION +1 -1
  6. data/VERSION_DATE +1 -1
  7. data/VERSION_NAME +1 -1
  8. data/bin/sass +1 -1
  9. data/bin/scss +1 -1
  10. data/lib/sass/cache_stores/filesystem.rb +6 -2
  11. data/lib/sass/css.rb +1 -3
  12. data/lib/sass/engine.rb +37 -46
  13. data/lib/sass/environment.rb +13 -17
  14. data/lib/sass/error.rb +6 -9
  15. data/lib/sass/exec/base.rb +187 -0
  16. data/lib/sass/exec/sass_convert.rb +264 -0
  17. data/lib/sass/exec/sass_scss.rb +424 -0
  18. data/lib/sass/exec.rb +5 -771
  19. data/lib/sass/features.rb +7 -0
  20. data/lib/sass/importers/base.rb +7 -2
  21. data/lib/sass/importers/filesystem.rb +9 -25
  22. data/lib/sass/importers.rb +0 -1
  23. data/lib/sass/media.rb +1 -4
  24. data/lib/sass/plugin/compiler.rb +200 -83
  25. data/lib/sass/plugin/staleness_checker.rb +1 -1
  26. data/lib/sass/plugin.rb +3 -3
  27. data/lib/sass/script/css_lexer.rb +1 -1
  28. data/lib/sass/script/functions.rb +622 -268
  29. data/lib/sass/script/lexer.rb +99 -34
  30. data/lib/sass/script/parser.rb +24 -23
  31. data/lib/sass/script/tree/funcall.rb +1 -1
  32. data/lib/sass/script/tree/interpolation.rb +20 -2
  33. data/lib/sass/script/tree/selector.rb +26 -0
  34. data/lib/sass/script/tree/string_interpolation.rb +1 -1
  35. data/lib/sass/script/tree.rb +1 -0
  36. data/lib/sass/script/value/base.rb +7 -5
  37. data/lib/sass/script/value/bool.rb +0 -5
  38. data/lib/sass/script/value/color.rb +39 -21
  39. data/lib/sass/script/value/helpers.rb +107 -0
  40. data/lib/sass/script/value/list.rb +0 -15
  41. data/lib/sass/script/value/null.rb +0 -5
  42. data/lib/sass/script/value/number.rb +62 -14
  43. data/lib/sass/script/value/string.rb +59 -11
  44. data/lib/sass/script/value.rb +0 -1
  45. data/lib/sass/scss/css_parser.rb +8 -2
  46. data/lib/sass/scss/parser.rb +190 -328
  47. data/lib/sass/scss/rx.rb +15 -6
  48. data/lib/sass/scss/static_parser.rb +298 -1
  49. data/lib/sass/selector/abstract_sequence.rb +28 -13
  50. data/lib/sass/selector/comma_sequence.rb +92 -13
  51. data/lib/sass/selector/pseudo.rb +256 -0
  52. data/lib/sass/selector/sequence.rb +94 -24
  53. data/lib/sass/selector/simple.rb +14 -25
  54. data/lib/sass/selector/simple_sequence.rb +97 -33
  55. data/lib/sass/selector.rb +57 -194
  56. data/lib/sass/shared.rb +1 -1
  57. data/lib/sass/source/map.rb +26 -12
  58. data/lib/sass/stack.rb +0 -6
  59. data/lib/sass/supports.rb +2 -3
  60. data/lib/sass/tree/at_root_node.rb +1 -0
  61. data/lib/sass/tree/charset_node.rb +1 -1
  62. data/lib/sass/tree/directive_node.rb +8 -2
  63. data/lib/sass/tree/error_node.rb +18 -0
  64. data/lib/sass/tree/extend_node.rb +1 -1
  65. data/lib/sass/tree/function_node.rb +4 -0
  66. data/lib/sass/tree/keyframe_rule_node.rb +15 -0
  67. data/lib/sass/tree/prop_node.rb +1 -1
  68. data/lib/sass/tree/rule_node.rb +12 -7
  69. data/lib/sass/tree/visitors/check_nesting.rb +38 -10
  70. data/lib/sass/tree/visitors/convert.rb +16 -18
  71. data/lib/sass/tree/visitors/cssize.rb +29 -29
  72. data/lib/sass/tree/visitors/deep_copy.rb +5 -0
  73. data/lib/sass/tree/visitors/perform.rb +45 -33
  74. data/lib/sass/tree/visitors/set_options.rb +14 -0
  75. data/lib/sass/tree/visitors/to_css.rb +15 -14
  76. data/lib/sass/util/subset_map.rb +1 -1
  77. data/lib/sass/util.rb +222 -99
  78. data/lib/sass/version.rb +5 -5
  79. data/lib/sass.rb +0 -5
  80. data/test/sass/cache_test.rb +62 -20
  81. data/test/sass/callbacks_test.rb +1 -1
  82. data/test/sass/compiler_test.rb +19 -10
  83. data/test/sass/conversion_test.rb +58 -1
  84. data/test/sass/css2sass_test.rb +23 -4
  85. data/test/sass/encoding_test.rb +219 -0
  86. data/test/sass/engine_test.rb +136 -199
  87. data/test/sass/exec_test.rb +2 -2
  88. data/test/sass/extend_test.rb +236 -19
  89. data/test/sass/functions_test.rb +295 -253
  90. data/test/sass/importer_test.rb +31 -21
  91. data/test/sass/logger_test.rb +1 -1
  92. data/test/sass/more_results/more_import.css +1 -1
  93. data/test/sass/plugin_test.rb +14 -13
  94. data/test/sass/results/compact.css +1 -1
  95. data/test/sass/results/complex.css +4 -4
  96. data/test/sass/results/expanded.css +1 -1
  97. data/test/sass/results/import.css +1 -1
  98. data/test/sass/results/import_charset_ibm866.css +2 -2
  99. data/test/sass/results/mixins.css +17 -17
  100. data/test/sass/results/nested.css +1 -1
  101. data/test/sass/results/parent_ref.css +2 -2
  102. data/test/sass/results/script.css +3 -3
  103. data/test/sass/results/scss_import.css +1 -1
  104. data/test/sass/script_conversion_test.rb +10 -7
  105. data/test/sass/script_test.rb +288 -74
  106. data/test/sass/scss/css_test.rb +141 -24
  107. data/test/sass/scss/rx_test.rb +4 -4
  108. data/test/sass/scss/scss_test.rb +457 -18
  109. data/test/sass/source_map_test.rb +115 -25
  110. data/test/sass/superselector_test.rb +191 -0
  111. data/test/sass/templates/scss_import.scss +2 -1
  112. data/test/sass/test_helper.rb +1 -1
  113. data/test/sass/util/multibyte_string_scanner_test.rb +1 -1
  114. data/test/sass/util/normalized_map_test.rb +1 -1
  115. data/test/sass/util/subset_map_test.rb +2 -2
  116. data/test/sass/util_test.rb +31 -1
  117. data/test/sass/value_helpers_test.rb +5 -7
  118. data/test/test_helper.rb +2 -2
  119. data/vendor/listen/CHANGELOG.md +1 -228
  120. data/vendor/listen/Gemfile +5 -15
  121. data/vendor/listen/README.md +111 -77
  122. data/vendor/listen/Rakefile +0 -42
  123. data/vendor/listen/lib/listen/adapter.rb +195 -82
  124. data/vendor/listen/lib/listen/adapters/bsd.rb +27 -64
  125. data/vendor/listen/lib/listen/adapters/darwin.rb +21 -58
  126. data/vendor/listen/lib/listen/adapters/linux.rb +23 -55
  127. data/vendor/listen/lib/listen/adapters/polling.rb +25 -34
  128. data/vendor/listen/lib/listen/adapters/windows.rb +50 -46
  129. data/vendor/listen/lib/listen/directory_record.rb +96 -61
  130. data/vendor/listen/lib/listen/listener.rb +135 -37
  131. data/vendor/listen/lib/listen/turnstile.rb +9 -5
  132. data/vendor/listen/lib/listen/version.rb +1 -1
  133. data/vendor/listen/lib/listen.rb +33 -19
  134. data/vendor/listen/listen.gemspec +6 -0
  135. data/vendor/listen/spec/listen/adapter_spec.rb +43 -77
  136. data/vendor/listen/spec/listen/adapters/polling_spec.rb +8 -8
  137. data/vendor/listen/spec/listen/directory_record_spec.rb +81 -56
  138. data/vendor/listen/spec/listen/listener_spec.rb +128 -39
  139. data/vendor/listen/spec/listen_spec.rb +15 -21
  140. data/vendor/listen/spec/spec_helper.rb +4 -0
  141. data/vendor/listen/spec/support/adapter_helper.rb +52 -15
  142. data/vendor/listen/spec/support/directory_record_helper.rb +7 -5
  143. data/vendor/listen/spec/support/listeners_helper.rb +30 -7
  144. metadata +25 -22
  145. data/ext/mkrf_conf.rb +0 -27
  146. data/lib/sass/importers/deprecated_path.rb +0 -51
  147. data/lib/sass/script/value/deprecated_false.rb +0 -55
  148. data/vendor/listen/lib/listen/dependency_manager.rb +0 -126
  149. data/vendor/listen/lib/listen/multi_listener.rb +0 -143
  150. data/vendor/listen/spec/listen/dependency_manager_spec.rb +0 -107
  151. data/vendor/listen/spec/listen/multi_listener_spec.rb +0 -174
data/lib/sass/exec.rb CHANGED
@@ -1,775 +1,9 @@
1
- require 'optparse'
2
- require 'fileutils'
3
-
4
1
  module Sass
5
- # This module handles the various Sass executables (`sass` and `sass-convert`).
2
+ # This module handles the Sass executables (`sass` and `sass-convert`).
6
3
  module Exec
7
- # An abstract class that encapsulates the executable code for all three executables.
8
- class Generic
9
- # @param args [Array<String>] The command-line arguments
10
- def initialize(args)
11
- @args = args
12
- @options = {}
13
- end
14
-
15
- # Parses the command-line arguments and runs the executable.
16
- # Calls `Kernel#exit` at the end, so it never returns.
17
- #
18
- # @see #parse
19
- def parse!
20
- # rubocop:disable RescueException
21
- begin
22
- parse
23
- rescue Exception => e
24
- raise e if @options[:trace] || e.is_a?(SystemExit)
25
-
26
- $stderr.print "#{e.class}: " unless e.class == RuntimeError
27
- $stderr.puts "#{e.message}"
28
- $stderr.puts " Use --trace for backtrace."
29
- exit 1
30
- end
31
- exit 0
32
- # rubocop:enable RescueException
33
- end
34
-
35
- # Parses the command-line arguments and runs the executable.
36
- # This does not handle exceptions or exit the program.
37
- #
38
- # @see #parse!
39
- def parse
40
- @opts = OptionParser.new(&method(:set_opts))
41
- @opts.parse!(@args)
42
-
43
- process_result
44
-
45
- @options
46
- end
47
-
48
- # @return [String] A description of the executable
49
- def to_s
50
- @opts.to_s
51
- end
52
-
53
- protected
54
-
55
- # Finds the line of the source template
56
- # on which an exception was raised.
57
- #
58
- # @param exception [Exception] The exception
59
- # @return [String] The line number
60
- def get_line(exception)
61
- # SyntaxErrors have weird line reporting
62
- # when there's trailing whitespace
63
- if exception.is_a?(::SyntaxError)
64
- return (exception.message.scan(/:(\d+)/).first || ["??"]).first
65
- end
66
- (exception.backtrace[0].scan(/:(\d+)/).first || ["??"]).first
67
- end
68
-
69
- # Tells optparse how to parse the arguments
70
- # available for all executables.
71
- #
72
- # This is meant to be overridden by subclasses
73
- # so they can add their own options.
74
- #
75
- # @param opts [OptionParser]
76
- def set_opts(opts)
77
- opts.on('-s', '--stdin', :NONE,
78
- 'Read input from standard input instead of an input file') do
79
- @options[:input] = $stdin
80
- end
81
-
82
- opts.on('--trace', :NONE, 'Show a full traceback on error') do
83
- @options[:trace] = true
84
- end
85
-
86
- opts.on('--unix-newlines', 'Use Unix-style newlines in written files.') do
87
- @options[:unix_newlines] = true if ::Sass::Util.windows?
88
- end
89
-
90
- opts.on_tail("-?", "-h", "--help", "Show this message") do
91
- puts opts
92
- exit
93
- end
94
-
95
- opts.on_tail("-v", "--version", "Print version") do
96
- puts("Sass #{::Sass.version[:string]}")
97
- exit
98
- end
99
- end
100
-
101
- # Processes the options set by the command-line arguments.
102
- # In particular, sets `@options[:input]` and `@options[:output]`
103
- # (and `@options[:sourcemap]` if one has been specified)
104
- # to appropriate IO streams.
105
- #
106
- # This is meant to be overridden by subclasses
107
- # so they can run their respective programs.
108
- def process_result
109
- input, output = @options[:input], @options[:output]
110
- args = @args.dup
111
- input ||=
112
- begin
113
- filename = args.shift
114
- @options[:filename] = filename
115
- open_file(filename) || $stdin
116
- end
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
123
-
124
- @options[:input], @options[:output] = input, output
125
- end
126
-
127
- COLORS = {:red => 31, :green => 32, :yellow => 33}
128
-
129
- # Prints a status message about performing the given action,
130
- # colored using the given color (via terminal escapes) if possible.
131
- #
132
- # @param name [#to_s] A short name for the action being performed.
133
- # Shouldn't be longer than 11 characters.
134
- # @param color [Symbol] The name of the color to use for this action.
135
- # Can be `:red`, `:green`, or `:yellow`.
136
- def puts_action(name, color, arg)
137
- return if @options[:for_engine][:quiet]
138
- printf color(color, "%11s %s\n"), name, arg
139
- STDOUT.flush
140
- end
141
-
142
- # Same as `Kernel.puts`, but doesn't print anything if the `--quiet` option is set.
143
- #
144
- # @param args [Array] Passed on to `Kernel.puts`
145
- def puts(*args)
146
- return if @options[:for_engine][:quiet]
147
- Kernel.puts(*args)
148
- end
149
-
150
- # Wraps the given string in terminal escapes
151
- # causing it to have the given color.
152
- # If terminal esapes aren't supported on this platform,
153
- # just returns the string instead.
154
- #
155
- # @param color [Symbol] The name of the color to use.
156
- # Can be `:red`, `:green`, or `:yellow`.
157
- # @param str [String] The string to wrap in the given color.
158
- # @return [String] The wrapped string.
159
- def color(color, str)
160
- raise "[BUG] Unrecognized color #{color}" unless COLORS[color]
161
-
162
- # Almost any real Unix terminal will support color,
163
- # so we just filter for Windows terms (which don't set TERM)
164
- # and not-real terminals, which aren't ttys.
165
- return str if ENV["TERM"].nil? || ENV["TERM"].empty? || !STDOUT.tty?
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
175
- end
176
-
177
- private
178
-
179
- def open_file(filename, flag = 'r')
180
- return if filename.nil?
181
- flag = 'wb' if @options[:unix_newlines] && flag == 'w'
182
- file = File.open(filename, flag)
183
- return file unless block_given?
184
- yield file
185
- file.close
186
- end
187
-
188
- def handle_load_error(err)
189
- dep = err.message[/^no such file to load -- (.*)/, 1]
190
- raise err if @options[:trace] || dep.nil? || dep.empty?
191
- $stderr.puts <<MESSAGE
192
- Required dependency #{dep} not found!
193
- Run "gem install #{dep}" to get it.
194
- Use --trace for backtrace.
195
- MESSAGE
196
- exit 1
197
- end
198
- end
199
-
200
- # The `sass` executable.
201
- class Sass < Generic
202
- attr_reader :default_syntax
203
-
204
- # @param args [Array<String>] The command-line arguments
205
- def initialize(args)
206
- super
207
- @options[:for_engine] = {
208
- :load_paths => default_sass_path
209
- }
210
- @default_syntax = :sass
211
- end
212
-
213
- protected
214
-
215
- # Tells optparse how to parse the arguments.
216
- #
217
- # @param opts [OptionParser]
218
- # @comment
219
- # rubocop:disable MethodLength
220
- def set_opts(opts)
221
- super
222
-
223
- opts.banner = <<END
224
- Usage: #{default_syntax} [options] [INPUT] [OUTPUT]
225
-
226
- Description:
227
- Converts SCSS or Sass files to CSS.
228
-
229
- Options:
230
- END
231
-
232
- if @default_syntax == :sass
233
- opts.on('--scss',
234
- 'Use the CSS-superset SCSS syntax.') do
235
- @options[:for_engine][:syntax] = :scss
236
- end
237
- else
238
- opts.on('--sass',
239
- 'Use the Indented syntax.') do
240
- @options[:for_engine][:syntax] = :sass
241
- end
242
- end
243
- opts.on('--watch', 'Watch files or directories for changes.',
244
- 'The location of the generated CSS can be set using a colon:',
245
- " #{@default_syntax} --watch input.#{@default_syntax}:output.css",
246
- " #{@default_syntax} --watch input-dir:output-dir") do
247
- @options[:watch] = true
248
- end
249
- opts.on('--update', 'Compile files or directories to CSS.',
250
- 'Locations are set like --watch.') do
251
- @options[:update] = true
252
- end
253
- opts.on('--stop-on-error', 'If a file fails to compile, exit immediately.',
254
- 'Only meaningful for --watch and --update.') do
255
- @options[:stop_on_error] = true
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
265
- opts.on('-c', '--check', "Just check syntax, don't evaluate.") do
266
- require 'stringio'
267
- @options[:check_syntax] = true
268
- @options[:output] = StringIO.new
269
- end
270
- style_desc = 'Output style. Can be nested (default), compact, compressed, or expanded.'
271
- opts.on('-t', '--style NAME', style_desc) do |name|
272
- @options[:for_engine][:style] = name.to_sym
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
279
- opts.on('-q', '--quiet', 'Silence warnings and status messages during compilation.') do
280
- @options[:for_engine][:quiet] = true
281
- end
282
- opts.on('--compass', 'Make Compass imports available and load project configuration.') do
283
- @options[:compass] = true
284
- end
285
- opts.on('-g', '--debug-info',
286
- 'Emit output that can be used by the FireSass Firebug plugin.') do
287
- @options[:for_engine][:debug_info] = true
288
- end
289
- opts.on('-l', '--line-numbers', '--line-comments',
290
- 'Emit comments in the generated CSS indicating the corresponding source line.') do
291
- @options[:for_engine][:line_numbers] = true
292
- end
293
- opts.on('-i', '--interactive',
294
- 'Run an interactive SassScript shell.') do
295
- @options[:interactive] = true
296
- end
297
- opts.on('-I', '--load-path PATH', 'Add a sass import path.') do |path|
298
- @options[:for_engine][:load_paths] << path
299
- end
300
- opts.on('-r', '--require LIB', 'Require a Ruby library before running Sass.') do |lib|
301
- require lib
302
- end
303
- opts.on('--cache-location PATH',
304
- 'The path to put cached Sass files. Defaults to .sass-cache.') do |loc|
305
- @options[:for_engine][:cache_location] = loc
306
- end
307
- opts.on('-C', '--no-cache', "Don't cache to sassc files.") do
308
- @options[:for_engine][:cache] = false
309
- end
310
- opts.on('--sourcemap', 'Create sourcemap files next to the generated CSS files.') do
311
- @options[:sourcemap] = true
312
- end
313
-
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
324
- Encoding.default_external = encoding
325
- end
326
- end
327
- end
328
- # @comment
329
- # rubocop:enable MethodLength
330
-
331
- # Processes the options set by the command-line arguments,
332
- # and runs the Sass compiler appropriately.
333
- def process_result
334
- require 'sass'
335
-
336
- if !@options[:update] && !@options[:watch] &&
337
- @args.first && colon_path?(@args.first)
338
- if @args.size == 1
339
- @args = split_colon_path(@args.first)
340
- else
341
- @options[:update] = true
342
- end
343
- end
344
- load_compass if @options[:compass]
345
- return interactive if @options[:interactive]
346
- return watch_or_update if @options[:watch] || @options[:update]
347
- super
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]
351
-
352
- begin
353
- input = @options[:input]
354
- output = @options[:output]
355
-
356
- @options[:for_engine][:syntax] ||= :scss if input.is_a?(File) && input.path =~ /\.scss$/
357
- @options[:for_engine][:syntax] ||= @default_syntax
358
- engine =
359
- if input.is_a?(File) && !@options[:check_syntax]
360
- ::Sass::Engine.for_file(input.path, @options[:for_engine])
361
- else
362
- # We don't need to do any special handling of @options[:check_syntax] here,
363
- # because the Sass syntax checking happens alongside evaluation
364
- # and evaluation doesn't actually evaluate any code anyway.
365
- ::Sass::Engine.new(input.read, @options[:for_engine])
366
- end
367
-
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
374
-
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
386
- rescue ::Sass::SyntaxError => e
387
- raise e if @options[:trace]
388
- raise e.sass_backtrace_str("standard input")
389
- ensure
390
- output.close if output.is_a? File
391
- end
392
- end
393
-
394
- private
395
-
396
- def load_compass
397
- begin
398
- require 'compass'
399
- rescue LoadError
400
- require 'rubygems'
401
- begin
402
- require 'compass'
403
- rescue LoadError
404
- puts "ERROR: Cannot load compass."
405
- exit 1
406
- end
407
- end
408
- Compass.add_project_configuration
409
- Compass.configuration.project_path ||= Dir.pwd
410
- @options[:for_engine][:load_paths] += Compass.configuration.sass_load_paths
411
- end
412
-
413
- def interactive
414
- require 'sass/repl'
415
- ::Sass::Repl.new(@options).run
416
- end
417
-
418
- # @comment
419
- # rubocop:disable MethodLength
420
- def watch_or_update
421
- require 'sass/plugin'
422
- ::Sass::Plugin.options.merge! @options[:for_engine]
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
431
-
432
- raise <<MSG if @args.empty?
433
- What files should I watch? Did you mean something like:
434
- #{@default_syntax} --watch input.#{@default_syntax}:output.css
435
- #{@default_syntax} --watch input-dir:output-dir
436
- MSG
437
-
438
- if !colon_path?(@args[0]) && probably_dest_dir?(@args[1])
439
- flag = @options[:update] ? "--update" : "--watch"
440
- err =
441
- if !File.exist?(@args[1])
442
- "doesn't exist"
443
- elsif @args[1] =~ /\.css$/
444
- "is a CSS file"
445
- end
446
- raise <<MSG if err
447
- File #{@args[1]} #{err}.
448
- Did you mean: #{@default_syntax} #{flag} #{@args[0]}:#{@args[1]}
449
- MSG
450
- end
451
-
452
- dirs, files = @args.map {|name| split_colon_path(name)}.
453
- partition {|i, _| File.directory? i}
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
459
- dirs.map! {|from, to| [from, to || from]}
460
- ::Sass::Plugin.options[:template_location] = dirs
461
-
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
466
- end
467
- end
468
-
469
- had_error = false
470
- ::Sass::Plugin.on_creating_directory {|dirname| puts_action :directory, :green, dirname}
471
- ::Sass::Plugin.on_deleting_css {|filename| puts_action :delete, :yellow, filename}
472
- ::Sass::Plugin.on_deleting_sourcemap {|filename| puts_action :delete, :yellow, filename}
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
-
481
- raise error unless error.is_a?(::Sass::SyntaxError) && !@options[:stop_on_error]
482
- had_error = true
483
- puts_action :error, :red,
484
- "#{error.sass_filename} (Line #{error.sass_line}: #{error.message})"
485
- STDOUT.flush
486
- end
487
-
488
- if @options[:update]
489
- ::Sass::Plugin.update_stylesheets(files)
490
- exit 1 if had_error
491
- return
492
- end
493
-
494
- puts ">>> Sass is watching for changes. Press Ctrl-C to stop."
495
-
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
508
-
509
- ::Sass::Plugin.watch(files)
510
- end
511
- # @comment
512
- # rubocop:enable MethodLength
513
-
514
- def colon_path?(path)
515
- !split_colon_path(path)[1].nil?
516
- end
517
-
518
- def split_colon_path(path)
519
- one, two = path.split(':', 2)
520
- if one && two && ::Sass::Util.windows? &&
521
- one =~ /\A[A-Za-z]\Z/ && two =~ /\A[\/\\]/
522
- # If we're on Windows and we were passed a drive letter path,
523
- # don't split on that colon.
524
- one2, two = two.split(':', 2)
525
- one = one + ':' + one2
526
- end
527
- return one, two
528
- end
529
-
530
- # Whether path is likely to be meant as the destination
531
- # in a source:dest pair.
532
- def probably_dest_dir?(path)
533
- return false unless path
534
- return false if colon_path?(path)
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
546
- end
547
- end
548
-
549
- class Scss < Sass
550
- # @param args [Array<String>] The command-line arguments
551
- def initialize(args)
552
- super
553
- @default_syntax = :scss
554
- end
555
- end
556
-
557
- # The `sass-convert` executable.
558
- class SassConvert < Generic
559
- # @param args [Array<String>] The command-line arguments
560
- def initialize(args)
561
- super
562
- require 'sass'
563
- @options[:for_tree] = {}
564
- @options[:for_engine] = {:cache => false, :read_cache => true}
565
- end
566
-
567
- # Tells optparse how to parse the arguments.
568
- #
569
- # @param opts [OptionParser]
570
- # @comment
571
- # rubocop:disable MethodLength
572
- def set_opts(opts)
573
- opts.banner = <<END
574
- Usage: sass-convert [options] [INPUT] [OUTPUT]
575
-
576
- Description:
577
- Converts between CSS, Sass, and SCSS files.
578
- E.g. converts from SCSS to Sass,
579
- or converts from CSS to SCSS (adding appropriate nesting).
580
-
581
- Options:
582
- END
583
-
584
- opts.on('-F', '--from FORMAT',
585
- 'The format to convert from. Can be css, scss, sass.',
586
- 'By default, this is inferred from the input filename.',
587
- 'If there is none, defaults to css.') do |name|
588
- @options[:from] = name.downcase.to_sym
589
- raise "sass-convert no longer supports LessCSS." if @options[:from] == :less
590
- unless [:css, :scss, :sass].include?(@options[:from])
591
- raise "Unknown format for sass-convert --from: #{name}"
592
- end
593
- end
594
-
595
- opts.on('-T', '--to FORMAT',
596
- 'The format to convert to. Can be scss or sass.',
597
- 'By default, this is inferred from the output filename.',
598
- 'If there is none, defaults to sass.') do |name|
599
- @options[:to] = name.downcase.to_sym
600
- unless [:scss, :sass].include?(@options[:to])
601
- raise "Unknown format for sass-convert --to: #{name}"
602
- end
603
- end
604
-
605
- opts.on('-R', '--recursive',
606
- 'Convert all the files in a directory. Requires --from and --to.') do
607
- @options[:recursive] = true
608
- end
609
-
610
- opts.on('-i', '--in-place',
611
- 'Convert a file to its own syntax.',
612
- 'This can be used to update some deprecated syntax.') do
613
- @options[:in_place] = true
614
- end
615
-
616
- opts.on('--dasherize', 'Convert underscores to dashes') do
617
- @options[:for_tree][:dasherize] = true
618
- end
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
-
631
- opts.on('--old', 'Output the old-style ":prop val" property syntax.',
632
- 'Only meaningful when generating Sass.') do
633
- @options[:for_tree][:old] = true
634
- end
635
-
636
- opts.on('-C', '--no-cache', "Don't cache to sassc files.") do
637
- @options[:for_engine][:read_cache] = false
638
- end
639
-
640
- unless ::Sass::Util.ruby1_8?
641
- opts.on('-E encoding',
642
- 'Specify the default encoding for Sass and CSS files.') do |encoding|
643
- Encoding.default_external = encoding
644
- end
645
- end
646
-
647
- super
648
- end
649
- # @comment
650
- # rubocop:enable MethodLength
651
-
652
- # Processes the options set by the command-line arguments,
653
- # and runs the CSS compiler appropriately.
654
- def process_result
655
- require 'sass'
656
-
657
- if @options[:recursive]
658
- process_directory
659
- return
660
- end
661
-
662
- super
663
- input = @options[:input]
664
- if File.directory?(input)
665
- raise "Error: '#{input.path}' is a directory (did you mean to use --recursive?)"
666
- end
667
- output = @options[:output]
668
- output = input if @options[:in_place]
669
- process_file(input, output)
670
- end
671
-
672
- private
673
-
674
- def process_directory
675
- unless (input = @options[:input] = @args.shift)
676
- raise "Error: directory required when using --recursive."
677
- end
678
-
679
- output = @options[:output] = @args.shift
680
- raise "Error: --from required when using --recursive." unless @options[:from]
681
- raise "Error: --to required when using --recursive." unless @options[:to]
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])
687
- raise "Error: '#{@options[:output]}' is not a directory"
688
- end
689
- @options[:output] ||= @options[:input]
690
-
691
- if @options[:to] == @options[:from] && !@options[:in_place]
692
- fmt = @options[:from]
693
- raise "Error: converting from #{fmt} to #{fmt} without --in-place"
694
- end
695
-
696
- ext = @options[:from]
697
- ::Sass::Util.glob("#{@options[:input]}/**/*.#{ext}") do |f|
698
- output =
699
- if @options[:in_place]
700
- f
701
- elsif @options[:output]
702
- output_name = f.gsub(/\.(c|sa|sc|le)ss$/, ".#{@options[:to]}")
703
- output_name[0...@options[:input].size] = @options[:output]
704
- output_name
705
- else
706
- f.gsub(/\.(c|sa|sc|le)ss$/, ".#{@options[:to]}")
707
- end
708
-
709
- unless File.directory?(File.dirname(output))
710
- puts_action :directory, :green, File.dirname(output)
711
- FileUtils.mkdir_p(File.dirname(output))
712
- end
713
- puts_action :convert, :green, f
714
- if File.exists?(output)
715
- puts_action :overwrite, :yellow, output
716
- else
717
- puts_action :create, :green, output
718
- end
719
-
720
- input = open_file(f)
721
- process_file(input, output)
722
- end
723
- end
724
-
725
- def process_file(input, output)
726
- if input.is_a?(File)
727
- @options[:from] ||=
728
- case input.path
729
- when /\.scss$/; :scss
730
- when /\.sass$/; :sass
731
- when /\.less$/; raise "sass-convert no longer supports LessCSS."
732
- when /\.css$/; :css
733
- end
734
- elsif @options[:in_place]
735
- raise "Error: the --in-place option requires a filename."
736
- end
737
-
738
- if output.is_a?(File)
739
- @options[:to] ||=
740
- case output.path
741
- when /\.scss$/; :scss
742
- when /\.sass$/; :sass
743
- end
744
- end
745
-
746
- @options[:from] ||= :css
747
- @options[:to] ||= :sass
748
- @options[:for_engine][:syntax] = @options[:from]
749
-
750
- out =
751
- ::Sass::Util.silence_sass_warnings do
752
- if @options[:from] == :css
753
- require 'sass/css'
754
- ::Sass::CSS.new(input.read, @options[:for_tree]).render(@options[:to])
755
- else
756
- if input.is_a?(File)
757
- ::Sass::Engine.for_file(input.path, @options[:for_engine])
758
- else
759
- ::Sass::Engine.new(input.read, @options[:for_engine])
760
- end.to_tree.send("to_#{@options[:to]}", @options[:for_tree])
761
- end
762
- end
763
-
764
- output = input.path if @options[:in_place]
765
- write_output(out, output)
766
- rescue ::Sass::SyntaxError => e
767
- raise e if @options[:trace]
768
- file = " of #{e.sass_filename}" if e.sass_filename
769
- raise "Error on line #{e.sass_line}#{file}: #{e.message}\n Use --trace for backtrace"
770
- rescue LoadError => err
771
- handle_load_error(err)
772
- end
773
- end
774
4
  end
775
5
  end
6
+
7
+ require 'sass/exec/base'
8
+ require 'sass/exec/sass_scss'
9
+ require 'sass/exec/sass_convert'