sass 3.2.0.alpha.35 → 3.2.0.alpha.49

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 (41) hide show
  1. data/README.md +1 -1
  2. data/REVISION +1 -1
  3. data/VERSION +1 -1
  4. data/lib/sass.rb +22 -0
  5. data/lib/sass/engine.rb +2 -2
  6. data/lib/sass/environment.rb +0 -1
  7. data/lib/sass/exec.rb +1 -1
  8. data/lib/sass/importers/filesystem.rb +1 -1
  9. data/lib/sass/plugin.rb +4 -8
  10. data/lib/sass/plugin/compiler.rb +42 -17
  11. data/lib/sass/plugin/configuration.rb +0 -2
  12. data/lib/sass/repl.rb +0 -1
  13. data/lib/sass/script/funcall.rb +6 -1
  14. data/lib/sass/script/functions.rb +2 -2
  15. data/lib/sass/script/number.rb +1 -1
  16. data/lib/sass/script/parser.rb +12 -5
  17. data/lib/sass/script/variable.rb +0 -1
  18. data/lib/sass/scss/css_parser.rb +1 -0
  19. data/lib/sass/scss/parser.rb +37 -12
  20. data/lib/sass/scss/rx.rb +8 -3
  21. data/lib/sass/selector.rb +21 -0
  22. data/lib/sass/selector/abstract_sequence.rb +7 -0
  23. data/lib/sass/tree/media_node.rb +4 -4
  24. data/lib/sass/tree/rule_node.rb +5 -0
  25. data/lib/sass/tree/visitors/check_nesting.rb +10 -10
  26. data/lib/sass/tree/visitors/convert.rb +3 -3
  27. data/lib/sass/tree/visitors/cssize.rb +5 -1
  28. data/lib/sass/tree/visitors/perform.rb +16 -2
  29. data/lib/sass/tree/visitors/to_css.rb +2 -1
  30. data/lib/sass/util.rb +4 -1
  31. data/test/sass/conversion_test.rb +32 -2
  32. data/test/sass/engine_test.rb +105 -1
  33. data/test/sass/extend_test.rb +65 -0
  34. data/test/sass/importer_test.rb +7 -0
  35. data/test/sass/plugin_test.rb +16 -13
  36. data/test/sass/script_conversion_test.rb +2 -0
  37. data/test/sass/script_test.rb +18 -0
  38. data/test/sass/scss/scss_test.rb +34 -0
  39. data/test/sass/test_helper.rb +1 -1
  40. data/test/test_helper.rb +1 -0
  41. metadata +3 -3
data/README.md CHANGED
@@ -54,7 +54,7 @@ To do so, just add
54
54
 
55
55
  to `config.ru`.
56
56
  Then any Sass files in `public/stylesheets/sass`
57
- will be compiled CSS files in `public/stylesheets` on every request.
57
+ will be compiled into CSS files in `public/stylesheets` on every request.
58
58
 
59
59
  To use Sass programatically,
60
60
  check out the [YARD documentation](http://sass-lang.com/docs/yardoc/).
data/REVISION CHANGED
@@ -1 +1 @@
1
- ac7e4266b54b32610016a4b8b651c9d4e18b1e62
1
+ 70a3b0e1abd689bf4f11749f3347e39e9d1c28fb
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.2.0.alpha.35
1
+ 3.2.0.alpha.49
@@ -17,6 +17,28 @@ require 'sass/version'
17
17
  #
18
18
  # Also see the {file:SASS_REFERENCE.md full Sass reference}.
19
19
  module Sass
20
+ # The global load paths for Sass files. This is meant for plugins and
21
+ # libraries to register the paths to their Sass stylesheets to that they may
22
+ # be `@imported`. This load path is used by every instance of [Sass::Engine].
23
+ # They are lower-precedence than any load paths passed in via the
24
+ # {file:SASS_REFERENCE.md#load_paths-option `:load_paths` option}.
25
+ #
26
+ # If the `SASS_PATH` environment variable is set,
27
+ # the initial value of `load_paths` will be initialized based on that.
28
+ # The variable should be a colon-separated list of path names
29
+ # (semicolon-separated on Windows).
30
+ #
31
+ # Note that files on the global load path are never compiled to CSS
32
+ # themselves, even if they aren't partials. They exist only to be imported.
33
+ #
34
+ # @example
35
+ # Sass.load_paths << File.dirname(__FILE__ + '/sass')
36
+ # @return [Array<String, Pathname, Sass::Importers::Base>]
37
+ def self.load_paths
38
+ @load_paths ||= ENV['SASS_PATH'] ?
39
+ ENV['SASS_PATH'].split(Sass::Util.windows? ? ';' : ':') : []
40
+ end
41
+
20
42
  # Compile a Sass or SCSS string to CSS.
21
43
  # Defaults to SCSS.
22
44
  #
@@ -173,7 +173,7 @@ module Sass
173
173
  # for quite a long time.
174
174
  options[:line_comments] ||= options[:line_numbers]
175
175
 
176
- options[:load_paths] = options[:load_paths].map do |p|
176
+ options[:load_paths] = (options[:load_paths] + Sass.load_paths).map do |p|
177
177
  next p unless p.is_a?(String) || (defined?(Pathname) && p.is_a?(Pathname))
178
178
  options[:filesystem_importer].new(p.to_s)
179
179
  end
@@ -692,7 +692,7 @@ WARNING
692
692
  :line => @line + 1) unless line.children.empty?
693
693
  Tree::CharsetNode.new(name)
694
694
  elsif directive == "media"
695
- Tree::MediaNode.new(value)
695
+ Tree::MediaNode.new(value.split(',').map {|s| s.strip})
696
696
  else
697
697
  Tree::DirectiveNode.new(line.text)
698
698
  end
@@ -25,7 +25,6 @@ module Sass
25
25
  # @param parent [Environment] See \{#parent}
26
26
  def initialize(parent = nil)
27
27
  @parent = parent
28
- set_var("important", Script::String.new("!important")) unless parent
29
28
  end
30
29
 
31
30
  # The environment of the caller of this environment's mixin or function.
@@ -389,7 +389,7 @@ MSG
389
389
  dirs.map! {|from, to| [from, to || from]}
390
390
  ::Sass::Plugin.options[:template_location] = dirs
391
391
 
392
- ::Sass::Plugin.on_updating_stylesheet do |_, css|
392
+ ::Sass::Plugin.on_updated_stylesheet do |_, css|
393
393
  if File.exists? css
394
394
  puts_action :overwrite, :yellow, css
395
395
  else
@@ -108,7 +108,7 @@ module Sass
108
108
  # @return [(String, Symbol)] A filename-syntax pair.
109
109
  def find_real_file(dir, name)
110
110
  for (f,s) in possible_files(remove_root(name))
111
- path = (dir == ".") ? f : "#{dir}/#{f}"
111
+ path = (dir == "." || Pathname.new(f).absolute?) ? f : "#{dir}/#{f}"
112
112
  if full_path = Dir[path].first
113
113
  full_path.gsub!(REDUNDANT_DIRECTORY,File::SEPARATOR)
114
114
  return full_path, s
@@ -92,14 +92,10 @@ module Sass
92
92
  # the second is the location of the CSS file that it should be compiled to.
93
93
  # @see #update_stylesheets
94
94
  def force_update_stylesheets(individual_files = [])
95
- old_options = options
96
- self.options = options.dup
97
- options[:never_update] = false
98
- options[:always_update] = true
99
- options[:cache] = false
100
- update_stylesheets(individual_files)
101
- ensure
102
- self.options = old_options
95
+ Compiler.new(options.dup.merge(
96
+ :never_update => false,
97
+ :always_update => true,
98
+ :cache => false)).update_stylesheets(individual_files)
103
99
  end
104
100
 
105
101
  # All other method invocations are proxied to the \{#compiler}.
@@ -38,7 +38,7 @@ module Sass::Plugin
38
38
  self.options.merge!(options)
39
39
  end
40
40
 
41
- # Register a callback to be run before stylesheets are mass-updated.
41
+ # Register a callback to be run after stylesheets are mass-updated.
42
42
  # This is run whenever \{#update\_stylesheets} is called,
43
43
  # unless the \{file:SASS_REFERENCE.md#never_update-option `:never_update` option}
44
44
  # is enabled.
@@ -51,6 +51,22 @@ module Sass::Plugin
51
51
  # the second is the target CSS file.
52
52
  define_callback :updating_stylesheets
53
53
 
54
+ # Register a callback to be run after a single stylesheet is updated.
55
+ # The callback is only run if the stylesheet is really updated;
56
+ # if the CSS file is fresh, this won't be run.
57
+ #
58
+ # Even if the \{file:SASS_REFERENCE.md#full_exception-option `:full_exception` option}
59
+ # is enabled, this callback won't be run
60
+ # when an exception CSS file is being written.
61
+ # To run an action for those files, use \{#on\_compilation\_error}.
62
+ #
63
+ # @yield [template, css]
64
+ # @yieldparam template [String]
65
+ # The location of the Sass/SCSS file being updated.
66
+ # @yieldparam css [String]
67
+ # The location of the CSS file being generated.
68
+ define_callback :updated_stylesheet
69
+
54
70
  # Register a callback to be run before a single stylesheet is updated.
55
71
  # The callback is only run if the stylesheet is guaranteed to be updated;
56
72
  # if the CSS file is fresh, this won't be run.
@@ -67,6 +83,13 @@ module Sass::Plugin
67
83
  # The location of the CSS file being generated.
68
84
  define_callback :updating_stylesheet
69
85
 
86
+ def on_updating_stylesheet_with_deprecation_warning(&block)
87
+ Sass::Util.sass_warn("Sass::Compiler#on_updating_stylesheet callback is deprecated and will be removed in a future release. Use Sass::Compiler#on_updated_stylesheet instead, which is run after stylesheet compilation.")
88
+ on_updating_stylesheet_without_deprecation_warning(&block)
89
+ end
90
+ alias_method :on_updating_stylesheet_without_deprecation_warning, :on_updating_stylesheet
91
+ alias_method :on_updating_stylesheet, :on_updating_stylesheet_with_deprecation_warning
92
+
70
93
  # Register a callback to be run when Sass decides not to update a stylesheet.
71
94
  # In particular, the callback is run when Sass finds that
72
95
  # the template file and none of its dependencies
@@ -160,28 +183,25 @@ module Sass::Plugin
160
183
  # The first string in each pair is the location of the Sass/SCSS file,
161
184
  # the second is the location of the CSS file that it should be compiled to.
162
185
  def update_stylesheets(individual_files = [])
163
- run_updating_stylesheets individual_files
164
186
  Sass::Plugin.checked_for_updates = true
165
187
  staleness_checker = StalenessChecker.new(engine_options)
166
188
 
167
- individual_files.each do |t, c|
168
- if options[:always_update] || staleness_checker.stylesheet_needs_update?(c, t)
169
- update_stylesheet(t, c)
170
- end
171
- end
172
-
173
189
  template_location_array.each do |template_location, css_location|
174
-
175
190
  Dir.glob(File.join(template_location, "**", "[^_]*.s[ca]ss")).sort.each do |file|
176
191
  # Get the relative path to the file
177
192
  name = file.sub(template_location.to_s.sub(/\/*$/, '/'), "")
178
193
  css = css_filename(name, css_location)
194
+ individual_files << [file, css]
195
+ end
196
+ end
179
197
 
180
- if options[:always_update] || staleness_checker.stylesheet_needs_update?(css, file)
181
- update_stylesheet file, css
182
- else
183
- run_not_updating_stylesheet file, css
184
- end
198
+ run_updating_stylesheets individual_files
199
+
200
+ individual_files.each do |file, css|
201
+ if options[:always_update] || staleness_checker.stylesheet_needs_update?(css, file)
202
+ update_stylesheet(file, css)
203
+ else
204
+ run_not_updating_stylesheet(file, css)
185
205
  end
186
206
  end
187
207
  end
@@ -318,18 +338,23 @@ module Sass::Plugin
318
338
  engine_opts = engine_options(:css_filename => css, :filename => filename)
319
339
  result = Sass::Engine.for_file(filename, engine_opts).render
320
340
  rescue Exception => e
341
+ compilation_error_occured = true
321
342
  run_compilation_error e, filename, css
322
343
  result = Sass::SyntaxError.exception_to_css(e, options)
323
344
  else
324
345
  run_updating_stylesheet filename, css
325
346
  end
326
347
 
327
- # Finally, write the file
348
+ write_file(css, result)
349
+ run_updated_stylesheet(filename, css) unless compilation_error_occured
350
+ end
351
+
352
+ def write_file(css, content)
328
353
  flag = 'w'
329
354
  flag = 'wb' if Sass::Util.windows? && options[:unix_newlines]
330
355
  File.open(css, flag) do |file|
331
- file.set_encoding(result.encoding) unless Sass::Util.ruby1_8?
332
- file.print(result)
356
+ file.set_encoding(content.encoding) unless Sass::Util.ruby1_8?
357
+ file.print(content)
333
358
  end
334
359
  end
335
360
 
@@ -31,8 +31,6 @@ module Sass
31
31
  # @return [{Symbol => Object}]
32
32
  def options
33
33
  @options ||= default_options.dup
34
- @options[:cache_store] ||= Sass::CacheStores::Filesystem.new(@options[:cache_location])
35
- @options
36
34
  end
37
35
 
38
36
  # Sets the options hash.
@@ -15,7 +15,6 @@ module Sass
15
15
  # Starts the read-eval-print loop.
16
16
  def run
17
17
  environment = Environment.new
18
- environment.set_var('important', Script::String.new('!important'))
19
18
  @line = 0
20
19
  loop do
21
20
  @line += 1
@@ -88,7 +88,12 @@ module Sass
88
88
  opts(Functions::EvaluationContext.new(environment.options).send(ruby_name, *args))
89
89
  end
90
90
  rescue ArgumentError => e
91
- raise e unless e.backtrace.any? {|t| t =~ /:in `(block in )?(#{name}|perform)'$/}
91
+ # If this is a legitimate Ruby-raised argument error, re-raise it.
92
+ # Otherwise, it's an error in the user's stylesheet, so wrap it.
93
+ if e.message =~ /^wrong number of arguments \(\d+ for \d+\)/ &&
94
+ e.backtrace[0] !~ /:in `(block in )?#{ruby_name}'$/
95
+ raise e
96
+ end
92
97
  raise Sass::SyntaxError.new("#{e.message} for `#{name}'")
93
98
  end
94
99
 
@@ -93,7 +93,7 @@ module Sass::Script
93
93
  # \{#adjust_color adjust-color($color, \[$red\], \[$green\], \[$blue\], \[$hue\], \[$saturation\], \[$lightness\], \[$alpha\]}
94
94
  # : Increase or decrease any of the components of a color.
95
95
  #
96
- # \{#scale_color scale-color($color, \[$red\], \[$green\], \[$blue\], \[$hue\], \[$saturation\], \[$lightness\], \[$alpha\]}
96
+ # \{#scale_color scale-color($color, \[$red\], \[$green\], \[$blue\], \[$saturation\], \[$lightness\], \[$alpha\]}
97
97
  # : Fluidly scale one or more components of a color.
98
98
  #
99
99
  # \{#change_color change-color($color, \[$red\], \[$green\], \[$blue\], \[$hue\], \[$saturation\], \[$lightness\], \[$alpha\]}
@@ -968,7 +968,7 @@ module Sass::Script
968
968
  #
969
969
  # Finally, the weight of color1 is renormalized to be within [0, 1]
970
970
  # and the weight of color2 is given by 1 minus the weight of color1.
971
- p = weight.value/100.0
971
+ p = (weight.value/100.0).to_f
972
972
  w = p*2 - 1
973
973
  a = color1.alpha - color2.alpha
974
974
 
@@ -360,7 +360,7 @@ module Sass::Script
360
360
  elsif num % 1 == 0.0
361
361
  num.to_i
362
362
  else
363
- (num * self.precision_factor).round / self.precision_factor
363
+ ((num * self.precision_factor).round / self.precision_factor).to_f
364
364
  end
365
365
  end
366
366
 
@@ -182,7 +182,11 @@ module Sass
182
182
  interp = try_ops_after_interp(#{ops.inspect}, #{name.inspect}) and return interp
183
183
  return unless e = #{sub}
184
184
  while tok = try_tok(#{ops.map {|o| o.inspect}.join(', ')})
185
- interp = try_op_before_interp(tok, e) and return interp
185
+ if interp = try_op_before_interp(tok, e)
186
+ return interp unless other_interp = try_ops_after_interp(#{ops.inspect}, #{name.inspect}, interp)
187
+ return other_interp
188
+ end
189
+
186
190
  line = @lexer.line
187
191
  e = Operation.new(e, assert_expr(#{sub.inspect}), tok.type)
188
192
  e.line = line
@@ -217,7 +221,10 @@ RUBY
217
221
  return unless e = interpolation
218
222
  arr = [e]
219
223
  while tok = try_tok(:comma)
220
- interp = try_op_before_interp(tok, e) and return interp
224
+ if interp = try_op_before_interp(tok, e)
225
+ return interp unless other_interp = try_ops_after_interp([:comma], :expr, interp)
226
+ return other_interp
227
+ end
221
228
  arr << assert_expr(:interpolation)
222
229
  end
223
230
  arr.size == 1 ? arr.first : node(List.new(arr, :comma), line)
@@ -235,15 +242,15 @@ RUBY
235
242
  interpolation(interp)
236
243
  end
237
244
 
238
- def try_ops_after_interp(ops, name)
245
+ def try_ops_after_interp(ops, name, prev = nil)
239
246
  return unless @lexer.after_interpolation?
240
247
  return unless op = try_tok(*ops)
241
- interp = try_op_before_interp(op) and return interp
248
+ interp = try_op_before_interp(op, prev) and return interp
242
249
 
243
250
  wa = @lexer.whitespace?
244
251
  str = Script::String.new(Lexer::OPERATORS_REVERSE[op.type])
245
252
  str.line = @lexer.line
246
- interp = Script::Interpolation.new(nil, str, assert_expr(name), !:wb, wa, :originally_text)
253
+ interp = Script::Interpolation.new(prev, str, assert_expr(name), !:wb, wa, :originally_text)
247
254
  interp.line = @lexer.line
248
255
  return interp
249
256
  end
@@ -21,7 +21,6 @@ module Sass
21
21
 
22
22
  # @return [String] A string representation of the variable
23
23
  def inspect(opts = {})
24
- return "!important" if name == "important"
25
24
  "$#{dasherize(name, opts)}"
26
25
  end
27
26
  alias_method :to_sass, :inspect
@@ -18,6 +18,7 @@ module Sass
18
18
 
19
19
  private
20
20
 
21
+ def placeholder_selector; nil; end
21
22
  def parent_selector; nil; end
22
23
  def interpolation; nil; end
23
24
  def interp_string; tok(STRING); end
@@ -294,20 +294,23 @@ module Sass
294
294
  def use_css_import?; false; end
295
295
 
296
296
  def media_directive
297
- val = str {media_query_list}.strip
298
- block(node(Sass::Tree::MediaNode.new(val)), :directive)
297
+ block(node(Sass::Tree::MediaNode.new(media_query_list)), :directive)
299
298
  end
300
299
 
301
300
  # http://www.w3.org/TR/css3-mediaqueries/#syntax
302
301
  def media_query_list
303
- return unless media_query
302
+ has_q = false
303
+ q = str {has_q = media_query}
304
+
305
+ return unless has_q
306
+ queries = [q.strip]
304
307
 
305
308
  ss
306
309
  while tok(/,/)
307
- ss; expr!(:media_query); ss
310
+ ss; queries << str {expr!(:media_query)}.strip; ss
308
311
  end
309
312
 
310
- true
313
+ queries
311
314
  end
312
315
 
313
316
  def media_query
@@ -444,7 +447,7 @@ module Sass
444
447
  end
445
448
 
446
449
  def selector_sequence
447
- if sel = tok(STATIC_SELECTOR)
450
+ if sel = tok(STATIC_SELECTOR, true)
448
451
  return [sel]
449
452
  end
450
453
 
@@ -506,12 +509,14 @@ module Sass
506
509
  def simple_selector_sequence
507
510
  # This allows for stuff like http://www.w3.org/TR/css3-animations/#keyframes-
508
511
  return expr unless e = element_name || id_selector || class_selector ||
509
- attrib || negation || pseudo || parent_selector || interpolation_selector
512
+ placeholder_selector || attrib || negation || pseudo || parent_selector ||
513
+ interpolation_selector
510
514
  res = [e]
511
515
 
512
516
  # The tok(/\*/) allows the "E*" hack
513
- while v = id_selector || class_selector || attrib || negation || pseudo ||
514
- interpolation_selector || (tok(/\*/) && Selector::Universal.new(nil))
517
+ while v = id_selector || class_selector || placeholder_selector || attrib ||
518
+ negation || pseudo || interpolation_selector ||
519
+ (tok(/\*/) && Selector::Universal.new(nil))
515
520
  res << v
516
521
  end
517
522
 
@@ -521,6 +526,9 @@ module Sass
521
526
  @scanner.pos = pos
522
527
  @line = line
523
528
  begin
529
+ # If we see "*E", don't force a throw because this could be the
530
+ # "*prop: val" hack.
531
+ expected('"{"') if res.length == 1 && res[0].is_a?(Selector::Universal)
524
532
  throw_error {expected('"{"')}
525
533
  rescue Sass::SyntaxError => e
526
534
  e.message << "\n\n\"#{sel}\" may only be used at the beginning of a selector."
@@ -548,6 +556,12 @@ module Sass
548
556
  Selector::Id.new(merge(expr!(:interp_name)))
549
557
  end
550
558
 
559
+ def placeholder_selector
560
+ return unless tok(/%/)
561
+ @expected = "placeholder name"
562
+ Selector::Placeholder.new(merge(expr!(:interp_ident)))
563
+ end
564
+
551
565
  def element_name
552
566
  return unless name = interp_ident || tok(/\*/) || (tok?(/\|/) && "")
553
567
  if tok(/\|/)
@@ -679,7 +693,7 @@ module Sass
679
693
  # we don't parse it at all, and instead return a plain old string
680
694
  # containing the value.
681
695
  # This results in a dramatic speed increase.
682
- if val = tok(STATIC_VALUE)
696
+ if val = tok(STATIC_VALUE, true)
683
697
  return space, Sass::Script::String.new(val.strip)
684
698
  end
685
699
  return space, sass_script(:parse)
@@ -768,7 +782,7 @@ MESSAGE
768
782
  end
769
783
 
770
784
  def interp_ident(start = IDENT)
771
- return unless val = tok(start) || interpolation
785
+ return unless val = tok(start) || interpolation || tok(IDENT_HYPHEN_INTERP, true)
772
786
  res = [val]
773
787
  while val = tok(NAME) || interpolation
774
788
  res << val
@@ -938,9 +952,20 @@ MESSAGE
938
952
  # This is important because `#tok` is called all the time.
939
953
  NEWLINE = "\n"
940
954
 
941
- def tok(rx)
955
+ def tok(rx, last_group_lookahead = false)
942
956
  res = @scanner.scan(rx)
943
957
  if res
958
+ # This fixes https://github.com/nex3/sass/issues/104, which affects
959
+ # Ruby 1.8.7 and REE. This fix is to replace the ?= zero-width
960
+ # positive lookahead operator in the Regexp (which matches without
961
+ # consuming the matched group), with a match that does consume the
962
+ # group, but then rewinds the scanner and removes the group from the
963
+ # end of the matched string. This fix makes the assumption that the
964
+ # matched group will always occur at the end of the match.
965
+ if last_group_lookahead && @scanner[-1]
966
+ @scanner.pos -= @scanner[-1].length
967
+ res.slice!(-@scanner[-1].length..-1)
968
+ end
944
969
  @line += res.count(NEWLINE)
945
970
  @expected = nil
946
971
  if !@strs.empty? && rx != COMMENT && rx != SINGLE_LINE_COMMENT