sass 3.4.25 → 3.5.0
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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/Rakefile +2 -117
- data/VERSION +1 -1
- data/VERSION_DATE +1 -1
- data/VERSION_NAME +1 -1
- data/lib/sass/cache_stores/filesystem.rb +1 -1
- data/lib/sass/css.rb +1 -2
- data/lib/sass/engine.rb +38 -20
- data/lib/sass/environment.rb +26 -5
- data/lib/sass/error.rb +2 -2
- data/lib/sass/exec/base.rb +2 -11
- data/lib/sass/features.rb +1 -0
- data/lib/sass/importers/filesystem.rb +2 -2
- data/lib/sass/plugin/compiler.rb +20 -50
- data/lib/sass/plugin/configuration.rb +2 -2
- data/lib/sass/railtie.rb +1 -1
- data/lib/sass/script/functions.rb +235 -43
- data/lib/sass/script/lexer.rb +13 -2
- data/lib/sass/script/parser.rb +69 -67
- data/lib/sass/script/tree/funcall.rb +35 -30
- data/lib/sass/script/tree/list_literal.rb +23 -8
- data/lib/sass/script/tree/map_literal.rb +2 -2
- data/lib/sass/script/tree/node.rb +0 -8
- data/lib/sass/script/tree/operation.rb +0 -7
- data/lib/sass/script/value/arg_list.rb +1 -1
- data/lib/sass/script/value/base.rb +17 -0
- data/lib/sass/script/value/callable.rb +25 -0
- data/lib/sass/script/value/color.rb +5 -0
- data/lib/sass/script/value/function.rb +19 -0
- data/lib/sass/script/value/helpers.rb +29 -9
- data/lib/sass/script/value/list.rb +35 -14
- data/lib/sass/script/value/map.rb +2 -2
- data/lib/sass/script/value/number.rb +1 -1
- data/lib/sass/script/value.rb +2 -0
- data/lib/sass/scss/parser.rb +99 -40
- data/lib/sass/scss/rx.rb +4 -10
- data/lib/sass/scss/static_parser.rb +5 -24
- data/lib/sass/selector/abstract_sequence.rb +7 -6
- data/lib/sass/selector/comma_sequence.rb +7 -4
- data/lib/sass/selector/pseudo.rb +19 -2
- data/lib/sass/selector/sequence.rb +35 -6
- data/lib/sass/selector/simple.rb +9 -2
- data/lib/sass/selector/simple_sequence.rb +8 -4
- data/lib/sass/selector.rb +4 -0
- data/lib/sass/source/map.rb +2 -2
- data/lib/sass/stack.rb +21 -1
- data/lib/sass/tree/charset_node.rb +1 -1
- data/lib/sass/tree/prop_node.rb +45 -53
- data/lib/sass/tree/rule_node.rb +3 -2
- data/lib/sass/tree/visitors/check_nesting.rb +1 -1
- data/lib/sass/tree/visitors/convert.rb +2 -3
- data/lib/sass/tree/visitors/cssize.rb +4 -15
- data/lib/sass/tree/visitors/deep_copy.rb +1 -1
- data/lib/sass/tree/visitors/extend.rb +2 -8
- data/lib/sass/tree/visitors/perform.rb +21 -9
- data/lib/sass/tree/visitors/set_options.rb +1 -1
- data/lib/sass/tree/visitors/to_css.rb +47 -13
- data/lib/sass/util/multibyte_string_scanner.rb +127 -131
- data/lib/sass/util/normalized_map.rb +1 -8
- data/lib/sass/util.rb +12 -290
- data/lib/sass/version.rb +0 -2
- data/lib/sass.rb +0 -7
- data/test/sass/cache_test.rb +0 -1
- data/test/sass/callbacks_test.rb +0 -1
- data/test/sass/compiler_test.rb +4 -15
- data/test/sass/conversion_test.rb +113 -163
- data/test/sass/css2sass_test.rb +16 -19
- data/test/sass/css_variable_test.rb +176 -71
- data/test/sass/encoding_test.rb +2 -33
- data/test/sass/engine_test.rb +71 -19
- data/test/sass/exec_test.rb +0 -1
- data/test/sass/extend_test.rb +37 -92
- data/test/sass/functions_test.rb +58 -14
- data/test/sass/importer_test.rb +2 -3
- data/test/sass/logger_test.rb +0 -1
- data/test/sass/plugin_test.rb +6 -10
- data/test/sass/results/script.css +1 -1
- data/test/sass/results/units.css +2 -2
- data/test/sass/script_conversion_test.rb +10 -2
- data/test/sass/script_test.rb +22 -24
- data/test/sass/scss/css_test.rb +3 -18
- data/test/sass/scss/rx_test.rb +0 -1
- data/test/sass/scss/scss_test.rb +49 -16
- data/test/sass/source_map_test.rb +13 -16
- data/test/sass/superselector_test.rb +0 -1
- data/test/sass/util/multibyte_string_scanner_test.rb +121 -124
- data/test/sass/util/normalized_map_test.rb +0 -1
- data/test/sass/util/subset_map_test.rb +0 -1
- data/test/sass/util_test.rb +4 -39
- data/test/sass/value_helpers_test.rb +0 -1
- data/test/sass-spec.yml +1 -1
- data/test/test_helper.rb +39 -0
- metadata +19 -44
- data/lib/sass/script/css_variable_warning.rb +0 -52
- data/lib/sass/util/cross_platform_random.rb +0 -19
- data/lib/sass/util/ordered_hash.rb +0 -192
- data/test/sass/results/import_charset_1_8.css +0 -5
- data/test/sass/templates/import_charset_1_8.sass +0 -6
- data/vendor/listen/CHANGELOG.md +0 -1
- data/vendor/listen/CONTRIBUTING.md +0 -38
- data/vendor/listen/Gemfile +0 -20
- data/vendor/listen/Guardfile +0 -8
- data/vendor/listen/LICENSE +0 -20
- data/vendor/listen/README.md +0 -349
- data/vendor/listen/Rakefile +0 -5
- data/vendor/listen/Vagrantfile +0 -96
- data/vendor/listen/lib/listen/adapter.rb +0 -327
- data/vendor/listen/lib/listen/adapters/bsd.rb +0 -75
- data/vendor/listen/lib/listen/adapters/darwin.rb +0 -48
- data/vendor/listen/lib/listen/adapters/linux.rb +0 -81
- data/vendor/listen/lib/listen/adapters/polling.rb +0 -58
- data/vendor/listen/lib/listen/adapters/windows.rb +0 -91
- data/vendor/listen/lib/listen/directory_record.rb +0 -406
- data/vendor/listen/lib/listen/listener.rb +0 -323
- data/vendor/listen/lib/listen/turnstile.rb +0 -32
- data/vendor/listen/lib/listen/version.rb +0 -3
- data/vendor/listen/lib/listen.rb +0 -54
- data/vendor/listen/listen.gemspec +0 -28
- data/vendor/listen/spec/listen/adapter_spec.rb +0 -149
- data/vendor/listen/spec/listen/adapters/bsd_spec.rb +0 -36
- data/vendor/listen/spec/listen/adapters/darwin_spec.rb +0 -37
- data/vendor/listen/spec/listen/adapters/linux_spec.rb +0 -47
- data/vendor/listen/spec/listen/adapters/polling_spec.rb +0 -68
- data/vendor/listen/spec/listen/adapters/windows_spec.rb +0 -30
- data/vendor/listen/spec/listen/directory_record_spec.rb +0 -1250
- data/vendor/listen/spec/listen/listener_spec.rb +0 -258
- data/vendor/listen/spec/listen/turnstile_spec.rb +0 -56
- data/vendor/listen/spec/listen_spec.rb +0 -67
- data/vendor/listen/spec/spec_helper.rb +0 -25
- data/vendor/listen/spec/support/adapter_helper.rb +0 -666
- data/vendor/listen/spec/support/directory_record_helper.rb +0 -57
- data/vendor/listen/spec/support/fixtures_helper.rb +0 -29
- data/vendor/listen/spec/support/listeners_helper.rb +0 -179
- data/vendor/listen/spec/support/platform_helper.rb +0 -15
|
@@ -66,10 +66,14 @@ module Sass
|
|
|
66
66
|
next [sseq_or_op] unless sseq_or_op.is_a?(SimpleSequence)
|
|
67
67
|
sseq_or_op.resolve_parent_refs(super_cseq).members
|
|
68
68
|
end).map do |path|
|
|
69
|
-
|
|
69
|
+
path_members = path.map do |seq_or_op|
|
|
70
70
|
next seq_or_op unless seq_or_op.is_a?(Sequence)
|
|
71
71
|
seq_or_op.members
|
|
72
|
-
end
|
|
72
|
+
end
|
|
73
|
+
if path_members.length == 2 && path_members[1][0] == "\n"
|
|
74
|
+
path_members[0].unshift path_members[1].shift
|
|
75
|
+
end
|
|
76
|
+
Sequence.new(path_members.flatten)
|
|
73
77
|
end)
|
|
74
78
|
end
|
|
75
79
|
|
|
@@ -115,7 +119,7 @@ module Sass
|
|
|
115
119
|
# specificity greater than or equal to that of the original selector.
|
|
116
120
|
# In order to ensure that, we record the original selector's
|
|
117
121
|
# (`extended.first`) original specificity.
|
|
118
|
-
extended.first.add_sources!([self]) if original && !
|
|
122
|
+
extended.first.add_sources!([self]) if original && !invisible?
|
|
119
123
|
|
|
120
124
|
extended.map {|seq| seq.members}
|
|
121
125
|
end
|
|
@@ -283,6 +287,9 @@ module Sass
|
|
|
283
287
|
next unless s1.first.is_a?(SimpleSequence) && s2.first.is_a?(SimpleSequence)
|
|
284
288
|
next s2 if parent_superselector?(s1, s2)
|
|
285
289
|
next s1 if parent_superselector?(s2, s1)
|
|
290
|
+
next unless must_unify?(s1, s2)
|
|
291
|
+
next unless (unified = Sequence.new(s1).unify(Sequence.new(s2)))
|
|
292
|
+
unified.members.first.members if unified.members.length == 1
|
|
286
293
|
end
|
|
287
294
|
|
|
288
295
|
diff = [[init]]
|
|
@@ -494,7 +501,7 @@ module Sass
|
|
|
494
501
|
return if seq1.size > seq2.size
|
|
495
502
|
return seq1.first.superselector?(seq2.last, seq2[0...-1]) if seq1.size == 1
|
|
496
503
|
|
|
497
|
-
_, si =
|
|
504
|
+
_, si = seq2.each_with_index.find do |e, i|
|
|
498
505
|
return if i == seq2.size - 1
|
|
499
506
|
next if e.is_a?(String)
|
|
500
507
|
seq1.first.superselector?(e, seq2[0...i])
|
|
@@ -537,6 +544,30 @@ module Sass
|
|
|
537
544
|
_superselector?(seq1 + [base], seq2 + [base])
|
|
538
545
|
end
|
|
539
546
|
|
|
547
|
+
# Returns whether two selectors must be unified to produce a valid
|
|
548
|
+
# combined selector. This is true when both selectors contain the same
|
|
549
|
+
# unique simple selector such as an id.
|
|
550
|
+
#
|
|
551
|
+
# @param seq1 [Array<SimpleSequence or String>]
|
|
552
|
+
# @param seq2 [Array<SimpleSequence or String>]
|
|
553
|
+
# @return [Boolean]
|
|
554
|
+
def must_unify?(seq1, seq2)
|
|
555
|
+
unique_selectors = seq1.map do |sseq|
|
|
556
|
+
next [] if sseq.is_a?(String)
|
|
557
|
+
sseq.members.select {|sel| sel.unique?}
|
|
558
|
+
end.flatten.to_set
|
|
559
|
+
|
|
560
|
+
return false if unique_selectors.empty?
|
|
561
|
+
|
|
562
|
+
seq2.any? do |sseq|
|
|
563
|
+
next false if sseq.is_a?(String)
|
|
564
|
+
sseq.members.any? do |sel|
|
|
565
|
+
next unless sel.unique?
|
|
566
|
+
unique_selectors.include?(sel)
|
|
567
|
+
end
|
|
568
|
+
end
|
|
569
|
+
end
|
|
570
|
+
|
|
540
571
|
# Removes redundant selectors from between multiple lists of
|
|
541
572
|
# selectors. This takes a list of lists of selector sequences;
|
|
542
573
|
# each individual list is assumed to have no redundancy within
|
|
@@ -597,8 +628,6 @@ module Sass
|
|
|
597
628
|
other.members.reject {|m| m == "\n"}.eql?(members.reject {|m| m == "\n"})
|
|
598
629
|
end
|
|
599
630
|
|
|
600
|
-
private
|
|
601
|
-
|
|
602
631
|
def path_has_two_subjects?(path)
|
|
603
632
|
subject = false
|
|
604
633
|
path.each do |sseq_or_op|
|
data/lib/sass/selector/simple.rb
CHANGED
|
@@ -14,6 +14,14 @@ module Sass
|
|
|
14
14
|
# @return [String, nil]
|
|
15
15
|
attr_accessor :filename
|
|
16
16
|
|
|
17
|
+
# Whether only one instance of this simple selector is allowed in a given
|
|
18
|
+
# complex selector.
|
|
19
|
+
#
|
|
20
|
+
# @return [Boolean]
|
|
21
|
+
def unique?
|
|
22
|
+
false
|
|
23
|
+
end
|
|
24
|
+
|
|
17
25
|
# @see #to_s
|
|
18
26
|
#
|
|
19
27
|
# @return [String]
|
|
@@ -74,9 +82,8 @@ module Sass
|
|
|
74
82
|
def unify(sels)
|
|
75
83
|
return sels.first.unify([self]) if sels.length == 1 && sels.first.is_a?(Universal)
|
|
76
84
|
return sels if sels.any? {|sel2| eql?(sel2)}
|
|
77
|
-
sels_with_ix = Sass::Util.enum_with_index(sels)
|
|
78
85
|
if !is_a?(Pseudo) || (sels.last.is_a?(Pseudo) && sels.last.type == :element)
|
|
79
|
-
_, i =
|
|
86
|
+
_, i = sels.each_with_index.find {|sel, _| sel.is_a?(Pseudo)}
|
|
80
87
|
end
|
|
81
88
|
return sels + [self] unless i
|
|
82
89
|
sels[0...i] + [self] + sels[i..-1]
|
|
@@ -163,7 +163,7 @@ module Sass
|
|
|
163
163
|
next sel if seen.include?([sel])
|
|
164
164
|
extended = sel.selector.do_extend(extends, parent_directives, replace, seen, false)
|
|
165
165
|
next sel if extended == sel.selector
|
|
166
|
-
extended.members.reject! {|seq| seq.
|
|
166
|
+
extended.members.reject! {|seq| seq.invisible?}
|
|
167
167
|
|
|
168
168
|
# For `:not()`, we usually want to get rid of any complex
|
|
169
169
|
# selectors because that will cause the selector to fail to
|
|
@@ -184,17 +184,16 @@ module Sass
|
|
|
184
184
|
result
|
|
185
185
|
end.flatten
|
|
186
186
|
|
|
187
|
-
groups =
|
|
187
|
+
groups = extends[members.to_set].group_by {|ex| ex.extender}.to_a
|
|
188
188
|
groups.map! do |seq, group|
|
|
189
189
|
sels = group.map {|e| e.target}.flatten
|
|
190
190
|
# If A {@extend B} and C {...},
|
|
191
191
|
# seq is A, sels is B, and self is C
|
|
192
192
|
|
|
193
193
|
self_without_sel = Sass::Util.array_minus(members, sels)
|
|
194
|
-
group.each {|e| e.
|
|
194
|
+
group.each {|e| e.success = true}
|
|
195
195
|
unified = seq.members.last.unify(SimpleSequence.new(self_without_sel, subject?))
|
|
196
196
|
next unless unified
|
|
197
|
-
group.each {|e| e.result = :succeeded}
|
|
198
197
|
group.each {|e| check_directives_match!(e, parent_directives)}
|
|
199
198
|
new_seq = Sequence.new(seq.members[0...-1] + [unified])
|
|
200
199
|
new_seq.add_sources!(sources + [seq])
|
|
@@ -287,6 +286,11 @@ module Sass
|
|
|
287
286
|
# @see Simple#to_s
|
|
288
287
|
def to_s(opts = {})
|
|
289
288
|
res = @members.map {|m| m.to_s(opts)}.join
|
|
289
|
+
|
|
290
|
+
# :not(%foo) may resolve to the empty string, but it should match every
|
|
291
|
+
# selector so we replace it with "*".
|
|
292
|
+
res = '*' if res.empty?
|
|
293
|
+
|
|
290
294
|
res << '!' if subject?
|
|
291
295
|
res
|
|
292
296
|
end
|
data/lib/sass/selector.rb
CHANGED
data/lib/sass/source/map.rb
CHANGED
|
@@ -93,8 +93,8 @@ module Sass::Source
|
|
|
93
93
|
raise ArgumentError.new("Sass::Source::Map#to_json requires either " \
|
|
94
94
|
"the :css_uri option or both the :css_path and :soucemap_path options.")
|
|
95
95
|
end
|
|
96
|
-
css_path &&= Sass::Util.pathname(
|
|
97
|
-
sourcemap_path &&= Sass::Util.pathname(
|
|
96
|
+
css_path &&= Sass::Util.pathname(File.absolute_path(css_path))
|
|
97
|
+
sourcemap_path &&= Sass::Util.pathname(File.absolute_path(sourcemap_path))
|
|
98
98
|
css_uri ||= Sass::Util.file_uri_from_path(
|
|
99
99
|
Sass::Util.relative_path_from(css_path, sourcemap_path.dirname))
|
|
100
100
|
|
data/lib/sass/stack.rb
CHANGED
|
@@ -98,8 +98,28 @@ module Sass
|
|
|
98
98
|
with_frame(filename, line, :mixin, name) {yield}
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
+
# Pushes a function frame onto the stack.
|
|
102
|
+
#
|
|
103
|
+
# @param filename [String] See \{Frame#filename}.
|
|
104
|
+
# @param line [String] See \{Frame#line}.
|
|
105
|
+
# @param name [String] See \{Frame#name}.
|
|
106
|
+
# @yield [] A block in which the new frame is on the stack.
|
|
107
|
+
def with_function(filename, line, name)
|
|
108
|
+
with_frame(filename, line, :function, name) {yield}
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Pushes a function frame onto the stack.
|
|
112
|
+
#
|
|
113
|
+
# @param filename [String] See \{Frame#filename}.
|
|
114
|
+
# @param line [String] See \{Frame#line}.
|
|
115
|
+
# @param name [String] See \{Frame#name}.
|
|
116
|
+
# @yield [] A block in which the new frame is on the stack.
|
|
117
|
+
def with_directive(filename, line, name)
|
|
118
|
+
with_frame(filename, line, :directive, name) {yield}
|
|
119
|
+
end
|
|
120
|
+
|
|
101
121
|
def to_s
|
|
102
|
-
|
|
122
|
+
(frames.reverse + [nil]).each_cons(2).each_with_index.
|
|
103
123
|
map do |(frame, caller), i|
|
|
104
124
|
"#{i == 0 ? 'on' : 'from'} line #{frame.line}" +
|
|
105
125
|
" of #{frame.filename || 'an unknown file'}" +
|
data/lib/sass/tree/prop_node.rb
CHANGED
|
@@ -20,7 +20,11 @@ module Sass::Tree
|
|
|
20
20
|
|
|
21
21
|
# The value of the property.
|
|
22
22
|
#
|
|
23
|
-
#
|
|
23
|
+
# For most properties, this will just contain a single Node. However, for
|
|
24
|
+
# CSS variables, it will contain multiple strings and nodes representing
|
|
25
|
+
# interpolation. Any adjacent strings will be merged together.
|
|
26
|
+
#
|
|
27
|
+
# @return [Array<String, Sass::Script::Tree::Node>]
|
|
24
28
|
attr_accessor :value
|
|
25
29
|
|
|
26
30
|
# The value of the property
|
|
@@ -52,14 +56,22 @@ module Sass::Tree
|
|
|
52
56
|
# @return [Sass::Source::Range]
|
|
53
57
|
attr_accessor :value_source_range
|
|
54
58
|
|
|
59
|
+
# Whether this represents a CSS custom property.
|
|
60
|
+
#
|
|
61
|
+
# @return [Boolean]
|
|
62
|
+
def custom_property?
|
|
63
|
+
name.first.is_a?(String) && name.first.start_with?("--")
|
|
64
|
+
end
|
|
65
|
+
|
|
55
66
|
# @param name [Array<String, Sass::Script::Tree::Node>] See \{#name}
|
|
56
|
-
# @param value [Sass::Script::Tree::Node] See \{#value}
|
|
67
|
+
# @param value [Array<String, Sass::Script::Tree::Node>] See \{#value}
|
|
57
68
|
# @param prop_syntax [Symbol] `:new` if this property uses `a: b`-style syntax,
|
|
58
69
|
# `:old` if it uses `:a b`-style syntax
|
|
59
70
|
def initialize(name, value, prop_syntax)
|
|
60
71
|
@name = Sass::Util.strip_string_array(
|
|
61
72
|
Sass::Util.merge_adjacent_strings(name))
|
|
62
|
-
@value = value
|
|
73
|
+
@value = Sass::Util.merge_adjacent_strings(value)
|
|
74
|
+
@value = Sass::Util.strip_string_array(@value) unless custom_property?
|
|
63
75
|
@tabs = 0
|
|
64
76
|
@prop_syntax = prop_syntax
|
|
65
77
|
super()
|
|
@@ -81,9 +93,10 @@ module Sass::Tree
|
|
|
81
93
|
# @return [String] The message
|
|
82
94
|
def pseudo_class_selector_message
|
|
83
95
|
if @prop_syntax == :new ||
|
|
84
|
-
|
|
85
|
-
!value.
|
|
86
|
-
!value.
|
|
96
|
+
custom_property? ||
|
|
97
|
+
!value.first.is_a?(Sass::Script::Tree::Literal) ||
|
|
98
|
+
!value.first.value.is_a?(Sass::Script::Value::String) ||
|
|
99
|
+
!value.first.value.value.empty?
|
|
87
100
|
return ""
|
|
88
101
|
end
|
|
89
102
|
|
|
@@ -98,73 +111,52 @@ module Sass::Tree
|
|
|
98
111
|
# @param fmt [Symbol] `:scss` or `:sass`.
|
|
99
112
|
def declaration(opts = {:old => @prop_syntax == :old}, fmt = :sass)
|
|
100
113
|
name = self.name.map {|n| n.is_a?(String) ? n : n.to_sass(opts)}.join
|
|
114
|
+
value = self.value.map {|n| n.is_a?(String) ? n : n.to_sass(opts)}.join
|
|
115
|
+
value = "(#{value})" if value_needs_parens?
|
|
116
|
+
|
|
101
117
|
if name[0] == ?:
|
|
102
|
-
raise Sass::SyntaxError.new("The \"#{name}: #{
|
|
118
|
+
raise Sass::SyntaxError.new("The \"#{name}: #{value}\"" +
|
|
103
119
|
" hack is not allowed in the Sass indented syntax")
|
|
104
120
|
end
|
|
105
121
|
|
|
122
|
+
# The indented syntax doesn't support newlines in custom property values,
|
|
123
|
+
# but we can losslessly convert them to spaces instead.
|
|
124
|
+
value = value.tr("\n", " ") if fmt == :sass
|
|
125
|
+
|
|
106
126
|
old = opts[:old] && fmt == :sass
|
|
107
|
-
|
|
108
|
-
mid = old ? '' : ':'
|
|
109
|
-
"#{initial}#{name}#{mid} #{self.class.val_to_sass(value, opts)}".rstrip
|
|
127
|
+
"#{old ? ':' : ''}#{name}#{old ? '' : ':'}#{custom_property? ? '' : ' '}#{value}".rstrip
|
|
110
128
|
end
|
|
111
129
|
|
|
112
130
|
# A property node is invisible if its value is empty.
|
|
113
131
|
#
|
|
114
132
|
# @return [Boolean]
|
|
115
133
|
def invisible?
|
|
116
|
-
resolved_value.empty?
|
|
134
|
+
!custom_property? && resolved_value.empty?
|
|
117
135
|
end
|
|
118
136
|
|
|
119
137
|
private
|
|
120
138
|
|
|
139
|
+
# Returns whether \{#value} neesd parentheses in order to be parsed
|
|
140
|
+
# properly as division.
|
|
141
|
+
def value_needs_parens?
|
|
142
|
+
return false if custom_property?
|
|
143
|
+
|
|
144
|
+
root = value.first
|
|
145
|
+
root.is_a?(Sass::Script::Tree::Operation) &&
|
|
146
|
+
root.operator == :div &&
|
|
147
|
+
root.operand1.is_a?(Sass::Script::Tree::Literal) &&
|
|
148
|
+
root.operand1.value.is_a?(Sass::Script::Value::Number) &&
|
|
149
|
+
root.operand1.value.original.nil? &&
|
|
150
|
+
root.operand2.is_a?(Sass::Script::Tree::Literal) &&
|
|
151
|
+
root.operand2.value.is_a?(Sass::Script::Value::Number) &&
|
|
152
|
+
root.operand2.value.original.nil?
|
|
153
|
+
end
|
|
154
|
+
|
|
121
155
|
def check!
|
|
122
156
|
return unless @options[:property_syntax] && @options[:property_syntax] != @prop_syntax
|
|
123
157
|
raise Sass::SyntaxError.new(
|
|
124
158
|
"Illegal property syntax: can't use #{@prop_syntax} syntax when " +
|
|
125
159
|
":property_syntax => #{@options[:property_syntax].inspect} is set.")
|
|
126
160
|
end
|
|
127
|
-
|
|
128
|
-
class << self
|
|
129
|
-
# @private
|
|
130
|
-
def val_to_sass(value, opts)
|
|
131
|
-
val_to_sass_comma(value, opts).to_sass(opts)
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
private
|
|
135
|
-
|
|
136
|
-
def val_to_sass_comma(node, opts)
|
|
137
|
-
return node unless node.is_a?(Sass::Script::Tree::Operation)
|
|
138
|
-
return val_to_sass_concat(node, opts) unless node.operator == :comma
|
|
139
|
-
|
|
140
|
-
Sass::Script::Tree::Operation.new(
|
|
141
|
-
val_to_sass_concat(node.operand1, opts),
|
|
142
|
-
val_to_sass_comma(node.operand2, opts),
|
|
143
|
-
node.operator)
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
def val_to_sass_concat(node, opts)
|
|
147
|
-
return node unless node.is_a?(Sass::Script::Tree::Operation)
|
|
148
|
-
return val_to_sass_div(node, opts) unless node.operator == :space
|
|
149
|
-
|
|
150
|
-
Sass::Script::Tree::Operation.new(
|
|
151
|
-
val_to_sass_div(node.operand1, opts),
|
|
152
|
-
val_to_sass_concat(node.operand2, opts),
|
|
153
|
-
node.operator)
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
def val_to_sass_div(node, opts)
|
|
157
|
-
unless node.is_a?(Sass::Script::Tree::Operation) && node.operator == :div &&
|
|
158
|
-
node.operand1.is_a?(Sass::Script::Tree::Literal) &&
|
|
159
|
-
node.operand1.value.is_a?(Sass::Script::Value::Number) &&
|
|
160
|
-
node.operand2.is_a?(Sass::Script::Tree::Literal) &&
|
|
161
|
-
node.operand2.value.is_a?(Sass::Script::Value::Number) &&
|
|
162
|
-
(!node.operand1.value.original || !node.operand2.value.original)
|
|
163
|
-
return node
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
Sass::Script::Value::String.new("(#{node.to_sass(opts)})")
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
161
|
end
|
|
170
162
|
end
|
data/lib/sass/tree/rule_node.rb
CHANGED
|
@@ -120,13 +120,14 @@ module Sass::Tree
|
|
|
120
120
|
#
|
|
121
121
|
# @return [{#to_s => #to_s}]
|
|
122
122
|
def debug_info
|
|
123
|
-
{:filename => filename &&
|
|
123
|
+
{:filename => filename &&
|
|
124
|
+
("file://" + URI::DEFAULT_PARSER.escape(File.expand_path(filename))),
|
|
124
125
|
:line => line}
|
|
125
126
|
end
|
|
126
127
|
|
|
127
128
|
# A rule node is invisible if it has only placeholder selectors.
|
|
128
129
|
def invisible?
|
|
129
|
-
resolved_rules.members.all? {|seq| seq.
|
|
130
|
+
resolved_rules.members.all? {|seq| seq.invisible?}
|
|
130
131
|
end
|
|
131
132
|
|
|
132
133
|
private
|
|
@@ -31,7 +31,7 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
|
|
|
31
31
|
if parent.is_a?(Sass::Tree::AtRootNode) && parent.resolved_value
|
|
32
32
|
old_parents = @parents
|
|
33
33
|
@parents = @parents.reject {|p| parent.exclude_node?(p)}
|
|
34
|
-
@parent =
|
|
34
|
+
@parent = @parents.reverse.each_with_index.
|
|
35
35
|
find {|p, i| !transparent_parent?(p, @parents[-i - 2])}.first
|
|
36
36
|
|
|
37
37
|
begin
|
|
@@ -203,8 +203,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
|
203
203
|
|
|
204
204
|
unless node.args.empty? && node.keywords.empty? && node.splat.nil?
|
|
205
205
|
args = node.args.map(&arg_to_sass)
|
|
206
|
-
keywords =
|
|
207
|
-
map {|k, v| "$#{dasherize(k)}: #{arg_to_sass[v]}"}
|
|
206
|
+
keywords = node.keywords.as_stored.to_a.map {|k, v| "$#{dasherize(k)}: #{arg_to_sass[v]}"}
|
|
208
207
|
|
|
209
208
|
if node.splat
|
|
210
209
|
splat = "#{arg_to_sass[node.splat]}..."
|
|
@@ -281,7 +280,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
|
281
280
|
# Visit rule-level nodes and return their conversion with appropriate
|
|
282
281
|
# whitespace added.
|
|
283
282
|
def visit_rule_level(nodes)
|
|
284
|
-
|
|
283
|
+
(nodes + [nil]).each_cons(2).map do |child, nxt|
|
|
285
284
|
visit(child) +
|
|
286
285
|
if nxt &&
|
|
287
286
|
(child.is_a?(Sass::Tree::CommentNode) && child.line + child.lines + 1 == nxt.line) ||
|
|
@@ -56,8 +56,7 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
|
56
56
|
@parents.pop
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
-
#
|
|
60
|
-
# and that it's at the top of the document.
|
|
59
|
+
# Converts the entire document to CSS.
|
|
61
60
|
#
|
|
62
61
|
# @return [(Tree::Node, Sass::Util::SubsetMap)] The resulting tree of static nodes
|
|
63
62
|
# *and* the extensions defined for this tree
|
|
@@ -65,14 +64,6 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
|
65
64
|
yield
|
|
66
65
|
|
|
67
66
|
if parent.nil?
|
|
68
|
-
# In Ruby 1.9 we can make all @charset nodes invisible
|
|
69
|
-
# and infer the final @charset from the encoding of the final string.
|
|
70
|
-
if Sass::Util.ruby1_8?
|
|
71
|
-
charset = node.children.find {|c| c.is_a?(Sass::Tree::CharsetNode)}
|
|
72
|
-
node.children.reject! {|c| c.is_a?(Sass::Tree::CharsetNode)}
|
|
73
|
-
node.children.unshift charset if charset
|
|
74
|
-
end
|
|
75
|
-
|
|
76
67
|
imports_to_move = []
|
|
77
68
|
import_limit = nil
|
|
78
69
|
i = -1
|
|
@@ -115,11 +106,9 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
|
115
106
|
# @attr node [Sass::Tree::ExtendNode] The node that produced this extend.
|
|
116
107
|
# @attr directives [Array<Sass::Tree::DirectiveNode>]
|
|
117
108
|
# The directives containing the `@extend`.
|
|
118
|
-
# @attr
|
|
119
|
-
#
|
|
120
|
-
|
|
121
|
-
# unified with the extender), or `:succeeded`.
|
|
122
|
-
Extend = Struct.new(:extender, :target, :node, :directives, :result)
|
|
109
|
+
# @attr success [Boolean]
|
|
110
|
+
# Whether this extend successfully matched a selector.
|
|
111
|
+
Extend = Struct.new(:extender, :target, :node, :directives, :success)
|
|
123
112
|
|
|
124
113
|
# Registers an extension in the `@extends` subset map.
|
|
125
114
|
def visit_extend(node)
|
|
@@ -61,7 +61,7 @@ class Sass::Tree::Visitors::DeepCopy < Sass::Tree::Visitors::Base
|
|
|
61
61
|
|
|
62
62
|
def visit_prop(node)
|
|
63
63
|
node.name = node.name.map {|c| c.is_a?(Sass::Script::Tree::Node) ? c.deep_copy : c}
|
|
64
|
-
node.value = node.value.deep_copy
|
|
64
|
+
node.value = node.value.map {|c| c.is_a?(Sass::Script::Tree::Node) ? c.deep_copy : c}
|
|
65
65
|
yield
|
|
66
66
|
end
|
|
67
67
|
|
|
@@ -49,19 +49,13 @@ class Sass::Tree::Visitors::Extend < Sass::Tree::Visitors::Base
|
|
|
49
49
|
|
|
50
50
|
def check_extends_fired!(extends)
|
|
51
51
|
extends.each_value do |ex|
|
|
52
|
-
next if ex.
|
|
52
|
+
next if ex.success || ex.node.optional?
|
|
53
53
|
message = "\"#{ex.extender}\" failed to @extend \"#{ex.target.join}\"."
|
|
54
|
-
reason =
|
|
55
|
-
if ex.result == :not_found
|
|
56
|
-
"The selector \"#{ex.target.join}\" was not found."
|
|
57
|
-
else
|
|
58
|
-
"No selectors matching \"#{ex.target.join}\" could be unified with \"#{ex.extender}\"."
|
|
59
|
-
end
|
|
60
54
|
|
|
61
55
|
# TODO(nweiz): this should use the Sass stack trace of the extend node.
|
|
62
56
|
raise Sass::SyntaxError.new(<<MESSAGE, :filename => ex.node.filename, :line => ex.node.line)
|
|
63
57
|
#{message}
|
|
64
|
-
#{
|
|
58
|
+
The selector "#{ex.target.join}" was not found.
|
|
65
59
|
Use "@extend #{ex.target.join} !optional" if the extend should be able to fail.
|
|
66
60
|
MESSAGE
|
|
67
61
|
end
|
|
@@ -290,7 +290,7 @@ WARNING
|
|
|
290
290
|
|
|
291
291
|
@environment.set_local_function(node.name,
|
|
292
292
|
Sass::Callable.new(node.name, node.args, node.splat, env,
|
|
293
|
-
node.children, false, "function"))
|
|
293
|
+
node.children, false, "function", :stylesheet))
|
|
294
294
|
[]
|
|
295
295
|
end
|
|
296
296
|
|
|
@@ -341,7 +341,7 @@ WARNING
|
|
|
341
341
|
env = Sass::Environment.new(@environment, node.options)
|
|
342
342
|
@environment.set_local_mixin(node.name,
|
|
343
343
|
Sass::Callable.new(node.name, node.args, node.splat, env,
|
|
344
|
-
node.children, node.has_content, "mixin"))
|
|
344
|
+
node.children, node.has_content, "mixin", :stylesheet))
|
|
345
345
|
[]
|
|
346
346
|
end
|
|
347
347
|
|
|
@@ -395,9 +395,19 @@ WARNING
|
|
|
395
395
|
# Runs any SassScript that may be embedded in a property.
|
|
396
396
|
def visit_prop(node)
|
|
397
397
|
node.resolved_name = run_interp(node.name)
|
|
398
|
-
|
|
399
|
-
node
|
|
400
|
-
|
|
398
|
+
|
|
399
|
+
# If the node's value is just a variable or similar, we may get a useful
|
|
400
|
+
# source range from evaluating it.
|
|
401
|
+
if node.value.length == 1 && node.value.first.is_a?(Sass::Script::Tree::Node)
|
|
402
|
+
result = node.value.first.perform(@environment)
|
|
403
|
+
node.resolved_value = result.to_s
|
|
404
|
+
node.value_source_range = result.source_range if result.source_range
|
|
405
|
+
elsif node.custom_property?
|
|
406
|
+
node.resolved_value = run_interp_no_strip(node.value)
|
|
407
|
+
else
|
|
408
|
+
node.resolved_value = run_interp(node.value)
|
|
409
|
+
end
|
|
410
|
+
|
|
401
411
|
yield
|
|
402
412
|
end
|
|
403
413
|
|
|
@@ -483,9 +493,11 @@ WARNING
|
|
|
483
493
|
def visit_warn(node)
|
|
484
494
|
res = node.expr.perform(@environment)
|
|
485
495
|
res = res.value if res.is_a?(Sass::Script::Value::String)
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
496
|
+
@environment.stack.with_directive(node.filename, node.line, "@warn") do
|
|
497
|
+
msg = "WARNING: #{res}\n "
|
|
498
|
+
msg << @environment.stack.to_s.gsub("\n", "\n ") << "\n"
|
|
499
|
+
Sass::Util.sass_warn msg
|
|
500
|
+
end
|
|
489
501
|
[]
|
|
490
502
|
end
|
|
491
503
|
|
|
@@ -556,7 +568,7 @@ WARNING
|
|
|
556
568
|
end
|
|
557
569
|
|
|
558
570
|
files << node.filename << node.imported_file.options[:filename]
|
|
559
|
-
msg << "\n" <<
|
|
571
|
+
msg << "\n" << files.each_cons(2).map do |m1, m2|
|
|
560
572
|
" #{m1} imports #{m2}"
|
|
561
573
|
end.join("\n")
|
|
562
574
|
raise Sass::SyntaxError.new(msg)
|
|
@@ -88,7 +88,7 @@ class Sass::Tree::Visitors::SetOptions < Sass::Tree::Visitors::Base
|
|
|
88
88
|
|
|
89
89
|
def visit_prop(node)
|
|
90
90
|
node.name.each {|c| c.options = @options if c.is_a?(Sass::Script::Tree::Node)}
|
|
91
|
-
node.value.options = @options
|
|
91
|
+
node.value.each {|c| c.options = @options if c.is_a?(Sass::Script::Tree::Node)}
|
|
92
92
|
yield
|
|
93
93
|
end
|
|
94
94
|
|
|
@@ -135,7 +135,7 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
|
|
|
135
135
|
|
|
136
136
|
output "\n"
|
|
137
137
|
|
|
138
|
-
unless
|
|
138
|
+
unless @result.ascii_only?
|
|
139
139
|
if node.style == :compressed
|
|
140
140
|
# A byte order mark is sufficient to tell browsers that this
|
|
141
141
|
# file is UTF-8 encoded, and will override any other detection
|
|
@@ -261,18 +261,20 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
|
|
|
261
261
|
end
|
|
262
262
|
|
|
263
263
|
def visit_prop(node)
|
|
264
|
-
return if node.resolved_value.empty?
|
|
264
|
+
return if node.resolved_value.empty? && !node.custom_property?
|
|
265
265
|
tab_str = ' ' * (@tabs + node.tabs)
|
|
266
266
|
output(tab_str)
|
|
267
267
|
for_node(node, :name) {output(node.resolved_name)}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
268
|
+
output(":")
|
|
269
|
+
output(" ") unless node.style == :compressed || node.custom_property?
|
|
270
|
+
for_node(node, :value) do
|
|
271
|
+
output(if node.custom_property?
|
|
272
|
+
format_custom_property_value(node)
|
|
273
|
+
else
|
|
274
|
+
node.resolved_value
|
|
275
|
+
end)
|
|
275
276
|
end
|
|
277
|
+
output(";") unless node.style == :compressed
|
|
276
278
|
end
|
|
277
279
|
|
|
278
280
|
# @comment
|
|
@@ -294,8 +296,8 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
|
|
|
294
296
|
end
|
|
295
297
|
|
|
296
298
|
joined_rules = node.resolved_rules.members.map do |seq|
|
|
297
|
-
next if seq.
|
|
298
|
-
rule_part = seq.to_s(:
|
|
299
|
+
next if seq.invisible?
|
|
300
|
+
rule_part = seq.to_s(style: node.style, placeholder: false)
|
|
299
301
|
if node.style == :compressed
|
|
300
302
|
rule_part.gsub!(/([^,])\s*\n\s*/m, '\1 ')
|
|
301
303
|
rule_part.gsub!(/\s*([+>])\s*/m, '\1')
|
|
@@ -384,9 +386,41 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
|
|
|
384
386
|
|
|
385
387
|
private
|
|
386
388
|
|
|
389
|
+
# Reformats the value of `node` so that it's nicely indented, preserving its
|
|
390
|
+
# existing relative indentation.
|
|
391
|
+
#
|
|
392
|
+
# @param node [Sass::Script::Tree::PropNode] A custom property node.
|
|
393
|
+
# @return [String]
|
|
394
|
+
def format_custom_property_value(node)
|
|
395
|
+
if node.style == :compact || node.style == :compressed || !node.resolved_value.include?("\n")
|
|
396
|
+
# Folding not involving newlines was done in the parser. We can safely
|
|
397
|
+
# fold newlines here because tokens like strings can't contain literal
|
|
398
|
+
# newlines, so we know any adjacent whitespace is tokenized as whitespace.
|
|
399
|
+
return node.resolved_value.gsub(/[ \t\r\f]*\n[ \t\r\f\n]*/, ' ')
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
# Find the smallest amount of indentation in the custom property and use
|
|
403
|
+
# that as the base indentation level.
|
|
404
|
+
lines = node.resolved_value.split("\n")
|
|
405
|
+
indented_lines = lines[1..-1]
|
|
406
|
+
min_indentation = indented_lines.
|
|
407
|
+
map {|line| line[/^[ \t]*/]}.
|
|
408
|
+
reject {|line| line.empty?}.
|
|
409
|
+
min_by {|line| line.length}
|
|
410
|
+
|
|
411
|
+
# Limit the base indentation to the same indentation level as the node name
|
|
412
|
+
# so that if *every* line is indented relative to the property name that's
|
|
413
|
+
# preserved.
|
|
414
|
+
if node.name_source_range
|
|
415
|
+
base_indentation = min_indentation[0...node.name_source_range.start_pos.offset - 1]
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
lines.first + "\n" + indented_lines.join("\n").gsub(/^#{base_indentation}/, ' ' * @tabs)
|
|
419
|
+
end
|
|
420
|
+
|
|
387
421
|
def debug_info_rule(debug_info, options)
|
|
388
422
|
node = Sass::Tree::DirectiveNode.resolved("@media -sass-debug-info")
|
|
389
|
-
|
|
423
|
+
debug_info.map {|k, v| [k.to_s, v.to_s]}.to_a.each do |k, v|
|
|
390
424
|
rule = Sass::Tree::RuleNode.new([""])
|
|
391
425
|
rule.resolved_rules = Sass::Selector::CommaSequence.new(
|
|
392
426
|
[Sass::Selector::Sequence.new(
|
|
@@ -395,7 +429,7 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
|
|
|
395
429
|
false)
|
|
396
430
|
])
|
|
397
431
|
])
|
|
398
|
-
prop = Sass::Tree::PropNode.new([""],
|
|
432
|
+
prop = Sass::Tree::PropNode.new([""], [""], :new)
|
|
399
433
|
prop.resolved_name = "font-family"
|
|
400
434
|
prop.resolved_value = Sass::SCSS::RX.escape_ident(v.to_s)
|
|
401
435
|
rule << prop
|