dartsass-ruby 3.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/LICENSE.txt +24 -0
- data/README.md +83 -0
- data/lib/dartsass-ruby.rb +3 -0
- data/lib/sassc/dependency.rb +17 -0
- data/lib/sassc/engine.rb +220 -0
- data/lib/sassc/error.rb +35 -0
- data/lib/sassc/functions_handler.rb +81 -0
- data/lib/sassc/import_handler.rb +222 -0
- data/lib/sassc/importer.rb +31 -0
- data/lib/sassc/protocol.rb +11 -0
- data/lib/sassc/sass_2_scss.rb +12 -0
- data/lib/sassc/script/functions.rb +8 -0
- data/lib/sassc/script/value/bool.rb +32 -0
- data/lib/sassc/script/value/color.rb +95 -0
- data/lib/sassc/script/value/list.rb +136 -0
- data/lib/sassc/script/value/map.rb +69 -0
- data/lib/sassc/script/value/number.rb +389 -0
- data/lib/sassc/script/value/string.rb +96 -0
- data/lib/sassc/script/value.rb +137 -0
- data/lib/sassc/script/value_conversion.rb +117 -0
- data/lib/sassc/script.rb +16 -0
- data/lib/sassc/url.rb +40 -0
- data/lib/sassc/util.rb +15 -0
- data/lib/sassc/version.rb +5 -0
- data/lib/sassc.rb +56 -0
- metadata +185 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SassC
|
4
|
+
class Importer
|
5
|
+
attr_reader :options
|
6
|
+
|
7
|
+
def initialize(options)
|
8
|
+
@options = options
|
9
|
+
end
|
10
|
+
|
11
|
+
def imports(path, parent_path)
|
12
|
+
# A custom importer must override this method.
|
13
|
+
# Custom importer may return an Import, or an array of Imports.
|
14
|
+
raise NotImplementedError
|
15
|
+
end
|
16
|
+
|
17
|
+
class Import
|
18
|
+
attr_accessor :path, :source, :source_map_path
|
19
|
+
|
20
|
+
def initialize(path, source: nil, source_map_path: nil)
|
21
|
+
@path = path
|
22
|
+
@source = source
|
23
|
+
@source_map_path = source_map_path
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
"Import: #{path} #{source} #{source_map_path}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# A SassScript object representing a boolean (true or false) value.
|
4
|
+
|
5
|
+
class SassC::Script::Value::Bool < SassC::Script::Value
|
6
|
+
|
7
|
+
# The true value in SassScript.
|
8
|
+
# This is assigned before new is overridden below so that we use the default implementation.
|
9
|
+
TRUE = new(true)
|
10
|
+
|
11
|
+
# The false value in SassScript.
|
12
|
+
# This is assigned before new is overridden below so that we use the default implementation.
|
13
|
+
FALSE = new(false)
|
14
|
+
|
15
|
+
# We override object creation so that users of the core API
|
16
|
+
# will not need to know that booleans are specific constants.
|
17
|
+
# Tests `value` for truthiness and returns the TRUE or FALSE constant.
|
18
|
+
def self.new(value)
|
19
|
+
value ? TRUE : FALSE
|
20
|
+
end
|
21
|
+
|
22
|
+
# The pure Ruby value of this Boolean
|
23
|
+
attr_reader :value
|
24
|
+
alias_method :to_bool, :value
|
25
|
+
|
26
|
+
# Returns the string "true" or "false" for this value
|
27
|
+
def to_s(opts = {})
|
28
|
+
@value.to_s
|
29
|
+
end
|
30
|
+
alias_method :to_sass, :to_s
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# A SassScript object representing a CSS color.
|
4
|
+
# This class provides a very bare-bones system for storing a RGB(A) or HSL(A)
|
5
|
+
# color and converting it to a CSS color function.
|
6
|
+
#
|
7
|
+
# If your Sass method accepts a color you will need to perform any
|
8
|
+
# needed color mathematics or transformations yourself.
|
9
|
+
|
10
|
+
class SassC::Script::Value::Color < SassC::Script::Value
|
11
|
+
|
12
|
+
attr_reader :red
|
13
|
+
attr_reader :green
|
14
|
+
attr_reader :blue
|
15
|
+
attr_reader :hue
|
16
|
+
attr_reader :saturation
|
17
|
+
attr_reader :lightness
|
18
|
+
attr_reader :alpha
|
19
|
+
|
20
|
+
# Creates a new color with (`red`, `green`, `blue`) or (`hue`, `saturation`, `lightness`
|
21
|
+
# values, plus an optional `alpha` transparency value.
|
22
|
+
def initialize(red:nil, green:nil, blue:nil, hue:nil, saturation:nil, lightness:nil, alpha:1.0)
|
23
|
+
if red && green && blue && alpha
|
24
|
+
@mode = :rgba
|
25
|
+
@red = red.to_i.clamp(0, 255)
|
26
|
+
@green = green.to_i.clamp(0, 255)
|
27
|
+
@blue = blue.to_i.clamp(0, 255)
|
28
|
+
@alpha = alpha.to_f.clamp(0.0, 1.0)
|
29
|
+
elsif hue && saturation && lightness && alpha
|
30
|
+
@mode = :hsla
|
31
|
+
@hue = hue.to_i.clamp(0, 360)
|
32
|
+
@saturation = saturation.to_i.clamp(0, 100)
|
33
|
+
@lightness = lightness.to_i.clamp(0, 100)
|
34
|
+
@alpha = alpha.to_f.clamp(0.0, 1.0)
|
35
|
+
else
|
36
|
+
raise SassC::UnsupportedValue, "Unable to determine color configuration for "
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns a CSS color declaration in the form
|
41
|
+
# `rgb(…)`, `rgba(…)`, `hsl(…)`, or `hsla(…)`.
|
42
|
+
def to_s
|
43
|
+
if rgba? && @alpha == 1.0
|
44
|
+
return "rgb(#{@red}, #{@green}, #{@blue})"
|
45
|
+
elsif rgba?
|
46
|
+
return "rgba(#{@red}, #{@green}, #{@blue}, #{alpha_string})"
|
47
|
+
elsif hsla? && @alpha == 1.0
|
48
|
+
return "hsl(#{@hue}, #{@saturation}%, #{@lightness}%)"
|
49
|
+
else # hsla?
|
50
|
+
return "hsla(#{@hue}, #{@saturation}%, #{@lightness}%, #{alpha_string})"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# True if this color has RGBA values
|
55
|
+
def rgba?
|
56
|
+
@mode == :rgba
|
57
|
+
end
|
58
|
+
|
59
|
+
# True if this color has HSLA values
|
60
|
+
def hlsa?
|
61
|
+
@mode == :hlsa
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns the alpha value of this color as a string
|
65
|
+
# and rounded to 8 decimal places.
|
66
|
+
def alpha_string
|
67
|
+
alpha.round(8).to_s
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns the values of this color in an array.
|
71
|
+
# Provided for compatibility between different SassC::Script::Value classes
|
72
|
+
def value
|
73
|
+
return [
|
74
|
+
red, green, blue,
|
75
|
+
hue, saturation, lightness,
|
76
|
+
alpha,
|
77
|
+
].compact
|
78
|
+
end
|
79
|
+
|
80
|
+
# True if this Color is equal to `other_color`
|
81
|
+
def eql?(other_color)
|
82
|
+
unless other_color.is_a?(self.class)
|
83
|
+
raise ArgumentError, "No implicit conversion of #{other_color.class} to #{self.class}"
|
84
|
+
end
|
85
|
+
self.value == other_color.value
|
86
|
+
end
|
87
|
+
alias_method :==, :eql?
|
88
|
+
|
89
|
+
# Returns a numeric value for comparing two Color objects
|
90
|
+
# This method is used internally by the Hash class and is not the same as `.to_h`
|
91
|
+
def hash
|
92
|
+
value.hash
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# A SassScript object representing a CSS list.
|
4
|
+
# This includes both comma-separated lists and space-separated lists.
|
5
|
+
|
6
|
+
class SassC::Script::Value::List < SassC::Script::Value
|
7
|
+
|
8
|
+
# The Ruby array containing the contents of the list.
|
9
|
+
#
|
10
|
+
# @return [Array<Value>]
|
11
|
+
attr_reader :value
|
12
|
+
alias_method :to_a, :value
|
13
|
+
|
14
|
+
# The operator separating the values of the list.
|
15
|
+
# Either `:comma` or `:space`.
|
16
|
+
#
|
17
|
+
# @return [Symbol]
|
18
|
+
attr_reader :separator
|
19
|
+
|
20
|
+
# Whether the list is surrounded by square brackets.
|
21
|
+
#
|
22
|
+
# @return [Boolean]
|
23
|
+
attr_reader :bracketed
|
24
|
+
|
25
|
+
# Creates a new list.
|
26
|
+
#
|
27
|
+
# @param value [Array<Value>] See \{#value}
|
28
|
+
# @param separator [Symbol] See \{#separator}
|
29
|
+
# @param bracketed [Boolean] See \{#bracketed}
|
30
|
+
def initialize(value, separator: nil, bracketed: false)
|
31
|
+
super(value)
|
32
|
+
@separator = separator
|
33
|
+
@bracketed = bracketed
|
34
|
+
end
|
35
|
+
|
36
|
+
# @see Value#options=
|
37
|
+
def options=(options)
|
38
|
+
super
|
39
|
+
value.each {|v| v.options = options}
|
40
|
+
end
|
41
|
+
|
42
|
+
# @see Value#eq
|
43
|
+
def eq(other)
|
44
|
+
SassC::Script::Value::Bool.new(
|
45
|
+
other.is_a?(List) && value == other.value &&
|
46
|
+
separator == other.separator && bracketed == other.bracketed
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
def hash
|
51
|
+
@hash ||= [value, separator, bracketed].hash
|
52
|
+
end
|
53
|
+
|
54
|
+
# @see Value#to_s
|
55
|
+
def to_s(opts = {})
|
56
|
+
if !bracketed && value.empty?
|
57
|
+
raise SassC::SyntaxError.new("#{inspect} isn't a valid CSS value.")
|
58
|
+
end
|
59
|
+
|
60
|
+
members = value.
|
61
|
+
reject {|e| e.is_a?(Null) || e.is_a?(List) && e.value.empty?}.
|
62
|
+
map {|e| e.to_s(opts)}
|
63
|
+
|
64
|
+
contents = members.join(sep_str)
|
65
|
+
bracketed ? "[#{contents}]" : contents
|
66
|
+
end
|
67
|
+
|
68
|
+
# @see Value#to_sass
|
69
|
+
def to_sass(opts = {})
|
70
|
+
return bracketed ? "[]" : "()" if value.empty?
|
71
|
+
members = value.map do |v|
|
72
|
+
if element_needs_parens?(v)
|
73
|
+
"(#{v.to_sass(opts)})"
|
74
|
+
else
|
75
|
+
v.to_sass(opts)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
if separator == :comma && members.length == 1
|
80
|
+
return "#{bracketed ? '[' : '('}#{members.first},#{bracketed ? ']' : ')'}"
|
81
|
+
end
|
82
|
+
|
83
|
+
contents = members.join(sep_str(nil))
|
84
|
+
bracketed ? "[#{contents}]" : contents
|
85
|
+
end
|
86
|
+
|
87
|
+
# @see Value#to_h
|
88
|
+
def to_h
|
89
|
+
return {} if value.empty?
|
90
|
+
super
|
91
|
+
end
|
92
|
+
|
93
|
+
# @see Value#inspect
|
94
|
+
def inspect
|
95
|
+
(bracketed ? '[' : '(') + value.map {|e| e.inspect}.join(sep_str(nil)) + (bracketed ? ']' : ')')
|
96
|
+
end
|
97
|
+
|
98
|
+
# Asserts an index is within the list.
|
99
|
+
#
|
100
|
+
# @private
|
101
|
+
#
|
102
|
+
# @param list [Sass::Script::Value::List] The list for which the index should be checked.
|
103
|
+
# @param n [Sass::Script::Value::Number] The index being checked.
|
104
|
+
def self.assert_valid_index(list, n)
|
105
|
+
if !n.int? || n.to_i == 0
|
106
|
+
raise ArgumentError.new("List index #{n} must be a non-zero integer")
|
107
|
+
elsif list.to_a.size == 0
|
108
|
+
raise ArgumentError.new("List index is #{n} but list has no items")
|
109
|
+
elsif n.to_i.abs > (size = list.to_a.size)
|
110
|
+
raise ArgumentError.new(
|
111
|
+
"List index is #{n} but list is only #{size} item#{'s' if size != 1} long")
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def element_needs_parens?(element)
|
118
|
+
if element.is_a?(List)
|
119
|
+
return false if element.value.length < 2
|
120
|
+
return false if element.bracketed
|
121
|
+
precedence = Sass::Script::Parser.precedence_of(separator || :space)
|
122
|
+
return Sass::Script::Parser.precedence_of(element.separator || :space) <= precedence
|
123
|
+
end
|
124
|
+
|
125
|
+
return false unless separator == :space
|
126
|
+
return false unless element.is_a?(Sass::Script::Tree::UnaryOperation)
|
127
|
+
element.operator == :minus || element.operator == :plus
|
128
|
+
end
|
129
|
+
|
130
|
+
def sep_str(opts = options)
|
131
|
+
return ' ' if separator == :space
|
132
|
+
return ',' if opts && opts[:style] == :compressed
|
133
|
+
', '
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class SassC::Script::Value::Map < SassC::Script::Value
|
4
|
+
|
5
|
+
# The Ruby hash containing the contents of this map.
|
6
|
+
# @return [Hash<Node, Node>]
|
7
|
+
attr_reader :value
|
8
|
+
alias_method :to_h, :value
|
9
|
+
|
10
|
+
# Creates a new map.
|
11
|
+
#
|
12
|
+
# @param hash [Hash<Node, Node>]
|
13
|
+
def initialize(hash)
|
14
|
+
super(hash)
|
15
|
+
end
|
16
|
+
|
17
|
+
# @see Value#options=
|
18
|
+
def options=(options)
|
19
|
+
super
|
20
|
+
value.each do |k, v|
|
21
|
+
k.options = options
|
22
|
+
v.options = options
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# @see Value#separator
|
27
|
+
def separator
|
28
|
+
:comma unless value.empty?
|
29
|
+
end
|
30
|
+
|
31
|
+
# @see Value#to_a
|
32
|
+
def to_a
|
33
|
+
value.map do |k, v|
|
34
|
+
list = SassC::Script::Value::List.new([k, v], separator: :space)
|
35
|
+
list.options = options
|
36
|
+
list
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# @see Value#eq
|
41
|
+
def eq(other)
|
42
|
+
SassC::Script::Value::Bool.new(other.is_a?(Map) && value == other.value)
|
43
|
+
end
|
44
|
+
|
45
|
+
def hash
|
46
|
+
@hash ||= value.hash
|
47
|
+
end
|
48
|
+
|
49
|
+
# @see Value#to_s
|
50
|
+
def to_s(opts = {})
|
51
|
+
raise SassC::SyntaxError.new("#{inspect} isn't a valid CSS value.")
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_sass(opts = {})
|
55
|
+
return "()" if value.empty?
|
56
|
+
|
57
|
+
to_sass = lambda do |value|
|
58
|
+
if value.is_a?(List) && value.separator == :comma
|
59
|
+
"(#{value.to_sass(opts)})"
|
60
|
+
else
|
61
|
+
value.to_sass(opts)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
"(#{value.map {|(k, v)| "#{to_sass[k]}: #{to_sass[v]}"}.join(', ')})"
|
66
|
+
end
|
67
|
+
alias_method :inspect, :to_sass
|
68
|
+
|
69
|
+
end
|