dartsass-ruby 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|