sass4 4.0.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 (147) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +13 -0
  3. data/AGENTS.md +534 -0
  4. data/CODE_OF_CONDUCT.md +10 -0
  5. data/CONTRIBUTING.md +148 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +242 -0
  8. data/VERSION +1 -0
  9. data/VERSION_NAME +1 -0
  10. data/bin/sass +13 -0
  11. data/bin/sass-convert +12 -0
  12. data/bin/scss +13 -0
  13. data/extra/sass-spec-ref.sh +40 -0
  14. data/extra/update_watch.rb +13 -0
  15. data/init.rb +18 -0
  16. data/lib/sass/cache_stores/base.rb +88 -0
  17. data/lib/sass/cache_stores/chain.rb +34 -0
  18. data/lib/sass/cache_stores/filesystem.rb +60 -0
  19. data/lib/sass/cache_stores/memory.rb +46 -0
  20. data/lib/sass/cache_stores/null.rb +25 -0
  21. data/lib/sass/cache_stores.rb +15 -0
  22. data/lib/sass/callbacks.rb +67 -0
  23. data/lib/sass/css.rb +407 -0
  24. data/lib/sass/deprecation.rb +55 -0
  25. data/lib/sass/engine.rb +1236 -0
  26. data/lib/sass/environment.rb +236 -0
  27. data/lib/sass/error.rb +198 -0
  28. data/lib/sass/exec/base.rb +188 -0
  29. data/lib/sass/exec/sass_convert.rb +283 -0
  30. data/lib/sass/exec/sass_scss.rb +436 -0
  31. data/lib/sass/exec.rb +9 -0
  32. data/lib/sass/features.rb +48 -0
  33. data/lib/sass/importers/base.rb +182 -0
  34. data/lib/sass/importers/deprecated_path.rb +51 -0
  35. data/lib/sass/importers/filesystem.rb +221 -0
  36. data/lib/sass/importers.rb +23 -0
  37. data/lib/sass/logger/base.rb +47 -0
  38. data/lib/sass/logger/delayed.rb +50 -0
  39. data/lib/sass/logger/log_level.rb +45 -0
  40. data/lib/sass/logger.rb +17 -0
  41. data/lib/sass/media.rb +210 -0
  42. data/lib/sass/plugin/compiler.rb +552 -0
  43. data/lib/sass/plugin/configuration.rb +134 -0
  44. data/lib/sass/plugin/generic.rb +15 -0
  45. data/lib/sass/plugin/merb.rb +48 -0
  46. data/lib/sass/plugin/rack.rb +60 -0
  47. data/lib/sass/plugin/rails.rb +47 -0
  48. data/lib/sass/plugin/staleness_checker.rb +199 -0
  49. data/lib/sass/plugin.rb +134 -0
  50. data/lib/sass/railtie.rb +10 -0
  51. data/lib/sass/repl.rb +57 -0
  52. data/lib/sass/root.rb +7 -0
  53. data/lib/sass/script/css_lexer.rb +33 -0
  54. data/lib/sass/script/css_parser.rb +36 -0
  55. data/lib/sass/script/functions.rb +3103 -0
  56. data/lib/sass/script/lexer.rb +518 -0
  57. data/lib/sass/script/parser.rb +1164 -0
  58. data/lib/sass/script/tree/funcall.rb +314 -0
  59. data/lib/sass/script/tree/interpolation.rb +220 -0
  60. data/lib/sass/script/tree/list_literal.rb +119 -0
  61. data/lib/sass/script/tree/literal.rb +49 -0
  62. data/lib/sass/script/tree/map_literal.rb +64 -0
  63. data/lib/sass/script/tree/node.rb +119 -0
  64. data/lib/sass/script/tree/operation.rb +149 -0
  65. data/lib/sass/script/tree/selector.rb +26 -0
  66. data/lib/sass/script/tree/string_interpolation.rb +125 -0
  67. data/lib/sass/script/tree/unary_operation.rb +69 -0
  68. data/lib/sass/script/tree/variable.rb +57 -0
  69. data/lib/sass/script/tree.rb +16 -0
  70. data/lib/sass/script/value/arg_list.rb +36 -0
  71. data/lib/sass/script/value/base.rb +258 -0
  72. data/lib/sass/script/value/bool.rb +35 -0
  73. data/lib/sass/script/value/callable.rb +25 -0
  74. data/lib/sass/script/value/color.rb +704 -0
  75. data/lib/sass/script/value/function.rb +19 -0
  76. data/lib/sass/script/value/helpers.rb +298 -0
  77. data/lib/sass/script/value/list.rb +135 -0
  78. data/lib/sass/script/value/map.rb +70 -0
  79. data/lib/sass/script/value/null.rb +44 -0
  80. data/lib/sass/script/value/number.rb +564 -0
  81. data/lib/sass/script/value/string.rb +138 -0
  82. data/lib/sass/script/value.rb +13 -0
  83. data/lib/sass/script.rb +66 -0
  84. data/lib/sass/scss/css_parser.rb +61 -0
  85. data/lib/sass/scss/parser.rb +1343 -0
  86. data/lib/sass/scss/rx.rb +134 -0
  87. data/lib/sass/scss/static_parser.rb +351 -0
  88. data/lib/sass/scss.rb +14 -0
  89. data/lib/sass/selector/abstract_sequence.rb +112 -0
  90. data/lib/sass/selector/comma_sequence.rb +195 -0
  91. data/lib/sass/selector/pseudo.rb +291 -0
  92. data/lib/sass/selector/sequence.rb +661 -0
  93. data/lib/sass/selector/simple.rb +124 -0
  94. data/lib/sass/selector/simple_sequence.rb +348 -0
  95. data/lib/sass/selector.rb +327 -0
  96. data/lib/sass/shared.rb +76 -0
  97. data/lib/sass/source/map.rb +209 -0
  98. data/lib/sass/source/position.rb +39 -0
  99. data/lib/sass/source/range.rb +41 -0
  100. data/lib/sass/stack.rb +140 -0
  101. data/lib/sass/supports.rb +225 -0
  102. data/lib/sass/tree/at_root_node.rb +83 -0
  103. data/lib/sass/tree/charset_node.rb +22 -0
  104. data/lib/sass/tree/comment_node.rb +82 -0
  105. data/lib/sass/tree/content_node.rb +9 -0
  106. data/lib/sass/tree/css_import_node.rb +68 -0
  107. data/lib/sass/tree/debug_node.rb +18 -0
  108. data/lib/sass/tree/directive_node.rb +59 -0
  109. data/lib/sass/tree/each_node.rb +24 -0
  110. data/lib/sass/tree/error_node.rb +18 -0
  111. data/lib/sass/tree/extend_node.rb +43 -0
  112. data/lib/sass/tree/for_node.rb +36 -0
  113. data/lib/sass/tree/function_node.rb +44 -0
  114. data/lib/sass/tree/if_node.rb +52 -0
  115. data/lib/sass/tree/import_node.rb +75 -0
  116. data/lib/sass/tree/keyframe_rule_node.rb +15 -0
  117. data/lib/sass/tree/media_node.rb +48 -0
  118. data/lib/sass/tree/mixin_def_node.rb +38 -0
  119. data/lib/sass/tree/mixin_node.rb +52 -0
  120. data/lib/sass/tree/node.rb +240 -0
  121. data/lib/sass/tree/prop_node.rb +162 -0
  122. data/lib/sass/tree/return_node.rb +19 -0
  123. data/lib/sass/tree/root_node.rb +44 -0
  124. data/lib/sass/tree/rule_node.rb +153 -0
  125. data/lib/sass/tree/supports_node.rb +38 -0
  126. data/lib/sass/tree/trace_node.rb +33 -0
  127. data/lib/sass/tree/variable_node.rb +36 -0
  128. data/lib/sass/tree/visitors/base.rb +72 -0
  129. data/lib/sass/tree/visitors/check_nesting.rb +173 -0
  130. data/lib/sass/tree/visitors/convert.rb +350 -0
  131. data/lib/sass/tree/visitors/cssize.rb +362 -0
  132. data/lib/sass/tree/visitors/deep_copy.rb +107 -0
  133. data/lib/sass/tree/visitors/extend.rb +64 -0
  134. data/lib/sass/tree/visitors/perform.rb +572 -0
  135. data/lib/sass/tree/visitors/set_options.rb +139 -0
  136. data/lib/sass/tree/visitors/to_css.rb +440 -0
  137. data/lib/sass/tree/warn_node.rb +18 -0
  138. data/lib/sass/tree/while_node.rb +18 -0
  139. data/lib/sass/util/multibyte_string_scanner.rb +151 -0
  140. data/lib/sass/util/normalized_map.rb +122 -0
  141. data/lib/sass/util/subset_map.rb +109 -0
  142. data/lib/sass/util/test.rb +9 -0
  143. data/lib/sass/util.rb +1137 -0
  144. data/lib/sass/version.rb +120 -0
  145. data/lib/sass.rb +102 -0
  146. data/rails/init.rb +1 -0
  147. metadata +283 -0
@@ -0,0 +1,436 @@
1
+ module Sass::Exec
2
+ # The `sass` and `scss` executables.
3
+ class SassScss < Base
4
+ attr_reader :default_syntax
5
+
6
+ # @param args [Array<String>] The command-line arguments
7
+ def initialize(args, default_syntax)
8
+ super(args)
9
+ @options[:sourcemap] = :auto
10
+ @options[:for_engine] = {
11
+ :load_paths => default_sass_path
12
+ }
13
+ @default_syntax = default_syntax
14
+ end
15
+
16
+ protected
17
+
18
+ # Tells optparse how to parse the arguments.
19
+ #
20
+ # @param opts [OptionParser]
21
+ def set_opts(opts)
22
+ opts.banner = <<END
23
+ Usage: #{default_syntax} [options] [INPUT] [OUTPUT]
24
+
25
+ Description:
26
+ Converts SCSS or Sass files to CSS.
27
+ END
28
+
29
+ common_options(opts)
30
+ watching_and_updating(opts)
31
+ input_and_output(opts)
32
+ miscellaneous(opts)
33
+ end
34
+
35
+ # Processes the options set by the command-line arguments,
36
+ # and runs the Sass compiler appropriately.
37
+ def process_result
38
+ require 'sass'
39
+
40
+ if !@options[:update] && !@options[:watch] &&
41
+ @args.first && colon_path?(@args.first)
42
+ if @args.size == 1
43
+ @args = split_colon_path(@args.first)
44
+ else
45
+ @fake_update = true
46
+ @options[:update] = true
47
+ end
48
+ end
49
+ load_compass if @options[:compass]
50
+ return interactive if @options[:interactive]
51
+ return watch_or_update if @options[:watch] || @options[:update]
52
+ super
53
+
54
+ if @options[:sourcemap] != :none && @options[:output_filename]
55
+ @options[:sourcemap_filename] = Sass::Util.sourcemap_name(@options[:output_filename])
56
+ end
57
+
58
+ @options[:for_engine][:filename] = @options[:filename]
59
+ @options[:for_engine][:css_filename] = @options[:output] if @options[:output].is_a?(String)
60
+ @options[:for_engine][:sourcemap_filename] = @options[:sourcemap_filename]
61
+ @options[:for_engine][:sourcemap] = @options[:sourcemap]
62
+
63
+ run
64
+ end
65
+
66
+ private
67
+
68
+ def common_options(opts)
69
+ opts.separator ''
70
+ opts.separator 'Common Options:'
71
+
72
+ opts.on('-I', '--load-path PATH', 'Specify a Sass import path.') do |path|
73
+ (@options[:for_engine][:load_paths] ||= []) << path
74
+ end
75
+
76
+ opts.on('-r', '--require LIB', 'Require a Ruby library before running Sass.') do |lib|
77
+ require lib
78
+ end
79
+
80
+ opts.on('--compass', 'Make Compass imports available and load project configuration.') do
81
+ @options[:compass] = true
82
+ end
83
+
84
+ opts.on('-t', '--style NAME', 'Output style. Can be nested (default), compact, ' \
85
+ 'compressed, or expanded.') do |name|
86
+ @options[:for_engine][:style] = name.to_sym
87
+ end
88
+
89
+ opts.on("-?", "-h", "--help", "Show this help message.") do
90
+ puts opts
91
+ exit
92
+ end
93
+
94
+ opts.on("-v", "--version", "Print the Sass version.") do
95
+ puts("Ruby Sass #{Sass.version[:string]}")
96
+ exit
97
+ end
98
+ end
99
+
100
+ def watching_and_updating(opts)
101
+ opts.separator ''
102
+ opts.separator 'Watching and Updating:'
103
+
104
+ opts.on('--watch', 'Watch files or directories for changes.',
105
+ 'The location of the generated CSS can be set using a colon:',
106
+ " #{@default_syntax} --watch input.#{@default_syntax}:output.css",
107
+ " #{@default_syntax} --watch input-dir:output-dir") do
108
+ @options[:watch] = true
109
+ end
110
+
111
+ # Polling is used by default on Windows.
112
+ unless Sass::Util.windows?
113
+ opts.on('--poll', 'Check for file changes manually, rather than relying on the OS.',
114
+ 'Only meaningful for --watch.') do
115
+ @options[:poll] = true
116
+ end
117
+ end
118
+
119
+ opts.on('--update', 'Compile files or directories to CSS.',
120
+ 'Locations are set like --watch.') do
121
+ @options[:update] = true
122
+ end
123
+
124
+ opts.on('-f', '--force', 'Recompile every Sass file, even if the CSS file is newer.',
125
+ 'Only meaningful for --update.') do
126
+ @options[:force] = true
127
+ end
128
+
129
+ opts.on('--stop-on-error', 'If a file fails to compile, exit immediately.',
130
+ 'Only meaningful for --watch and --update.') do
131
+ @options[:stop_on_error] = true
132
+ end
133
+ end
134
+
135
+ def input_and_output(opts)
136
+ opts.separator ''
137
+ opts.separator 'Input and Output:'
138
+
139
+ if @default_syntax == :sass
140
+ opts.on('--scss',
141
+ 'Use the CSS-superset SCSS syntax.') do
142
+ @options[:for_engine][:syntax] = :scss
143
+ end
144
+ else
145
+ opts.on('--sass',
146
+ 'Use the indented Sass syntax.') do
147
+ @options[:for_engine][:syntax] = :sass
148
+ end
149
+ end
150
+
151
+ # This is optional for backwards-compatibility with Sass 3.3, which didn't
152
+ # enable sourcemaps by default and instead used "--sourcemap" to do so.
153
+ opts.on(:OPTIONAL, '--sourcemap=TYPE',
154
+ 'How to link generated output to the source files.',
155
+ ' auto (default): relative paths where possible, file URIs elsewhere',
156
+ ' file: always absolute file URIs',
157
+ ' inline: include the source text in the sourcemap',
158
+ ' none: no sourcemaps') do |type|
159
+ if type && !%w(auto file inline none).include?(type)
160
+ $stderr.puts "Unknown sourcemap type #{type}.\n\n"
161
+ $stderr.puts opts
162
+ exit
163
+ elsif type.nil?
164
+ Sass::Util.sass_warn <<MESSAGE.rstrip
165
+ DEPRECATION WARNING: Passing --sourcemap without a value is deprecated.
166
+ Sourcemaps are now generated by default, so this flag has no effect.
167
+ MESSAGE
168
+ end
169
+
170
+ @options[:sourcemap] = (type || :auto).to_sym
171
+ end
172
+
173
+ opts.on('-s', '--stdin', :NONE,
174
+ 'Read input from standard input instead of an input file.',
175
+ 'This is the default if no input file is specified.') do
176
+ @options[:input] = $stdin
177
+ end
178
+
179
+ encoding_option(opts)
180
+
181
+ opts.on('--unix-newlines', 'Use Unix-style newlines in written files.',
182
+ ('Always true on Unix.' unless Sass::Util.windows?)) do
183
+ @options[:unix_newlines] = true if Sass::Util.windows?
184
+ end
185
+
186
+ opts.on('-g', '--debug-info',
187
+ 'Emit output that can be used by the FireSass Firebug plugin.') do
188
+ @options[:for_engine][:debug_info] = true
189
+ end
190
+
191
+ opts.on('-l', '--line-numbers', '--line-comments',
192
+ 'Emit comments in the generated CSS indicating the corresponding source line.') do
193
+ @options[:for_engine][:line_numbers] = true
194
+ end
195
+ end
196
+
197
+ def miscellaneous(opts)
198
+ opts.separator ''
199
+ opts.separator 'Miscellaneous:'
200
+
201
+ opts.on('-i', '--interactive',
202
+ 'Run an interactive SassScript shell.') do
203
+ @options[:interactive] = true
204
+ end
205
+
206
+ opts.on('-c', '--check', "Just check syntax, don't evaluate.") do
207
+ require 'stringio'
208
+ @options[:check_syntax] = true
209
+ @options[:output] = StringIO.new
210
+ end
211
+
212
+ opts.on('--precision NUMBER_OF_DIGITS', Integer,
213
+ "How many digits of precision to use when outputting decimal numbers.",
214
+ "Defaults to #{Sass::Script::Value::Number.precision}.") do |precision|
215
+ Sass::Script::Value::Number.precision = precision
216
+ end
217
+
218
+ opts.on('--cache-location PATH',
219
+ 'The path to save parsed Sass files. Defaults to .sass-cache.') do |loc|
220
+ @options[:for_engine][:cache_location] = loc
221
+ end
222
+
223
+ opts.on('-C', '--no-cache', "Don't cache parsed Sass files.") do
224
+ @options[:for_engine][:cache] = false
225
+ end
226
+
227
+ opts.on('--trace', :NONE, 'Show a full Ruby stack trace on error.') do
228
+ @options[:trace] = true
229
+ end
230
+
231
+ opts.on('-q', '--quiet', 'Silence warnings and status messages during compilation.') do
232
+ @options[:for_engine][:quiet] = true
233
+ end
234
+ end
235
+
236
+ def load_compass
237
+ begin
238
+ require 'compass'
239
+ rescue LoadError
240
+ require 'rubygems'
241
+ begin
242
+ require 'compass'
243
+ rescue LoadError
244
+ puts "ERROR: Cannot load compass."
245
+ exit 1
246
+ end
247
+ end
248
+ Compass.add_project_configuration
249
+ Compass.configuration.project_path ||= Dir.pwd
250
+ @options[:for_engine][:load_paths] ||= []
251
+ @options[:for_engine][:load_paths] += Compass.configuration.sass_load_paths
252
+ end
253
+
254
+ def interactive
255
+ require 'sass/repl'
256
+ Sass::Repl.new(@options).run
257
+ end
258
+
259
+ def watch_or_update
260
+ require 'sass/plugin'
261
+ Sass::Plugin.options.merge! @options[:for_engine]
262
+ Sass::Plugin.options[:unix_newlines] = @options[:unix_newlines]
263
+ Sass::Plugin.options[:poll] = @options[:poll]
264
+ Sass::Plugin.options[:sourcemap] = @options[:sourcemap]
265
+
266
+ if @options[:force]
267
+ raise "The --force flag may only be used with --update." unless @options[:update]
268
+ Sass::Plugin.options[:always_update] = true
269
+ end
270
+
271
+ raise <<MSG if @args.empty?
272
+ What files should I watch? Did you mean something like:
273
+ #{@default_syntax} --watch input.#{@default_syntax}:output.css
274
+ #{@default_syntax} --watch input-dir:output-dir
275
+ MSG
276
+
277
+ if !colon_path?(@args[0]) && probably_dest_dir?(@args[1])
278
+ flag = @options[:update] ? "--update" : "--watch"
279
+ err =
280
+ if !File.exist?(@args[1])
281
+ "doesn't exist"
282
+ elsif @args[1] =~ /\.css$/
283
+ "is a CSS file"
284
+ end
285
+ raise <<MSG if err
286
+ File #{@args[1]} #{err}.
287
+ Did you mean: #{@default_syntax} #{flag} #{@args[0]}:#{@args[1]}
288
+ MSG
289
+ end
290
+
291
+ dirs, files = @args.map {|name| split_colon_path(name)}.
292
+ partition {|i, _| File.directory? i}
293
+
294
+ if @fake_update && !dirs.empty?
295
+ # Issue 1602.
296
+ Sass::Util.sass_warn <<WARNING.strip
297
+ DEPRECATION WARNING: Compiling directories without --update or --watch is
298
+ deprecated and won't work in future versions of Sass. Instead use:
299
+ #{@default_syntax} --update #{@args}
300
+ WARNING
301
+ end
302
+
303
+ files.map! do |from, to|
304
+ to ||= from.gsub(/\.[^.]*?$/, '.css')
305
+ sourcemap = Sass::Util.sourcemap_name(to) if @options[:sourcemap]
306
+ [from, to, sourcemap]
307
+ end
308
+ dirs.map! {|from, to| [from, to || from]}
309
+ Sass::Plugin.options[:template_location] = dirs
310
+
311
+ Sass::Plugin.on_updated_stylesheet do |_, css, sourcemap|
312
+ [css, sourcemap].each do |file|
313
+ next unless file
314
+ puts_action :write, :green, file
315
+ end
316
+ end
317
+
318
+ had_error = false
319
+ Sass::Plugin.on_creating_directory {|dirname| puts_action :directory, :green, dirname}
320
+ Sass::Plugin.on_deleting_css {|filename| puts_action :delete, :yellow, filename}
321
+ Sass::Plugin.on_deleting_sourcemap {|filename| puts_action :delete, :yellow, filename}
322
+ Sass::Plugin.on_compilation_error do |error, _, _|
323
+ if error.is_a?(SystemCallError) && !@options[:stop_on_error]
324
+ had_error = true
325
+ puts_action :error, :red, error.message
326
+ STDOUT.flush
327
+ next
328
+ end
329
+
330
+ raise error unless error.is_a?(Sass::SyntaxError) && !@options[:stop_on_error]
331
+ had_error = true
332
+ puts_action :error, :red,
333
+ "#{error.sass_filename} (Line #{error.sass_line}: #{error.message})"
334
+ STDOUT.flush
335
+ end
336
+
337
+ if @options[:update]
338
+ Sass::Plugin.update_stylesheets(files)
339
+ exit 1 if had_error
340
+ return
341
+ end
342
+
343
+ puts ">>> Sass is watching for changes. Press Ctrl-C to stop."
344
+
345
+ Sass::Plugin.on_template_modified do |template|
346
+ puts ">>> Change detected to: #{template}"
347
+ STDOUT.flush
348
+ end
349
+ Sass::Plugin.on_template_created do |template|
350
+ puts ">>> New template detected: #{template}"
351
+ STDOUT.flush
352
+ end
353
+ Sass::Plugin.on_template_deleted do |template|
354
+ puts ">>> Deleted template detected: #{template}"
355
+ STDOUT.flush
356
+ end
357
+
358
+ Sass::Plugin.watch(files)
359
+ end
360
+
361
+ def run
362
+ input = @options[:input]
363
+ output = @options[:output]
364
+
365
+ if input == $stdin
366
+ # See issue 1745
367
+ (@options[:for_engine][:load_paths] ||= []) << ::Sass::Importers::DeprecatedPath.new(".")
368
+ end
369
+
370
+ @options[:for_engine][:syntax] ||= :scss if input.is_a?(File) && input.path =~ /\.scss$/
371
+ @options[:for_engine][:syntax] ||= @default_syntax
372
+ engine =
373
+ if input.is_a?(File) && !@options[:check_syntax]
374
+ Sass::Engine.for_file(input.path, @options[:for_engine])
375
+ else
376
+ # We don't need to do any special handling of @options[:check_syntax] here,
377
+ # because the Sass syntax checking happens alongside evaluation
378
+ # and evaluation doesn't actually evaluate any code anyway.
379
+ Sass::Engine.new(input.read, @options[:for_engine])
380
+ end
381
+
382
+ input.close if input.is_a?(File)
383
+
384
+ if @options[:sourcemap] != :none && @options[:sourcemap_filename]
385
+ relative_sourcemap_path = Sass::Util.relative_path_from(
386
+ @options[:sourcemap_filename], Sass::Util.pathname(@options[:output_filename]).dirname)
387
+ rendered, mapping = engine.render_with_sourcemap(relative_sourcemap_path.to_s)
388
+ write_output(rendered, output)
389
+ write_output(
390
+ mapping.to_json(
391
+ :type => @options[:sourcemap],
392
+ :css_path => @options[:output_filename],
393
+ :sourcemap_path => @options[:sourcemap_filename]) + "\n",
394
+ @options[:sourcemap_filename])
395
+ else
396
+ write_output(engine.render, output)
397
+ end
398
+ rescue Sass::SyntaxError => e
399
+ write_output(Sass::SyntaxError.exception_to_css(e), output) if output.is_a?(String)
400
+ raise e
401
+ ensure
402
+ output.close if output.is_a? File
403
+ end
404
+
405
+ def colon_path?(path)
406
+ !split_colon_path(path)[1].nil?
407
+ end
408
+
409
+ def split_colon_path(path)
410
+ one, two = path.split(':', 2)
411
+ if one && two && Sass::Util.windows? &&
412
+ one =~ /\A[A-Za-z]\Z/ && two =~ %r{\A[/\\]}
413
+ # If we're on Windows and we were passed a drive letter path,
414
+ # don't split on that colon.
415
+ one2, two = two.split(':', 2)
416
+ one = one + ':' + one2
417
+ end
418
+ return one, two
419
+ end
420
+
421
+ # Whether path is likely to be meant as the destination
422
+ # in a source:dest pair.
423
+ def probably_dest_dir?(path)
424
+ return false unless path
425
+ return false if colon_path?(path)
426
+ Sass::Util.glob(File.join(path, "*.s[ca]ss")).empty?
427
+ end
428
+
429
+ def default_sass_path
430
+ return unless ENV['SASS_PATH']
431
+ # The select here prevents errors when the environment's
432
+ # load paths specified do not exist.
433
+ ENV['SASS_PATH'].split(File::PATH_SEPARATOR).select {|d| File.directory?(d)}
434
+ end
435
+ end
436
+ end
data/lib/sass/exec.rb ADDED
@@ -0,0 +1,9 @@
1
+ module Sass
2
+ # This module handles the Sass executables (`sass` and `sass-convert`).
3
+ module Exec
4
+ end
5
+ end
6
+
7
+ require 'sass/exec/base'
8
+ require 'sass/exec/sass_scss'
9
+ require 'sass/exec/sass_convert'
@@ -0,0 +1,48 @@
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
+ #
8
+ # When this is updated, the documentation of `feature-exists()` should be
9
+ # updated as well.
10
+ KNOWN_FEATURES = Set[*%w(
11
+ global-variable-shadowing
12
+ extend-selector-pseudoclass
13
+ units-level-3
14
+ at-error
15
+ custom-property
16
+ )]
17
+
18
+ # Check if a feature exists by name. This is used to implement
19
+ # the Sass function `feature-exists($feature)`
20
+ #
21
+ # @param feature_name [String] The case sensitive name of the feature to
22
+ # check if it exists in this version of Sass.
23
+ # @return [Boolean] whether the feature of that name exists.
24
+ def has_feature?(feature_name)
25
+ KNOWN_FEATURES.include?(feature_name)
26
+ end
27
+
28
+ # Add a feature to Sass. Plugins can use this to easily expose their
29
+ # availability to end users. Plugins must prefix their feature
30
+ # names with a dash to distinguish them from official features.
31
+ #
32
+ # @example
33
+ # Sass.add_feature("-import-globbing")
34
+ # Sass.add_feature("-math-cos")
35
+ #
36
+ #
37
+ # @param feature_name [String] The case sensitive name of the feature to
38
+ # to add to Sass. Must begin with a dash.
39
+ def add_feature(feature_name)
40
+ unless feature_name[0] == ?-
41
+ raise ArgumentError.new("Plugin feature names must begin with a dash")
42
+ end
43
+ KNOWN_FEATURES << feature_name
44
+ end
45
+ end
46
+
47
+ extend Features
48
+ end
@@ -0,0 +1,182 @@
1
+ module Sass
2
+ module Importers
3
+ # The abstract base class for Sass importers.
4
+ # All importers should inherit from this.
5
+ #
6
+ # At the most basic level, an importer is given a string
7
+ # and must return a {Sass::Engine} containing some Sass code.
8
+ # This string can be interpreted however the importer wants;
9
+ # however, subclasses are encouraged to use the URI format
10
+ # for pathnames.
11
+ #
12
+ # Importers that have some notion of "relative imports"
13
+ # should take a single load path in their constructor,
14
+ # and interpret paths as relative to that.
15
+ # They should also implement the \{#find\_relative} method.
16
+ #
17
+ # Importers should be serializable via `Marshal.dump`.
18
+ #
19
+ # @abstract
20
+ class Base
21
+ # Find a Sass file relative to another file.
22
+ # Importers without a notion of "relative paths"
23
+ # should just return nil here.
24
+ #
25
+ # If the importer does have a notion of "relative paths",
26
+ # it should ignore its load path during this method.
27
+ #
28
+ # See \{#find} for important information on how this method should behave.
29
+ #
30
+ # The `:filename` option passed to the returned {Sass::Engine}
31
+ # should be of a format that could be passed to \{#find}.
32
+ #
33
+ # @param uri [String] The URI to import. This is not necessarily relative,
34
+ # but this method should only return true if it is.
35
+ # @param base [String] The base filename. If `uri` is relative,
36
+ # it should be interpreted as relative to `base`.
37
+ # `base` is guaranteed to be in a format importable by this importer.
38
+ # @param options [{Symbol => Object}] Options for the Sass file
39
+ # containing the `@import` that's currently being resolved.
40
+ # @return [Sass::Engine, nil] An Engine containing the imported file,
41
+ # or nil if it couldn't be found or was in the wrong format.
42
+ def find_relative(uri, base, options)
43
+ Sass::Util.abstract(self)
44
+ end
45
+
46
+ # Find a Sass file, if it exists.
47
+ #
48
+ # This is the primary entry point of the Importer.
49
+ # It corresponds directly to an `@import` statement in Sass.
50
+ # It should do three basic things:
51
+ #
52
+ # * Determine if the URI is in this importer's format.
53
+ # If not, return nil.
54
+ # * Determine if the file indicated by the URI actually exists and is readable.
55
+ # If not, return nil.
56
+ # * Read the file and place the contents in a {Sass::Engine}.
57
+ # Return that engine.
58
+ #
59
+ # If this importer's format allows for file extensions,
60
+ # it should treat them the same way as the default {Filesystem} importer.
61
+ # If the URI explicitly has a `.sass` or `.scss` filename,
62
+ # the importer should look for that exact file
63
+ # and import it as the syntax indicated.
64
+ # If it doesn't exist, the importer should return nil.
65
+ #
66
+ # If the URI doesn't have either of these extensions,
67
+ # the importer should look for files with the extensions.
68
+ # If no such files exist, it should return nil.
69
+ #
70
+ # The {Sass::Engine} to be returned should be passed `options`,
71
+ # with a few modifications. `:syntax` should be set appropriately,
72
+ # `:filename` should be set to `uri`,
73
+ # and `:importer` should be set to this importer.
74
+ #
75
+ # @param uri [String] The URI to import.
76
+ # @param options [{Symbol => Object}] Options for the Sass file
77
+ # containing the `@import` that's currently being resolved.
78
+ # This is safe for subclasses to modify destructively.
79
+ # Callers should only pass in a value they don't mind being destructively modified.
80
+ # @return [Sass::Engine, nil] An Engine containing the imported file,
81
+ # or nil if it couldn't be found or was in the wrong format.
82
+ def find(uri, options)
83
+ Sass::Util.abstract(self)
84
+ end
85
+
86
+ # Returns the time the given Sass file was last modified.
87
+ #
88
+ # If the given file has been deleted or the time can't be accessed
89
+ # for some other reason, this should return nil.
90
+ #
91
+ # @param uri [String] The URI of the file to check.
92
+ # Comes from a `:filename` option set on an engine returned by this importer.
93
+ # @param options [{Symbol => Object}] Options for the Sass file
94
+ # containing the `@import` currently being checked.
95
+ # @return [Time, nil]
96
+ def mtime(uri, options)
97
+ Sass::Util.abstract(self)
98
+ end
99
+
100
+ # Get the cache key pair for the given Sass URI.
101
+ # The URI need not be checked for validity.
102
+ #
103
+ # The only strict requirement is that the returned pair of strings
104
+ # uniquely identify the file at the given URI.
105
+ # However, the first component generally corresponds roughly to the directory,
106
+ # and the second to the basename, of the URI.
107
+ #
108
+ # Note that keys must be unique *across importers*.
109
+ # Thus it's probably a good idea to include the importer name
110
+ # at the beginning of the first component.
111
+ #
112
+ # @param uri [String] A URI known to be valid for this importer.
113
+ # @param options [{Symbol => Object}] Options for the Sass file
114
+ # containing the `@import` currently being checked.
115
+ # @return [(String, String)] The key pair which uniquely identifies
116
+ # the file at the given URI.
117
+ def key(uri, options)
118
+ Sass::Util.abstract(self)
119
+ end
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
+ # If an absolute "file:" URI can be produced for an imported file, that
128
+ # should be preferred to returning `nil`. However, a URL relative to
129
+ # `sourcemap_directory` should be preferred over an absolute "file:" URI.
130
+ #
131
+ # @param uri [String] A URI known to be valid for this importer.
132
+ # @param sourcemap_directory [String, NilClass] The absolute path to a
133
+ # directory on disk where the sourcemap will be saved. If uri refers to
134
+ # a file on disk that's accessible relative to sourcemap_directory, this
135
+ # may return a relative URL. This may be `nil` if the sourcemap's
136
+ # eventual location is unknown.
137
+ # @return [String?] The publicly-visible URL for this file, or `nil`
138
+ # indicating that no publicly-visible URL exists. This should be
139
+ # appropriately URL-escaped.
140
+ def public_url(uri, sourcemap_directory)
141
+ return if @public_url_warning_issued
142
+ @public_url_warning_issued = true
143
+ Sass::Util.sass_warn <<WARNING
144
+ WARNING: #{self.class.name} should define the #public_url method.
145
+ WARNING
146
+ nil
147
+ end
148
+
149
+ # A string representation of the importer.
150
+ # Should be overridden by subclasses.
151
+ #
152
+ # This is used to help debugging,
153
+ # and should usually just show the load path encapsulated by this importer.
154
+ #
155
+ # @return [String]
156
+ def to_s
157
+ Sass::Util.abstract(self)
158
+ end
159
+
160
+ # If the importer is based on files on the local filesystem
161
+ # this method should return folders which should be watched
162
+ # for changes.
163
+ #
164
+ # @return [Array<String>] List of absolute paths of directories to watch
165
+ def directories_to_watch
166
+ []
167
+ end
168
+
169
+ # If this importer is based on files on the local filesystem This method
170
+ # should return true if the file, when changed, should trigger a
171
+ # recompile.
172
+ #
173
+ # It is acceptable for non-sass files to be watched and trigger a recompile.
174
+ #
175
+ # @param filename [String] The absolute filename for a file that has changed.
176
+ # @return [Boolean] When the file changed should cause a recompile.
177
+ def watched_file?(filename)
178
+ false
179
+ end
180
+ end
181
+ end
182
+ end