sass 3.3.0.alpha.229 → 3.3.0.alpha.231
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.
- data/REVISION +1 -1
- data/VERSION +1 -1
- data/VERSION_DATE +1 -1
- data/lib/sass/engine.rb +1 -0
- data/lib/sass/script/value/list.rb +14 -0
- data/lib/sass/stack.rb +111 -0
- data/lib/sass/tree/visitors/perform.rb +41 -54
- metadata +5 -4
data/REVISION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
4b12122cb838c1c8b2488b2c0bb3f252d261de2a
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.3.0.alpha.
|
1
|
+
3.3.0.alpha.231
|
data/VERSION_DATE
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
23 August 2013 23:13:38 GMT
|
data/lib/sass/engine.rb
CHANGED
@@ -42,6 +42,20 @@ module Sass::Script::Value
|
|
42
42
|
return value.reject {|e| e.is_a?(Null) || e.is_a?(List) && e.value.empty?}.map {|e| e.to_s(opts)}.join(sep_str)
|
43
43
|
end
|
44
44
|
|
45
|
+
# @see Value#to_sass
|
46
|
+
def to_sass(opts = {})
|
47
|
+
return "()" if value.empty?
|
48
|
+
precedence = Sass::Script::Parser.precedence_of(separator)
|
49
|
+
value.reject {|e| e.is_a?(Null)}.map do |v|
|
50
|
+
if v.is_a?(List) && Sass::Script::Parser.precedence_of(v.separator) <= precedence ||
|
51
|
+
separator == :space && v.is_a?(UnaryOperation) && (v.operator == :minus || v.operator == :plus)
|
52
|
+
"(#{v.to_sass(opts)})"
|
53
|
+
else
|
54
|
+
v.to_sass(opts)
|
55
|
+
end
|
56
|
+
end.join(sep_str(nil))
|
57
|
+
end
|
58
|
+
|
45
59
|
# @see Value#inspect
|
46
60
|
def inspect
|
47
61
|
"(#{value.map {|e| e.inspect}.join(sep_str(nil))})"
|
data/lib/sass/stack.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
module Sass
|
2
|
+
# A class representing the stack when compiling a Sass file.
|
3
|
+
class Stack
|
4
|
+
# TODO: use this to generate stack information for Sass::SyntaxErrors.
|
5
|
+
|
6
|
+
# A single stack frame.
|
7
|
+
class Frame
|
8
|
+
# The filename of the file in which this stack frame was created.
|
9
|
+
#
|
10
|
+
# @return [String]
|
11
|
+
attr_reader :filename
|
12
|
+
|
13
|
+
# The line number on which this stack frame was created.
|
14
|
+
#
|
15
|
+
# @return [String]
|
16
|
+
attr_reader :line
|
17
|
+
|
18
|
+
# The type of this stack frame. This can be `:import`, `:mixin`, or
|
19
|
+
# `:base`.
|
20
|
+
#
|
21
|
+
# `:base` indicates that this is the bottom-most frame, meaning that it
|
22
|
+
# represents a single line of code rather than a nested context. The stack
|
23
|
+
# will only ever have one base frame, and it will always be the most
|
24
|
+
# deeply-nested frame.
|
25
|
+
#
|
26
|
+
# @return [Symbol?]
|
27
|
+
attr_reader :type
|
28
|
+
|
29
|
+
# The name of the stack frame. For mixin frames, this is the mixin name;
|
30
|
+
# otherwise, it's `nil`.
|
31
|
+
#
|
32
|
+
# @return [String?]
|
33
|
+
attr_reader :name
|
34
|
+
|
35
|
+
def initialize(filename, line, type, name = nil)
|
36
|
+
@filename = filename
|
37
|
+
@line = line
|
38
|
+
@type = type
|
39
|
+
@name = name
|
40
|
+
end
|
41
|
+
|
42
|
+
# Whether this frame represents an import.
|
43
|
+
#
|
44
|
+
# @return [Boolean]
|
45
|
+
def is_import?
|
46
|
+
type == :import
|
47
|
+
end
|
48
|
+
|
49
|
+
# Whether this frame represents a mixin.
|
50
|
+
#
|
51
|
+
# @return [Boolean]
|
52
|
+
def is_mixin?
|
53
|
+
type == :mixin
|
54
|
+
end
|
55
|
+
|
56
|
+
# Whether this is the base frame.
|
57
|
+
#
|
58
|
+
# @return [Boolean]
|
59
|
+
def is_base?
|
60
|
+
type == :base
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# The stack frames. The last frame is the most deeply-nested.
|
65
|
+
#
|
66
|
+
# @return [Array<Frame>]
|
67
|
+
attr_reader :frames
|
68
|
+
|
69
|
+
def initialize
|
70
|
+
@frames = []
|
71
|
+
end
|
72
|
+
|
73
|
+
# Pushes a base frame onto the stack.
|
74
|
+
#
|
75
|
+
# @param filename [String] See \{Frame#filename}.
|
76
|
+
# @param line [String] See \{Frame#line}.
|
77
|
+
# @yield [] A block in which the new frame is on the stack.
|
78
|
+
def with_base(filename, line)
|
79
|
+
with_frame(filename, line, :base) {yield}
|
80
|
+
end
|
81
|
+
|
82
|
+
# Pushes an import frame onto the stack.
|
83
|
+
#
|
84
|
+
# @param filename [String] See \{Frame#filename}.
|
85
|
+
# @param line [String] See \{Frame#line}.
|
86
|
+
# @yield [] A block in which the new frame is on the stack.
|
87
|
+
def with_import(filename, line)
|
88
|
+
with_frame(filename, line, :import) {yield}
|
89
|
+
end
|
90
|
+
|
91
|
+
# Pushes a mixin frame onto the stack.
|
92
|
+
#
|
93
|
+
# @param filename [String] See \{Frame#filename}.
|
94
|
+
# @param line [String] See \{Frame#line}.
|
95
|
+
# @param name [String] See \{Frame#name}.
|
96
|
+
# @yield [] A block in which the new frame is on the stack.
|
97
|
+
def with_mixin(filename, line, name)
|
98
|
+
with_frame(filename, line, :mixin, name) {yield}
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def with_frame(filename, line, type, name = nil)
|
104
|
+
@frames.pop if @frames.last && @frames.last.type == :base
|
105
|
+
@frames.push(Frame.new(filename, line, type, name))
|
106
|
+
yield
|
107
|
+
ensure
|
108
|
+
@frames.pop unless type == :base && @frames.last && @frames.last.type != :base
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -91,13 +91,12 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
91
91
|
|
92
92
|
def initialize(env)
|
93
93
|
@environment = env
|
94
|
-
|
95
|
-
@stack = []
|
94
|
+
@stack = Sass::Stack.new
|
96
95
|
end
|
97
96
|
|
98
97
|
# If an exception is raised, this adds proper metadata to the backtrace.
|
99
98
|
def visit(node)
|
100
|
-
super(node.dup)
|
99
|
+
@stack.with_base(node.filename, node.line) {super(node.dup)}
|
101
100
|
rescue Sass::SyntaxError => e
|
102
101
|
e.modify_backtrace(:filename => node.filename, :line => node.line)
|
103
102
|
raise e
|
@@ -221,21 +220,22 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
221
220
|
return resolved_node
|
222
221
|
end
|
223
222
|
file = node.imported_file
|
224
|
-
|
223
|
+
if @stack.frames.any? {|f| f.is_import? && f.filename == file.options[:filename]}
|
224
|
+
handle_import_loop!(node)
|
225
|
+
end
|
225
226
|
|
226
227
|
begin
|
227
|
-
@stack.
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
228
|
+
@stack.with_import(node.filename, node.line) do
|
229
|
+
root = file.to_tree
|
230
|
+
Sass::Tree::Visitors::CheckNesting.visit(root)
|
231
|
+
node.children = root.children.map {|c| visit(c)}.flatten
|
232
|
+
node
|
233
|
+
end
|
232
234
|
rescue Sass::SyntaxError => e
|
233
235
|
e.modify_backtrace(:filename => node.imported_file.options[:filename])
|
234
236
|
e.add_backtrace(:filename => node.filename, :line => node.line)
|
235
237
|
raise e
|
236
238
|
end
|
237
|
-
ensure
|
238
|
-
@stack.pop unless path
|
239
239
|
end
|
240
240
|
|
241
241
|
# Loads a mixin into the environment.
|
@@ -249,27 +249,28 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
249
249
|
# Runs a mixin.
|
250
250
|
def visit_mixin(node)
|
251
251
|
include_loop = true
|
252
|
-
handle_include_loop!(node) if @stack.any? {|
|
252
|
+
handle_include_loop!(node) if @stack.frames.any? {|f| f.is_mixin? && f.name == node.name}
|
253
253
|
include_loop = false
|
254
254
|
|
255
|
-
@stack.
|
256
|
-
|
255
|
+
@stack.with_mixin(node.filename, node.line, node.name) do
|
256
|
+
raise Sass::SyntaxError.new("Undefined mixin '#{node.name}'.") unless mixin = @environment.mixin(node.name)
|
257
257
|
|
258
|
-
|
259
|
-
|
260
|
-
|
258
|
+
if node.children.any? && !mixin.has_content
|
259
|
+
raise Sass::SyntaxError.new(%Q{Mixin "#{node.name}" does not accept a content block.})
|
260
|
+
end
|
261
261
|
|
262
|
-
|
263
|
-
|
264
|
-
|
262
|
+
args = node.args.map {|a| a.perform(@environment)}
|
263
|
+
keywords = Sass::Util.map_hash(node.keywords) {|k, v| [k, v.perform(@environment)]}
|
264
|
+
splat = node.splat.perform(@environment) if node.splat
|
265
265
|
|
266
|
-
|
267
|
-
|
268
|
-
|
266
|
+
self.class.perform_arguments(mixin, args, keywords, splat) do |env|
|
267
|
+
env.caller = Sass::Environment.new(@environment)
|
268
|
+
env.content = node.children if node.has_children
|
269
269
|
|
270
|
-
|
271
|
-
|
272
|
-
|
270
|
+
trace_node = Sass::Tree::TraceNode.from_node(node.name, node)
|
271
|
+
with_environment(env) {trace_node.children = mixin.tree.map {|c| visit(c)}.flatten}
|
272
|
+
trace_node
|
273
|
+
end
|
273
274
|
end
|
274
275
|
rescue Sass::SyntaxError => e
|
275
276
|
unless include_loop
|
@@ -277,22 +278,19 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
277
278
|
e.add_backtrace(:line => node.line)
|
278
279
|
end
|
279
280
|
raise e
|
280
|
-
ensure
|
281
|
-
@stack.pop unless include_loop
|
282
281
|
end
|
283
282
|
|
284
283
|
def visit_content(node)
|
285
284
|
return [] unless content = @environment.content
|
286
|
-
@stack.
|
287
|
-
|
288
|
-
|
289
|
-
|
285
|
+
@stack.with_mixin(node.filename, node.line, '@content') do
|
286
|
+
trace_node = Sass::Tree::TraceNode.from_node('@content', node)
|
287
|
+
with_environment(@environment.caller) {trace_node.children = content.map {|c| visit(c.dup)}.flatten}
|
288
|
+
trace_node
|
289
|
+
end
|
290
290
|
rescue Sass::SyntaxError => e
|
291
291
|
e.modify_backtrace(:mixin => '@content', :line => node.line)
|
292
292
|
e.add_backtrace(:line => node.line)
|
293
293
|
raise e
|
294
|
-
ensure
|
295
|
-
@stack.pop if content
|
296
294
|
end
|
297
295
|
|
298
296
|
# Runs any SassScript that may be embedded in a property.
|
@@ -317,11 +315,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
317
315
|
parser = Sass::SCSS::StaticParser.new(run_interp(node.rule),
|
318
316
|
node.filename, node.options[:importer], node.line)
|
319
317
|
node.parsed_rules ||= parser.parse_selector
|
320
|
-
if node.options[:trace_selectors]
|
321
|
-
@stack.push(:filename => node.filename, :line => node.line)
|
322
|
-
node.stack_trace = stack_trace
|
323
|
-
@stack.pop
|
324
|
-
end
|
318
|
+
node.stack_trace = stack_trace if node.options[:trace_selectors]
|
325
319
|
yield
|
326
320
|
end
|
327
321
|
|
@@ -341,15 +335,12 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
341
335
|
|
342
336
|
# Prints the expression to STDERR with a stylesheet trace.
|
343
337
|
def visit_warn(node)
|
344
|
-
@stack.push(:filename => node.filename, :line => node.line)
|
345
338
|
res = node.expr.perform(@environment)
|
346
339
|
res = res.value if res.is_a?(Sass::Script::Value::String)
|
347
340
|
msg = "WARNING: #{res}\n "
|
348
341
|
msg << stack_trace.join("\n ") << "\n"
|
349
342
|
Sass::Util.sass_warn msg
|
350
343
|
[]
|
351
|
-
ensure
|
352
|
-
@stack.pop
|
353
344
|
end
|
354
345
|
|
355
346
|
# Runs the child nodes until the continuation expression becomes false.
|
@@ -392,16 +383,12 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
392
383
|
private
|
393
384
|
|
394
385
|
def stack_trace
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
msg << " of #{entry[:filename] || "an unknown file"}"
|
401
|
-
msg << ", in `#{entry[:caller]}'" if entry[:caller]
|
402
|
-
trace << msg
|
386
|
+
Sass::Util.enum_with_index(Sass::Util.enum_cons(@stack.frames.reverse + [nil], 2)).
|
387
|
+
map do |(frame, caller), i|
|
388
|
+
"#{i == 0 ? "on" : "from"} line #{frame.line}" +
|
389
|
+
" of #{frame.filename || "an unknown file"}" +
|
390
|
+
(caller && caller.name ? ", in `#{caller.name}'" : "")
|
403
391
|
end
|
404
|
-
trace
|
405
392
|
end
|
406
393
|
|
407
394
|
def run_interp_no_strip(text)
|
@@ -421,8 +408,8 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
421
408
|
def handle_include_loop!(node)
|
422
409
|
msg = "An @include loop has been found:"
|
423
410
|
content_count = 0
|
424
|
-
mixins = @stack.reverse.map {|
|
425
|
-
if
|
411
|
+
mixins = @stack.frames.select {|f| f.is_mixin?}.reverse.map {|f| f.name}.select do |name|
|
412
|
+
if name == '@content'
|
426
413
|
content_count += 1
|
427
414
|
false
|
428
415
|
elsif content_count > 0
|
@@ -444,7 +431,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
444
431
|
|
445
432
|
def handle_import_loop!(node)
|
446
433
|
msg = "An @import loop has been found:"
|
447
|
-
files = @stack.map {|
|
434
|
+
files = @stack.frames.select {|f| f.is_import?}.map {|f| f.filename}.compact
|
448
435
|
if node.filename == node.imported_file.options[:filename]
|
449
436
|
raise Sass::SyntaxError.new("#{msg} #{node.filename} imports itself")
|
450
437
|
end
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sass
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 592302787
|
5
5
|
prerelease: 6
|
6
6
|
segments:
|
7
7
|
- 3
|
8
8
|
- 3
|
9
9
|
- 0
|
10
10
|
- alpha
|
11
|
-
-
|
12
|
-
version: 3.3.0.alpha.
|
11
|
+
- 231
|
12
|
+
version: 3.3.0.alpha.231
|
13
13
|
platform: ruby
|
14
14
|
authors:
|
15
15
|
- Nathan Weizenbaum
|
@@ -19,7 +19,7 @@ autorequire:
|
|
19
19
|
bindir: bin
|
20
20
|
cert_chain: []
|
21
21
|
|
22
|
-
date: 2013-08-
|
22
|
+
date: 2013-08-23 00:00:00 -04:00
|
23
23
|
default_executable:
|
24
24
|
dependencies:
|
25
25
|
- !ruby/object:Gem::Dependency
|
@@ -192,6 +192,7 @@ files:
|
|
192
192
|
- lib/sass/tree/visitors/to_css.rb
|
193
193
|
- lib/sass/tree/warn_node.rb
|
194
194
|
- lib/sass/tree/while_node.rb
|
195
|
+
- lib/sass/stack.rb
|
195
196
|
- lib/sass/util/multibyte_string_scanner.rb
|
196
197
|
- lib/sass/util/normalized_map.rb
|
197
198
|
- lib/sass/util/ordered_hash.rb
|