sass-embedded 0.13.4 → 0.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/ext/sass/extconf.rb +52 -1
  3. data/lib/sass/compile_error.rb +1 -10
  4. data/lib/sass/compile_result.rb +1 -5
  5. data/lib/sass/embedded/channel.rb +2 -2
  6. data/lib/sass/embedded/compile_context/function_registry.rb +83 -0
  7. data/lib/sass/embedded/compile_context/importer_registry.rb +97 -0
  8. data/lib/sass/embedded/compile_context.rb +21 -176
  9. data/lib/sass/embedded/compiler/path.rb +1 -3
  10. data/lib/sass/embedded/compiler/requirements.rb +1 -1
  11. data/lib/sass/embedded/compiler.rb +7 -19
  12. data/lib/sass/embedded/{render.rb → legacy.rb} +79 -3
  13. data/lib/sass/embedded/observer.rb +2 -0
  14. data/lib/sass/embedded/protofier.rb +329 -0
  15. data/lib/sass/embedded/version.rb +1 -1
  16. data/lib/sass/embedded/version_context.rb +2 -3
  17. data/lib/sass/embedded.rb +39 -54
  18. data/lib/sass/embedded_protocol.rb +0 -6
  19. data/lib/sass/logger/source_location.rb +3 -9
  20. data/lib/sass/logger/source_span.rb +1 -13
  21. data/lib/sass/logger.rb +3 -3
  22. data/lib/sass/script_error.rb +5 -0
  23. data/lib/sass/value/argument_list.rb +24 -0
  24. data/lib/sass/value/boolean.rb +42 -0
  25. data/lib/sass/value/color.rb +213 -0
  26. data/lib/sass/value/function.rb +35 -0
  27. data/lib/sass/value/fuzzy_math.rb +81 -0
  28. data/lib/sass/value/list.rb +60 -0
  29. data/lib/sass/value/map.rb +57 -0
  30. data/lib/sass/value/null.rb +39 -0
  31. data/lib/sass/value/number/unit.rb +186 -0
  32. data/lib/sass/value/number.rb +272 -0
  33. data/lib/sass/value/string.rb +43 -0
  34. data/lib/sass/value.rb +92 -0
  35. data/lib/sass.rb +47 -48
  36. metadata +26 -57
  37. data/lib/sass/embedded/platform.rb +0 -55
  38. data/lib/sass/embedded/url.rb +0 -36
  39. data/lib/sass/file_importer.rb +0 -10
  40. data/lib/sass/importer.rb +0 -14
  41. data/lib/sass/importer_result.rb +0 -14
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Value
5
+ # Sass's argument list type.
6
+ #
7
+ # An argument list comes from a rest argument. It's distinct from a normal {List} in that it may contain a keyword
8
+ # map as well as the positional arguments.
9
+ class ArgumentList < Sass::Value::List
10
+ def initialize(contents = [], keywords = {}, separator = ',')
11
+ super(contents, separator: separator)
12
+
13
+ @id = 0
14
+ @keywords_accessed = false
15
+ @keywords = keywords.transform_keys(&:to_s).freeze
16
+ end
17
+
18
+ def keywords
19
+ @keywords_accessed = true
20
+ @keywords
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Value
5
+ # Sass's boolean type.
6
+ class Boolean < Sass::Value
7
+ def initialize(value) # rubocop:disable Lint/MissingSuper
8
+ @value = value
9
+ end
10
+
11
+ attr_reader :value
12
+
13
+ alias to_bool value
14
+
15
+ def assert_boolean(_name = nil)
16
+ self
17
+ end
18
+
19
+ def ==(other)
20
+ other.is_a?(Sass::Value::Boolean) && other.value == value
21
+ end
22
+
23
+ def hash
24
+ @hash ||= value.hash
25
+ end
26
+
27
+ def !
28
+ value ? Boolean::FALSE : Boolean::TRUE
29
+ end
30
+
31
+ # Sass's true value.
32
+ TRUE = Boolean.new(true)
33
+
34
+ # Sass's false value.
35
+ FALSE = Boolean.new(false)
36
+
37
+ def self.new(value)
38
+ value ? Boolean::TRUE : Boolean::FALSE
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,213 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Value
5
+ # Sass's color type.
6
+ #
7
+ # No matter what representation was originally used to create this color, all of its channels are accessible.
8
+ class Color < Sass::Value
9
+ def initialize(red: nil, green: nil, blue: nil, # rubocop:disable Lint/MissingSuper
10
+ hue: nil, saturation: nil, lightness: nil,
11
+ whiteness: nil, blackness: nil,
12
+ alpha: nil)
13
+ @alpha = alpha.nil? ? 1 : FuzzyMath.assert_between(alpha, 0, 1, 'alpha')
14
+ if red && green && blue
15
+ @red = FuzzyMath.assert_between(FuzzyMath.round(red), 0, 255, 'red')
16
+ @green = FuzzyMath.assert_between(FuzzyMath.round(green), 0, 255, 'green')
17
+ @blue = FuzzyMath.assert_between(FuzzyMath.round(blue), 0, 255, 'blue')
18
+ elsif hue && saturation && lightness
19
+ @hue = hue % 360
20
+ @saturation = FuzzyMath.assert_between(saturation, 0, 100, 'saturation')
21
+ @lightness = FuzzyMath.assert_between(lightness, 0, 100, 'lightness')
22
+ elsif hue && whiteness && blackness
23
+ @hue = hue % 360
24
+ @whiteness = FuzzyMath.assert_between(whiteness, 0, 100, 'whiteness')
25
+ @blackness = FuzzyMath.assert_between(blackness, 0, 100, 'blackness')
26
+ hwb_to_rgb
27
+ @whiteness = @blackness = nil
28
+ else
29
+ raise error 'Invalid Color'
30
+ end
31
+ end
32
+
33
+ def change(red: nil,
34
+ green: nil,
35
+ blue: nil,
36
+ hue: nil,
37
+ saturation: nil,
38
+ lightness: nil,
39
+ whiteness: nil,
40
+ blackness: nil,
41
+ alpha: nil)
42
+ if whiteness || blackness
43
+ Sass::Value::Color.new(hue: hue || self.hue,
44
+ whiteness: whiteness || self.whiteness,
45
+ blackness: blackness || self.blackness,
46
+ alpha: alpha || self.alpha)
47
+ elsif hue || saturation || lightness
48
+ Sass::Value::Color.new(hue: hue || self.hue,
49
+ saturation: saturation || self.saturation,
50
+ lightness: lightness || self.lightness,
51
+ alpha: alpha || self.alpha)
52
+ elsif red || green || blue
53
+ Sass::Value::Color.new(red: red ? FuzzyMath.round(red) : self.red,
54
+ green: green ? FuzzyMath.round(green) : self.green,
55
+ blue: blue ? FuzzyMath.round(blue) : self.blue,
56
+ alpha: alpha || self.alpha)
57
+ else
58
+ dup.instance_eval do
59
+ @alpha = FuzzyMath.assert_between(alpha, 0, 1, 'alpha')
60
+ self
61
+ end
62
+ end
63
+ end
64
+
65
+ def red
66
+ hsl_to_rgb if @red.nil?
67
+
68
+ @red
69
+ end
70
+
71
+ def green
72
+ hsl_to_rgb if @green.nil?
73
+
74
+ @green
75
+ end
76
+
77
+ def blue
78
+ hsl_to_rgb if @blue.nil?
79
+
80
+ @blue
81
+ end
82
+
83
+ def hue
84
+ rgb_to_hsl if @hue.nil?
85
+
86
+ @hue
87
+ end
88
+
89
+ def saturation
90
+ rgb_to_hsl if @saturation.nil?
91
+
92
+ @saturation
93
+ end
94
+
95
+ def lightness
96
+ rgb_to_hsl if @lightness.nil?
97
+
98
+ @lightness
99
+ end
100
+
101
+ def whiteness
102
+ @whiteness ||= Rational([red, green, blue].min, 255) * 100
103
+ end
104
+
105
+ def blackness
106
+ @blackness ||= 100 - (Rational([red, green, blue].max, 255) * 100)
107
+ end
108
+
109
+ attr_reader :alpha
110
+
111
+ def assert_color(_name = nil)
112
+ self
113
+ end
114
+
115
+ def ==(other)
116
+ other.is_a?(Sass::Value::Color) &&
117
+ other.red == red &&
118
+ other.green == green &&
119
+ other.blue == blue &&
120
+ other.alpha == alpha
121
+ end
122
+
123
+ def hash
124
+ @hash ||= red.hash ^ green.hash ^ blue.hash ^ alpha.hash
125
+ end
126
+
127
+ private
128
+
129
+ def rgb_to_hsl
130
+ scaled_red = Rational(red, 255)
131
+ scaled_green = Rational(green, 255)
132
+ scaled_blue = Rational(blue, 255)
133
+
134
+ max = [scaled_red, scaled_green, scaled_blue].max
135
+ min = [scaled_red, scaled_green, scaled_blue].min
136
+ delta = max - min
137
+
138
+ if max == min
139
+ @hue = 0
140
+ elsif max == scaled_red
141
+ @hue = (60 * (scaled_green - scaled_blue) / delta) % 360
142
+ elsif max == scaled_green
143
+ @hue = (120 + (60 * (scaled_blue - scaled_red) / delta)) % 360
144
+ elsif max == scaled_blue
145
+ @hue = (240 + (60 * (scaled_red - scaled_green) / delta)) % 360
146
+ end
147
+
148
+ lightness = @lightness = 50 * (max + min)
149
+
150
+ @saturation = if max == min
151
+ 0
152
+ elsif lightness < 50
153
+ 100 * delta / (max + min)
154
+ else
155
+ 100 * delta / (2 - max - min)
156
+ end
157
+ end
158
+
159
+ def hsl_to_rgb
160
+ scaled_hue = Rational(hue, 360)
161
+ scaled_saturation = Rational(saturation, 100)
162
+ scaled_lightness = Rational(lightness, 100)
163
+
164
+ tmp2 = if scaled_lightness <= 0.5
165
+ scaled_lightness * (scaled_saturation + 1)
166
+ else
167
+ scaled_lightness + scaled_saturation - (scaled_lightness * scaled_saturation)
168
+ end
169
+ tmp1 = (scaled_lightness * 2) - tmp2
170
+ @red = FuzzyMath.round(hsl_hue_to_rgb(tmp1, tmp2, scaled_hue + Rational(1, 3)) * 255)
171
+ @green = FuzzyMath.round(hsl_hue_to_rgb(tmp1, tmp2, scaled_hue) * 255)
172
+ @blue = FuzzyMath.round(hsl_hue_to_rgb(tmp1, tmp2, scaled_hue - Rational(1, 3)) * 255)
173
+ end
174
+
175
+ def hsl_hue_to_rgb(tmp1, tmp2, hue)
176
+ hue += 1 if hue.negative?
177
+ hue -= 1 if hue > 1
178
+
179
+ if hue < Rational(1, 6)
180
+ tmp1 + ((tmp2 - tmp1) * hue * 6)
181
+ elsif hue < Rational(1, 2)
182
+ tmp2
183
+ elsif hue < Rational(2, 3)
184
+ tmp1 + ((tmp2 - tmp1) * (Rational(2, 3) - hue) * 6)
185
+ else
186
+ tmp1
187
+ end
188
+ end
189
+
190
+ def hwb_to_rgb
191
+ scaled_hue = Rational(hue, 360)
192
+ scaled_whiteness = Rational(whiteness, 100)
193
+ scaled_blackness = Rational(blackness, 100)
194
+
195
+ sum = scaled_whiteness + scaled_blackness
196
+ if sum > 1
197
+ scaled_whiteness /= sum
198
+ scaled_blackness /= sum
199
+ end
200
+
201
+ factor = 1 - scaled_whiteness - scaled_blackness
202
+ @red = hwb_hue_to_rgb(factor, scaled_whiteness, scaled_hue + Rational(1, 3))
203
+ @green = hwb_hue_to_rgb(factor, scaled_whiteness, scaled_hue)
204
+ @blue = hwb_hue_to_rgb(factor, scaled_whiteness, scaled_hue - Rational(1, 3))
205
+ end
206
+
207
+ def hwb_hue_to_rgb(factor, scaled_whiteness, scaled_hue)
208
+ channel = (hsl_hue_to_rgb(0, 1, scaled_hue) * factor) + scaled_whiteness
209
+ FuzzyMath.round(channel * 255)
210
+ end
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Value
5
+ # Sass's function type.
6
+ class Function < Sass::Value
7
+ def initialize(id_or_signature, callback = nil) # rubocop:disable Lint/MissingSuper
8
+ if id_or_signature.is_a? Numeric
9
+ @id = id_or_signature
10
+ else
11
+ @signature = id_or_signature
12
+ @callback = callback
13
+ end
14
+ end
15
+
16
+ attr_reader :id, :signature, :callback
17
+
18
+ def assert_function(_name = nil)
19
+ self
20
+ end
21
+
22
+ def ==(other)
23
+ if id.nil?
24
+ other.equal? self
25
+ else
26
+ other.is_a?(Sass::Value::Function) && other.id == id
27
+ end
28
+ end
29
+
30
+ def hash
31
+ id.nil? ? signature.hash : id.hash
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Value
5
+ # Sass's {FuzzyMath} module.
6
+ module FuzzyMath
7
+ PRECISION = 10
8
+
9
+ EPSILON = 10.pow(-PRECISION - 1)
10
+
11
+ INVERSE_EPSILON = 1 / EPSILON
12
+
13
+ module_function
14
+
15
+ def equals(number1, number2)
16
+ (number1 - number2).abs < EPSILON
17
+ end
18
+
19
+ def less_than(number1, number2)
20
+ number1 < number2 && !equals(number1, number2)
21
+ end
22
+
23
+ def less_than_or_equals(number1, number2)
24
+ number1 < number2 || equals(number1, number2)
25
+ end
26
+
27
+ def greater_than(number1, number2)
28
+ number1 > number2 && !equals(number1, number2)
29
+ end
30
+
31
+ def greater_than_or_equals(number1, number2)
32
+ number1 > number2 || equals(number1, number2)
33
+ end
34
+
35
+ def integer?(number)
36
+ return false unless number.finite?
37
+ return true if number.integer?
38
+
39
+ equals((number - 0.5).abs % 1, 0.5)
40
+ end
41
+
42
+ def to_i(number)
43
+ integer?(number) ? number.round : nil
44
+ end
45
+
46
+ def round(number)
47
+ if number.positive?
48
+ less_than(number % 1, 0.5) ? number.floor : number.ceil
49
+ else
50
+ less_than_or_equals(number % 1, 0.5) ? number.floor : number.ceil
51
+ end
52
+ end
53
+
54
+ def between(number, min, max)
55
+ return min if equals(number, min)
56
+ return max if equals(number, max)
57
+ return number if number > min && number < max
58
+
59
+ nil
60
+ end
61
+
62
+ def assert_between(number, min, max, name)
63
+ result = between(number, min, max)
64
+ return result unless result.nil?
65
+
66
+ message = "#{number} must be between #{min} and #{max}."
67
+ raise Sass::ScriptError, name.nil? ? message : "$#{name}: #{message}"
68
+ end
69
+
70
+ def hash(number)
71
+ @hash ||= if number.finite?
72
+ (number * INVERSE_EPSILON).round.hash
73
+ else
74
+ number.hash
75
+ end
76
+ end
77
+ end
78
+
79
+ private_constant :FuzzyMath
80
+ end
81
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Value
5
+ # Sass's list type.
6
+ class List < Sass::Value
7
+ def initialize(contents = [], separator: ',', bracketed: false) # rubocop:disable Lint/MissingSuper
8
+ if separator.nil? && contents.length > 1
9
+ raise error 'A list with more than one element must have an explicit separator'
10
+ end
11
+
12
+ @contents = contents.freeze
13
+ @separator = separator.freeze
14
+ @bracketed = bracketed.freeze
15
+ end
16
+
17
+ attr_reader :contents, :separator
18
+
19
+ alias to_a contents
20
+
21
+ def bracketed?
22
+ @bracketed
23
+ end
24
+
25
+ def assert_map(name = nil)
26
+ to_a.empty? ? Sass::Value::Map.new({}) : super.assert_map(name)
27
+ end
28
+
29
+ def to_map
30
+ to_a.empty? ? Sass::Value::Map.new({}) : nil
31
+ end
32
+
33
+ def at(index)
34
+ index = index.floor
35
+ index = to_a.length + index if index.negative?
36
+ return nil if index.negative? || index >= to_a.length
37
+
38
+ to_a[index]
39
+ end
40
+
41
+ def ==(other)
42
+ (other.is_a?(Sass::Value::List) &&
43
+ other.contents == contents &&
44
+ other.separator == separator &&
45
+ other.bracketed? == bracketed?) ||
46
+ (to_a.empty? && other.is_a?(Sass::Value::Map) && other.to_a.empty?)
47
+ end
48
+
49
+ def hash
50
+ @hash ||= contents.hash
51
+ end
52
+
53
+ private
54
+
55
+ def to_a_length
56
+ to_a.length
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Value
5
+ # Sass's map type.
6
+ class Map < Sass::Value
7
+ def initialize(contents = {}) # rubocop:disable Lint/MissingSuper
8
+ @contents = contents.freeze
9
+ end
10
+
11
+ attr_reader :contents
12
+
13
+ def separator
14
+ contents.empty? ? nil : ','
15
+ end
16
+
17
+ def assert_map(_name = nil)
18
+ self
19
+ end
20
+
21
+ def to_map
22
+ self
23
+ end
24
+
25
+ def to_a
26
+ contents.to_a.map { |entry| Sass::Value::List.new(entry, separator: ' ') }
27
+ end
28
+
29
+ def at(index)
30
+ if index.is_a? Numeric
31
+ index = index.floor
32
+ index = to_a.length + index if index.negative?
33
+ return nil if index.negative? || index >= to_a.length
34
+
35
+ to_a[index]
36
+ else
37
+ contents[index]
38
+ end
39
+ end
40
+
41
+ def ==(other)
42
+ (other.is_a?(Sass::Value::Map) && other.contents == contents) ||
43
+ (contents.empty? && other.is_a?(Sass::Value::List) && other.to_a.empty?)
44
+ end
45
+
46
+ def hash
47
+ @hash ||= contents.hash
48
+ end
49
+
50
+ private
51
+
52
+ def to_a_length
53
+ contents.length
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Value
5
+ # Sass's null type.
6
+ class Null < Sass::Value
7
+ def initialize # rubocop:disable Lint/MissingSuper
8
+ @value = nil
9
+ end
10
+
11
+ attr_reader :value
12
+
13
+ alias to_nil value
14
+
15
+ def to_bool
16
+ false
17
+ end
18
+
19
+ def ==(other)
20
+ other.is_a?(Sass::Value::Null)
21
+ end
22
+
23
+ def hash
24
+ @hash ||= value.hash
25
+ end
26
+
27
+ def !
28
+ Boolean::TRUE
29
+ end
30
+
31
+ # Sass's null value.
32
+ NULL = Null.new
33
+
34
+ def self.new
35
+ NULL
36
+ end
37
+ end
38
+ end
39
+ end