sass 3.4.21 → 3.4.22

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 (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.