sass-embedded 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c03a96231c8305e27f3deb1a35e308808482159a5dd25b4c8191da9c1b25500d
4
- data.tar.gz: d6ba46dd80433759554a8c83d1fe8a8b4fd66461ffb063c859011d4ab4e3b6cc
3
+ metadata.gz: d1086f5483670120b40e9741187e5150a5af9e9ad5d38159d6d57459edec350b
4
+ data.tar.gz: d6afead31f102a04ad4d11c82e1381b597c285ae0206564640763173e2b67fc6
5
5
  SHA512:
6
- metadata.gz: a4be81866a1672fd02da1cf1c66d08a2a0dd50cb002ee1b6fd0cce4d70c4e02222c11662a69307e7e33c58bf1dedc968d7ec72b2ebb3fa35296b1520896be2f5
7
- data.tar.gz: 39b348a11dd2277bb43e7bd936b08462057caac6180cb5b0548384cb64da47d1255606d8ce0a0ab2d0a4d891efee26cff190568eec1a782426011c4fc15ac2b0
6
+ metadata.gz: c8c534da41310d6b7e6228624d26a6e3dd94bccc4b67b2e9a25bff82251cb3b4a5d312c58ec1e498f944731cc99f4fdcb66b117278640cee2fc3d81f95924f66
7
+ data.tar.gz: 6d8808fc65dbb7fc4b43976beb6c8573635d874e9d72a7949f20eda5406e582ea0933a99f844256ec284e3ecf1e8e6f02b967f50854fde661470bf6834550abb
data/.rubocop.yml CHANGED
@@ -7,8 +7,5 @@ AllCops:
7
7
 
8
8
  NewCops: enable
9
9
 
10
- Layout/LineLength:
11
- Enabled: false
12
-
13
10
  Metrics:
14
11
  Enabled: false
data/ext/extconf.rb CHANGED
@@ -8,12 +8,24 @@ require 'fileutils'
8
8
  require_relative '../lib/sass/platform'
9
9
 
10
10
  module Sass
11
- # Install dependencies for sass-embedded during gem install
11
+ # The dependency downloader. This will download all the dependencies
12
+ # during gem installation, and the Makefile will unpack all downloaded
13
+ # dependencies. By default it will download the latest release of each
14
+ # dependency from GitHub releases.
15
+ #
16
+ # It is possible to specify an alternative source or version of each
17
+ # dependency. Local sources can be used for offline installation.
18
+ #
19
+ # @example
20
+ # gem install sass-embedded -- \
21
+ # --with-protoc=file:///path/to/protoc-*.zip
22
+ # --with-sass-embedded=file:///path/to/sass_embedded-*.(tar.gz|zip) \
23
+ # --with-sass-embedded-protocol=file:///path/to/embedded_sass.proto \
12
24
  class Extconf
13
25
  def initialize
26
+ get_with_config('protoc', true) { latest_protoc }
14
27
  get_with_config('sass-embedded', true) { latest_sass_embedded }
15
28
  get_with_config('sass-embedded-protocol', true) { latest_sass_embedded_protocol }
16
- get_with_config('protoc', true) { latest_protoc }
17
29
  end
18
30
 
19
31
  private
data/lib/sass.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # The Sass module
3
+ # The Sass module. This module will communicate with Embedded Dart Sass using
4
+ # the Embedded Sass protocol.
4
5
  module Sass
5
6
  class << self
6
7
  # The global include_paths for Sass files. This is meant for plugins and
@@ -29,12 +30,14 @@ module Sass
29
30
  embedded.info
30
31
  end
31
32
 
32
- # The global render methods. This method automatically instantiates a
33
+ # The global render method. This method automatically instantiates a
33
34
  # global {Sass::Embedded} instance when invoked the first time and call
34
- # `:render` method on the instance thereafter. The global {Sass::Embedded}
35
- # is automatically closed via {Kernel.at_exit}.
35
+ # `:render` method on the instance thereafter. See {Sass::Embedded#render}
36
+ # for supported options.
36
37
  # @example
37
- # Sass.render(options)
38
+ # Sass.render(data: 'h1 { font-size: 40px; }')
39
+ # @example
40
+ # Sass.render(file: 'style.css')
38
41
  # @return [Hash]
39
42
  def render(**kwargs)
40
43
  embedded.render(**kwargs)
@@ -55,5 +58,7 @@ require_relative 'sass/error'
55
58
  require_relative 'sass/platform'
56
59
  require_relative 'sass/util'
57
60
  require_relative 'sass/transport'
58
- require_relative 'sass/context'
61
+ require_relative 'sass/observer'
62
+ require_relative 'sass/info'
63
+ require_relative 'sass/render'
59
64
  require_relative 'sass/embedded'
data/lib/sass/embedded.rb CHANGED
@@ -1,10 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'json'
4
3
  require 'base64'
4
+ require 'json'
5
5
 
6
6
  module Sass
7
- # The interface for using dart-sass-embedded
7
+ # The {Embedded} user interface for using dart-sass-embedded. Each instance
8
+ # will create its own {Transport}.
9
+ # @example
10
+ # embedded = Sass::Embedded.new
11
+ # result = embedded.render(data: 'h1 { font-size: 40px; }')
12
+ # result = embedded.render(file: 'style.css')
13
+ # embedded.close
8
14
  class Embedded
9
15
  def initialize
10
16
  @transport = Transport.new
@@ -13,7 +19,7 @@ module Sass
13
19
  end
14
20
 
15
21
  def info
16
- @info ||= InfoContext.new(@transport, next_id).fetch
22
+ @info ||= Info.new(@transport, next_id).fetch
17
23
  end
18
24
 
19
25
  def render(data: nil,
@@ -40,41 +46,41 @@ module Sass
40
46
  indent_width = parse_indent_width(indent_width)
41
47
  linefeed = parse_linefeed(linefeed)
42
48
 
43
- response = RenderContext.new(@transport, next_id,
44
- data: data,
45
- file: file,
46
- indented_syntax: indented_syntax,
47
- include_paths: include_paths,
48
- output_style: output_style,
49
- source_map: source_map,
50
- out_file: out_file,
51
- functions: functions,
52
- importer: importer).fetch
53
-
54
- if response.failure
49
+ result = Render.new(@transport, next_id,
50
+ data: data,
51
+ file: file,
52
+ indented_syntax: indented_syntax,
53
+ include_paths: include_paths,
54
+ output_style: output_style,
55
+ source_map: source_map,
56
+ out_file: out_file,
57
+ functions: functions,
58
+ importer: importer).fetch
59
+
60
+ if result.failure
55
61
  raise RenderError.new(
56
- response.failure.message,
57
- response.failure.formatted,
58
- if response.failure.span.nil?
62
+ result.failure.message,
63
+ result.failure.formatted,
64
+ if result.failure.span.nil?
59
65
  nil
60
- elsif response.failure.span.url == ''
66
+ elsif result.failure.span.url == ''
61
67
  'stdin'
62
68
  else
63
- Util.path(response.failure.span.url)
69
+ Util.path(result.failure.span.url)
64
70
  end,
65
- response.failure.span ? response.failure.span.start.line + 1 : nil,
66
- response.failure.span ? response.failure.span.start.column + 1 : nil,
71
+ result.failure.span ? result.failure.span.start.line + 1 : nil,
72
+ result.failure.span ? result.failure.span.start.column + 1 : nil,
67
73
  1
68
74
  )
69
75
  end
70
76
 
71
- map, source_map = post_process_map(map: response.success.source_map,
77
+ map, source_map = post_process_map(map: result.success.source_map,
72
78
  file: file,
73
79
  out_file: out_file,
74
80
  source_map: source_map,
75
81
  source_map_root: source_map_root)
76
82
 
77
- css = post_process_css(css: response.success.css,
83
+ css = post_process_css(css: result.success.css,
78
84
  indent_type: indent_type,
79
85
  indent_width: indent_width,
80
86
  linefeed: linefeed,
@@ -186,7 +192,7 @@ module Sass
186
192
  when :tab
187
193
  "\t"
188
194
  else
189
- raise ArgumentError, 'indent_type must be :space or :tab'
195
+ raise ArgumentError, 'indent_type must be one of :space, :tab'
190
196
  end
191
197
  end
192
198
 
@@ -219,276 +225,5 @@ module Sass
219
225
  @id
220
226
  end
221
227
  end
222
-
223
- # InfoContext
224
- class InfoContext < Context
225
- def initialize(transport, id)
226
- super(transport, id)
227
- @transport.send EmbeddedProtocol::InboundMessage::VersionRequest.new(id: @id)
228
- end
229
-
230
- def update(error, message)
231
- raise error unless error.nil?
232
-
233
- response = message[message.message.to_s]
234
-
235
- case response
236
- when EmbeddedProtocol::ProtocolError
237
- raise ProtocolError, response.message
238
- when EmbeddedProtocol::OutboundMessage::VersionResponse
239
- return unless response.id == @id
240
-
241
- Thread.new do
242
- super(nil, response)
243
- end
244
- end
245
- rescue StandardError => e
246
- Thread.new do
247
- super(e, nil)
248
- end
249
- end
250
- end
251
-
252
- # RenderContext
253
- class RenderContext < Context
254
- def initialize(transport, id,
255
- data:,
256
- file:,
257
- indented_syntax:,
258
- include_paths:,
259
- output_style:,
260
- source_map:,
261
- out_file:,
262
- functions:,
263
- importer:)
264
- raise ArgumentError, 'either data or file must be set' if file.nil? && data.nil?
265
-
266
- super(transport, id)
267
-
268
- @data = data
269
- @file = file
270
- @indented_syntax = indented_syntax
271
- @include_paths = include_paths
272
- @output_style = output_style
273
- @source_map = source_map
274
- @out_file = out_file
275
- @global_functions = functions.keys
276
- @functions = functions.transform_keys do |key|
277
- key.to_s.split('(')[0].chomp
278
- end
279
- @importer = importer
280
- @import_responses = {}
281
-
282
- @transport.send compile_request
283
- end
284
-
285
- def update(error, message)
286
- raise error unless error.nil?
287
-
288
- response = message[message.message.to_s]
289
-
290
- case response
291
- when EmbeddedProtocol::ProtocolError
292
- raise ProtocolError, response.message
293
- when EmbeddedProtocol::OutboundMessage::CompileResponse
294
- return unless response.id == @id
295
-
296
- Thread.new do
297
- super(nil, response)
298
- end
299
- when EmbeddedProtocol::OutboundMessage::LogEvent
300
- # not implemented yet
301
- when EmbeddedProtocol::OutboundMessage::CanonicalizeRequest
302
- return unless response['compilation_id'] == @id
303
-
304
- Thread.new do
305
- @transport.send canonicalize_response(response)
306
- end
307
- when EmbeddedProtocol::OutboundMessage::ImportRequest
308
- return unless response['compilation_id'] == @id
309
-
310
- Thread.new do
311
- @transport.send import_response(response)
312
- end
313
- when EmbeddedProtocol::OutboundMessage::FileImportRequest
314
- raise NotImplementedError, 'FileImportRequest is not implemented'
315
- when EmbeddedProtocol::OutboundMessage::FunctionCallRequest
316
- return unless response['compilation_id'] == @id
317
-
318
- Thread.new do
319
- @transport.send function_call_response(response)
320
- end
321
- end
322
- rescue StandardError => e
323
- Thread.new do
324
- super(e, nil)
325
- end
326
- end
327
-
328
- private
329
-
330
- def compile_request
331
- EmbeddedProtocol::InboundMessage::CompileRequest.new(
332
- id: @id,
333
- string: string,
334
- path: path,
335
- style: style,
336
- source_map: source_map,
337
- importers: importers,
338
- global_functions: global_functions,
339
- alert_color: $stderr.tty?,
340
- alert_ascii: Platform::OS == 'windows'
341
- )
342
- end
343
-
344
- def canonicalize_response(canonicalize_request)
345
- url = Util.file_uri(File.absolute_path(canonicalize_request.url, (@file.nil? ? 'stdin' : @file)))
346
-
347
- begin
348
- result = @importer[canonicalize_request.importer_id].call canonicalize_request.url, @file
349
- raise result if result.is_a? StandardError
350
- rescue StandardError => e
351
- return EmbeddedProtocol::InboundMessage::CanonicalizeResponse.new(
352
- id: canonicalize_request.id,
353
- error: e.message
354
- )
355
- end
356
-
357
- if result&.key? :contents
358
- @import_responses[url] = EmbeddedProtocol::InboundMessage::ImportResponse.new(
359
- id: canonicalize_request.id,
360
- success: EmbeddedProtocol::InboundMessage::ImportResponse::ImportSuccess.new(
361
- contents: result[:contents],
362
- syntax: EmbeddedProtocol::Syntax::SCSS,
363
- source_map_url: nil
364
- )
365
- )
366
- EmbeddedProtocol::InboundMessage::CanonicalizeResponse.new(
367
- id: canonicalize_request.id,
368
- url: url
369
- )
370
- elsif result&.key? :file
371
- canonicalized_url = Util.file_uri(result[:file])
372
-
373
- # TODO: FileImportRequest is not supported yet.
374
- # Workaround by reading contents and return it when server asks
375
- @import_responses[canonicalized_url] = EmbeddedProtocol::InboundMessage::ImportResponse.new(
376
- id: canonicalize_request.id,
377
- success: EmbeddedProtocol::InboundMessage::ImportResponse::ImportSuccess.new(
378
- contents: File.read(result[:file]),
379
- syntax: EmbeddedProtocol::Syntax::SCSS,
380
- source_map_url: nil
381
- )
382
- )
383
-
384
- EmbeddedProtocol::InboundMessage::CanonicalizeResponse.new(
385
- id: canonicalize_request.id,
386
- url: canonicalized_url
387
- )
388
- else
389
- EmbeddedProtocol::InboundMessage::CanonicalizeResponse.new(
390
- id: canonicalize_request.id
391
- )
392
- end
393
- end
394
-
395
- def import_response(import_request)
396
- url = import_request.url
397
-
398
- if @import_responses.key? url
399
- @import_responses[url].id = import_request.id
400
- else
401
- @import_responses[url] = EmbeddedProtocol::InboundMessage::ImportResponse.new(
402
- id: import_request.id,
403
- error: "Failed to import: #{url}"
404
- )
405
- end
406
-
407
- @import_responses[url]
408
- end
409
-
410
- def function_call_response(function_call_request)
411
- EmbeddedProtocol::InboundMessage::FunctionCallResponse.new(
412
- id: function_call_request.id,
413
- success: @functions[function_call_request.name].call(*function_call_request.arguments)
414
- )
415
- rescue StandardError => e
416
- EmbeddedProtocol::InboundMessage::FunctionCallResponse.new(
417
- id: function_call_request.id,
418
- error: e.message
419
- )
420
- end
421
-
422
- def syntax
423
- if @indented_syntax == true
424
- EmbeddedProtocol::Syntax::INDENTED
425
- else
426
- EmbeddedProtocol::Syntax::SCSS
427
- end
428
- end
429
-
430
- def url
431
- return if @file.nil?
432
-
433
- Util.file_uri @file
434
- end
435
-
436
- def string
437
- return if @data.nil?
438
-
439
- EmbeddedProtocol::InboundMessage::CompileRequest::StringInput.new(
440
- source: @data,
441
- url: url,
442
- syntax: syntax
443
- )
444
- end
445
-
446
- def path
447
- @file if @data.nil?
448
- end
449
-
450
- def style
451
- case @output_style&.to_sym
452
- when :expanded
453
- EmbeddedProtocol::OutputStyle::EXPANDED
454
- when :compressed
455
- EmbeddedProtocol::OutputStyle::COMPRESSED
456
- when :nested, :compact
457
- raise ArgumentError, "#{@output_style} is not a supported output_style"
458
- else
459
- raise ArgumentError, "#{@output_style} is not a valid utput_style"
460
- end
461
- end
462
-
463
- def source_map
464
- @source_map.is_a?(String) || (@source_map == true && !@out_file.nil?)
465
- end
466
-
467
- attr_reader :global_functions
468
-
469
- # Order
470
- # 1. Loading a file relative to the file in which the @use or @import appeared.
471
- # 2. Each custom importer.
472
- # 3. Loading a file relative to the current working directory.
473
- # 4. Each load path in includePaths
474
- # 5. Each load path specified in the SASS_PATH environment variable, which should be semicolon-separated on Windows and colon-separated elsewhere.
475
- def importers
476
- custom_importers = @importer.map.with_index do |_, id|
477
- EmbeddedProtocol::InboundMessage::CompileRequest::Importer.new(
478
- importer_id: id
479
- )
480
- end
481
-
482
- include_path_importers = @include_paths
483
- .concat(Sass.include_paths)
484
- .map do |include_path|
485
- EmbeddedProtocol::InboundMessage::CompileRequest::Importer.new(
486
- path: File.absolute_path(include_path)
487
- )
488
- end
489
-
490
- custom_importers.concat include_path_importers
491
- end
492
- end
493
228
  end
494
229
  end
data/lib/sass/error.rb CHANGED
@@ -5,7 +5,7 @@ module Sass
5
5
 
6
6
  class ProtocolError < SassError; end
7
7
 
8
- # The error returned by {Sass.render}
8
+ # The error returned by {Embedded#render}.
9
9
  class RenderError < SassError
10
10
  attr_accessor :formatted, :file, :line, :column, :status
11
11
 
data/lib/sass/info.rb ADDED
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ # The {Observer} for {Embedded#info}.
5
+ class Info < Observer
6
+ def initialize(transport, id)
7
+ super(transport, id)
8
+ @transport.send EmbeddedProtocol::InboundMessage::VersionRequest.new(id: @id)
9
+ end
10
+
11
+ def update(error, message)
12
+ raise error unless error.nil?
13
+
14
+ case message
15
+ when EmbeddedProtocol::ProtocolError
16
+ raise ProtocolError, message.message
17
+ when EmbeddedProtocol::OutboundMessage::VersionResponse
18
+ return unless message.id == @id
19
+
20
+ Thread.new do
21
+ super(nil, message)
22
+ end
23
+ end
24
+ rescue StandardError => e
25
+ Thread.new do
26
+ super(e, nil)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,35 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sass
4
- # An abstract context for maintaining state and observing transport
5
- class Context
4
+ # The abstract {Observer} for tracking state and observing messages
5
+ # from {Transport}.
6
+ class Observer
6
7
  def initialize(transport, id)
7
- raise NotImplementedError if instance_of? Context
8
+ raise NotImplementedError if instance_of? Observer
8
9
 
9
10
  @transport = transport
10
11
  @id = id
11
12
  @mutex = Mutex.new
12
13
  @condition_variable = ConditionVariable.new
13
- @response = nil
14
14
  @error = nil
15
+ @message = nil
15
16
  @transport.add_observer self
16
17
  end
17
18
 
18
19
  def fetch
19
20
  @mutex.synchronize do
20
- @condition_variable.wait(@mutex) if @error.nil? && @response.nil?
21
+ @condition_variable.wait(@mutex) if @error.nil? && @message.nil?
21
22
  end
22
23
 
23
24
  raise @error unless @error.nil?
24
25
 
25
- @response
26
+ @message
26
27
  end
27
28
 
28
29
  def update(error, message)
29
30
  @transport.delete_observer self
30
31
  @mutex.synchronize do
31
32
  @error = error
32
- @response = message
33
+ @message = message
33
34
  @condition_variable.broadcast
34
35
  end
35
36
  end
@@ -0,0 +1,242 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ # The {Observer} for {Embedded#render}.
5
+ class Render < Observer
6
+ def initialize(transport, id,
7
+ data:,
8
+ file:,
9
+ indented_syntax:,
10
+ include_paths:,
11
+ output_style:,
12
+ source_map:,
13
+ out_file:,
14
+ functions:,
15
+ importer:)
16
+ raise ArgumentError, 'either data or file must be set' if file.nil? && data.nil?
17
+
18
+ super(transport, id)
19
+
20
+ @data = data
21
+ @file = file
22
+ @indented_syntax = indented_syntax
23
+ @include_paths = include_paths
24
+ @output_style = output_style
25
+ @source_map = source_map
26
+ @out_file = out_file
27
+ @global_functions = functions.keys
28
+ @functions = functions.transform_keys do |key|
29
+ key.to_s.split('(')[0].chomp
30
+ end
31
+ @importer = importer
32
+ @import_responses = {}
33
+
34
+ @transport.send compile_request
35
+ end
36
+
37
+ def update(error, message)
38
+ raise error unless error.nil?
39
+
40
+ case message
41
+ when EmbeddedProtocol::ProtocolError
42
+ raise ProtocolError, message.message
43
+ when EmbeddedProtocol::OutboundMessage::CompileResponse
44
+ return unless message.id == @id
45
+
46
+ Thread.new do
47
+ super(nil, message)
48
+ end
49
+ when EmbeddedProtocol::OutboundMessage::LogEvent
50
+ # not implemented yet
51
+ when EmbeddedProtocol::OutboundMessage::CanonicalizeRequest
52
+ return unless message['compilation_id'] == @id
53
+
54
+ Thread.new do
55
+ @transport.send canonicalize_response message
56
+ end
57
+ when EmbeddedProtocol::OutboundMessage::ImportRequest
58
+ return unless message['compilation_id'] == @id
59
+
60
+ Thread.new do
61
+ @transport.send import_response message
62
+ end
63
+ when EmbeddedProtocol::OutboundMessage::FileImportRequest
64
+ raise NotImplementedError, 'FileImportRequest is not implemented'
65
+ when EmbeddedProtocol::OutboundMessage::FunctionCallRequest
66
+ return unless message['compilation_id'] == @id
67
+
68
+ Thread.new do
69
+ @transport.send function_call_response message
70
+ end
71
+ end
72
+ rescue StandardError => e
73
+ Thread.new do
74
+ super(e, nil)
75
+ end
76
+ end
77
+
78
+ private
79
+
80
+ def compile_request
81
+ EmbeddedProtocol::InboundMessage::CompileRequest.new(
82
+ id: @id,
83
+ string: string,
84
+ path: path,
85
+ style: style,
86
+ source_map: source_map,
87
+ importers: importers,
88
+ global_functions: global_functions,
89
+ alert_color: $stderr.tty?,
90
+ alert_ascii: Platform::OS == 'windows'
91
+ )
92
+ end
93
+
94
+ def canonicalize_response(canonicalize_request)
95
+ url = Util.file_uri(File.absolute_path(canonicalize_request.url, (@file.nil? ? 'stdin' : @file)))
96
+
97
+ begin
98
+ result = @importer[canonicalize_request.importer_id].call canonicalize_request.url, @file
99
+ raise result if result.is_a? StandardError
100
+ rescue StandardError => e
101
+ return EmbeddedProtocol::InboundMessage::CanonicalizeResponse.new(
102
+ id: canonicalize_request.id,
103
+ error: e.message
104
+ )
105
+ end
106
+
107
+ if result&.key? :contents
108
+ @import_responses[url] = EmbeddedProtocol::InboundMessage::ImportResponse.new(
109
+ id: canonicalize_request.id,
110
+ success: EmbeddedProtocol::InboundMessage::ImportResponse::ImportSuccess.new(
111
+ contents: result[:contents],
112
+ syntax: EmbeddedProtocol::Syntax::SCSS,
113
+ source_map_url: nil
114
+ )
115
+ )
116
+ EmbeddedProtocol::InboundMessage::CanonicalizeResponse.new(
117
+ id: canonicalize_request.id,
118
+ url: url
119
+ )
120
+ elsif result&.key? :file
121
+ canonicalized_url = Util.file_uri(result[:file])
122
+
123
+ # TODO: FileImportRequest is not supported yet.
124
+ # Workaround by reading contents and return it when server asks
125
+ @import_responses[canonicalized_url] = EmbeddedProtocol::InboundMessage::ImportResponse.new(
126
+ id: canonicalize_request.id,
127
+ success: EmbeddedProtocol::InboundMessage::ImportResponse::ImportSuccess.new(
128
+ contents: File.read(result[:file]),
129
+ syntax: EmbeddedProtocol::Syntax::SCSS,
130
+ source_map_url: nil
131
+ )
132
+ )
133
+
134
+ EmbeddedProtocol::InboundMessage::CanonicalizeResponse.new(
135
+ id: canonicalize_request.id,
136
+ url: canonicalized_url
137
+ )
138
+ else
139
+ EmbeddedProtocol::InboundMessage::CanonicalizeResponse.new(
140
+ id: canonicalize_request.id
141
+ )
142
+ end
143
+ end
144
+
145
+ def import_response(import_request)
146
+ url = import_request.url
147
+
148
+ if @import_responses.key? url
149
+ @import_responses[url].id = import_request.id
150
+ else
151
+ @import_responses[url] = EmbeddedProtocol::InboundMessage::ImportResponse.new(
152
+ id: import_request.id,
153
+ error: "Failed to import: #{url}"
154
+ )
155
+ end
156
+
157
+ @import_responses[url]
158
+ end
159
+
160
+ def function_call_response(function_call_request)
161
+ EmbeddedProtocol::InboundMessage::FunctionCallResponse.new(
162
+ id: function_call_request.id,
163
+ success: @functions[function_call_request.name].call(*function_call_request.arguments)
164
+ )
165
+ rescue StandardError => e
166
+ EmbeddedProtocol::InboundMessage::FunctionCallResponse.new(
167
+ id: function_call_request.id,
168
+ error: e.message
169
+ )
170
+ end
171
+
172
+ def syntax
173
+ if @indented_syntax == true
174
+ EmbeddedProtocol::Syntax::INDENTED
175
+ else
176
+ EmbeddedProtocol::Syntax::SCSS
177
+ end
178
+ end
179
+
180
+ def url
181
+ return if @file.nil?
182
+
183
+ Util.file_uri @file
184
+ end
185
+
186
+ def string
187
+ return if @data.nil?
188
+
189
+ EmbeddedProtocol::InboundMessage::CompileRequest::StringInput.new(
190
+ source: @data,
191
+ url: url,
192
+ syntax: syntax
193
+ )
194
+ end
195
+
196
+ def path
197
+ @file if @data.nil?
198
+ end
199
+
200
+ def style
201
+ case @output_style&.to_sym
202
+ when :expanded
203
+ EmbeddedProtocol::OutputStyle::EXPANDED
204
+ when :compressed
205
+ EmbeddedProtocol::OutputStyle::COMPRESSED
206
+ else
207
+ raise ArgumentError, 'output_style must be one of :expanded, :compressed'
208
+ end
209
+ end
210
+
211
+ def source_map
212
+ @source_map.is_a?(String) || (@source_map == true && !@out_file.nil?)
213
+ end
214
+
215
+ attr_reader :global_functions
216
+
217
+ # Order
218
+ # 1. Loading a file relative to the file in which the @use or @import appeared.
219
+ # 2. Each custom importer.
220
+ # 3. Loading a file relative to the current working directory.
221
+ # 4. Each load path in includePaths
222
+ # 5. Each load path specified in the SASS_PATH environment variable, which should
223
+ # be semicolon-separated on Windows and colon-separated elsewhere.
224
+ def importers
225
+ custom_importers = @importer.map.with_index do |_, id|
226
+ EmbeddedProtocol::InboundMessage::CompileRequest::Importer.new(
227
+ importer_id: id
228
+ )
229
+ end
230
+
231
+ include_path_importers = @include_paths
232
+ .concat(Sass.include_paths)
233
+ .map do |include_path|
234
+ EmbeddedProtocol::InboundMessage::CompileRequest::Importer.new(
235
+ path: File.absolute_path(include_path)
236
+ )
237
+ end
238
+
239
+ custom_importers.concat include_path_importers
240
+ end
241
+ end
242
+ end
@@ -5,9 +5,9 @@ require 'observer'
5
5
  require_relative '../../ext/embedded_sass_pb'
6
6
 
7
7
  module Sass
8
- # The interface for communicating with dart-sass-embedded.
9
- # It handles message serialization and deserialization as well as
10
- # tracking concurrent request and response
8
+ # The {Observable} {Transport} for low level communication with
9
+ # dart-sass-embedded using protocol buffers via stdio. Received messages
10
+ # can be observed by an {Observer}.
11
11
  class Transport
12
12
  include Observable
13
13
 
@@ -17,24 +17,31 @@ module Sass
17
17
 
18
18
  PROTOCOL_ERROR_ID = 4_294_967_295
19
19
 
20
+ ONEOF_MESSAGE = EmbeddedProtocol::InboundMessage
21
+ .descriptor
22
+ .lookup_oneof('message')
23
+ .collect do |field_descriptor|
24
+ [field_descriptor.subtype, field_descriptor.name]
25
+ end.to_h
26
+
20
27
  def initialize
21
- @stdin_semaphore = Mutex.new
22
- @observerable_semaphore = Mutex.new
28
+ @observerable_mutex = Mutex.new
29
+ @stdin_mutex = Mutex.new
23
30
  @stdin, @stdout, @stderr, @wait_thread = Open3.popen3(DART_SASS_EMBEDDED)
24
31
  pipe @stderr, $stderr
25
32
  receive
26
33
  end
27
34
 
28
35
  def add_observer(*args)
29
- @observerable_semaphore.synchronize do
36
+ @observerable_mutex.synchronize do
30
37
  super(*args)
31
38
  end
32
39
  end
33
40
 
34
- def send(req)
35
- req_kind = req.class.name.split('::').last.gsub(/\B(?=[A-Z])/, '_').downcase
36
- message = EmbeddedProtocol::InboundMessage.new(req_kind => req)
37
- write message.to_proto
41
+ def send(message)
42
+ write EmbeddedProtocol::InboundMessage.new(
43
+ ONEOF_MESSAGE[message.class.descriptor] => message
44
+ ).to_proto
38
45
  end
39
46
 
40
47
  def close
@@ -63,9 +70,9 @@ module Sass
63
70
  end
64
71
  payload = @stdout.read length
65
72
  message = EmbeddedProtocol::OutboundMessage.decode payload
66
- @observerable_semaphore.synchronize do
73
+ @observerable_mutex.synchronize do
67
74
  changed
68
- notify_observers nil, message
75
+ notify_observers nil, message[message.message.to_s]
69
76
  end
70
77
  rescue Interrupt
71
78
  break
@@ -84,7 +91,7 @@ module Sass
84
91
  rescue Interrupt
85
92
  break
86
93
  rescue IOError => e
87
- @observerable_semaphore.synchronize do
94
+ @observerable_mutex.synchronize do
88
95
  notify_observers e, nil
89
96
  end
90
97
  close
@@ -94,7 +101,7 @@ module Sass
94
101
  end
95
102
 
96
103
  def write(payload)
97
- @stdin_semaphore.synchronize do
104
+ @stdin_mutex.synchronize do
98
105
  length = payload.length
99
106
  while length.positive?
100
107
  @stdin.write ((length > 0x7f ? 0x80 : 0) | (length & 0x7f)).chr
data/lib/sass/util.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require 'pathname'
4
4
 
5
5
  module Sass
6
- # Utilities functions
6
+ # The utilitiy module.
7
7
  module Util
8
8
  module_function
9
9
 
data/lib/sass/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sass
4
- VERSION = '0.4.2'
4
+ VERSION = '0.5.0'
5
5
  end
@@ -12,6 +12,8 @@ module Sass
12
12
  @embedded.close
13
13
  end
14
14
 
15
+ # rubocop:disable Layout/LineLength
16
+
15
17
  def render(sass)
16
18
  @embedded.render(data: sass,
17
19
  functions: {
@@ -163,6 +165,8 @@ module Sass
163
165
  })[:css]
164
166
  end
165
167
 
168
+ # rubocop:enable Layout/LineLength
169
+
166
170
  def test_functions_may_return_sass_string_type
167
171
  assert_sass <<-SCSS, <<-CSS
168
172
  div { url: url(sass_return_path("foo.svg")); }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sass-embedded
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - なつき
@@ -155,10 +155,12 @@ files:
155
155
  - ext/Makefile
156
156
  - ext/extconf.rb
157
157
  - lib/sass.rb
158
- - lib/sass/context.rb
159
158
  - lib/sass/embedded.rb
160
159
  - lib/sass/error.rb
160
+ - lib/sass/info.rb
161
+ - lib/sass/observer.rb
161
162
  - lib/sass/platform.rb
163
+ - lib/sass/render.rb
162
164
  - lib/sass/transport.rb
163
165
  - lib/sass/util.rb
164
166
  - lib/sass/version.rb