sass-embedded 0.15.0 → 0.17.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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/ext/sass/extconf.rb +3 -48
  3. data/ext/sass/package.json +5 -0
  4. data/lib/sass/compile_error.rb +1 -10
  5. data/lib/sass/compile_result.rb +1 -5
  6. data/lib/sass/embedded/channel.rb +2 -2
  7. data/lib/sass/embedded/compile_context/function_registry.rb +85 -0
  8. data/lib/sass/embedded/compile_context/importer_registry.rb +99 -0
  9. data/lib/sass/embedded/compile_context/logger_registry.rb +45 -0
  10. data/lib/sass/embedded/compile_context/value_protofier.rb +237 -0
  11. data/lib/sass/embedded/compile_context.rb +31 -236
  12. data/lib/sass/embedded/compiler.rb +10 -43
  13. data/lib/sass/embedded/{render.rb → legacy.rb} +41 -0
  14. data/lib/sass/embedded/observer.rb +2 -0
  15. data/lib/sass/embedded/protofier.rb +91 -0
  16. data/lib/sass/embedded/structifier.rb +30 -0
  17. data/lib/sass/embedded/varint.rb +33 -0
  18. data/lib/sass/embedded/version.rb +1 -1
  19. data/lib/sass/embedded/version_context.rb +2 -3
  20. data/lib/sass/embedded.rb +39 -52
  21. data/lib/sass/embedded_protocol.rb +0 -6
  22. data/lib/sass/logger/source_location.rb +3 -9
  23. data/lib/sass/logger/source_span.rb +1 -13
  24. data/lib/sass/logger.rb +3 -3
  25. data/lib/sass/script_error.rb +5 -0
  26. data/lib/sass/value/argument_list.rb +24 -0
  27. data/lib/sass/value/boolean.rb +42 -0
  28. data/lib/sass/value/color.rb +213 -0
  29. data/lib/sass/value/function.rb +35 -0
  30. data/lib/sass/value/fuzzy_math.rb +81 -0
  31. data/lib/sass/value/list.rb +60 -0
  32. data/lib/sass/value/map.rb +57 -0
  33. data/lib/sass/value/null.rb +39 -0
  34. data/lib/sass/value/number/unit.rb +186 -0
  35. data/lib/sass/value/number.rb +272 -0
  36. data/lib/sass/value/string.rb +43 -0
  37. data/lib/sass/value.rb +92 -0
  38. data/lib/sass.rb +51 -48
  39. metadata +31 -53
  40. data/lib/sass/embedded/compiler/requirements.rb +0 -9
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../embedded_protocol'
4
- require_relative '../logger/source_span'
5
- require_relative 'observer'
6
-
7
3
  module Sass
8
4
  class Embedded
9
5
  # The {Observer} for {Embedded#compile}.
@@ -31,38 +27,33 @@ module Sass
31
27
  logger:,
32
28
  quiet_deps:,
33
29
  verbose:)
34
- @path = path
35
- @source = source
36
-
37
- @importer = to_struct(importer)
38
- @load_paths = load_paths
39
- @syntax = syntax
40
- @url = url
41
-
42
- @source_map = source_map
43
- @source_map_include_sources = source_map_include_sources
44
- @style = style
45
-
46
- @global_functions = functions.keys.map(&:to_s)
47
-
48
- @functions = functions.transform_keys do |key|
49
- key.to_s.split('(')[0].chomp
50
- end
51
- @importers = importers.map do |obj|
52
- to_struct(obj)
53
- end
54
-
55
- @alert_ascii = alert_ascii
56
- @alert_color = alert_color
57
-
58
- @logger = to_struct(logger)
59
-
60
- @quiet_deps = quiet_deps
61
- @verbose = verbose
30
+ @function_registery = FunctionRegistry.new(functions)
31
+ @importer_registery = ImporterRegistry.new(importers, load_paths)
32
+ @logger_registery = LoggerRegistry.new(logger)
62
33
 
63
34
  super(channel)
64
35
 
65
- send_message compile_request
36
+ send_message EmbeddedProtocol::InboundMessage::CompileRequest.new(
37
+ id: id,
38
+ string: unless source.nil?
39
+ EmbeddedProtocol::InboundMessage::CompileRequest::StringInput.new(
40
+ source: source,
41
+ url: url&.to_s,
42
+ syntax: Protofier.to_proto_syntax(syntax),
43
+ importer: importer.nil? ? nil : @importer_registery.register(importer)
44
+ )
45
+ end,
46
+ path: path,
47
+ style: Protofier.to_proto_output_style(style),
48
+ source_map: source_map,
49
+ source_map_include_sources: source_map_include_sources,
50
+ importers: @importer_registery.importers,
51
+ global_functions: @function_registery.global_functions,
52
+ alert_ascii: alert_ascii,
53
+ alert_color: alert_color,
54
+ quiet_deps: quiet_deps,
55
+ verbose: verbose
56
+ )
66
57
  end
67
58
 
68
59
  def update(error, message)
@@ -78,30 +69,30 @@ module Sass
78
69
  when EmbeddedProtocol::OutboundMessage::LogEvent
79
70
  return unless message.compilation_id == id
80
71
 
81
- log message
72
+ @logger_registery.log message
82
73
  when EmbeddedProtocol::OutboundMessage::CanonicalizeRequest
83
74
  return unless message.compilation_id == id
84
75
 
85
76
  Thread.new do
86
- send_message canonicalize_response message
77
+ send_message @importer_registery.canonicalize message
87
78
  end
88
79
  when EmbeddedProtocol::OutboundMessage::ImportRequest
89
80
  return unless message.compilation_id == id
90
81
 
91
82
  Thread.new do
92
- send_message import_response message
83
+ send_message @importer_registery.import message
93
84
  end
94
85
  when EmbeddedProtocol::OutboundMessage::FileImportRequest
95
86
  return unless message.compilation_id == id
96
87
 
97
88
  Thread.new do
98
- send_message file_import_response message
89
+ send_message @importer_registery.file_import message
99
90
  end
100
91
  when EmbeddedProtocol::OutboundMessage::FunctionCallRequest
101
92
  return unless message.compilation_id == id
102
93
 
103
94
  Thread.new do
104
- send_message function_call_response message
95
+ send_message @function_registery.function_call message
105
96
  end
106
97
  end
107
98
  rescue StandardError => e
@@ -109,204 +100,8 @@ module Sass
109
100
  super(e, nil)
110
101
  end
111
102
  end
112
-
113
- private
114
-
115
- def log(event)
116
- case event.type
117
- when :DEBUG
118
- if @logger.respond_to? :debug
119
- @logger.debug(event.message, span: Logger::SourceSpan.from_proto(event.span))
120
- else
121
- Kernel.warn(event.formatted)
122
- end
123
- when :DEPRECATION_WARNING
124
- if @logger.respond_to? :warn
125
- @logger.warn(event.message, deprecation: true,
126
- span: Logger::SourceSpan.from_proto(event.span),
127
- stack: event.stack_trace)
128
- else
129
- Kernel.warn(event.formatted)
130
- end
131
- when :WARNING
132
- if @logger.respond_to? :warn
133
- @logger.warn(event.message, deprecation: false,
134
- span: Logger::SourceSpan.from_proto(event.span),
135
- stack: event.stack_trace)
136
- else
137
- Kernel.warn(event.formatted)
138
- end
139
- end
140
- end
141
-
142
- def compile_request
143
- EmbeddedProtocol::InboundMessage::CompileRequest.new(
144
- id: id,
145
- string: unless @source.nil?
146
- EmbeddedProtocol::InboundMessage::CompileRequest::StringInput.new(
147
- source: @source,
148
- url: @url&.to_s,
149
- syntax: to_proto_syntax(@syntax),
150
- importer: @importer.nil? ? nil : to_proto_importer(@importer, @importers.length)
151
- )
152
- end,
153
- path: @path,
154
- style: to_proto_output_style(@style),
155
- source_map: @source_map,
156
- source_map_include_sources: @source_map_include_sources,
157
- importers: to_proto_importers(@importers, @load_paths),
158
- global_functions: @global_functions,
159
- alert_ascii: @alert_ascii,
160
- alert_color: @alert_color
161
- )
162
- end
163
-
164
- def canonicalize_response(canonicalize_request)
165
- importer = importer_of_id canonicalize_request.importer_id
166
- url = importer.canonicalize(canonicalize_request.url, from_import: canonicalize_request.from_import)&.to_s
167
-
168
- EmbeddedProtocol::InboundMessage::CanonicalizeResponse.new(
169
- id: canonicalize_request.id,
170
- url: url
171
- )
172
- rescue StandardError => e
173
- EmbeddedProtocol::InboundMessage::CanonicalizeResponse.new(
174
- id: canonicalize_request.id,
175
- error: e.message
176
- )
177
- end
178
-
179
- def import_response(import_request)
180
- importer = importer_of_id import_request.importer_id
181
- importer_result = to_struct importer.load(import_request.url)
182
-
183
- EmbeddedProtocol::InboundMessage::ImportResponse.new(
184
- id: import_request.id,
185
- success: EmbeddedProtocol::InboundMessage::ImportResponse::ImportSuccess.new(
186
- contents: importer_result.contents,
187
- syntax: to_proto_syntax(importer_result.syntax),
188
- source_map_url: importer_result.respond_to?(:source_map_url) ? importer_result.source_map_url&.to_s : nil
189
- )
190
- )
191
- rescue StandardError => e
192
- EmbeddedProtocol::InboundMessage::ImportResponse.new(
193
- id: import_request.id,
194
- error: e.message
195
- )
196
- end
197
-
198
- def file_import_response(file_import_request)
199
- importer = importer_of_id file_import_request.importer_id
200
- file_url = importer.find_file_url(file_import_request.url, from_import: file_import_request.from_import)&.to_s
201
-
202
- raise "file_url must be a file: URL, was \"#{file_url}\"" if !file_url.nil? && !file_url.start_with?('file:')
203
-
204
- EmbeddedProtocol::InboundMessage::FileImportResponse.new(
205
- id: file_import_request.id,
206
- file_url: file_url
207
- )
208
- rescue StandardError => e
209
- EmbeddedProtocol::InboundMessage::FileImportResponse.new(
210
- id: file_import_request.id,
211
- error: e.message
212
- )
213
- end
214
-
215
- def function_call_response(function_call_request)
216
- EmbeddedProtocol::InboundMessage::FunctionCallResponse.new(
217
- id: function_call_request.id,
218
- success: @functions[function_call_request.name].call(*function_call_request.arguments),
219
- accessed_argument_lists: function_call_request.arguments
220
- .filter { |argument| argument.value == :argument_list }
221
- .map { |argument| argument.argument_list.id }
222
- )
223
- rescue StandardError => e
224
- EmbeddedProtocol::InboundMessage::FunctionCallResponse.new(
225
- id: function_call_request.id,
226
- error: e.message
227
- )
228
- end
229
-
230
- def to_proto_syntax(syntax)
231
- case syntax&.to_sym
232
- when :scss
233
- EmbeddedProtocol::Syntax::SCSS
234
- when :indented
235
- EmbeddedProtocol::Syntax::INDENTED
236
- when :css
237
- EmbeddedProtocol::Syntax::CSS
238
- else
239
- raise ArgumentError, 'syntax must be one of :scss, :indented, :css'
240
- end
241
- end
242
-
243
- def to_proto_output_style(style)
244
- case style&.to_sym
245
- when :expanded
246
- EmbeddedProtocol::OutputStyle::EXPANDED
247
- when :compressed
248
- EmbeddedProtocol::OutputStyle::COMPRESSED
249
- else
250
- raise ArgumentError, 'style must be one of :expanded, :compressed'
251
- end
252
- end
253
-
254
- def to_proto_importer(importer, id)
255
- is_importer = importer.respond_to?(:canonicalize) && importer.respond_to?(:load)
256
- is_file_importer = importer.respond_to?(:find_file_url)
257
-
258
- if is_importer && !is_file_importer
259
- EmbeddedProtocol::InboundMessage::CompileRequest::Importer.new(
260
- importer_id: id
261
- )
262
- elsif is_file_importer && !is_importer
263
- EmbeddedProtocol::InboundMessage::CompileRequest::Importer.new(
264
- file_importer_id: id
265
- )
266
- else
267
- raise ArgumentError, 'importer must be an Importer or a FileImporter'
268
- end
269
- end
270
-
271
- def to_proto_importers(importers, load_paths)
272
- proto_importers = importers.map.with_index do |importer, id|
273
- to_proto_importer(importer, id)
274
- end
275
-
276
- load_paths.each do |load_path|
277
- proto_importers << EmbeddedProtocol::InboundMessage::CompileRequest::Importer.new(
278
- path: File.absolute_path(load_path)
279
- )
280
- end
281
-
282
- proto_importers
283
- end
284
-
285
- def importer_of_id(id)
286
- if id == @importers.length
287
- @importer
288
- else
289
- @importers[id]
290
- end
291
- end
292
-
293
- def to_struct(obj)
294
- return obj unless obj.is_a? Hash
295
-
296
- struct = Object.new
297
- obj.each do |key, value|
298
- if value.respond_to? :call
299
- struct.define_singleton_method key.to_sym do |*args, **kwargs|
300
- value.call(*args, **kwargs)
301
- end
302
- else
303
- struct.define_singleton_method key.to_sym do
304
- value
305
- end
306
- end
307
- end
308
- struct
309
- end
310
103
  end
104
+
105
+ private_constant :CompileContext
311
106
  end
312
107
  end
@@ -2,9 +2,6 @@
2
2
 
3
3
  require 'observer'
4
4
  require 'open3'
5
- require_relative '../embedded_protocol'
6
- require_relative 'compiler/path'
7
- require_relative 'protocol_error'
8
5
 
9
6
  module Sass
10
7
  class Embedded
@@ -14,14 +11,7 @@ module Sass
14
11
  class Compiler
15
12
  include Observable
16
13
 
17
- ONEOF_MESSAGE = EmbeddedProtocol::InboundMessage
18
- .descriptor
19
- .lookup_oneof('message')
20
- .to_h do |field_descriptor|
21
- [field_descriptor.subtype, field_descriptor.name]
22
- end
23
-
24
- private_constant :ONEOF_MESSAGE
14
+ PROTOCOL_ERROR_ID = 4_294_967_295
25
15
 
26
16
  def initialize
27
17
  @observerable_mutex = Mutex.new
@@ -35,7 +25,7 @@ module Sass
35
25
  warn(@stderr.readline, uplevel: 1)
36
26
  end
37
27
  poll do
38
- receive_proto read
28
+ receive_message Protofier.from_proto_message read
39
29
  end
40
30
  end
41
31
 
@@ -60,9 +50,7 @@ module Sass
60
50
  end
61
51
 
62
52
  def send_message(message)
63
- write EmbeddedProtocol::InboundMessage.new(
64
- ONEOF_MESSAGE[message.class.descriptor] => message
65
- ).to_proto
53
+ write Protofier.to_proto_message message
66
54
  end
67
55
 
68
56
  def close
@@ -86,7 +74,7 @@ module Sass
86
74
  private
87
75
 
88
76
  def half_closed?
89
- @id == EmbeddedProtocol::PROTOCOL_ERROR_ID
77
+ @id == PROTOCOL_ERROR_ID
90
78
  end
91
79
 
92
80
  def poll
@@ -108,9 +96,7 @@ module Sass
108
96
  end
109
97
  end
110
98
 
111
- def receive_proto(proto)
112
- payload = EmbeddedProtocol::OutboundMessage.decode(proto)
113
- message = payload[payload.message.to_s]
99
+ def receive_message(message)
114
100
  case message
115
101
  when EmbeddedProtocol::ProtocolError
116
102
  raise ProtocolError, message.message
@@ -120,37 +106,18 @@ module Sass
120
106
  end
121
107
 
122
108
  def read
123
- length = read_varint(@stdout)
109
+ length = Varint.read(@stdout)
124
110
  @stdout.read(length)
125
111
  end
126
112
 
127
113
  def write(payload)
128
114
  @stdin_mutex.synchronize do
129
- write_varint(@stdin, payload.length)
130
- @stdin.write payload
131
- end
132
- end
133
-
134
- def read_varint(readable)
135
- value = bits = 0
136
- loop do
137
- byte = readable.readbyte
138
- value |= (byte & 0x7f) << bits
139
- bits += 7
140
- break if byte < 0x80
115
+ Varint.write(@stdin, payload.length)
116
+ @stdin.write(payload)
141
117
  end
142
- value
143
- end
144
-
145
- def write_varint(writeable, value)
146
- bytes = []
147
- until value < 0x80
148
- bytes << (0x80 | (value & 0x7f))
149
- value >>= 7
150
- end
151
- bytes << value
152
- writeable.write bytes.pack('C*')
153
118
  end
154
119
  end
120
+
121
+ private_constant :Compiler
155
122
  end
156
123
  end
@@ -5,7 +5,48 @@ require 'json'
5
5
  require 'pathname'
6
6
  require 'uri'
7
7
 
8
+ # The Sass module.
9
+ #
10
+ # This communicates with Embedded Dart Sass using the Embedded Sass protocol.
8
11
  module Sass
12
+ class << self
13
+ # @deprecated
14
+ # The global {.include_paths} for Sass files. This is meant for plugins and
15
+ # libraries to register the paths to their Sass stylesheets to that they may
16
+ # be included via `@import` or `@use`. This include path is used by every
17
+ # instance of {Sass::Embedded}. They are lower-precedence than any include
18
+ # paths passed in via the `include_paths` option.
19
+ #
20
+ # If the `SASS_PATH` environment variable is set,
21
+ # the initial value of `include_paths` will be initialized based on that.
22
+ # The variable should be a colon-separated list of path names
23
+ # (semicolon-separated on Windows).
24
+ #
25
+ # @example
26
+ # Sass.include_paths << File.dirname(__FILE__) + '/sass'
27
+ # @return [Array]
28
+ def include_paths
29
+ Embedded.include_paths
30
+ end
31
+
32
+ # @deprecated
33
+ # The global {.render} method. This instantiates a global {Embedded} instance
34
+ # and calls {Embedded#render}.
35
+ #
36
+ # See {Embedded#render} for supported options.
37
+ #
38
+ # @example
39
+ # Sass.render(data: 'h1 { font-size: 40px; }')
40
+ # @example
41
+ # Sass.render(file: 'style.css')
42
+ # @return [Result]
43
+ # @raise [ProtocolError]
44
+ # @raise [RenderError]
45
+ def render(**kwargs)
46
+ instance.render(**kwargs)
47
+ end
48
+ end
49
+
9
50
  # The {Embedded} host for using dart-sass-embedded. Each instance creates
10
51
  # its own {Channel}.
11
52
  #
@@ -43,5 +43,7 @@ module Sass
43
43
  @subscription.send_message(*args)
44
44
  end
45
45
  end
46
+
47
+ private_constant :Observer
46
48
  end
47
49
  end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Embedded
5
+ # The {Protofier} between Pure Ruby types and Protobuf Ruby types.
6
+ module Protofier
7
+ ONEOF_MESSAGE = EmbeddedProtocol::InboundMessage
8
+ .descriptor
9
+ .lookup_oneof('message')
10
+ .to_h do |field_descriptor|
11
+ [field_descriptor.subtype, field_descriptor.name]
12
+ end
13
+
14
+ private_constant :ONEOF_MESSAGE
15
+
16
+ module_function
17
+
18
+ def from_proto_compile_response(compile_response)
19
+ if compile_response.result == :failure
20
+ raise CompileError.new(
21
+ compile_response.failure.formatted || compile_response.failure.message,
22
+ compile_response.failure.message,
23
+ compile_response.failure.stack_trace,
24
+ from_proto_source_span(compile_response.failure.span)
25
+ )
26
+ end
27
+
28
+ CompileResult.new(
29
+ compile_response.success.css,
30
+ compile_response.success.source_map,
31
+ compile_response.success.loaded_urls
32
+ )
33
+ end
34
+
35
+ def from_proto_source_span(source_span)
36
+ return nil if source_span.nil?
37
+
38
+ Logger::SourceSpan.new(from_proto_source_location(source_span.start),
39
+ from_proto_source_location(source_span.end),
40
+ source_span.text,
41
+ source_span.url,
42
+ source_span.context)
43
+ end
44
+
45
+ def from_proto_source_location(source_location)
46
+ return nil if source_location.nil?
47
+
48
+ Logger::SourceLocation.new(source_location.offset,
49
+ source_location.line,
50
+ source_location.column)
51
+ end
52
+
53
+ def from_proto_message(proto)
54
+ message = EmbeddedProtocol::OutboundMessage.decode(proto)
55
+ message.method(message.message).call
56
+ end
57
+
58
+ def to_proto_message(message)
59
+ EmbeddedProtocol::InboundMessage.new(
60
+ ONEOF_MESSAGE[message.class.descriptor] => message
61
+ ).to_proto
62
+ end
63
+
64
+ def to_proto_syntax(syntax)
65
+ case syntax&.to_sym
66
+ when :scss
67
+ EmbeddedProtocol::Syntax::SCSS
68
+ when :indented
69
+ EmbeddedProtocol::Syntax::INDENTED
70
+ when :css
71
+ EmbeddedProtocol::Syntax::CSS
72
+ else
73
+ raise ArgumentError, 'syntax must be one of :scss, :indented, :css'
74
+ end
75
+ end
76
+
77
+ def to_proto_output_style(style)
78
+ case style&.to_sym
79
+ when :expanded
80
+ EmbeddedProtocol::OutputStyle::EXPANDED
81
+ when :compressed
82
+ EmbeddedProtocol::OutputStyle::COMPRESSED
83
+ else
84
+ raise ArgumentError, 'style must be one of :expanded, :compressed'
85
+ end
86
+ end
87
+ end
88
+
89
+ private_constant :Protofier
90
+ end
91
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Embedded
5
+ # The {Structifier} that convert {::Hash} to {Struct}-like object.
6
+ module Structifier
7
+ module_function
8
+
9
+ def to_struct(obj)
10
+ return obj unless obj.is_a? Hash
11
+
12
+ struct = Object.new
13
+ obj.each do |key, value|
14
+ if value.respond_to? :call
15
+ struct.define_singleton_method key.to_sym do |*args, **kwargs|
16
+ value.call(*args, **kwargs)
17
+ end
18
+ else
19
+ struct.define_singleton_method key.to_sym do
20
+ value
21
+ end
22
+ end
23
+ end
24
+ struct
25
+ end
26
+ end
27
+
28
+ private_constant :Structifier
29
+ end
30
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Embedded
5
+ # Read and write {Varint} from {::IO}.
6
+ module Varint
7
+ module_function
8
+
9
+ def read(readable)
10
+ value = bits = 0
11
+ loop do
12
+ byte = readable.readbyte
13
+ value |= (byte & 0x7f) << bits
14
+ bits += 7
15
+ break if byte < 0x80
16
+ end
17
+ value
18
+ end
19
+
20
+ def write(writeable, value)
21
+ bytes = []
22
+ until value < 0x80
23
+ bytes << (0x80 | (value & 0x7f))
24
+ value >>= 7
25
+ end
26
+ bytes << value
27
+ writeable.write bytes.pack('C*')
28
+ end
29
+ end
30
+
31
+ private_constant :Varint
32
+ end
33
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Sass
4
4
  class Embedded
5
- VERSION = '0.15.0'
5
+ VERSION = '0.17.0'
6
6
  end
7
7
  end
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../embedded_protocol'
4
- require_relative 'observer'
5
-
6
3
  module Sass
7
4
  class Embedded
8
5
  # The {Observer} for {Embedded#info}.
@@ -32,5 +29,7 @@ module Sass
32
29
  end
33
30
  end
34
31
  end
32
+
33
+ private_constant :VersionContext
35
34
  end
36
35
  end