sass 3.4.0 → 3.4.25
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/.yardopts +3 -1
- data/CODE_OF_CONDUCT.md +10 -0
- data/CONTRIBUTING.md +148 -0
- data/MIT-LICENSE +1 -1
- data/README.md +26 -20
- data/Rakefile +103 -20
- data/VERSION +1 -1
- data/VERSION_DATE +1 -1
- data/extra/sass-spec-ref.sh +32 -0
- data/extra/update_watch.rb +1 -1
- data/lib/sass/cache_stores/filesystem.rb +7 -7
- data/lib/sass/cache_stores/memory.rb +4 -5
- data/lib/sass/callbacks.rb +2 -2
- data/lib/sass/css.rb +11 -10
- data/lib/sass/deprecation.rb +55 -0
- data/lib/sass/engine.rb +83 -38
- data/lib/sass/environment.rb +26 -2
- data/lib/sass/error.rb +12 -12
- data/lib/sass/exec/base.rb +15 -3
- data/lib/sass/exec/sass_convert.rb +34 -15
- data/lib/sass/exec/sass_scss.rb +23 -7
- data/lib/sass/features.rb +2 -2
- data/lib/sass/importers/base.rb +1 -1
- data/lib/sass/importers/deprecated_path.rb +51 -0
- data/lib/sass/importers/filesystem.rb +24 -16
- data/lib/sass/importers.rb +1 -0
- data/lib/sass/logger/base.rb +8 -2
- data/lib/sass/logger/delayed.rb +50 -0
- data/lib/sass/logger.rb +8 -3
- data/lib/sass/plugin/compiler.rb +42 -25
- data/lib/sass/plugin/configuration.rb +38 -22
- data/lib/sass/plugin/merb.rb +2 -2
- data/lib/sass/plugin/rack.rb +3 -3
- data/lib/sass/plugin/rails.rb +1 -1
- data/lib/sass/plugin/staleness_checker.rb +3 -3
- data/lib/sass/plugin.rb +3 -2
- data/lib/sass/script/css_parser.rb +2 -3
- data/lib/sass/script/css_variable_warning.rb +52 -0
- data/lib/sass/script/functions.rb +140 -73
- data/lib/sass/script/lexer.rb +37 -22
- data/lib/sass/script/parser.rb +235 -40
- data/lib/sass/script/tree/funcall.rb +12 -5
- data/lib/sass/script/tree/interpolation.rb +109 -4
- data/lib/sass/script/tree/list_literal.rb +31 -4
- data/lib/sass/script/tree/literal.rb +4 -0
- data/lib/sass/script/tree/node.rb +21 -3
- data/lib/sass/script/tree/operation.rb +54 -1
- data/lib/sass/script/tree/string_interpolation.rb +58 -37
- data/lib/sass/script/tree/variable.rb +1 -1
- data/lib/sass/script/value/base.rb +10 -9
- data/lib/sass/script/value/color.rb +42 -24
- data/lib/sass/script/value/helpers.rb +16 -6
- data/lib/sass/script/value/map.rb +1 -1
- data/lib/sass/script/value/number.rb +52 -19
- data/lib/sass/script/value/string.rb +46 -5
- data/lib/sass/script.rb +3 -3
- data/lib/sass/scss/css_parser.rb +16 -2
- data/lib/sass/scss/parser.rb +120 -75
- data/lib/sass/scss/rx.rb +9 -10
- data/lib/sass/scss/static_parser.rb +19 -14
- data/lib/sass/scss.rb +0 -2
- data/lib/sass/selector/abstract_sequence.rb +8 -6
- data/lib/sass/selector/comma_sequence.rb +25 -9
- data/lib/sass/selector/pseudo.rb +45 -35
- data/lib/sass/selector/sequence.rb +54 -18
- data/lib/sass/selector/simple.rb +11 -11
- data/lib/sass/selector/simple_sequence.rb +34 -15
- data/lib/sass/selector.rb +7 -10
- data/lib/sass/shared.rb +1 -1
- data/lib/sass/source/map.rb +7 -4
- data/lib/sass/source/position.rb +4 -4
- data/lib/sass/stack.rb +2 -2
- data/lib/sass/supports.rb +8 -10
- data/lib/sass/tree/comment_node.rb +1 -1
- data/lib/sass/tree/css_import_node.rb +9 -1
- data/lib/sass/tree/function_node.rb +8 -3
- data/lib/sass/tree/import_node.rb +6 -5
- data/lib/sass/tree/node.rb +5 -3
- data/lib/sass/tree/prop_node.rb +5 -6
- data/lib/sass/tree/rule_node.rb +14 -4
- data/lib/sass/tree/visitors/check_nesting.rb +18 -22
- data/lib/sass/tree/visitors/convert.rb +43 -26
- data/lib/sass/tree/visitors/cssize.rb +5 -1
- data/lib/sass/tree/visitors/deep_copy.rb +1 -1
- data/lib/sass/tree/visitors/extend.rb +15 -13
- data/lib/sass/tree/visitors/perform.rb +42 -17
- data/lib/sass/tree/visitors/set_options.rb +1 -1
- data/lib/sass/tree/visitors/to_css.rb +58 -30
- data/lib/sass/util/multibyte_string_scanner.rb +0 -2
- data/lib/sass/util/normalized_map.rb +0 -1
- data/lib/sass/util/subset_map.rb +1 -2
- data/lib/sass/util.rb +125 -68
- data/lib/sass/version.rb +2 -2
- data/lib/sass.rb +10 -3
- data/test/sass/compiler_test.rb +6 -2
- data/test/sass/conversion_test.rb +187 -53
- data/test/sass/css2sass_test.rb +50 -1
- data/test/sass/css_variable_test.rb +132 -0
- data/test/sass/engine_test.rb +207 -61
- data/test/sass/exec_test.rb +10 -0
- data/test/sass/extend_test.rb +101 -29
- data/test/sass/functions_test.rb +60 -9
- data/test/sass/importer_test.rb +9 -0
- data/test/sass/more_templates/more1.sass +10 -10
- data/test/sass/more_templates/more_import.sass +2 -2
- data/test/sass/plugin_test.rb +10 -8
- data/test/sass/results/script.css +3 -3
- data/test/sass/script_conversion_test.rb +58 -29
- data/test/sass/script_test.rb +430 -53
- data/test/sass/scss/css_test.rb +73 -7
- data/test/sass/scss/rx_test.rb +4 -0
- data/test/sass/scss/scss_test.rb +309 -4
- data/test/sass/source_map_test.rb +152 -74
- data/test/sass/superselector_test.rb +19 -0
- data/test/sass/templates/_partial.sass +1 -1
- data/test/sass/templates/basic.sass +10 -10
- data/test/sass/templates/bork1.sass +1 -1
- data/test/sass/templates/bork5.sass +1 -1
- data/test/sass/templates/compact.sass +10 -10
- data/test/sass/templates/complex.sass +187 -187
- data/test/sass/templates/compressed.sass +10 -10
- data/test/sass/templates/expanded.sass +10 -10
- data/test/sass/templates/import.sass +2 -2
- data/test/sass/templates/importee.sass +3 -3
- data/test/sass/templates/mixins.sass +22 -22
- data/test/sass/templates/multiline.sass +4 -4
- data/test/sass/templates/nested.sass +13 -13
- data/test/sass/templates/parent_ref.sass +12 -12
- data/test/sass/templates/script.sass +70 -70
- data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +1 -1
- data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +2 -2
- data/test/sass/templates/subdir/subdir.sass +3 -3
- data/test/sass/templates/units.sass +10 -10
- data/test/sass/util/multibyte_string_scanner_test.rb +10 -2
- data/test/sass/util_test.rb +15 -44
- data/test/sass-spec.yml +3 -0
- data/test/test_helper.rb +5 -4
- metadata +302 -295
- data/CONTRIBUTING +0 -3
- data/lib/sass/scss/script_lexer.rb +0 -15
- data/lib/sass/scss/script_parser.rb +0 -25
@@ -15,6 +15,11 @@ module Sass::Tree
|
|
15
15
|
# @return [String]
|
16
16
|
attr_accessor :resolved_uri
|
17
17
|
|
18
|
+
# The supports condition for this import.
|
19
|
+
#
|
20
|
+
# @return [Sass::Supports::Condition]
|
21
|
+
attr_accessor :supports_condition
|
22
|
+
|
18
23
|
# The media query for this rule, interspersed with
|
19
24
|
# {Sass::Script::Tree::Node}s representing `#{}`-interpolation. Any adjacent
|
20
25
|
# strings will be merged together.
|
@@ -30,9 +35,11 @@ module Sass::Tree
|
|
30
35
|
|
31
36
|
# @param uri [String, Sass::Script::Tree::Node] See \{#uri}
|
32
37
|
# @param query [Array<String, Sass::Script::Tree::Node>] See \{#query}
|
33
|
-
|
38
|
+
# @param supports_condition [Sass::Supports::Condition] See \{#supports_condition}
|
39
|
+
def initialize(uri, query = [], supports_condition = nil)
|
34
40
|
@uri = uri
|
35
41
|
@query = query
|
42
|
+
@supports_condition = supports_condition
|
36
43
|
super('')
|
37
44
|
end
|
38
45
|
|
@@ -52,6 +59,7 @@ module Sass::Tree
|
|
52
59
|
@resolved_value ||=
|
53
60
|
begin
|
54
61
|
str = "@import #{resolved_uri}"
|
62
|
+
str << " supports(#{supports_condition.to_css})" if supports_condition
|
55
63
|
str << " #{resolved_query.to_css}" if resolved_query
|
56
64
|
str
|
57
65
|
end
|
@@ -20,6 +20,12 @@ module Sass
|
|
20
20
|
# @return [Script::Tree::Node?]
|
21
21
|
attr_accessor :splat
|
22
22
|
|
23
|
+
# Strips out any vendor prefixes.
|
24
|
+
# @return [String] The normalized name of the directive.
|
25
|
+
def normalized_name
|
26
|
+
@normalized_name ||= name.gsub(/^(?:-[a-zA-Z0-9]+-)?/, '\1')
|
27
|
+
end
|
28
|
+
|
23
29
|
# @param name [String] The function name
|
24
30
|
# @param args [Array<(Script::Tree::Node, Script::Tree::Node)>]
|
25
31
|
# The arguments for the function.
|
@@ -30,9 +36,8 @@ module Sass
|
|
30
36
|
@splat = splat
|
31
37
|
super()
|
32
38
|
|
33
|
-
|
34
|
-
|
35
|
-
end
|
39
|
+
return unless %w(and or not).include?(name)
|
40
|
+
raise Sass::SyntaxError.new("Invalid function name \"#{name}\".")
|
36
41
|
end
|
37
42
|
end
|
38
43
|
end
|
@@ -55,13 +55,14 @@ module Sass
|
|
55
55
|
return f if f
|
56
56
|
end
|
57
57
|
|
58
|
-
|
58
|
+
lines = ["File to import not found or unreadable: #{@imported_filename}."]
|
59
|
+
|
59
60
|
if paths.size == 1
|
60
|
-
|
61
|
-
|
62
|
-
|
61
|
+
lines << "Load path: #{paths.first}"
|
62
|
+
elsif !paths.empty?
|
63
|
+
lines << "Load paths:\n #{paths.join("\n ")}"
|
63
64
|
end
|
64
|
-
raise SyntaxError.new(
|
65
|
+
raise SyntaxError.new(lines.join("\n"))
|
65
66
|
rescue SyntaxError => e
|
66
67
|
raise SyntaxError.new(e.message, :line => line, :filename => @filename)
|
67
68
|
end
|
data/lib/sass/tree/node.rb
CHANGED
@@ -69,7 +69,7 @@ module Sass
|
|
69
69
|
|
70
70
|
# The line of the document on which this node appeared.
|
71
71
|
#
|
72
|
-
# @return [
|
72
|
+
# @return [Integer]
|
73
73
|
attr_accessor :line
|
74
74
|
|
75
75
|
# The source range in the document on which this node appeared.
|
@@ -83,13 +83,15 @@ module Sass
|
|
83
83
|
attr_writer :filename
|
84
84
|
|
85
85
|
# The options hash for the node.
|
86
|
-
# See {file:SASS_REFERENCE.md#
|
86
|
+
# See {file:SASS_REFERENCE.md#Options the Sass options documentation}.
|
87
87
|
#
|
88
88
|
# @return [{Symbol => Object}]
|
89
89
|
attr_reader :options
|
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.
|
@@ -149,7 +151,7 @@ module Sass
|
|
149
151
|
# @return [Boolean]
|
150
152
|
def invisible?; false; end
|
151
153
|
|
152
|
-
# The output style. See {file:SASS_REFERENCE.md#
|
154
|
+
# The output style. See {file:SASS_REFERENCE.md#Options the Sass options documentation}.
|
153
155
|
#
|
154
156
|
# @return [Symbol]
|
155
157
|
def style
|
data/lib/sass/tree/prop_node.rb
CHANGED
@@ -39,7 +39,7 @@ module Sass::Tree
|
|
39
39
|
# * This is a child property of another property
|
40
40
|
# * The parent property has a value, and thus will be rendered
|
41
41
|
#
|
42
|
-
# @return [
|
42
|
+
# @return [Integer]
|
43
43
|
attr_accessor :tabs
|
44
44
|
|
45
45
|
# The source range in which the property name appears.
|
@@ -119,11 +119,10 @@ module Sass::Tree
|
|
119
119
|
private
|
120
120
|
|
121
121
|
def check!
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
end
|
122
|
+
return unless @options[:property_syntax] && @options[:property_syntax] != @prop_syntax
|
123
|
+
raise Sass::SyntaxError.new(
|
124
|
+
"Illegal property syntax: can't use #{@prop_syntax} syntax when " +
|
125
|
+
":property_syntax => #{@options[:property_syntax].inspect} is set.")
|
127
126
|
end
|
128
127
|
|
129
128
|
class << self
|
data/lib/sass/tree/rule_node.rb
CHANGED
@@ -40,7 +40,7 @@ module Sass::Tree
|
|
40
40
|
# * This is a child rule of another rule
|
41
41
|
# * The parent rule has properties, and thus will be rendered
|
42
42
|
#
|
43
|
-
# @return [
|
43
|
+
# @return [Integer]
|
44
44
|
attr_accessor :tabs
|
45
45
|
|
46
46
|
# The entire selector source range for this rule.
|
@@ -132,14 +132,24 @@ module Sass::Tree
|
|
132
132
|
private
|
133
133
|
|
134
134
|
def try_to_parse_non_interpolated_rules
|
135
|
-
|
136
|
-
|
137
|
-
|
135
|
+
@parsed_rules = nil
|
136
|
+
return unless @rule.all? {|t| t.is_a?(String)}
|
137
|
+
|
138
|
+
# We don't use real filename/line info because we don't have it yet.
|
139
|
+
# When we get it, we'll set it on the parsed rules if possible.
|
140
|
+
parser = nil
|
141
|
+
warnings = Sass::Util.silence_warnings do
|
138
142
|
parser = Sass::SCSS::StaticParser.new(@rule.join.strip, nil, nil, 1)
|
139
143
|
# rubocop:disable RescueModifier
|
140
144
|
@parsed_rules = parser.parse_selector rescue nil
|
141
145
|
# rubocop:enable RescueModifier
|
146
|
+
|
147
|
+
$stderr.string
|
142
148
|
end
|
149
|
+
|
150
|
+
# If parsing produces a warning, throw away the result so we can parse
|
151
|
+
# later with the real filename info.
|
152
|
+
@parsed_rules = nil unless warnings.empty?
|
143
153
|
end
|
144
154
|
end
|
145
155
|
end
|
@@ -4,6 +4,8 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
|
|
4
4
|
|
5
5
|
def initialize
|
6
6
|
@parents = []
|
7
|
+
@parent = nil
|
8
|
+
@current_mixin_def = nil
|
7
9
|
end
|
8
10
|
|
9
11
|
def visit(node)
|
@@ -90,9 +92,8 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
|
|
90
92
|
|
91
93
|
VALID_EXTEND_PARENTS = [Sass::Tree::RuleNode, Sass::Tree::MixinDefNode, Sass::Tree::MixinNode]
|
92
94
|
def invalid_extend_parent?(parent, child)
|
93
|
-
|
94
|
-
|
95
|
-
end
|
95
|
+
return if is_any_of?(parent, VALID_EXTEND_PARENTS)
|
96
|
+
"Extend directives may only be used within rules."
|
96
97
|
end
|
97
98
|
|
98
99
|
INVALID_IMPORT_PARENTS = CONTROL_NODES +
|
@@ -110,15 +111,13 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
|
|
110
111
|
end
|
111
112
|
|
112
113
|
def invalid_mixindef_parent?(parent, child)
|
113
|
-
|
114
|
-
|
115
|
-
end
|
114
|
+
return if (@parents.map {|p| p.class} & INVALID_IMPORT_PARENTS).empty?
|
115
|
+
"Mixins may not be defined within control directives or other mixins."
|
116
116
|
end
|
117
117
|
|
118
118
|
def invalid_function_parent?(parent, child)
|
119
|
-
|
120
|
-
|
121
|
-
end
|
119
|
+
return if (@parents.map {|p| p.class} & INVALID_IMPORT_PARENTS).empty?
|
120
|
+
"Functions may not be defined within control directives or other mixins."
|
122
121
|
end
|
123
122
|
|
124
123
|
VALID_FUNCTION_CHILDREN = [
|
@@ -126,27 +125,24 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
|
|
126
125
|
Sass::Tree::VariableNode, Sass::Tree::WarnNode, Sass::Tree::ErrorNode
|
127
126
|
] + CONTROL_NODES
|
128
127
|
def invalid_function_child?(parent, child)
|
129
|
-
|
130
|
-
|
131
|
-
end
|
128
|
+
return if is_any_of?(child, VALID_FUNCTION_CHILDREN)
|
129
|
+
"Functions can only contain variable declarations and control directives."
|
132
130
|
end
|
133
131
|
|
134
|
-
VALID_PROP_CHILDREN =
|
135
|
-
|
136
|
-
|
132
|
+
VALID_PROP_CHILDREN = CONTROL_NODES + [Sass::Tree::CommentNode,
|
133
|
+
Sass::Tree::PropNode,
|
134
|
+
Sass::Tree::MixinNode]
|
137
135
|
def invalid_prop_child?(parent, child)
|
138
|
-
|
139
|
-
|
140
|
-
end
|
136
|
+
return if is_any_of?(child, VALID_PROP_CHILDREN)
|
137
|
+
"Illegal nesting: Only properties may be nested beneath properties."
|
141
138
|
end
|
142
139
|
|
143
140
|
VALID_PROP_PARENTS = [Sass::Tree::RuleNode, Sass::Tree::KeyframeRuleNode, Sass::Tree::PropNode,
|
144
141
|
Sass::Tree::MixinDefNode, Sass::Tree::DirectiveNode, Sass::Tree::MixinNode]
|
145
142
|
def invalid_prop_parent?(parent, child)
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
end
|
143
|
+
return if is_any_of?(parent, VALID_PROP_PARENTS)
|
144
|
+
"Properties are only allowed within rules, directives, mixin includes, or other properties." +
|
145
|
+
child.pseudo_class_selector_message
|
150
146
|
end
|
151
147
|
|
152
148
|
def invalid_return_parent?(parent, child)
|
@@ -18,15 +18,19 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
18
18
|
@tabs = 0
|
19
19
|
# 2 spaces by default
|
20
20
|
@tab_chars = @options[:indent] || " "
|
21
|
+
@is_else = false
|
21
22
|
end
|
22
23
|
|
23
24
|
def visit_children(parent)
|
24
25
|
@tabs += 1
|
25
26
|
return @format == :sass ? "\n" : " {}\n" if parent.children.empty?
|
27
|
+
|
28
|
+
res = visit_rule_level(parent.children)
|
29
|
+
|
26
30
|
if @format == :sass
|
27
|
-
"\n"
|
31
|
+
"\n" + res.rstrip + "\n"
|
28
32
|
else
|
29
|
-
" {\n" +
|
33
|
+
" {\n" + res.rstrip + "\n#{@tab_chars * (@tabs - 1)}}\n"
|
30
34
|
end
|
31
35
|
ensure
|
32
36
|
@tabs -= 1
|
@@ -34,20 +38,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
34
38
|
|
35
39
|
# Ensures proper spacing between top-level nodes.
|
36
40
|
def visit_root(node)
|
37
|
-
|
38
|
-
visit(child) +
|
39
|
-
if nxt &&
|
40
|
-
(child.is_a?(Sass::Tree::CommentNode) &&
|
41
|
-
child.line + child.lines + 1 == nxt.line) ||
|
42
|
-
(child.is_a?(Sass::Tree::ImportNode) && nxt.is_a?(Sass::Tree::ImportNode) &&
|
43
|
-
child.line + 1 == nxt.line) ||
|
44
|
-
(child.is_a?(Sass::Tree::VariableNode) && nxt.is_a?(Sass::Tree::VariableNode) &&
|
45
|
-
child.line + 1 == nxt.line)
|
46
|
-
""
|
47
|
-
else
|
48
|
-
"\n"
|
49
|
-
end
|
50
|
-
end.join.rstrip + "\n"
|
41
|
+
visit_rule_level(node.children)
|
51
42
|
end
|
52
43
|
|
53
44
|
def visit_charset(node)
|
@@ -57,14 +48,14 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
57
48
|
def visit_comment(node)
|
58
49
|
value = interp_to_src(node.value)
|
59
50
|
if @format == :sass
|
60
|
-
content = value.gsub(
|
51
|
+
content = value.gsub(%r{\*/$}, '').rstrip
|
61
52
|
if content =~ /\A[ \t]/
|
62
53
|
# Re-indent SCSS comments like this:
|
63
54
|
# /* foo
|
64
55
|
# bar
|
65
56
|
# baz */
|
66
57
|
content.gsub!(/^/, ' ')
|
67
|
-
content.sub!(
|
58
|
+
content.sub!(%r{\A([ \t]*)/\*}, '/*\1')
|
68
59
|
end
|
69
60
|
|
70
61
|
if content.include?("\n")
|
@@ -78,13 +69,13 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
78
69
|
end
|
79
70
|
end
|
80
71
|
|
81
|
-
content.gsub!(
|
72
|
+
content.gsub!(%r{\A/\*}, '//') if node.type == :silent
|
82
73
|
content.gsub!(/^/, tab_str)
|
83
74
|
content = content.rstrip + "\n"
|
84
75
|
else
|
85
76
|
spaces = (@tab_chars * [@tabs - value[/^ */].size, 0].max)
|
86
77
|
content = if node.type == :silent
|
87
|
-
value.gsub(
|
78
|
+
value.gsub(%r{^[/ ]\*}, '//').gsub(%r{ *\*/$}, '')
|
88
79
|
else
|
89
80
|
value
|
90
81
|
end.gsub(/^/, spaces) + "\n"
|
@@ -104,7 +95,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
104
95
|
res = "#{tab_str}#{interp_to_src(node.value)}"
|
105
96
|
res.gsub!(/^@import \#\{(.*)\}([^}]*)$/, '@import \1\2')
|
106
97
|
return res + "#{semi}\n" unless node.has_children
|
107
|
-
res + yield
|
98
|
+
res + yield
|
108
99
|
end
|
109
100
|
|
110
101
|
def visit_each(node)
|
@@ -113,13 +104,13 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
113
104
|
end
|
114
105
|
|
115
106
|
def visit_extend(node)
|
116
|
-
"#{tab_str}@extend #{selector_to_src(node.selector).lstrip}
|
117
|
-
"#{
|
107
|
+
"#{tab_str}@extend #{selector_to_src(node.selector).lstrip}" +
|
108
|
+
"#{' !optional' if node.optional?}#{semi}\n"
|
118
109
|
end
|
119
110
|
|
120
111
|
def visit_for(node)
|
121
112
|
"#{tab_str}@for $#{dasherize(node.var)} from #{node.from.to_sass(@options)} " +
|
122
|
-
"#{node.exclusive ?
|
113
|
+
"#{node.exclusive ? 'to' : 'through'} #{node.to.to_sass(@options)}#{yield}"
|
123
114
|
end
|
124
115
|
|
125
116
|
def visit_function(node)
|
@@ -173,6 +164,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
173
164
|
else
|
174
165
|
str = "#{tab_str}@import #{node.uri}"
|
175
166
|
end
|
167
|
+
str << " supports(#{node.supports_condition.to_src(@options)})" if node.supports_condition
|
176
168
|
str << " #{interp_to_src(node.query)}" unless node.query.empty?
|
177
169
|
"#{str}#{semi}\n"
|
178
170
|
end
|
@@ -274,14 +266,39 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
274
266
|
"#{tab_str}@at-root #{query_interp_to_src(node.query)}#{yield}"
|
275
267
|
elsif node.children.length == 1 && node.children.first.is_a?(Sass::Tree::RuleNode)
|
276
268
|
rule = node.children.first
|
277
|
-
"#{tab_str}@at-root #{selector_to_src(rule.rule)}#{visit_children(rule)}"
|
269
|
+
"#{tab_str}@at-root #{selector_to_src(rule.rule).lstrip}#{visit_children(rule)}"
|
278
270
|
else
|
279
271
|
"#{tab_str}@at-root#{yield}"
|
280
272
|
end
|
281
273
|
end
|
282
274
|
|
275
|
+
def visit_keyframerule(node)
|
276
|
+
"#{tab_str}#{node.resolved_value}#{yield}"
|
277
|
+
end
|
278
|
+
|
283
279
|
private
|
284
280
|
|
281
|
+
# Visit rule-level nodes and return their conversion with appropriate
|
282
|
+
# whitespace added.
|
283
|
+
def visit_rule_level(nodes)
|
284
|
+
Sass::Util.enum_cons(nodes + [nil], 2).map do |child, nxt|
|
285
|
+
visit(child) +
|
286
|
+
if nxt &&
|
287
|
+
(child.is_a?(Sass::Tree::CommentNode) && child.line + child.lines + 1 == nxt.line) ||
|
288
|
+
(child.is_a?(Sass::Tree::ImportNode) && nxt.is_a?(Sass::Tree::ImportNode) &&
|
289
|
+
child.line + 1 == nxt.line) ||
|
290
|
+
(child.is_a?(Sass::Tree::VariableNode) && nxt.is_a?(Sass::Tree::VariableNode) &&
|
291
|
+
child.line + 1 == nxt.line) ||
|
292
|
+
(child.is_a?(Sass::Tree::PropNode) && nxt.is_a?(Sass::Tree::PropNode)) ||
|
293
|
+
(child.is_a?(Sass::Tree::MixinNode) && nxt.is_a?(Sass::Tree::MixinNode) &&
|
294
|
+
child.line + 1 == nxt.line)
|
295
|
+
""
|
296
|
+
else
|
297
|
+
"\n"
|
298
|
+
end
|
299
|
+
end.join.rstrip + "\n"
|
300
|
+
end
|
301
|
+
|
285
302
|
def interp_to_src(interp)
|
286
303
|
interp.map {|r| r.is_a?(String) ? r : r.to_sass(@options)}.join
|
287
304
|
end
|
@@ -326,7 +343,7 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
|
326
343
|
|
327
344
|
def dasherize(s)
|
328
345
|
if @options[:dasherize]
|
329
|
-
s.
|
346
|
+
s.tr('_', '-')
|
330
347
|
else
|
331
348
|
s
|
332
349
|
end
|
@@ -222,7 +222,11 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
222
222
|
# Bubbles a directive up through RuleNodes.
|
223
223
|
def visit_directive(node)
|
224
224
|
return node unless node.has_children
|
225
|
-
|
225
|
+
if parent.is_a?(Sass::Tree::RuleNode)
|
226
|
+
# @keyframes shouldn't include the rule nodes, so we manually create a
|
227
|
+
# bubble that doesn't have the parent's contents for them.
|
228
|
+
return node.normalized_name == '@keyframes' ? Bubble.new(node) : bubble(node)
|
229
|
+
end
|
226
230
|
|
227
231
|
yield
|
228
232
|
|
@@ -55,7 +55,7 @@ class Sass::Tree::Visitors::DeepCopy < Sass::Tree::Visitors::Base
|
|
55
55
|
|
56
56
|
def visit_mixin(node)
|
57
57
|
node.args = node.args.map {|a| a.deep_copy}
|
58
|
-
node.keywords = Hash[node.keywords.map {|k, v| [k, v.deep_copy]}]
|
58
|
+
node.keywords = Sass::Util::NormalizedMap.new(Hash[node.keywords.map {|k, v| [k, v.deep_copy]}])
|
59
59
|
yield
|
60
60
|
end
|
61
61
|
|
@@ -44,25 +44,27 @@ class Sass::Tree::Visitors::Extend < Sass::Tree::Visitors::Base
|
|
44
44
|
node.resolved_rules = node.resolved_rules.do_extend(@extends, @parent_directives)
|
45
45
|
end
|
46
46
|
|
47
|
-
|
47
|
+
class << self
|
48
|
+
private
|
48
49
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
50
|
+
def check_extends_fired!(extends)
|
51
|
+
extends.each_value do |ex|
|
52
|
+
next if ex.result == :succeeded || ex.node.optional?
|
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
|
59
60
|
|
60
|
-
|
61
|
-
|
61
|
+
# TODO(nweiz): this should use the Sass stack trace of the extend node.
|
62
|
+
raise Sass::SyntaxError.new(<<MESSAGE, :filename => ex.node.filename, :line => ex.node.line)
|
62
63
|
#{message}
|
63
64
|
#{reason}
|
64
65
|
Use "@extend #{ex.target.join} !optional" if the extend should be able to fail.
|
65
66
|
MESSAGE
|
67
|
+
end
|
66
68
|
end
|
67
69
|
end
|
68
70
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# A visitor for converting a dynamic Sass tree into a static Sass tree.
|
2
2
|
class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
3
|
+
@@function_name_deprecation = Sass::Deprecation.new
|
4
|
+
|
3
5
|
class << self
|
4
6
|
# @param root [Tree::Node] The root node of the tree to visit.
|
5
7
|
# @param environment [Sass::Environment] The lexical environment.
|
@@ -11,7 +13,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
11
13
|
# @api private
|
12
14
|
# @comment
|
13
15
|
# rubocop:disable MethodLength
|
14
|
-
def perform_arguments(callable, args, splat)
|
16
|
+
def perform_arguments(callable, args, splat, environment)
|
15
17
|
desc = "#{callable.type.capitalize} #{callable.name}"
|
16
18
|
downcase_desc = "#{callable.type} #{callable.name}"
|
17
19
|
|
@@ -41,20 +43,26 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
41
43
|
# raising happens in the ensure clause at the end of this function.
|
42
44
|
return if keyword_exception && !callable.splat
|
43
45
|
|
44
|
-
if args.size > callable.args.size && !callable.splat
|
45
|
-
takes = callable.args.size
|
46
|
-
passed = args.size
|
47
|
-
raise Sass::SyntaxError.new(
|
48
|
-
"#{desc} takes #{takes} argument#{'s' unless takes == 1} " +
|
49
|
-
"but #{passed} #{passed == 1 ? 'was' : 'were'} passed.")
|
50
|
-
end
|
51
|
-
|
52
46
|
splat_sep = :comma
|
53
47
|
if splat
|
54
48
|
args += splat.to_a
|
55
49
|
splat_sep = splat.separator
|
56
50
|
end
|
57
51
|
|
52
|
+
if args.size > callable.args.size && !callable.splat
|
53
|
+
extra_args_because_of_splat = splat && args.size - splat.to_a.size <= callable.args.size
|
54
|
+
|
55
|
+
takes = callable.args.size
|
56
|
+
passed = args.size
|
57
|
+
message = "#{desc} takes #{takes} argument#{'s' unless takes == 1} " +
|
58
|
+
"but #{passed} #{passed == 1 ? 'was' : 'were'} passed."
|
59
|
+
raise Sass::SyntaxError.new(message) unless extra_args_because_of_splat
|
60
|
+
# TODO: when the deprecation period is over, make this an error.
|
61
|
+
Sass::Util.sass_warn("WARNING: #{message}\n" +
|
62
|
+
environment.stack.to_s.gsub(/^/m, " " * 8) + "\n" +
|
63
|
+
"This will be an error in future versions of Sass.")
|
64
|
+
end
|
65
|
+
|
58
66
|
env = Sass::Environment.new(callable.environment)
|
59
67
|
callable.args.zip(args[0...callable.args.length]) do |(var, default), value|
|
60
68
|
if value && keywords.has_key?(var.name)
|
@@ -144,6 +152,8 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
144
152
|
|
145
153
|
def initialize(env)
|
146
154
|
@environment = env
|
155
|
+
@in_keyframes = false
|
156
|
+
@at_root_without_rule = false
|
147
157
|
end
|
148
158
|
|
149
159
|
# If an exception is raised, this adds proper metadata to the backtrace.
|
@@ -222,7 +232,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
222
232
|
def visit_each(node)
|
223
233
|
list = node.list.perform(@environment)
|
224
234
|
|
225
|
-
with_environment Sass::
|
235
|
+
with_environment Sass::SemiGlobalEnvironment.new(@environment) do
|
226
236
|
list.to_a.map do |value|
|
227
237
|
if node.vars.length == 1
|
228
238
|
@environment.set_local_var(node.vars.first, value)
|
@@ -256,7 +266,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
256
266
|
direction = from.to_i > to.to_i ? -1 : 1
|
257
267
|
range = Range.new(direction * from.to_i, direction * to.to_i, node.exclusive)
|
258
268
|
|
259
|
-
with_environment Sass::
|
269
|
+
with_environment Sass::SemiGlobalEnvironment.new(@environment) do
|
260
270
|
range.map do |i|
|
261
271
|
@environment.set_local_var(node.var,
|
262
272
|
Sass::Script::Value::Number.new(direction * i,
|
@@ -269,9 +279,18 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
269
279
|
# Loads the function into the environment.
|
270
280
|
def visit_function(node)
|
271
281
|
env = Sass::Environment.new(@environment, node.options)
|
282
|
+
|
283
|
+
if node.normalized_name == 'calc' || node.normalized_name == 'element' ||
|
284
|
+
node.name == 'expression' || node.name == 'url'
|
285
|
+
@@function_name_deprecation.warn(node.filename, node.line, <<WARNING)
|
286
|
+
Naming a function "#{node.name}" is disallowed and will be an error in future versions of Sass.
|
287
|
+
This name conflicts with an existing CSS function with special parse rules.
|
288
|
+
WARNING
|
289
|
+
end
|
290
|
+
|
272
291
|
@environment.set_local_function(node.name,
|
273
292
|
Sass::Callable.new(node.name, node.args, node.splat, env,
|
274
|
-
node.children,
|
293
|
+
node.children, false, "function"))
|
275
294
|
[]
|
276
295
|
end
|
277
296
|
|
@@ -279,8 +298,9 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
279
298
|
# otherwise, tries the else nodes.
|
280
299
|
def visit_if(node)
|
281
300
|
if node.expr.nil? || node.expr.perform(@environment).to_bool
|
282
|
-
|
283
|
-
|
301
|
+
with_environment Sass::SemiGlobalEnvironment.new(@environment) do
|
302
|
+
node.children.map {|c| visit(c)}
|
303
|
+
end.flatten
|
284
304
|
elsif node.else
|
285
305
|
visit(node.else)
|
286
306
|
else
|
@@ -293,6 +313,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
293
313
|
def visit_import(node)
|
294
314
|
if (path = node.css_import?)
|
295
315
|
resolved_node = Sass::Tree::CssImportNode.resolved("url(#{path})")
|
316
|
+
resolved_node.options = node.options
|
296
317
|
resolved_node.source_range = node.source_range
|
297
318
|
return resolved_node
|
298
319
|
end
|
@@ -331,14 +352,14 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
331
352
|
raise Sass::SyntaxError.new("Undefined mixin '#{node.name}'.") unless mixin
|
332
353
|
|
333
354
|
if node.children.any? && !mixin.has_content
|
334
|
-
raise Sass::SyntaxError.new(%
|
355
|
+
raise Sass::SyntaxError.new(%(Mixin "#{node.name}" does not accept a content block.))
|
335
356
|
end
|
336
357
|
|
337
358
|
args = node.args.map {|a| a.perform(@environment)}
|
338
359
|
keywords = Sass::Util.map_vals(node.keywords) {|v| v.perform(@environment)}
|
339
360
|
splat = self.class.perform_splat(node.splat, keywords, node.kwarg_splat, @environment)
|
340
361
|
|
341
|
-
self.class.perform_arguments(mixin, args, splat) do |env|
|
362
|
+
self.class.perform_arguments(mixin, args, splat, @environment) do |env|
|
342
363
|
env.caller = Sass::Environment.new(@environment)
|
343
364
|
env.content = [node.children, @environment] if node.has_children
|
344
365
|
|
@@ -397,6 +418,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
397
418
|
keyframe_rule_node.line = node.line
|
398
419
|
keyframe_rule_node.filename = node.filename
|
399
420
|
keyframe_rule_node.source_range = node.source_range
|
421
|
+
keyframe_rule_node.has_children = node.has_children
|
400
422
|
with_environment Sass::Environment.new(@environment, node.options) do
|
401
423
|
keyframe_rule_node.children = node.children.map {|c| visit(c)}.flatten
|
402
424
|
end
|
@@ -470,7 +492,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
470
492
|
# Runs the child nodes until the continuation expression becomes false.
|
471
493
|
def visit_while(node)
|
472
494
|
children = []
|
473
|
-
with_environment Sass::
|
495
|
+
with_environment Sass::SemiGlobalEnvironment.new(@environment) do
|
474
496
|
children += node.children.map {|c| visit(c)} while node.expr.perform(@environment).to_bool
|
475
497
|
end
|
476
498
|
children.flatten
|
@@ -507,6 +529,9 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
|
|
507
529
|
node.filename, node.options[:importer], node.line)
|
508
530
|
node.resolved_query ||= parser.parse_media_query_list
|
509
531
|
end
|
532
|
+
if node.supports_condition
|
533
|
+
node.supports_condition.perform(@environment)
|
534
|
+
end
|
510
535
|
yield
|
511
536
|
end
|
512
537
|
|
@@ -80,7 +80,7 @@ class Sass::Tree::Visitors::SetOptions < Sass::Tree::Visitors::Base
|
|
80
80
|
|
81
81
|
def visit_mixin(node)
|
82
82
|
node.args.each {|a| a.options = @options}
|
83
|
-
node.keywords.each {|
|
83
|
+
node.keywords.each {|_k, v| v.options = @options}
|
84
84
|
node.splat.options = @options if node.splat
|
85
85
|
node.kwarg_splat.options = @options if node.kwarg_splat
|
86
86
|
yield
|