sass 3.4.21 → 3.4.22

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +122 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +15 -9
  5. data/Rakefile +71 -18
  6. data/VERSION +1 -1
  7. data/VERSION_DATE +1 -1
  8. data/lib/sass.rb +7 -0
  9. data/lib/sass/callbacks.rb +2 -2
  10. data/lib/sass/css.rb +9 -8
  11. data/lib/sass/engine.rb +14 -8
  12. data/lib/sass/environment.rb +8 -1
  13. data/lib/sass/error.rb +5 -5
  14. data/lib/sass/exec/sass_convert.rb +14 -2
  15. data/lib/sass/exec/sass_scss.rb +2 -25
  16. data/lib/sass/features.rb +2 -2
  17. data/lib/sass/importers/filesystem.rb +6 -3
  18. data/lib/sass/plugin/compiler.rb +15 -7
  19. data/lib/sass/script/css_variable_warning.rb +52 -0
  20. data/lib/sass/script/functions.rb +5 -5
  21. data/lib/sass/script/lexer.rb +3 -1
  22. data/lib/sass/script/parser.rb +67 -15
  23. data/lib/sass/script/tree/funcall.rb +10 -3
  24. data/lib/sass/script/tree/node.rb +8 -0
  25. data/lib/sass/script/tree/operation.rb +7 -0
  26. data/lib/sass/script/value/base.rb +1 -0
  27. data/lib/sass/script/value/color.rb +6 -4
  28. data/lib/sass/script/value/helpers.rb +2 -2
  29. data/lib/sass/script/value/number.rb +5 -5
  30. data/lib/sass/scss.rb +0 -2
  31. data/lib/sass/scss/css_parser.rb +1 -2
  32. data/lib/sass/scss/parser.rb +20 -10
  33. data/lib/sass/scss/rx.rb +2 -2
  34. data/lib/sass/scss/static_parser.rb +11 -13
  35. data/lib/sass/selector/pseudo.rb +1 -1
  36. data/lib/sass/selector/sequence.rb +2 -2
  37. data/lib/sass/selector/simple_sequence.rb +4 -4
  38. data/lib/sass/stack.rb +2 -2
  39. data/lib/sass/tree/function_node.rb +1 -1
  40. data/lib/sass/tree/node.rb +2 -0
  41. data/lib/sass/tree/visitors/check_nesting.rb +2 -0
  42. data/lib/sass/tree/visitors/convert.rb +8 -7
  43. data/lib/sass/tree/visitors/perform.rb +4 -2
  44. data/lib/sass/tree/visitors/to_css.rb +10 -10
  45. data/lib/sass/util.rb +8 -7
  46. data/test/sass-spec.yml +3 -0
  47. data/test/sass/compiler_test.rb +1 -1
  48. data/test/sass/css_variable_test.rb +132 -0
  49. data/test/sass/exec_test.rb +10 -0
  50. data/test/sass/script_test.rb +1 -1
  51. data/test/sass/scss/scss_test.rb +10 -0
  52. metadata +79 -77
  53. data/lib/sass/scss/script_lexer.rb +0 -15
  54. data/lib/sass/scss/script_parser.rb +0 -25
@@ -128,12 +128,15 @@ module Sass::Script::Tree
128
128
  splat = Sass::Tree::Visitors::Perform.perform_splat(
129
129
  @splat, keywords, @kwarg_splat, environment)
130
130
  if (fn = environment.function(@name))
131
+ css_variable_warning.warn! if css_variable_warning
131
132
  return without_original(perform_sass_fn(fn, args, splat, environment))
132
133
  end
133
134
 
134
135
  args = construct_ruby_args(ruby_name, args, splat, environment)
135
136
 
136
137
  if Sass::Script::Functions.callable?(ruby_name)
138
+ css_variable_warning.warn! if css_variable_warning
139
+
137
140
  local_environment = Sass::Environment.new(environment.global_env, environment.options)
138
141
  local_environment.caller = Sass::ReadOnlyEnvironment.new(environment, environment.options)
139
142
  result = opts(Sass::Script::Functions::EvaluationContext.new(
@@ -208,7 +211,7 @@ module Sass::Script::Tree
208
211
 
209
212
  argnames = signature.args[args.size..-1] || []
210
213
  deprecated_argnames = (signature.deprecated && signature.deprecated[args.size..-1]) || []
211
- args = args + argnames.zip(deprecated_argnames).map do |(argname, deprecated_argname)|
214
+ args += argnames.zip(deprecated_argnames).map do |(argname, deprecated_argname)|
212
215
  if keywords.has_key?(argname)
213
216
  keywords.delete(argname)
214
217
  elsif deprecated_argname && keywords.has_key?(deprecated_argname)
@@ -296,8 +299,12 @@ module Sass::Script::Tree
296
299
  message = "wrong number of arguments (#{given} for #{expected})"
297
300
  end
298
301
  end
299
- elsif e.message =~ /^wrong number of arguments \(\d+ for \d+\)/ &&
300
- e.backtrace[0] !~ /:in `(block in )?#{ruby_name}'$/
302
+ elsif (md = /^wrong number of arguments \(given (\d+), expected (\d+)\)/.match(e.message)) &&
303
+ e.backtrace[0] =~ /:in `#{ruby_name}'$/
304
+ # Handle ruby 2.3 error formatting
305
+ message = "wrong number of arguments (#{md[1]} for #{md[2]})"
306
+ elsif e.message =~ /^wrong number of arguments/ &&
307
+ e.backtrace[0] !~ /:in `(block in )?#{ruby_name}'$/
301
308
  raise e
302
309
  end
303
310
  raise Sass::SyntaxError.new("#{message} for `#{name}'")
@@ -23,6 +23,14 @@ module Sass::Script::Tree
23
23
  # @return [String]
24
24
  attr_accessor :filename
25
25
 
26
+ # The warning that this node should emit if it executes in a way that's not
27
+ # safe for a CSS variable value.
28
+ #
29
+ # This is `nil` if this is not in a CSS variable value.
30
+ #
31
+ # @return [Sass::Script::CssVariableWarning]
32
+ attr_accessor :css_variable_warning
33
+
26
34
  # Sets the options hash for this node,
27
35
  # as well as for all child nodes.
28
36
  # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
@@ -78,6 +78,13 @@ module Sass::Script::Tree
78
78
  "Invalid null operation: \"#{value1.inspect} #{@operator} #{value2.inspect}\".")
79
79
  end
80
80
 
81
+ if css_variable_warning && @operator == :div &&
82
+ !(value1.is_a?(Sass::Script::Value::Number) && value1.original &&
83
+ value2.is_a?(Sass::Script::Value::Number) && value2.original) &&
84
+ !(value1.is_a?(Sass::Script::Value::String) && value2.is_a?(Sass::Script::Value::String))
85
+ css_variable_warning.warn!
86
+ end
87
+
81
88
  begin
82
89
  result = opts(value1.send(@operator, value2))
83
90
  rescue NoMethodError => e
@@ -22,6 +22,7 @@ module Sass::Script::Value
22
22
  def initialize(value = nil)
23
23
  value.freeze unless value.nil? || value == true || value == false
24
24
  @value = value
25
+ @options = nil
25
26
  end
26
27
 
27
28
  # Sets the options hash for this node,
@@ -25,7 +25,8 @@ module Sass::Script::Value
25
25
  rgba
26
26
  end
27
27
 
28
- ALTERNATE_COLOR_NAMES = Sass::Util.map_vals({
28
+ ALTERNATE_COLOR_NAMES = Sass::Util.map_vals(
29
+ {
29
30
  'aqua' => 0x00FFFFFF,
30
31
  'darkgrey' => 0xA9A9A9FF,
31
32
  'darkslategrey' => 0x2F4F4FFF,
@@ -35,10 +36,11 @@ module Sass::Script::Value
35
36
  'lightgrey' => 0xD3D3D3FF,
36
37
  'lightslategrey' => 0x778899FF,
37
38
  'slategrey' => 0x708090FF,
38
- }, &method(:int_to_rgba))
39
+ }, &method(:int_to_rgba))
39
40
 
40
41
  # A hash from color names to `[red, green, blue]` value arrays.
41
- COLOR_NAMES = Sass::Util.map_vals({
42
+ COLOR_NAMES = Sass::Util.map_vals(
43
+ {
42
44
  'aliceblue' => 0xF0F8FFFF,
43
45
  'antiquewhite' => 0xFAEBD7FF,
44
46
  'aquamarine' => 0x7FFFD4FF,
@@ -179,7 +181,7 @@ module Sass::Script::Value
179
181
  'whitesmoke' => 0xF5F5F5FF,
180
182
  'yellow' => 0xFFFF00FF,
181
183
  'yellowgreen' => 0x9ACD32FF
182
- }, &method(:int_to_rgba))
184
+ }, &method(:int_to_rgba))
183
185
 
184
186
  # A hash from `[red, green, blue, alpha]` value arrays to color names.
185
187
  COLOR_NAMES_REVERSE = COLOR_NAMES.invert.freeze
@@ -252,14 +252,14 @@ module Sass::Script::Value
252
252
  def parse_unit_string(unit_string)
253
253
  denominator_units = numerator_units = Sass::Script::Value::Number::NO_UNITS
254
254
  return numerator_units, denominator_units unless unit_string && unit_string.length > 0
255
- num_over_denominator = unit_string.split(/ *\/ */)
255
+ num_over_denominator = unit_string.split(%r{ */ *})
256
256
  unless (1..2).include?(num_over_denominator.size)
257
257
  raise ArgumentError.new("Malformed unit string: #{unit_string}")
258
258
  end
259
259
  numerator_units = num_over_denominator[0].split(/ *\* */)
260
260
  denominator_units = (num_over_denominator[1] || "").split(/ *\* */)
261
261
  [[numerator_units, "numerator"], [denominator_units, "denominator"]].each do |units, name|
262
- if unit_string =~ /\// && units.size == 0
262
+ if unit_string =~ %r{/} && units.size == 0
263
263
  raise ArgumentError.new("Malformed unit string: #{unit_string}")
264
264
  end
265
265
  if units.any? {|unit| unit !~ VALID_UNIT}
@@ -71,6 +71,7 @@ module Sass::Script::Value
71
71
  super(value)
72
72
  @numerator_units = numerator_units
73
73
  @denominator_units = denominator_units
74
+ @options = nil
74
75
  normalize!
75
76
  end
76
77
 
@@ -469,11 +470,10 @@ module Sass::Script::Value
469
470
  sans_common_units(@numerator_units, @denominator_units)
470
471
 
471
472
  @denominator_units.each_with_index do |d, i|
472
- if convertable?(d) && (u = @numerator_units.find(&method(:convertable?)))
473
- @value /= conversion_factor(d, u)
474
- @denominator_units.delete_at(i)
475
- @numerator_units.delete_at(@numerator_units.index(u))
476
- end
473
+ next unless convertable?(d) && (u = @numerator_units.find(&method(:convertable?)))
474
+ @value /= conversion_factor(d, u)
475
+ @denominator_units.delete_at(i)
476
+ @numerator_units.delete_at(@numerator_units.index(u))
477
477
  end
478
478
  end
479
479
 
@@ -1,6 +1,4 @@
1
1
  require 'sass/scss/rx'
2
- require 'sass/scss/script_lexer'
3
- require 'sass/scss/script_parser'
4
2
  require 'sass/scss/parser'
5
3
  require 'sass/scss/static_parser'
6
4
  require 'sass/scss/css_parser'
@@ -50,8 +50,7 @@ module Sass
50
50
  block(node(Sass::Tree::KeyframeRuleNode.new(selector.strip), start_pos), :ruleset)
51
51
  end
52
52
 
53
- @sass_script_parser = Class.new(Sass::Script::CssParser)
54
- @sass_script_parser.send(:include, ScriptParser)
53
+ @sass_script_parser = Sass::Script::CssParser
55
54
  end
56
55
  end
57
56
  end
@@ -28,6 +28,8 @@ module Sass
28
28
  @line = line
29
29
  @offset = offset
30
30
  @strs = []
31
+ @expected = nil
32
+ @throw_error = false
31
33
  end
32
34
 
33
35
  # Parses an SCSS document.
@@ -509,7 +511,7 @@ module Sass
509
511
 
510
512
  def moz_document_function
511
513
  val = interp_uri || _interp_string(:url_prefix) ||
512
- _interp_string(:domain) || function(!:allow_var) || interpolation
514
+ _interp_string(:domain) || function(false) || interpolation
513
515
  return unless val
514
516
  ss
515
517
  val
@@ -638,7 +640,7 @@ module Sass
638
640
  # are disallowed by the CSS spec,
639
641
  # but they're included here for compatibility
640
642
  # with some proprietary MS properties
641
- str {ss if tok(/[\/,:.=]/)}
643
+ str {ss if tok(%r{[/,:.=]})}
642
644
  end
643
645
 
644
646
  def ruleset
@@ -764,7 +766,7 @@ module Sass
764
766
  value_start_pos = source_position
765
767
  value = nil
766
768
  error = catch_error do
767
- value = value!
769
+ value = value!(name.first.is_a?(String) && name.first.start_with?("--"))
768
770
  if tok?(/\{/)
769
771
  # Properties that are ambiguous with selectors can't have additional
770
772
  # properties nested beneath them.
@@ -857,7 +859,7 @@ module Sass
857
859
  tok!(/:/)
858
860
  ss
859
861
  value_start_pos = source_position
860
- value = value!
862
+ value = value!(name.first.is_a?(String) && name.first.start_with?("--"))
861
863
  value_end_pos = source_position
862
864
  ss
863
865
  require_block = tok?(/\{/)
@@ -871,7 +873,7 @@ module Sass
871
873
  nested_properties! node
872
874
  end
873
875
 
874
- def value!
876
+ def value!(css_variable = false)
875
877
  if tok?(/\{/)
876
878
  str = Sass::Script::Tree::Literal.new(Sass::Script::Value::String.new(""))
877
879
  str.line = source_position.line
@@ -891,10 +893,19 @@ module Sass
891
893
  str.source_range = range(start_pos)
892
894
  return str
893
895
  end
894
- sass_script(:parse)
896
+
897
+ sass_script(:parse, css_variable)
895
898
  end
896
899
 
897
900
  def nested_properties!(node)
901
+ if node.name.first.is_a?(String) && node.name.first.start_with?("--")
902
+ Sass::Util.sass_warn(<<WARNING)
903
+ DEPRECATION WARNING on line #{@line}#{" of #{@filename}" if @filename}:
904
+ Sass 3.6 will change the way CSS variables are parsed. Instead of being parsed as
905
+ normal properties, they will not allow any Sass-specific behavior other than \#{}.
906
+ WARNING
907
+ end
908
+
898
909
  @expected = 'expression (e.g. 1px, bold) or "{"'
899
910
  block(node, :property)
900
911
  end
@@ -1027,8 +1038,7 @@ module Sass
1027
1038
  node
1028
1039
  end
1029
1040
 
1030
- @sass_script_parser = Class.new(Sass::Script::Parser)
1031
- @sass_script_parser.send(:include, ScriptParser)
1041
+ @sass_script_parser = Sass::Script::Parser
1032
1042
 
1033
1043
  class << self
1034
1044
  # @private
@@ -1037,7 +1047,7 @@ module Sass
1037
1047
 
1038
1048
  def sass_script(*args)
1039
1049
  parser = self.class.sass_script_parser.new(@scanner, @line, @offset,
1040
- :filename => @filename, :importer => @importer)
1050
+ :filename => @filename, :importer => @importer, :allow_extra_text => true)
1041
1051
  result = parser.send(*args)
1042
1052
  unless @strs.empty?
1043
1053
  # Convert to CSS manually so that comments are ignored.
@@ -1102,7 +1112,7 @@ module Sass
1102
1112
 
1103
1113
  unless name
1104
1114
  # Display basic regexps as plain old strings
1105
- source = rx.source.gsub(/\\\//, '/')
1115
+ source = rx.source.gsub(%r{\\/}, '/')
1106
1116
  string = rx.source.gsub(/\\(.)/, '\1')
1107
1117
  name = source == Regexp.escape(string) ? string.inspect : rx.inspect
1108
1118
  end
@@ -32,7 +32,7 @@ module Sass
32
32
  # @return [String] The escaped character
33
33
  # @private
34
34
  def self.escape_char(c)
35
- return "\\%06x" % (Sass::Util.ord(c)) unless c =~ /[ -\/:-~]/
35
+ return "\\%06x" % (Sass::Util.ord(c)) unless c =~ %r{[ -/:-~]}
36
36
  "\\#{c}"
37
37
  end
38
38
 
@@ -133,7 +133,7 @@ module Sass
133
133
  STRING_NOINTERP = /#{STRING1_NOINTERP}|#{STRING2_NOINTERP}/
134
134
 
135
135
  STATIC_COMPONENT = /#{IDENT}|#{STRING_NOINTERP}|#{HEXCOLOR}|[+-]?#{NUMBER}|\!important/i
136
- STATIC_VALUE = /#{STATIC_COMPONENT}(\s*[\s,\/]\s*#{STATIC_COMPONENT})*([;}])/i
136
+ STATIC_VALUE = %r(#{STATIC_COMPONENT}(\s*[\s,/]\s*#{STATIC_COMPONENT})*([;}]))i
137
137
  STATIC_SELECTOR = /(#{NMCHAR}|[ \t]|[,>+*]|[:#.]#{NMSTART}){1,50}([{])/i
138
138
  end
139
139
  end
@@ -62,7 +62,7 @@ module Sass
62
62
  private
63
63
 
64
64
  def moz_document_function
65
- val = tok(URI) || tok(URL_PREFIX) || tok(DOMAIN) || function(!:allow_var)
65
+ val = tok(URI) || tok(URL_PREFIX) || tok(DOMAIN) || function(false)
66
66
  return unless val
67
67
  ss
68
68
  [val]
@@ -78,7 +78,7 @@ module Sass
78
78
  def use_css_import?; true; end
79
79
 
80
80
  def special_directive(name, start_pos)
81
- return unless %w[media import charset -moz-document].include?(name)
81
+ return unless %w(media import charset -moz-document).include?(name)
82
82
  super
83
83
  end
84
84
 
@@ -89,13 +89,12 @@ module Sass
89
89
  ws = ''
90
90
  while tok(/,/)
91
91
  ws << str {ss}
92
- if (sel = selector)
93
- selectors << sel
94
- if ws.include?("\n")
95
- selectors[-1] = Selector::Sequence.new(["\n"] + selectors.last.members)
96
- end
97
- ws = ''
92
+ next unless (sel = selector)
93
+ selectors << sel
94
+ if ws.include?("\n")
95
+ selectors[-1] = Selector::Sequence.new(["\n"] + selectors.last.members)
98
96
  end
97
+ ws = ''
99
98
  end
100
99
  Selector::CommaSequence.new(selectors)
101
100
  end
@@ -140,11 +139,11 @@ MESSAGE
140
139
  end
141
140
 
142
141
  def reference_combinator
143
- return unless tok(/\//)
142
+ return unless tok(%r{/})
144
143
  res = '/'
145
144
  ns, name = expr!(:qualified_name)
146
145
  res << ns << '|' if ns
147
- res << name << tok!(/\//)
146
+ res << name << tok!(%r{/})
148
147
  res
149
148
  end
150
149
 
@@ -266,9 +265,9 @@ MESSAGE
266
265
  return ns, name
267
266
  end
268
267
 
269
- SELECTOR_PSEUDO_CLASSES = %w[not matches current any has host host-context].to_set
268
+ SELECTOR_PSEUDO_CLASSES = %w(not matches current any has host host-context).to_set
270
269
 
271
- PREFIXED_SELECTOR_PSEUDO_CLASSES = %w[nth-child nth-last-child].to_set
270
+ PREFIXED_SELECTOR_PSEUDO_CLASSES = %w(nth-child nth-last-child).to_set
272
271
 
273
272
  def pseudo
274
273
  s = tok(/::?/)
@@ -362,7 +361,6 @@ MESSAGE
362
361
  end
363
362
 
364
363
  @sass_script_parser = Class.new(Sass::Script::CssParser)
365
- @sass_script_parser.send(:include, ScriptParser)
366
364
  end
367
365
  end
368
366
  end
@@ -9,7 +9,7 @@ module Sass
9
9
  # selectors.
10
10
  #
11
11
  # @return [Set<String>]
12
- ACTUALLY_ELEMENTS = %w[after before first-line first-letter].to_set
12
+ ACTUALLY_ELEMENTS = %w(after before first-line first-letter).to_set
13
13
 
14
14
  # Like \{#type}, but returns the type of selector this looks like, rather
15
15
  # than the type it is semantically. This only differs from type for
@@ -395,10 +395,10 @@ module Sass
395
395
  ([merged, '+'] if merged)
396
396
  ].compact
397
397
  end
398
- elsif op1 == '>' && %w[~ +].include?(op2)
398
+ elsif op1 == '>' && %w(~ +).include?(op2)
399
399
  res.unshift sel2, op2
400
400
  seq1.push sel1, op1
401
- elsif op2 == '>' && %w[~ +].include?(op1)
401
+ elsif op2 == '>' && %w(~ +).include?(op1)
402
402
  res.unshift sel1, op1
403
403
  seq2.push sel2, op2
404
404
  elsif op1 == op2
@@ -88,7 +88,7 @@ module Sass
88
88
  def resolve_parent_refs(super_cseq)
89
89
  resolved_members = @members.map do |sel|
90
90
  next sel unless sel.is_a?(Pseudo) && sel.selector
91
- sel.with_selector(sel.selector.resolve_parent_refs(super_cseq, !:implicit_parent))
91
+ sel.with_selector(sel.selector.resolve_parent_refs(super_cseq, false))
92
92
  end.flatten
93
93
 
94
94
  # Parent selector only appears as the first selector in the sequence
@@ -161,7 +161,7 @@ module Sass
161
161
  members = self.members.map do |sel|
162
162
  next sel unless sel.is_a?(Pseudo) && sel.selector
163
163
  next sel if seen.include?([sel])
164
- extended = sel.selector.do_extend(extends, parent_directives, replace, seen, !:original)
164
+ extended = sel.selector.do_extend(extends, parent_directives, replace, seen, false)
165
165
  next sel if extended == sel.selector
166
166
  extended.members.reject! {|seq| seq.has_placeholder?}
167
167
 
@@ -204,7 +204,7 @@ module Sass
204
204
  groups.map! do |sels, seq|
205
205
  next [] if seen.include?(sels)
206
206
  seq.do_extend(
207
- extends, parent_directives, !:replace, seen_with_pseudo_selectors + [sels], !:original)
207
+ extends, parent_directives, false, seen_with_pseudo_selectors + [sels], false)
208
208
  end
209
209
  groups.flatten!
210
210
 
@@ -260,7 +260,7 @@ module Sass
260
260
 
261
261
  # Some psuedo-selectors can be subselectors of non-pseudo selectors.
262
262
  # Pull those out here so we can efficiently check against them below.
263
- their_subselector_pseudos = %w[matches any nth-child nth-last-child].
263
+ their_subselector_pseudos = %w(matches any nth-child nth-last-child).
264
264
  map {|name| their_spcs[name] || []}.flatten
265
265
 
266
266
  # If `self`'s non-pseudo simple selectors aren't a subset of `their_sseq`'s,
@@ -101,8 +101,8 @@ module Sass
101
101
  def to_s
102
102
  Sass::Util.enum_with_index(Sass::Util.enum_cons(frames.reverse + [nil], 2)).
103
103
  map do |(frame, caller), i|
104
- "#{i == 0 ? "on" : "from"} line #{frame.line}" +
105
- " of #{frame.filename || "an unknown file"}" +
104
+ "#{i == 0 ? 'on' : 'from'} line #{frame.line}" +
105
+ " of #{frame.filename || 'an unknown file'}" +
106
106
  (caller && caller.name ? ", in `#{caller.name}'" : "")
107
107
  end.join("\n")
108
108
  end
@@ -36,7 +36,7 @@ module Sass
36
36
  @splat = splat
37
37
  super()
38
38
 
39
- return unless %w[and or not].include?(name)
39
+ return unless %w(and or not).include?(name)
40
40
  raise Sass::SyntaxError.new("Invalid function name \"#{name}\".")
41
41
  end
42
42
  end
@@ -90,6 +90,8 @@ module Sass
90
90
 
91
91
  def initialize
92
92
  @children = []
93
+ @filename = nil
94
+ @options = nil
93
95
  end
94
96
 
95
97
  # Sets the options hash for the node and all its children.