sass4 4.0.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 +7 -0
- data/.yardopts +13 -0
- data/AGENTS.md +534 -0
- data/CODE_OF_CONDUCT.md +10 -0
- data/CONTRIBUTING.md +148 -0
- data/MIT-LICENSE +20 -0
- data/README.md +242 -0
- data/VERSION +1 -0
- data/VERSION_NAME +1 -0
- data/bin/sass +13 -0
- data/bin/sass-convert +12 -0
- data/bin/scss +13 -0
- data/extra/sass-spec-ref.sh +40 -0
- data/extra/update_watch.rb +13 -0
- data/init.rb +18 -0
- data/lib/sass/cache_stores/base.rb +88 -0
- data/lib/sass/cache_stores/chain.rb +34 -0
- data/lib/sass/cache_stores/filesystem.rb +60 -0
- data/lib/sass/cache_stores/memory.rb +46 -0
- data/lib/sass/cache_stores/null.rb +25 -0
- data/lib/sass/cache_stores.rb +15 -0
- data/lib/sass/callbacks.rb +67 -0
- data/lib/sass/css.rb +407 -0
- data/lib/sass/deprecation.rb +55 -0
- data/lib/sass/engine.rb +1236 -0
- data/lib/sass/environment.rb +236 -0
- data/lib/sass/error.rb +198 -0
- data/lib/sass/exec/base.rb +188 -0
- data/lib/sass/exec/sass_convert.rb +283 -0
- data/lib/sass/exec/sass_scss.rb +436 -0
- data/lib/sass/exec.rb +9 -0
- data/lib/sass/features.rb +48 -0
- data/lib/sass/importers/base.rb +182 -0
- data/lib/sass/importers/deprecated_path.rb +51 -0
- data/lib/sass/importers/filesystem.rb +221 -0
- data/lib/sass/importers.rb +23 -0
- data/lib/sass/logger/base.rb +47 -0
- data/lib/sass/logger/delayed.rb +50 -0
- data/lib/sass/logger/log_level.rb +45 -0
- data/lib/sass/logger.rb +17 -0
- data/lib/sass/media.rb +210 -0
- data/lib/sass/plugin/compiler.rb +552 -0
- data/lib/sass/plugin/configuration.rb +134 -0
- data/lib/sass/plugin/generic.rb +15 -0
- data/lib/sass/plugin/merb.rb +48 -0
- data/lib/sass/plugin/rack.rb +60 -0
- data/lib/sass/plugin/rails.rb +47 -0
- data/lib/sass/plugin/staleness_checker.rb +199 -0
- data/lib/sass/plugin.rb +134 -0
- data/lib/sass/railtie.rb +10 -0
- data/lib/sass/repl.rb +57 -0
- data/lib/sass/root.rb +7 -0
- data/lib/sass/script/css_lexer.rb +33 -0
- data/lib/sass/script/css_parser.rb +36 -0
- data/lib/sass/script/functions.rb +3103 -0
- data/lib/sass/script/lexer.rb +518 -0
- data/lib/sass/script/parser.rb +1164 -0
- data/lib/sass/script/tree/funcall.rb +314 -0
- data/lib/sass/script/tree/interpolation.rb +220 -0
- data/lib/sass/script/tree/list_literal.rb +119 -0
- data/lib/sass/script/tree/literal.rb +49 -0
- data/lib/sass/script/tree/map_literal.rb +64 -0
- data/lib/sass/script/tree/node.rb +119 -0
- data/lib/sass/script/tree/operation.rb +149 -0
- data/lib/sass/script/tree/selector.rb +26 -0
- data/lib/sass/script/tree/string_interpolation.rb +125 -0
- data/lib/sass/script/tree/unary_operation.rb +69 -0
- data/lib/sass/script/tree/variable.rb +57 -0
- data/lib/sass/script/tree.rb +16 -0
- data/lib/sass/script/value/arg_list.rb +36 -0
- data/lib/sass/script/value/base.rb +258 -0
- data/lib/sass/script/value/bool.rb +35 -0
- data/lib/sass/script/value/callable.rb +25 -0
- data/lib/sass/script/value/color.rb +704 -0
- data/lib/sass/script/value/function.rb +19 -0
- data/lib/sass/script/value/helpers.rb +298 -0
- data/lib/sass/script/value/list.rb +135 -0
- data/lib/sass/script/value/map.rb +70 -0
- data/lib/sass/script/value/null.rb +44 -0
- data/lib/sass/script/value/number.rb +564 -0
- data/lib/sass/script/value/string.rb +138 -0
- data/lib/sass/script/value.rb +13 -0
- data/lib/sass/script.rb +66 -0
- data/lib/sass/scss/css_parser.rb +61 -0
- data/lib/sass/scss/parser.rb +1343 -0
- data/lib/sass/scss/rx.rb +134 -0
- data/lib/sass/scss/static_parser.rb +351 -0
- data/lib/sass/scss.rb +14 -0
- data/lib/sass/selector/abstract_sequence.rb +112 -0
- data/lib/sass/selector/comma_sequence.rb +195 -0
- data/lib/sass/selector/pseudo.rb +291 -0
- data/lib/sass/selector/sequence.rb +661 -0
- data/lib/sass/selector/simple.rb +124 -0
- data/lib/sass/selector/simple_sequence.rb +348 -0
- data/lib/sass/selector.rb +327 -0
- data/lib/sass/shared.rb +76 -0
- data/lib/sass/source/map.rb +209 -0
- data/lib/sass/source/position.rb +39 -0
- data/lib/sass/source/range.rb +41 -0
- data/lib/sass/stack.rb +140 -0
- data/lib/sass/supports.rb +225 -0
- data/lib/sass/tree/at_root_node.rb +83 -0
- data/lib/sass/tree/charset_node.rb +22 -0
- data/lib/sass/tree/comment_node.rb +82 -0
- data/lib/sass/tree/content_node.rb +9 -0
- data/lib/sass/tree/css_import_node.rb +68 -0
- data/lib/sass/tree/debug_node.rb +18 -0
- data/lib/sass/tree/directive_node.rb +59 -0
- data/lib/sass/tree/each_node.rb +24 -0
- data/lib/sass/tree/error_node.rb +18 -0
- data/lib/sass/tree/extend_node.rb +43 -0
- data/lib/sass/tree/for_node.rb +36 -0
- data/lib/sass/tree/function_node.rb +44 -0
- data/lib/sass/tree/if_node.rb +52 -0
- data/lib/sass/tree/import_node.rb +75 -0
- data/lib/sass/tree/keyframe_rule_node.rb +15 -0
- data/lib/sass/tree/media_node.rb +48 -0
- data/lib/sass/tree/mixin_def_node.rb +38 -0
- data/lib/sass/tree/mixin_node.rb +52 -0
- data/lib/sass/tree/node.rb +240 -0
- data/lib/sass/tree/prop_node.rb +162 -0
- data/lib/sass/tree/return_node.rb +19 -0
- data/lib/sass/tree/root_node.rb +44 -0
- data/lib/sass/tree/rule_node.rb +153 -0
- data/lib/sass/tree/supports_node.rb +38 -0
- data/lib/sass/tree/trace_node.rb +33 -0
- data/lib/sass/tree/variable_node.rb +36 -0
- data/lib/sass/tree/visitors/base.rb +72 -0
- data/lib/sass/tree/visitors/check_nesting.rb +173 -0
- data/lib/sass/tree/visitors/convert.rb +350 -0
- data/lib/sass/tree/visitors/cssize.rb +362 -0
- data/lib/sass/tree/visitors/deep_copy.rb +107 -0
- data/lib/sass/tree/visitors/extend.rb +64 -0
- data/lib/sass/tree/visitors/perform.rb +572 -0
- data/lib/sass/tree/visitors/set_options.rb +139 -0
- data/lib/sass/tree/visitors/to_css.rb +440 -0
- data/lib/sass/tree/warn_node.rb +18 -0
- data/lib/sass/tree/while_node.rb +18 -0
- data/lib/sass/util/multibyte_string_scanner.rb +151 -0
- data/lib/sass/util/normalized_map.rb +122 -0
- data/lib/sass/util/subset_map.rb +109 -0
- data/lib/sass/util/test.rb +9 -0
- data/lib/sass/util.rb +1137 -0
- data/lib/sass/version.rb +120 -0
- data/lib/sass.rb +102 -0
- data/rails/init.rb +1 -0
- metadata +283 -0
@@ -0,0 +1,64 @@
|
|
1
|
+
module Sass::Script::Tree
|
2
|
+
# A class representing a map literal. When resolved, this returns a
|
3
|
+
# {Sass::Script::Node::Map}.
|
4
|
+
class MapLiteral < Node
|
5
|
+
# The key/value pairs that make up this map node. This isn't a Hash so that
|
6
|
+
# we can detect key collisions once all the keys have been performed.
|
7
|
+
#
|
8
|
+
# @return [Array<(Node, Node)>]
|
9
|
+
attr_reader :pairs
|
10
|
+
|
11
|
+
# Creates a new map literal.
|
12
|
+
#
|
13
|
+
# @param pairs [Array<(Node, Node)>] See \{#pairs}
|
14
|
+
def initialize(pairs)
|
15
|
+
@pairs = pairs
|
16
|
+
end
|
17
|
+
|
18
|
+
# @see Node#children
|
19
|
+
def children
|
20
|
+
@pairs.flatten
|
21
|
+
end
|
22
|
+
|
23
|
+
# @see Node#to_sass
|
24
|
+
def to_sass(opts = {})
|
25
|
+
return "()" if pairs.empty?
|
26
|
+
|
27
|
+
to_sass = lambda do |value|
|
28
|
+
if value.is_a?(ListLiteral) && value.separator == :comma
|
29
|
+
"(#{value.to_sass(opts)})"
|
30
|
+
else
|
31
|
+
value.to_sass(opts)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
"(" + pairs.map {|(k, v)| "#{to_sass[k]}: #{to_sass[v]}"}.join(', ') + ")"
|
36
|
+
end
|
37
|
+
alias_method :inspect, :to_sass
|
38
|
+
|
39
|
+
# @see Node#deep_copy
|
40
|
+
def deep_copy
|
41
|
+
node = dup
|
42
|
+
node.instance_variable_set('@pairs',
|
43
|
+
pairs.map {|(k, v)| [k.deep_copy, v.deep_copy]})
|
44
|
+
node
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
# @see Node#_perform
|
50
|
+
def _perform(environment)
|
51
|
+
keys = Set.new
|
52
|
+
map = Sass::Script::Value::Map.new(Hash[pairs.map do |(k, v)|
|
53
|
+
k, v = k.perform(environment), v.perform(environment)
|
54
|
+
if keys.include?(k)
|
55
|
+
raise Sass::SyntaxError.new("Duplicate key #{k.inspect} in map #{to_sass}.")
|
56
|
+
end
|
57
|
+
keys << k
|
58
|
+
[k, v]
|
59
|
+
end])
|
60
|
+
map.options = options
|
61
|
+
map
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module Sass::Script::Tree
|
2
|
+
# The abstract superclass for SassScript parse tree nodes.
|
3
|
+
#
|
4
|
+
# Use \{#perform} to evaluate a parse tree.
|
5
|
+
class Node
|
6
|
+
# The options hash for this node.
|
7
|
+
#
|
8
|
+
# @return [{Symbol => Object}]
|
9
|
+
attr_reader :options
|
10
|
+
|
11
|
+
# The line of the document on which this node appeared.
|
12
|
+
#
|
13
|
+
# @return [Integer]
|
14
|
+
attr_accessor :line
|
15
|
+
|
16
|
+
# The source range in the document on which this node appeared.
|
17
|
+
#
|
18
|
+
# @return [Sass::Source::Range]
|
19
|
+
attr_accessor :source_range
|
20
|
+
|
21
|
+
# The file name of the document on which this node appeared.
|
22
|
+
#
|
23
|
+
# @return [String]
|
24
|
+
attr_accessor :filename
|
25
|
+
|
26
|
+
# Sets the options hash for this node,
|
27
|
+
# as well as for all child nodes.
|
28
|
+
# See {file:SASS_REFERENCE.md#Options the Sass options documentation}.
|
29
|
+
#
|
30
|
+
# @param options [{Symbol => Object}] The options
|
31
|
+
def options=(options)
|
32
|
+
@options = options
|
33
|
+
children.each do |c|
|
34
|
+
if c.is_a? Hash
|
35
|
+
c.values.each {|v| v.options = options}
|
36
|
+
else
|
37
|
+
c.options = options
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Evaluates the node.
|
43
|
+
#
|
44
|
+
# \{#perform} shouldn't be overridden directly;
|
45
|
+
# instead, override \{#\_perform}.
|
46
|
+
#
|
47
|
+
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
48
|
+
# @return [Sass::Script::Value] The SassScript object that is the value of the SassScript
|
49
|
+
def perform(environment)
|
50
|
+
_perform(environment)
|
51
|
+
rescue Sass::SyntaxError => e
|
52
|
+
e.modify_backtrace(:line => line)
|
53
|
+
raise e
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns all child nodes of this node.
|
57
|
+
#
|
58
|
+
# @return [Array<Node>]
|
59
|
+
def children
|
60
|
+
Sass::Util.abstract(self)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns the text of this SassScript expression.
|
64
|
+
#
|
65
|
+
# @options opts :quote [String]
|
66
|
+
# The preferred quote style for quoted strings. If `:none`, strings are
|
67
|
+
# always emitted unquoted.
|
68
|
+
#
|
69
|
+
# @return [String]
|
70
|
+
def to_sass(opts = {})
|
71
|
+
Sass::Util.abstract(self)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns a deep clone of this node.
|
75
|
+
# The child nodes are cloned, but options are not.
|
76
|
+
#
|
77
|
+
# @return [Node]
|
78
|
+
def deep_copy
|
79
|
+
Sass::Util.abstract(self)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Forces any division operations with number literals in this expression to
|
83
|
+
# do real division, rather than returning strings.
|
84
|
+
def force_division!
|
85
|
+
children.each {|c| c.force_division!}
|
86
|
+
end
|
87
|
+
|
88
|
+
protected
|
89
|
+
|
90
|
+
# Converts underscores to dashes if the :dasherize option is set.
|
91
|
+
def dasherize(s, opts)
|
92
|
+
if opts[:dasherize]
|
93
|
+
s.tr('_', '-')
|
94
|
+
else
|
95
|
+
s
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Evaluates this node.
|
100
|
+
# Note that all {Sass::Script::Value} objects created within this method
|
101
|
+
# should have their \{#options} attribute set, probably via \{#opts}.
|
102
|
+
#
|
103
|
+
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
104
|
+
# @return [Sass::Script::Value] The SassScript object that is the value of the SassScript
|
105
|
+
# @see #perform
|
106
|
+
def _perform(environment)
|
107
|
+
Sass::Util.abstract(self)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Sets the \{#options} field on the given value and returns it.
|
111
|
+
#
|
112
|
+
# @param value [Sass::Script::Value]
|
113
|
+
# @return [Sass::Script::Value]
|
114
|
+
def opts(value)
|
115
|
+
value.options = options
|
116
|
+
value
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
module Sass::Script::Tree
|
2
|
+
# A SassScript parse node representing a binary operation,
|
3
|
+
# such as `$a + $b` or `"foo" + 1`.
|
4
|
+
class Operation < Node
|
5
|
+
@@color_arithmetic_deprecation = Sass::Deprecation.new
|
6
|
+
@@unitless_equals_deprecation = Sass::Deprecation.new
|
7
|
+
|
8
|
+
attr_reader :operand1
|
9
|
+
attr_reader :operand2
|
10
|
+
attr_reader :operator
|
11
|
+
|
12
|
+
# @param operand1 [Sass::Script::Tree::Node] The parse-tree node
|
13
|
+
# for the right-hand side of the operator
|
14
|
+
# @param operand2 [Sass::Script::Tree::Node] The parse-tree node
|
15
|
+
# for the left-hand side of the operator
|
16
|
+
# @param operator [Symbol] The operator to perform.
|
17
|
+
# This should be one of the binary operator names in {Sass::Script::Lexer::OPERATORS}
|
18
|
+
def initialize(operand1, operand2, operator)
|
19
|
+
@operand1 = operand1
|
20
|
+
@operand2 = operand2
|
21
|
+
@operator = operator
|
22
|
+
super()
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [String] A human-readable s-expression representation of the operation
|
26
|
+
def inspect
|
27
|
+
"(#{@operator.inspect} #{@operand1.inspect} #{@operand2.inspect})"
|
28
|
+
end
|
29
|
+
|
30
|
+
# @see Node#to_sass
|
31
|
+
def to_sass(opts = {})
|
32
|
+
o1 = operand_to_sass @operand1, :left, opts
|
33
|
+
o2 = operand_to_sass @operand2, :right, opts
|
34
|
+
sep =
|
35
|
+
case @operator
|
36
|
+
when :comma; ", "
|
37
|
+
when :space; " "
|
38
|
+
else; " #{Sass::Script::Lexer::OPERATORS_REVERSE[@operator]} "
|
39
|
+
end
|
40
|
+
"#{o1}#{sep}#{o2}"
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns the operands for this operation.
|
44
|
+
#
|
45
|
+
# @return [Array<Node>]
|
46
|
+
# @see Node#children
|
47
|
+
def children
|
48
|
+
[@operand1, @operand2]
|
49
|
+
end
|
50
|
+
|
51
|
+
# @see Node#deep_copy
|
52
|
+
def deep_copy
|
53
|
+
node = dup
|
54
|
+
node.instance_variable_set('@operand1', @operand1.deep_copy)
|
55
|
+
node.instance_variable_set('@operand2', @operand2.deep_copy)
|
56
|
+
node
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
|
61
|
+
# Evaluates the operation.
|
62
|
+
#
|
63
|
+
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
64
|
+
# @return [Sass::Script::Value] The SassScript object that is the value of the operation
|
65
|
+
# @raise [Sass::SyntaxError] if the operation is undefined for the operands
|
66
|
+
def _perform(environment)
|
67
|
+
value1 = @operand1.perform(environment)
|
68
|
+
|
69
|
+
# Special-case :and and :or to support short-circuiting.
|
70
|
+
if @operator == :and
|
71
|
+
return value1.to_bool ? @operand2.perform(environment) : value1
|
72
|
+
elsif @operator == :or
|
73
|
+
return value1.to_bool ? value1 : @operand2.perform(environment)
|
74
|
+
end
|
75
|
+
|
76
|
+
value2 = @operand2.perform(environment)
|
77
|
+
|
78
|
+
if (value1.is_a?(Sass::Script::Value::Null) || value2.is_a?(Sass::Script::Value::Null)) &&
|
79
|
+
@operator != :eq && @operator != :neq
|
80
|
+
raise Sass::SyntaxError.new(
|
81
|
+
"Invalid null operation: \"#{value1.inspect} #{@operator} #{value2.inspect}\".")
|
82
|
+
end
|
83
|
+
|
84
|
+
begin
|
85
|
+
result = opts(value1.send(@operator, value2))
|
86
|
+
rescue NoMethodError => e
|
87
|
+
raise e unless e.name.to_s == @operator.to_s
|
88
|
+
raise Sass::SyntaxError.new("Undefined operation: \"#{value1} #{@operator} #{value2}\".")
|
89
|
+
end
|
90
|
+
|
91
|
+
warn_for_color_arithmetic(value1, value2)
|
92
|
+
warn_for_unitless_equals(value1, value2, result)
|
93
|
+
|
94
|
+
result
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def warn_for_color_arithmetic(value1, value2)
|
100
|
+
return unless @operator == :plus || @operator == :times || @operator == :minus ||
|
101
|
+
@operator == :div || @operator == :mod
|
102
|
+
|
103
|
+
if value1.is_a?(Sass::Script::Value::Number)
|
104
|
+
return unless value2.is_a?(Sass::Script::Value::Color)
|
105
|
+
elsif value1.is_a?(Sass::Script::Value::Color)
|
106
|
+
return unless value2.is_a?(Sass::Script::Value::Color) || value2.is_a?(Sass::Script::Value::Number)
|
107
|
+
else
|
108
|
+
return
|
109
|
+
end
|
110
|
+
|
111
|
+
@@color_arithmetic_deprecation.warn(filename, line, <<WARNING)
|
112
|
+
The operation `#{value1} #{@operator} #{value2}` is deprecated and will be an error in future versions.
|
113
|
+
Consider using Sass's color functions instead.
|
114
|
+
https://sass-lang.com/documentation/Sass/Script/Functions.html#other_color_functions
|
115
|
+
WARNING
|
116
|
+
end
|
117
|
+
|
118
|
+
def warn_for_unitless_equals(value1, value2, result)
|
119
|
+
return unless @operator == :eq || @operator == :neq
|
120
|
+
return unless value1.is_a?(Sass::Script::Value::Number)
|
121
|
+
return unless value2.is_a?(Sass::Script::Value::Number)
|
122
|
+
return unless value1.unitless? != value2.unitless?
|
123
|
+
return unless result == (if @operator == :eq
|
124
|
+
Sass::Script::Value::Bool::TRUE
|
125
|
+
else
|
126
|
+
Sass::Script::Value::Bool::FALSE
|
127
|
+
end)
|
128
|
+
|
129
|
+
operation = "#{value1.to_sass} #{@operator == :eq ? '==' : '!='} #{value2.to_sass}"
|
130
|
+
future_value = @operator == :neq
|
131
|
+
@@unitless_equals_deprecation.warn(filename, line, <<WARNING)
|
132
|
+
The result of `#{operation}` will be `#{future_value}` in future releases of Sass.
|
133
|
+
Unitless numbers will no longer be equal to the same numbers with units.
|
134
|
+
WARNING
|
135
|
+
end
|
136
|
+
|
137
|
+
def operand_to_sass(op, side, opts)
|
138
|
+
return "(#{op.to_sass(opts)})" if op.is_a?(Sass::Script::Tree::ListLiteral)
|
139
|
+
return op.to_sass(opts) unless op.is_a?(Operation)
|
140
|
+
|
141
|
+
pred = Sass::Script::Parser.precedence_of(@operator)
|
142
|
+
sub_pred = Sass::Script::Parser.precedence_of(op.operator)
|
143
|
+
assoc = Sass::Script::Parser.associative?(@operator)
|
144
|
+
return "(#{op.to_sass(opts)})" if sub_pred < pred ||
|
145
|
+
(side == :right && sub_pred == pred && !assoc)
|
146
|
+
op.to_sass(opts)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Sass::Script::Tree
|
2
|
+
# A SassScript node that will resolve to the current selector.
|
3
|
+
class Selector < Node
|
4
|
+
def initialize; end
|
5
|
+
|
6
|
+
def children
|
7
|
+
[]
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_sass(opts = {})
|
11
|
+
'&'
|
12
|
+
end
|
13
|
+
|
14
|
+
def deep_copy
|
15
|
+
dup
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def _perform(environment)
|
21
|
+
selector = environment.selector
|
22
|
+
return opts(Sass::Script::Value::Null.new) unless selector
|
23
|
+
opts(selector.to_sass_script)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
module Sass::Script::Tree
|
2
|
+
# A SassScript object representing `#{}` interpolation within a string.
|
3
|
+
#
|
4
|
+
# @see Interpolation
|
5
|
+
class StringInterpolation < Node
|
6
|
+
# @return [Literal] The string literal before this interpolation.
|
7
|
+
attr_reader :before
|
8
|
+
|
9
|
+
# @return [Node] The SassScript within the interpolation
|
10
|
+
attr_reader :mid
|
11
|
+
|
12
|
+
# @return [StringInterpolation, Literal]
|
13
|
+
# The string literal or string interpolation before this interpolation.
|
14
|
+
attr_reader :after
|
15
|
+
|
16
|
+
# Whether this is a CSS string or a CSS identifier. The difference is that
|
17
|
+
# strings are written with double-quotes, while identifiers aren't.
|
18
|
+
#
|
19
|
+
# String interpolations are only ever identifiers if they're quote-like
|
20
|
+
# functions such as `url()`.
|
21
|
+
#
|
22
|
+
# @return [Symbol] `:string` or `:identifier`
|
23
|
+
def type
|
24
|
+
@before.value.type
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns the quote character that should be used to wrap a Sass
|
28
|
+
# representation of this interpolation.
|
29
|
+
def quote
|
30
|
+
quote_for(self) || '"'
|
31
|
+
end
|
32
|
+
|
33
|
+
# Interpolation in a string is of the form `"before #{mid} after"`,
|
34
|
+
# where `before` and `after` may include more interpolation.
|
35
|
+
#
|
36
|
+
# @param before [StringInterpolation, Literal] See {StringInterpolation#before}
|
37
|
+
# @param mid [Node] See {StringInterpolation#mid}
|
38
|
+
# @param after [Literal] See {StringInterpolation#after}
|
39
|
+
def initialize(before, mid, after)
|
40
|
+
@before = before
|
41
|
+
@mid = mid
|
42
|
+
@after = after
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [String] A human-readable s-expression representation of the interpolation
|
46
|
+
def inspect
|
47
|
+
"(string_interpolation #{@before.inspect} #{@mid.inspect} #{@after.inspect})"
|
48
|
+
end
|
49
|
+
|
50
|
+
# @see Node#to_sass
|
51
|
+
def to_sass(opts = {})
|
52
|
+
quote = type == :string ? opts[:quote] || quote_for(self) || '"' : :none
|
53
|
+
opts = opts.merge(:quote => quote)
|
54
|
+
|
55
|
+
res = ""
|
56
|
+
res << quote if quote != :none
|
57
|
+
res << _to_sass(before, opts)
|
58
|
+
res << '#{' << @mid.to_sass(opts.merge(:quote => nil)) << '}'
|
59
|
+
res << _to_sass(after, opts)
|
60
|
+
res << quote if quote != :none
|
61
|
+
res
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns the three components of the interpolation, `before`, `mid`, and `after`.
|
65
|
+
#
|
66
|
+
# @return [Array<Node>]
|
67
|
+
# @see #initialize
|
68
|
+
# @see Node#children
|
69
|
+
def children
|
70
|
+
[@before, @mid, @after].compact
|
71
|
+
end
|
72
|
+
|
73
|
+
# @see Node#deep_copy
|
74
|
+
def deep_copy
|
75
|
+
node = dup
|
76
|
+
node.instance_variable_set('@before', @before.deep_copy) if @before
|
77
|
+
node.instance_variable_set('@mid', @mid.deep_copy)
|
78
|
+
node.instance_variable_set('@after', @after.deep_copy) if @after
|
79
|
+
node
|
80
|
+
end
|
81
|
+
|
82
|
+
protected
|
83
|
+
|
84
|
+
# Evaluates the interpolation.
|
85
|
+
#
|
86
|
+
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
87
|
+
# @return [Sass::Script::Value::String]
|
88
|
+
# The SassScript string that is the value of the interpolation
|
89
|
+
def _perform(environment)
|
90
|
+
res = ""
|
91
|
+
before = @before.perform(environment)
|
92
|
+
res << before.value
|
93
|
+
mid = @mid.perform(environment)
|
94
|
+
res << (mid.is_a?(Sass::Script::Value::String) ? mid.value : mid.to_s(:quote => :none))
|
95
|
+
res << @after.perform(environment).value
|
96
|
+
opts(Sass::Script::Value::String.new(res, before.type))
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def _to_sass(string_or_interp, opts)
|
102
|
+
result = string_or_interp.to_sass(opts)
|
103
|
+
opts[:quote] == :none ? result : result.slice(1...-1)
|
104
|
+
end
|
105
|
+
|
106
|
+
def quote_for(string_or_interp)
|
107
|
+
if string_or_interp.is_a?(Sass::Script::Tree::Literal)
|
108
|
+
return nil if string_or_interp.value.value.empty?
|
109
|
+
return '"' if string_or_interp.value.value.include?("'")
|
110
|
+
return "'" if string_or_interp.value.value.include?('"')
|
111
|
+
return nil
|
112
|
+
end
|
113
|
+
|
114
|
+
# Double-quotes take precedence over single quotes.
|
115
|
+
before_quote = quote_for(string_or_interp.before)
|
116
|
+
return '"' if before_quote == '"'
|
117
|
+
after_quote = quote_for(string_or_interp.after)
|
118
|
+
return '"' if after_quote == '"'
|
119
|
+
|
120
|
+
# Returns "'" if either or both insist on single quotes, and nil
|
121
|
+
# otherwise.
|
122
|
+
before_quote || after_quote
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Sass::Script::Tree
|
2
|
+
# A SassScript parse node representing a unary operation,
|
3
|
+
# such as `-$b` or `not true`.
|
4
|
+
#
|
5
|
+
# Currently only `-`, `/`, and `not` are unary operators.
|
6
|
+
class UnaryOperation < Node
|
7
|
+
# @return [Symbol] The operation to perform
|
8
|
+
attr_reader :operator
|
9
|
+
|
10
|
+
# @return [Script::Node] The parse-tree node for the object of the operator
|
11
|
+
attr_reader :operand
|
12
|
+
|
13
|
+
# @param operand [Script::Node] See \{#operand}
|
14
|
+
# @param operator [Symbol] See \{#operator}
|
15
|
+
def initialize(operand, operator)
|
16
|
+
@operand = operand
|
17
|
+
@operator = operator
|
18
|
+
super()
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [String] A human-readable s-expression representation of the operation
|
22
|
+
def inspect
|
23
|
+
"(#{@operator.inspect} #{@operand.inspect})"
|
24
|
+
end
|
25
|
+
|
26
|
+
# @see Node#to_sass
|
27
|
+
def to_sass(opts = {})
|
28
|
+
operand = @operand.to_sass(opts)
|
29
|
+
if @operand.is_a?(Operation) ||
|
30
|
+
(@operator == :minus &&
|
31
|
+
(operand =~ Sass::SCSS::RX::IDENT) == 0)
|
32
|
+
operand = "(#{@operand.to_sass(opts)})"
|
33
|
+
end
|
34
|
+
op = Sass::Script::Lexer::OPERATORS_REVERSE[@operator]
|
35
|
+
op + (op =~ /[a-z]/ ? " " : "") + operand
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns the operand of the operation.
|
39
|
+
#
|
40
|
+
# @return [Array<Node>]
|
41
|
+
# @see Node#children
|
42
|
+
def children
|
43
|
+
[@operand]
|
44
|
+
end
|
45
|
+
|
46
|
+
# @see Node#deep_copy
|
47
|
+
def deep_copy
|
48
|
+
node = dup
|
49
|
+
node.instance_variable_set('@operand', @operand.deep_copy)
|
50
|
+
node
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
# Evaluates the operation.
|
56
|
+
#
|
57
|
+
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
58
|
+
# @return [Sass::Script::Value] The SassScript object that is the value of the operation
|
59
|
+
# @raise [Sass::SyntaxError] if the operation is undefined for the operand
|
60
|
+
def _perform(environment)
|
61
|
+
operator = "unary_#{@operator}"
|
62
|
+
value = @operand.perform(environment)
|
63
|
+
value.send(operator)
|
64
|
+
rescue NoMethodError => e
|
65
|
+
raise e unless e.name.to_s == operator.to_s
|
66
|
+
raise Sass::SyntaxError.new("Undefined unary operation: \"#{@operator} #{value}\".")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Sass::Script::Tree
|
2
|
+
# A SassScript parse node representing a variable.
|
3
|
+
class Variable < Node
|
4
|
+
# The name of the variable.
|
5
|
+
#
|
6
|
+
# @return [String]
|
7
|
+
attr_reader :name
|
8
|
+
|
9
|
+
# The underscored name of the variable.
|
10
|
+
#
|
11
|
+
# @return [String]
|
12
|
+
attr_reader :underscored_name
|
13
|
+
|
14
|
+
# @param name [String] See \{#name}
|
15
|
+
def initialize(name)
|
16
|
+
@name = name
|
17
|
+
@underscored_name = name.tr("-", "_")
|
18
|
+
super()
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [String] A string representation of the variable
|
22
|
+
def inspect(opts = {})
|
23
|
+
"$#{dasherize(name, opts)}"
|
24
|
+
end
|
25
|
+
alias_method :to_sass, :inspect
|
26
|
+
|
27
|
+
# Returns an empty array.
|
28
|
+
#
|
29
|
+
# @return [Array<Node>] empty
|
30
|
+
# @see Node#children
|
31
|
+
def children
|
32
|
+
[]
|
33
|
+
end
|
34
|
+
|
35
|
+
# @see Node#deep_copy
|
36
|
+
def deep_copy
|
37
|
+
dup
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
# Evaluates the variable.
|
43
|
+
#
|
44
|
+
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
45
|
+
# @return [Sass::Script::Value] The SassScript object that is the value of the variable
|
46
|
+
# @raise [Sass::SyntaxError] if the variable is undefined
|
47
|
+
def _perform(environment)
|
48
|
+
val = environment.var(name)
|
49
|
+
raise Sass::SyntaxError.new("Undefined variable: \"$#{name}\".") unless val
|
50
|
+
if val.is_a?(Sass::Script::Value::Number) && val.original
|
51
|
+
val = val.dup
|
52
|
+
val.original = nil
|
53
|
+
end
|
54
|
+
val
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# The module containing nodes in the SassScript parse tree. These nodes are
|
2
|
+
# all subclasses of {Sass::Script::Tree::Node}.
|
3
|
+
module Sass::Script::Tree
|
4
|
+
end
|
5
|
+
|
6
|
+
require 'sass/script/tree/node'
|
7
|
+
require 'sass/script/tree/variable'
|
8
|
+
require 'sass/script/tree/funcall'
|
9
|
+
require 'sass/script/tree/operation'
|
10
|
+
require 'sass/script/tree/unary_operation'
|
11
|
+
require 'sass/script/tree/interpolation'
|
12
|
+
require 'sass/script/tree/string_interpolation'
|
13
|
+
require 'sass/script/tree/literal'
|
14
|
+
require 'sass/script/tree/list_literal'
|
15
|
+
require 'sass/script/tree/map_literal'
|
16
|
+
require 'sass/script/tree/selector'
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Sass::Script::Value
|
2
|
+
# A SassScript object representing a variable argument list. This works just
|
3
|
+
# like a normal list, but can also contain keyword arguments.
|
4
|
+
#
|
5
|
+
# The keyword arguments attached to this list are unused except when this is
|
6
|
+
# passed as a glob argument to a function or mixin.
|
7
|
+
class ArgList < List
|
8
|
+
# Whether \{#keywords} has been accessed. If so, we assume that all keywords
|
9
|
+
# were valid for the function that created this ArgList.
|
10
|
+
#
|
11
|
+
# @return [Boolean]
|
12
|
+
attr_accessor :keywords_accessed
|
13
|
+
|
14
|
+
# Creates a new argument list.
|
15
|
+
#
|
16
|
+
# @param value [Array<Value>] See \{List#value}.
|
17
|
+
# @param keywords [Hash<String, Value>, NormalizedMap<Value>] See \{#keywords}
|
18
|
+
# @param separator [String] See \{List#separator}.
|
19
|
+
def initialize(value, keywords, separator)
|
20
|
+
super(value, separator: separator)
|
21
|
+
if keywords.is_a?(Sass::Util::NormalizedMap)
|
22
|
+
@keywords = keywords
|
23
|
+
else
|
24
|
+
@keywords = Sass::Util::NormalizedMap.new(keywords)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# The keyword arguments attached to this list.
|
29
|
+
#
|
30
|
+
# @return [NormalizedMap<Value>]
|
31
|
+
def keywords
|
32
|
+
@keywords_accessed = true
|
33
|
+
@keywords
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|