sass 3.2.0.alpha.11 → 3.2.0.alpha.21

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 (81) hide show
  1. data/REVISION +1 -1
  2. data/VERSION +1 -1
  3. data/lib/sass/cache_stores/base.rb +4 -9
  4. data/lib/sass/cache_stores/filesystem.rb +2 -0
  5. data/lib/sass/css.rb +2 -1
  6. data/lib/sass/engine.rb +28 -8
  7. data/lib/sass/environment.rb +26 -0
  8. data/lib/sass/exec.rb +13 -0
  9. data/lib/sass/importers/base.rb +2 -1
  10. data/lib/sass/script/funcall.rb +8 -0
  11. data/lib/sass/script/interpolation.rb +9 -0
  12. data/lib/sass/script/list.rb +7 -0
  13. data/lib/sass/script/literal.rb +5 -0
  14. data/lib/sass/script/node.rb +8 -0
  15. data/lib/sass/script/number.rb +28 -5
  16. data/lib/sass/script/operation.rb +8 -0
  17. data/lib/sass/script/string_interpolation.rb +9 -0
  18. data/lib/sass/script/unary_operation.rb +7 -0
  19. data/lib/sass/script/variable.rb +5 -0
  20. data/lib/sass/scss/parser.rb +26 -13
  21. data/lib/sass/scss/rx.rb +1 -1
  22. data/lib/sass/scss/static_parser.rb +2 -2
  23. data/lib/sass/tree/content_node.rb +9 -0
  24. data/lib/sass/tree/debug_node.rb +1 -6
  25. data/lib/sass/tree/each_node.rb +1 -7
  26. data/lib/sass/tree/extend_node.rb +1 -1
  27. data/lib/sass/tree/for_node.rb +2 -7
  28. data/lib/sass/tree/function_node.rb +1 -6
  29. data/lib/sass/tree/if_node.rb +1 -19
  30. data/lib/sass/tree/mixin_def_node.rb +5 -6
  31. data/lib/sass/tree/mixin_node.rb +2 -7
  32. data/lib/sass/tree/node.rb +4 -19
  33. data/lib/sass/tree/prop_node.rb +0 -5
  34. data/lib/sass/tree/return_node.rb +1 -6
  35. data/lib/sass/tree/rule_node.rb +9 -7
  36. data/lib/sass/tree/trace_node.rb +32 -0
  37. data/lib/sass/tree/variable_node.rb +1 -7
  38. data/lib/sass/tree/visitors/check_nesting.rb +30 -13
  39. data/lib/sass/tree/visitors/convert.rb +5 -1
  40. data/lib/sass/tree/visitors/cssize.rb +3 -3
  41. data/lib/sass/tree/visitors/deep_copy.rb +87 -0
  42. data/lib/sass/tree/visitors/perform.rb +36 -16
  43. data/lib/sass/tree/visitors/set_options.rb +97 -0
  44. data/lib/sass/tree/visitors/to_css.rb +5 -1
  45. data/lib/sass/tree/warn_node.rb +1 -7
  46. data/lib/sass/tree/while_node.rb +1 -7
  47. data/test/sass/cache_test.rb +15 -0
  48. data/test/sass/conversion_test.rb +38 -0
  49. data/test/sass/css2sass_test.rb +9 -0
  50. data/test/sass/engine_test.rb +248 -17
  51. data/test/sass/scss/css_test.rb +4 -2
  52. data/test/sass/scss/scss_test.rb +53 -12
  53. data/vendor/fssm/Gemfile +3 -0
  54. data/vendor/fssm/LICENSE +1 -1
  55. data/vendor/fssm/README.markdown +55 -27
  56. data/vendor/fssm/Rakefile +6 -54
  57. data/vendor/fssm/example.rb +6 -3
  58. data/vendor/fssm/fssm.gemspec +17 -70
  59. data/vendor/fssm/lib/fssm.rb +7 -3
  60. data/vendor/fssm/lib/fssm/backends/fsevents.rb +1 -1
  61. data/vendor/fssm/lib/fssm/backends/inotify.rb +2 -2
  62. data/vendor/fssm/lib/fssm/backends/polling.rb +2 -2
  63. data/vendor/fssm/lib/fssm/backends/rbfsevent.rb +42 -0
  64. data/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +10 -10
  65. data/vendor/fssm/lib/fssm/monitor.rb +19 -9
  66. data/vendor/fssm/lib/fssm/path.rb +24 -21
  67. data/vendor/fssm/lib/fssm/pathname.rb +13 -479
  68. data/vendor/fssm/lib/fssm/state/directory.rb +29 -11
  69. data/vendor/fssm/lib/fssm/state/file.rb +1 -1
  70. data/vendor/fssm/lib/fssm/support.rb +41 -12
  71. data/vendor/fssm/lib/fssm/tree.rb +6 -6
  72. data/vendor/fssm/lib/fssm/version.rb +3 -0
  73. data/vendor/fssm/profile/prof-cache.rb +3 -3
  74. data/vendor/fssm/profile/prof-pathname-rubinius.rb +35 -0
  75. data/vendor/fssm/profile/prof-pathname.rb +7 -7
  76. data/vendor/fssm/spec/count_down_latch.rb +151 -0
  77. data/vendor/fssm/spec/monitor_spec.rb +202 -0
  78. data/vendor/fssm/spec/path_spec.rb +36 -15
  79. data/vendor/fssm/spec/spec_helper.rb +6 -6
  80. metadata +14 -4
  81. data/vendor/fssm/VERSION.yml +0 -5
@@ -175,7 +175,11 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
175
175
  keywords = node.keywords.map {|k, v| "$#{dasherize(k)}: #{v.to_sass(@options)}"}.join(', ')
176
176
  arglist = "(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords})"
177
177
  end
178
- "#{tab_str}#{@format == :sass ? '+' : '@include '}#{dasherize(node.name)}#{arglist}#{semi}\n"
178
+ "#{tab_str}#{@format == :sass ? '+' : '@include '}#{dasherize(node.name)}#{arglist}#{node.has_children ? yield : semi}\n"
179
+ end
180
+
181
+ def visit_content(node)
182
+ "#{tab_str}@content#{semi}\n"
179
183
  end
180
184
 
181
185
  def visit_prop(node)
@@ -121,9 +121,9 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
121
121
  (node.children.empty? ? [] : [node]) + media
122
122
  end
123
123
 
124
- # Asserts that all the mixin's children are valid in their new location.
125
- def visit_mixin(node)
126
- # Don't use #visit_children to avoid adding the mixin node to the list of parents.
124
+ # Asserts that all the traced children are valid in their new location.
125
+ def visit_trace(node)
126
+ # Don't use #visit_children to avoid adding the trace node to the list of parents.
127
127
  node.children.map {|c| visit(c)}.flatten
128
128
  rescue Sass::SyntaxError => e
129
129
  e.modify_backtrace(:mixin => node.name, :filename => node.filename, :line => node.line)
@@ -0,0 +1,87 @@
1
+ # A visitor for copying the full structure of a Sass tree.
2
+ class Sass::Tree::Visitors::DeepCopy < Sass::Tree::Visitors::Base
3
+ protected
4
+
5
+ def visit(node)
6
+ super(node.dup)
7
+ end
8
+
9
+ def visit_children(parent)
10
+ parent.children = parent.children.map {|c| visit(c)}
11
+ parent
12
+ end
13
+
14
+ def visit_debug(node)
15
+ node.expr = node.expr.deep_copy
16
+ yield
17
+ end
18
+
19
+ def visit_each(node)
20
+ node.list = node.list.deep_copy
21
+ yield
22
+ end
23
+
24
+ def visit_extend(node)
25
+ node.selector = node.selector.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c}
26
+ yield
27
+ end
28
+
29
+ def visit_for(node)
30
+ node.from = node.from.deep_copy
31
+ node.to = node.to.deep_copy
32
+ yield
33
+ end
34
+
35
+ def visit_function(node)
36
+ node.args = node.args.map {|k, v| [k.deep_copy, v && v.deep_copy]}
37
+ yield
38
+ end
39
+
40
+ def visit_if(node)
41
+ node.expr = node.expr.deep_copy if node.expr
42
+ node.else = visit(node.else) if node.else
43
+ yield
44
+ end
45
+
46
+ def visit_mixin_def(node)
47
+ node.args = node.args.map {|k, v| [k.deep_copy, v && v.deep_copy]}
48
+ yield
49
+ end
50
+
51
+ def visit_mixin(node)
52
+ node.args = node.args.map {|a| a.deep_copy}
53
+ node.keywords = Hash[node.keywords.map {|k, v| [k, v.deep_copy]}]
54
+ yield
55
+ end
56
+
57
+ def visit_prop(node)
58
+ node.name = node.name.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c}
59
+ node.value = node.value.deep_copy
60
+ yield
61
+ end
62
+
63
+ def visit_return(node)
64
+ node.expr = node.expr.deep_copy
65
+ yield
66
+ end
67
+
68
+ def visit_rule(node)
69
+ node.rule = node.rule.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c}
70
+ yield
71
+ end
72
+
73
+ def visit_variable(node)
74
+ node.expr = node.expr.deep_copy
75
+ yield
76
+ end
77
+
78
+ def visit_warn(node)
79
+ node.expr = node.expr.deep_copy
80
+ yield
81
+ end
82
+
83
+ def visit_while(node)
84
+ node.expr = node.expr.deep_copy
85
+ yield
86
+ end
87
+ end
@@ -13,7 +13,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
13
13
  @environment = env
14
14
  end
15
15
 
16
- # If an exception is raised, this add proper metadata to the backtrace.
16
+ # If an exception is raised, this adds proper metadata to the backtrace.
17
17
  def visit(node)
18
18
  super(node.dup)
19
19
  rescue Sass::SyntaxError => e
@@ -89,8 +89,8 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
89
89
  # Runs SassScript interpolation in the selector,
90
90
  # and then parses the result into a {Sass::Selector::CommaSequence}.
91
91
  def visit_extend(node)
92
- parser = Sass::SCSS::CssParser.new(run_interp(node.selector), node.line)
93
- node.resolved_selector = parser.parse_selector(node.filename)
92
+ parser = Sass::SCSS::CssParser.new(run_interp(node.selector), node.filename, node.line)
93
+ node.resolved_selector = parser.parse_selector
94
94
  node
95
95
  end
96
96
 
@@ -116,7 +116,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
116
116
  # Loads the function into the environment.
117
117
  def visit_function(node)
118
118
  @environment.set_function(node.name,
119
- Sass::Callable.new(node.name, node.args, @environment, node.children))
119
+ Sass::Callable.new(node.name, node.args, @environment, node.children, !:has_content))
120
120
  []
121
121
  end
122
122
 
@@ -155,7 +155,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
155
155
  # Loads a mixin into the environment.
156
156
  def visit_mixindef(node)
157
157
  @environment.set_mixin(node.name,
158
- Sass::Callable.new(node.name, node.args, @environment, node.children))
158
+ Sass::Callable.new(node.name, node.args, @environment, node.children, node.has_content))
159
159
  []
160
160
  end
161
161
 
@@ -168,6 +168,10 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
168
168
  original_env.prepare_frame(:mixin => node.name)
169
169
  raise Sass::SyntaxError.new("Undefined mixin '#{node.name}'.") unless mixin = @environment.mixin(node.name)
170
170
 
171
+ if node.children.any? && !mixin.has_content
172
+ raise Sass::SyntaxError.new(%Q{Mixin "#{node.name}" does not accept a content block.})
173
+ end
174
+
171
175
  passed_args = node.args.dup
172
176
  passed_keywords = node.keywords.dup
173
177
 
@@ -196,9 +200,12 @@ END
196
200
  raise Sass::SyntaxError.new("Mixin #{node.name} is missing parameter #{var.inspect}.") unless env.var(var.name)
197
201
  env
198
202
  end
203
+ environment.caller = Sass::Environment.new(original_env)
204
+ environment.content = node.children if node.has_children
199
205
 
200
- with_environment(environment) {node.children = mixin.tree.map {|c| visit(c)}.flatten}
201
- node
206
+ trace_node = Sass::Tree::TraceNode.from_node(node.name, node)
207
+ with_environment(environment) {trace_node.children = mixin.tree.map {|c| visit(c)}.flatten}
208
+ trace_node
202
209
  rescue Sass::SyntaxError => e
203
210
  if original_env # Don't add backtrace info if this is an @include loop
204
211
  e.modify_backtrace(:mixin => node.name, :line => node.line)
@@ -209,6 +216,17 @@ END
209
216
  original_env.pop_frame if original_env
210
217
  end
211
218
 
219
+ def visit_content(node)
220
+ raise Sass::SyntaxError.new("No @content passed.") unless content = @environment.content
221
+ trace_node = Sass::Tree::TraceNode.from_node('@content', node)
222
+ with_environment(@environment.caller) {trace_node.children = content.map {|c| visit(c.dup)}.flatten}
223
+ trace_node
224
+ rescue Sass::SyntaxError => e
225
+ e.modify_backtrace(:mixin => '@content', :line => node.line)
226
+ e.add_backtrace(:line => node.line)
227
+ raise e
228
+ end
229
+
212
230
  # Runs any SassScript that may be embedded in a property.
213
231
  def visit_prop(node)
214
232
  node.resolved_name = run_interp(node.name)
@@ -225,8 +243,13 @@ END
225
243
  # Runs SassScript interpolation in the selector,
226
244
  # and then parses the result into a {Sass::Selector::CommaSequence}.
227
245
  def visit_rule(node)
228
- parser = Sass::SCSS::StaticParser.new(run_interp(node.rule), node.line)
229
- node.parsed_rules ||= parser.parse_selector(node.filename)
246
+ parser = Sass::SCSS::StaticParser.new(run_interp(node.rule), node.filename, node.line)
247
+ node.parsed_rules ||= parser.parse_selector
248
+ if node.options[:trace_selectors]
249
+ @environment.push_frame(:filename => node.filename, :line => node.line)
250
+ node.stack_trace = @environment.stack_trace
251
+ @environment.pop_frame
252
+ end
230
253
  yield
231
254
  end
232
255
 
@@ -243,13 +266,10 @@ END
243
266
  @environment.push_frame(:filename => node.filename, :line => node.line)
244
267
  res = node.expr.perform(@environment)
245
268
  res = res.value if res.is_a?(Sass::Script::String)
246
- msg = "WARNING: #{res}\n"
247
- @environment.stack.reverse.each_with_index do |entry, i|
248
- msg << " #{i == 0 ? "on" : "from"} line #{entry[:line]}" <<
249
- " of #{entry[:filename] || "an unknown file"}"
250
- msg << ", in `#{entry[:mixin]}'" if entry[:mixin]
251
- msg << "\n"
252
- end
269
+ msg = "WARNING: #{res}\n "
270
+ msg << @environment.stack_trace.join("\n ")
271
+ # JRuby doesn't automatically add a newline for #warn
272
+ msg << (RUBY_PLATFORM =~ /java/ ? "\n\n" : "\n")
253
273
  Sass::Util.sass_warn msg
254
274
  []
255
275
  ensure
@@ -0,0 +1,97 @@
1
+ # A visitor for setting options on the Sass tree
2
+ class Sass::Tree::Visitors::SetOptions < Sass::Tree::Visitors::Base
3
+ # @param root [Tree::Node] The root node of the tree to visit.
4
+ # @param options [{Symbol => Object}] The options has to set.
5
+ def self.visit(root, options); new(options).send(:visit, root); end
6
+
7
+ protected
8
+
9
+ def initialize(options)
10
+ @options = options
11
+ end
12
+
13
+ def visit(node)
14
+ node.instance_variable_set('@options', @options)
15
+ super
16
+ end
17
+
18
+ def visit_debug(node)
19
+ node.expr.options = @options
20
+ yield
21
+ end
22
+
23
+ def visit_each(node)
24
+ node.list.options = @options
25
+ yield
26
+ end
27
+
28
+ def visit_extend(node)
29
+ node.selector.each {|c| c.options = @options if c.is_a?(Sass::Script::Node)}
30
+ yield
31
+ end
32
+
33
+ def visit_for(node)
34
+ node.from.options = @options
35
+ node.to.options = @options
36
+ yield
37
+ end
38
+
39
+ def visit_function(node)
40
+ node.args.each do |k, v|
41
+ k.options = @options
42
+ v.options = @options if v
43
+ end
44
+ yield
45
+ end
46
+
47
+ def visit_if(node)
48
+ node.expr.options = @options if node.expr
49
+ visit(node.else) if node.else
50
+ yield
51
+ end
52
+
53
+ def visit_mixin_def(node)
54
+ node.args.each do |k, v|
55
+ k.options = @options
56
+ v.options = @options if v
57
+ end
58
+ yield
59
+ end
60
+
61
+ def visit_mixin(node)
62
+ node.args.each {|a| a.options = @options}
63
+ node.keywords.each {|k, v| v.options = @options}
64
+ yield
65
+ end
66
+
67
+ def visit_prop(node)
68
+ node.name.each {|c| c.options = @options if c.is_a?(Sass::Script::Node)}
69
+ node.value.options = @options
70
+ yield
71
+ end
72
+
73
+ def visit_return(node)
74
+ node.expr.options = @options
75
+ yield
76
+ end
77
+
78
+ def visit_rule(node)
79
+ node.rule.each {|c| c.options = @options if c.is_a?(Sass::Script::Node)}
80
+ yield
81
+ end
82
+
83
+ def visit_variable(node)
84
+ node.expr.options = @options
85
+ yield
86
+ end
87
+
88
+ def visit_warn(node)
89
+ node.expr.options = @options
90
+ yield
91
+ end
92
+
93
+ def visit_while(node)
94
+ node.expr.options = @options
95
+ yield
96
+ end
97
+ end
@@ -156,6 +156,10 @@ MESSAGE
156
156
  if node.style != :compressed
157
157
  if node.options[:debug_info]
158
158
  to_return << visit(debug_info_rule(node.debug_info, node.options)) << "\n"
159
+ elsif node.options[:trace_selectors]
160
+ to_return << "#{old_spaces}/* "
161
+ to_return << node.stack_trace.join("\n #{old_spaces}")
162
+ to_return << " */\n"
159
163
  elsif node.options[:line_comments]
160
164
  to_return << "#{old_spaces}/* line #{node.line}"
161
165
 
@@ -204,7 +208,7 @@ MESSAGE
204
208
  [Sass::Selector::Element.new(k.to_s.gsub(/[^\w-]/, "\\\\\\0"), nil)])
205
209
  ])
206
210
  ])
207
- prop = Sass::Tree::PropNode.new([""], "", :new)
211
+ prop = Sass::Tree::PropNode.new([""], Sass::Script::String.new(''), :new)
208
212
  prop.resolved_name = "font-family"
209
213
  prop.resolved_value = Sass::SCSS::RX.escape_ident(v.to_s)
210
214
  rule << prop
@@ -6,19 +6,13 @@ module Sass
6
6
  class WarnNode < Node
7
7
  # The expression to print.
8
8
  # @return [Script::Node]
9
- attr_reader :expr
9
+ attr_accessor :expr
10
10
 
11
11
  # @param expr [Script::Node] The expression to print
12
12
  def initialize(expr)
13
13
  @expr = expr
14
14
  super()
15
15
  end
16
-
17
- # Returns sub nodes that are not tree children.
18
- def subnodes
19
- Array(expr)
20
- end
21
-
22
16
  end
23
17
  end
24
18
  end
@@ -7,18 +7,12 @@ module Sass::Tree
7
7
  class WhileNode < Node
8
8
  # The parse tree for the continuation expression.
9
9
  # @return [Script::Node]
10
- attr_reader :expr
10
+ attr_accessor :expr
11
11
 
12
12
  # @param expr [Script::Node] See \{#expr}
13
13
  def initialize(expr)
14
14
  @expr = expr
15
15
  super()
16
16
  end
17
-
18
- # Returns sub nodes that are not tree children.
19
- def subnodes
20
- Array(expr)
21
- end
22
-
23
17
  end
24
18
  end
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  require File.dirname(__FILE__) + '/../test_helper'
3
+ require File.dirname(__FILE__) + '/test_helper'
3
4
  require 'sass/engine'
4
5
 
5
6
  class CacheTest < Test::Unit::TestCase
@@ -11,6 +12,7 @@ class CacheTest < Test::Unit::TestCase
11
12
 
12
13
  def teardown
13
14
  FileUtils.rm_rf @@cache_dir
15
+ clean_up_sassc
14
16
  end
15
17
 
16
18
  def test_file_cache_writes_a_file
@@ -64,6 +66,19 @@ class CacheTest < Test::Unit::TestCase
64
66
  assert_equal an_object, cache.retrieve("an_object", "")
65
67
  end
66
68
 
69
+ class Unmarshalable
70
+ def _dump(_)
71
+ raise 'Unmarshalable'
72
+ end
73
+ end
74
+
75
+ def test_cache_node_with_unmarshalable_option
76
+ engine = Sass::Engine.new("foo {a: b + c}",
77
+ :syntax => :scss, :object => Unmarshalable.new, :filename => 'file.scss',
78
+ :importer => Sass::Importers::Filesystem.new(absolutize('templates')))
79
+ engine.to_tree
80
+ end
81
+
67
82
  private
68
83
  def root_node
69
84
  Sass::Engine.new(<<-SCSS, :syntax => :scss).to_tree
@@ -1152,6 +1152,44 @@ SASS
1152
1152
  SCSS
1153
1153
  end
1154
1154
 
1155
+ def test_content_conversion
1156
+ assert_renders(<<SASS, <<SCSS)
1157
+ $color: blue
1158
+
1159
+ =context($class, $color: red)
1160
+ .\#{$class}
1161
+ background-color: $color
1162
+ @content
1163
+ border-color: $color
1164
+
1165
+ +context(parent)
1166
+ +context(child, $color: yellow)
1167
+ color: $color
1168
+ SASS
1169
+ $color: blue;
1170
+
1171
+ @mixin context($class, $color: red) {
1172
+ .\#{$class} {
1173
+ background-color: $color;
1174
+ @content;
1175
+ border-color: $color; } }
1176
+
1177
+ @include context(parent) {
1178
+ @include context(child, $color: yellow) {
1179
+ color: $color; } }
1180
+ SCSS
1181
+
1182
+ end
1183
+
1184
+ def test_empty_content
1185
+ assert_scss_to_scss(<<SCSS)
1186
+ @mixin foo {
1187
+ @content; }
1188
+
1189
+ @include foo {}
1190
+ SCSS
1191
+ end
1192
+
1155
1193
  private
1156
1194
 
1157
1195
  def assert_sass_to_sass(sass, options = {})