sass-embedded 0.8.2 → 0.9.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 +4 -4
- data/ext/sass/extconf.rb +3 -2
- data/lib/sass/embedded/channel.rb +62 -0
- data/lib/sass/embedded/{compile.rb → compile_context.rb} +9 -10
- data/lib/sass/embedded/compiler/path.rb +13 -0
- data/lib/sass/embedded/compiler/requirements.rb +9 -0
- data/lib/sass/embedded/compiler.rb +146 -7
- data/lib/sass/embedded/observer.rb +11 -7
- data/lib/sass/embedded/version.rb +1 -1
- data/lib/sass/embedded/{info.rb → version_context.rb} +5 -7
- data/lib/sass/embedded.rb +9 -19
- data/lib/sass/embedded_protocol.rb +9 -0
- metadata +10 -8
- data/lib/sass/embedded/protocol.rb +0 -3
- data/lib/sass/embedded/transport.rb +0 -131
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c5bcaee2299b4f0b9d4d1d23b25773fdad87d1a294a4a34eeec0028d1f89a25c
|
4
|
+
data.tar.gz: 75ce2615300b0d59e73f715ae43007baf09a9818ea56d80b8be4d54b1f561c69
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 93cf20b2df8d4c363797df90e459f52c6dedf5e540cb5b6eb1cd110dded76477adf82f6c0bc0652f1b5201f535265053f7838654952a4bcc7f338bcc69c9464a
|
7
|
+
data.tar.gz: c4190c8fc8e60a37fa834dfdac6fe1842e66d39ac8d0ad9bc1c15ea5c18c16766f19e194be746fc963a5604c740f438c6371aa59c10dd59d452a526fea6cbf0d
|
data/ext/sass/extconf.rb
CHANGED
@@ -5,7 +5,8 @@ require 'fileutils'
|
|
5
5
|
require 'json'
|
6
6
|
require 'mkmf'
|
7
7
|
require 'open-uri'
|
8
|
-
require_relative '../../lib/sass/embedded/compiler'
|
8
|
+
require_relative '../../lib/sass/embedded/compiler/path'
|
9
|
+
require_relative '../../lib/sass/embedded/compiler/requirements'
|
9
10
|
require_relative '../../lib/sass/embedded/platform'
|
10
11
|
|
11
12
|
module Sass
|
@@ -210,7 +211,7 @@ module Sass
|
|
210
211
|
def default_sass_embedded_protocol
|
211
212
|
repo = 'sass/embedded-protocol'
|
212
213
|
|
213
|
-
tag_name = IO.popen([Compiler::
|
214
|
+
tag_name = IO.popen([Compiler::PATH, '--version']) do |file|
|
214
215
|
JSON.parse(file.read)['protocolVersion']
|
215
216
|
end
|
216
217
|
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'compiler'
|
4
|
+
|
5
|
+
module Sass
|
6
|
+
class Embedded
|
7
|
+
# The {Channel} for {Compiler} calls. Each instance creates its own
|
8
|
+
# {Compiler}. A new {Compiler} is automatically created when the existing
|
9
|
+
# {Compiler} runs out of unique request id.
|
10
|
+
class Channel
|
11
|
+
def initialize
|
12
|
+
@mutex = Mutex.new
|
13
|
+
@compiler = Compiler.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def close
|
17
|
+
@mutex.synchronize do
|
18
|
+
@compiler.close
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def closed?
|
23
|
+
@mutex.synchronize do
|
24
|
+
@compiler.closed?
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def subscribe(observer)
|
29
|
+
@mutex.synchronize do
|
30
|
+
begin
|
31
|
+
id = @compiler.add_observer(observer)
|
32
|
+
rescue IOError
|
33
|
+
@compiler = Compiler.new
|
34
|
+
id = @compiler.add_observer(observer)
|
35
|
+
end
|
36
|
+
Subscription.new @compiler, observer, id
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# The {Subscription} between {Compiler} and {Observer}.
|
41
|
+
class Subscription
|
42
|
+
attr_reader :id
|
43
|
+
|
44
|
+
def initialize(compiler, observer, id)
|
45
|
+
@compiler = compiler
|
46
|
+
@observer = observer
|
47
|
+
@id = id
|
48
|
+
end
|
49
|
+
|
50
|
+
def unsubscribe
|
51
|
+
@compiler.delete_observer(@observer)
|
52
|
+
end
|
53
|
+
|
54
|
+
def send_message(*args)
|
55
|
+
@compiler.send_message(*args)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private_constant :Subscription
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative '../embedded_protocol'
|
3
4
|
require_relative 'observer'
|
4
|
-
require_relative 'protocol'
|
5
5
|
require_relative 'util'
|
6
6
|
|
7
7
|
module Sass
|
@@ -10,7 +10,7 @@ module Sass
|
|
10
10
|
class CompileContext
|
11
11
|
include Observer
|
12
12
|
|
13
|
-
def initialize(
|
13
|
+
def initialize(channel,
|
14
14
|
data:,
|
15
15
|
file:,
|
16
16
|
indented_syntax:,
|
@@ -22,7 +22,6 @@ module Sass
|
|
22
22
|
importer:)
|
23
23
|
raise ArgumentError, 'either data or file must be set' if file.nil? && data.nil?
|
24
24
|
|
25
|
-
@id = id
|
26
25
|
@data = data
|
27
26
|
@file = file
|
28
27
|
@indented_syntax = indented_syntax
|
@@ -37,7 +36,7 @@ module Sass
|
|
37
36
|
@importer = importer
|
38
37
|
@import_responses = {}
|
39
38
|
|
40
|
-
super(
|
39
|
+
super(channel)
|
41
40
|
|
42
41
|
send_message compile_request
|
43
42
|
end
|
@@ -47,23 +46,23 @@ module Sass
|
|
47
46
|
|
48
47
|
case message
|
49
48
|
when EmbeddedProtocol::OutboundMessage::CompileResponse
|
50
|
-
return unless message.id ==
|
49
|
+
return unless message.id == id
|
51
50
|
|
52
51
|
Thread.new do
|
53
52
|
super(nil, message)
|
54
53
|
end
|
55
54
|
when EmbeddedProtocol::OutboundMessage::LogEvent
|
56
|
-
return unless message.compilation_id ==
|
55
|
+
return unless message.compilation_id == id && $stderr.tty?
|
57
56
|
|
58
57
|
warn message.formatted
|
59
58
|
when EmbeddedProtocol::OutboundMessage::CanonicalizeRequest
|
60
|
-
return unless message.compilation_id ==
|
59
|
+
return unless message.compilation_id == id
|
61
60
|
|
62
61
|
Thread.new do
|
63
62
|
send_message canonicalize_response message
|
64
63
|
end
|
65
64
|
when EmbeddedProtocol::OutboundMessage::ImportRequest
|
66
|
-
return unless message.compilation_id ==
|
65
|
+
return unless message.compilation_id == id
|
67
66
|
|
68
67
|
Thread.new do
|
69
68
|
send_message import_response message
|
@@ -71,7 +70,7 @@ module Sass
|
|
71
70
|
when EmbeddedProtocol::OutboundMessage::FileImportRequest
|
72
71
|
raise NotImplementedError, 'FileImportRequest is not implemented'
|
73
72
|
when EmbeddedProtocol::OutboundMessage::FunctionCallRequest
|
74
|
-
return unless message.compilation_id ==
|
73
|
+
return unless message.compilation_id == id
|
75
74
|
|
76
75
|
Thread.new do
|
77
76
|
send_message function_call_response message
|
@@ -87,7 +86,7 @@ module Sass
|
|
87
86
|
|
88
87
|
def compile_request
|
89
88
|
EmbeddedProtocol::InboundMessage::CompileRequest.new(
|
90
|
-
id:
|
89
|
+
id: id,
|
91
90
|
string: string,
|
92
91
|
path: path,
|
93
92
|
style: style,
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../platform'
|
4
|
+
|
5
|
+
module Sass
|
6
|
+
class Embedded
|
7
|
+
class Compiler
|
8
|
+
PATH = File.absolute_path(
|
9
|
+
"../../../../ext/sass/sass_embedded/dart-sass-embedded#{Platform::OS == 'windows' ? '.bat' : ''}", __dir__
|
10
|
+
)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -1,17 +1,156 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require 'observer'
|
4
|
+
require 'open3'
|
5
|
+
require_relative '../embedded_protocol'
|
6
|
+
require_relative 'compiler/path'
|
7
|
+
require_relative 'error'
|
4
8
|
|
5
9
|
module Sass
|
6
10
|
class Embedded
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
+
# The {::Observable} {Compiler} for low level communication with
|
12
|
+
# `dart-sass-embedded` using protocol buffers via stdio. Received messages
|
13
|
+
# can be observed by an {Observer}.
|
14
|
+
class Compiler
|
15
|
+
include Observable
|
11
16
|
|
12
|
-
|
17
|
+
ONEOF_MESSAGE = EmbeddedProtocol::InboundMessage
|
18
|
+
.descriptor
|
19
|
+
.lookup_oneof('message')
|
20
|
+
.collect do |field_descriptor|
|
21
|
+
[field_descriptor.subtype, field_descriptor.name]
|
22
|
+
end.to_h
|
13
23
|
|
14
|
-
|
24
|
+
private_constant :ONEOF_MESSAGE
|
25
|
+
|
26
|
+
def initialize
|
27
|
+
@observerable_mutex = Mutex.new
|
28
|
+
@id = 0
|
29
|
+
@stdin_mutex = Mutex.new
|
30
|
+
@stdin, @stdout, @stderr, @wait_thread = Open3.popen3(PATH)
|
31
|
+
|
32
|
+
[@stdin, @stdout].each(&:binmode)
|
33
|
+
|
34
|
+
poll do
|
35
|
+
warn(@stderr.readline, uplevel: 1)
|
36
|
+
end
|
37
|
+
poll do
|
38
|
+
receive_proto read
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def add_observer(*args)
|
43
|
+
@observerable_mutex.synchronize do
|
44
|
+
raise IOError, 'half-closed compiler' if half_closed?
|
45
|
+
|
46
|
+
super(*args)
|
47
|
+
|
48
|
+
id = @id
|
49
|
+
@id = @id.next
|
50
|
+
id
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def delete_observer(*args)
|
55
|
+
@observerable_mutex.synchronize do
|
56
|
+
super(*args)
|
57
|
+
|
58
|
+
close if half_closed? && count_observers.zero?
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def send_message(message)
|
63
|
+
write EmbeddedProtocol::InboundMessage.new(
|
64
|
+
ONEOF_MESSAGE[message.class.descriptor] => message
|
65
|
+
).to_proto
|
66
|
+
end
|
67
|
+
|
68
|
+
def close
|
69
|
+
delete_observers
|
70
|
+
|
71
|
+
@stdin_mutex.synchronize do
|
72
|
+
@stdin.close unless @stdin.closed?
|
73
|
+
@stdout.close unless @stdout.closed?
|
74
|
+
@stderr.close unless @stderr.closed?
|
75
|
+
end
|
76
|
+
|
77
|
+
@wait_thread.value
|
78
|
+
end
|
79
|
+
|
80
|
+
def closed?
|
81
|
+
@stdin_mutex.synchronize do
|
82
|
+
@stdin.closed?
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def half_closed?
|
89
|
+
@id == EmbeddedProtocol::PROTOCOL_ERROR_ID
|
90
|
+
end
|
91
|
+
|
92
|
+
def poll
|
93
|
+
Thread.new do
|
94
|
+
loop do
|
95
|
+
yield
|
96
|
+
rescue StandardError => e
|
97
|
+
notify_observers(e, nil)
|
98
|
+
close
|
99
|
+
break
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def notify_observers(*args)
|
105
|
+
@observerable_mutex.synchronize do
|
106
|
+
changed
|
107
|
+
super(*args)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def receive_proto(proto)
|
112
|
+
payload = EmbeddedProtocol::OutboundMessage.decode(proto)
|
113
|
+
message = payload[payload.message.to_s]
|
114
|
+
case message
|
115
|
+
when EmbeddedProtocol::ProtocolError
|
116
|
+
raise ProtocolError, message.message
|
117
|
+
else
|
118
|
+
notify_observers(nil, message)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def read
|
123
|
+
length = read_varint(@stdout)
|
124
|
+
@stdout.read(length)
|
125
|
+
end
|
126
|
+
|
127
|
+
def write(payload)
|
128
|
+
@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
|
141
|
+
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
|
+
end
|
15
154
|
end
|
16
155
|
end
|
17
156
|
end
|
@@ -2,15 +2,15 @@
|
|
2
2
|
|
3
3
|
module Sass
|
4
4
|
class Embedded
|
5
|
-
# The {Observer} module for
|
5
|
+
# The {Observer} module for communicating with {Compiler}.
|
6
6
|
module Observer
|
7
|
-
def initialize(
|
8
|
-
@transport = transport
|
7
|
+
def initialize(channel)
|
9
8
|
@mutex = Mutex.new
|
10
9
|
@condition_variable = ConditionVariable.new
|
11
10
|
@error = nil
|
12
11
|
@message = nil
|
13
|
-
|
12
|
+
|
13
|
+
@subscription = channel.subscribe(self)
|
14
14
|
end
|
15
15
|
|
16
16
|
def receive_message
|
@@ -24,7 +24,7 @@ module Sass
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def update(error, message)
|
27
|
-
@
|
27
|
+
@subscription.unsubscribe
|
28
28
|
@mutex.synchronize do
|
29
29
|
@error = error
|
30
30
|
@message = message
|
@@ -34,8 +34,12 @@ module Sass
|
|
34
34
|
|
35
35
|
private
|
36
36
|
|
37
|
-
def
|
38
|
-
@
|
37
|
+
def id
|
38
|
+
@subscription.id
|
39
|
+
end
|
40
|
+
|
41
|
+
def send_message(*args)
|
42
|
+
@subscription.send_message(*args)
|
39
43
|
end
|
40
44
|
end
|
41
45
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative '../embedded_protocol'
|
3
4
|
require_relative 'observer'
|
4
|
-
require_relative 'protocol'
|
5
5
|
|
6
6
|
module Sass
|
7
7
|
class Embedded
|
@@ -9,12 +9,10 @@ module Sass
|
|
9
9
|
class VersionContext
|
10
10
|
include Observer
|
11
11
|
|
12
|
-
def initialize(
|
13
|
-
|
12
|
+
def initialize(channel)
|
13
|
+
super(channel)
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
send_message EmbeddedProtocol::InboundMessage::VersionRequest.new(id: @id)
|
15
|
+
send_message EmbeddedProtocol::InboundMessage::VersionRequest.new(id: id)
|
18
16
|
end
|
19
17
|
|
20
18
|
def update(error, message)
|
@@ -22,7 +20,7 @@ module Sass
|
|
22
20
|
|
23
21
|
case message
|
24
22
|
when EmbeddedProtocol::OutboundMessage::VersionResponse
|
25
|
-
return unless message.id ==
|
23
|
+
return unless message.id == id
|
26
24
|
|
27
25
|
Thread.new do
|
28
26
|
super(nil, message)
|
data/lib/sass/embedded.rb
CHANGED
@@ -2,17 +2,17 @@
|
|
2
2
|
|
3
3
|
require 'base64'
|
4
4
|
require 'json'
|
5
|
-
require_relative 'embedded/
|
5
|
+
require_relative 'embedded/channel'
|
6
|
+
require_relative 'embedded/compile_context'
|
6
7
|
require_relative 'embedded/error'
|
7
|
-
require_relative 'embedded/info'
|
8
8
|
require_relative 'embedded/result'
|
9
|
-
require_relative 'embedded/transport'
|
10
9
|
require_relative 'embedded/util'
|
11
10
|
require_relative 'embedded/version'
|
11
|
+
require_relative 'embedded/version_context'
|
12
12
|
|
13
13
|
module Sass
|
14
14
|
# The {Embedded} host for using dart-sass-embedded. Each instance creates
|
15
|
-
# its own {
|
15
|
+
# its own {Channel}.
|
16
16
|
#
|
17
17
|
# @example
|
18
18
|
# embedded = Sass::Embedded.new
|
@@ -29,16 +29,14 @@ module Sass
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def initialize
|
32
|
-
@
|
33
|
-
@id_semaphore = Mutex.new
|
34
|
-
@id = 0
|
32
|
+
@channel = Channel.new
|
35
33
|
end
|
36
34
|
|
37
35
|
# The {Embedded#info} method.
|
38
36
|
#
|
39
37
|
# @raise [ProtocolError]
|
40
38
|
def info
|
41
|
-
@info ||= VersionContext.new(@
|
39
|
+
@info ||= VersionContext.new(@channel).receive_message
|
42
40
|
end
|
43
41
|
|
44
42
|
# The {Embedded#render} method.
|
@@ -70,7 +68,7 @@ module Sass
|
|
70
68
|
indent_width = parse_indent_width(indent_width)
|
71
69
|
linefeed = parse_linefeed(linefeed)
|
72
70
|
|
73
|
-
message = CompileContext.new(@
|
71
|
+
message = CompileContext.new(@channel,
|
74
72
|
data: data,
|
75
73
|
file: file,
|
76
74
|
indented_syntax: indented_syntax,
|
@@ -124,11 +122,11 @@ module Sass
|
|
124
122
|
end
|
125
123
|
|
126
124
|
def close
|
127
|
-
@
|
125
|
+
@channel.close
|
128
126
|
end
|
129
127
|
|
130
128
|
def closed?
|
131
|
-
@
|
129
|
+
@channel.closed?
|
132
130
|
end
|
133
131
|
|
134
132
|
private
|
@@ -254,13 +252,5 @@ module Sass
|
|
254
252
|
raise ArgumentError, 'linefeed must be one of :lf, :lfcr, :cr, :crlf'
|
255
253
|
end
|
256
254
|
end
|
257
|
-
|
258
|
-
def next_id
|
259
|
-
@id_semaphore.synchronize do
|
260
|
-
@id += 1
|
261
|
-
@id = 0 if @id == Compiler::PROTOCOL_ERROR_ID
|
262
|
-
@id
|
263
|
-
end
|
264
|
-
end
|
265
255
|
end
|
266
256
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sass-embedded
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- なつき
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-09-
|
11
|
+
date: 2021-09-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: google-protobuf
|
@@ -139,24 +139,26 @@ files:
|
|
139
139
|
- lib/sass-embedded.rb
|
140
140
|
- lib/sass.rb
|
141
141
|
- lib/sass/embedded.rb
|
142
|
-
- lib/sass/embedded/
|
142
|
+
- lib/sass/embedded/channel.rb
|
143
|
+
- lib/sass/embedded/compile_context.rb
|
143
144
|
- lib/sass/embedded/compiler.rb
|
145
|
+
- lib/sass/embedded/compiler/path.rb
|
146
|
+
- lib/sass/embedded/compiler/requirements.rb
|
144
147
|
- lib/sass/embedded/error.rb
|
145
|
-
- lib/sass/embedded/info.rb
|
146
148
|
- lib/sass/embedded/observer.rb
|
147
149
|
- lib/sass/embedded/platform.rb
|
148
|
-
- lib/sass/embedded/protocol.rb
|
149
150
|
- lib/sass/embedded/result.rb
|
150
151
|
- lib/sass/embedded/struct.rb
|
151
|
-
- lib/sass/embedded/transport.rb
|
152
152
|
- lib/sass/embedded/util.rb
|
153
153
|
- lib/sass/embedded/version.rb
|
154
|
+
- lib/sass/embedded/version_context.rb
|
155
|
+
- lib/sass/embedded_protocol.rb
|
154
156
|
homepage: https://github.com/ntkme/sass-embedded-host-ruby
|
155
157
|
licenses:
|
156
158
|
- MIT
|
157
159
|
metadata:
|
158
|
-
documentation_uri: https://www.rubydoc.info/gems/sass-embedded/0.
|
159
|
-
source_code_uri: https://github.com/ntkme/sass-embedded-host-ruby/tree/v0.
|
160
|
+
documentation_uri: https://www.rubydoc.info/gems/sass-embedded/0.9.0
|
161
|
+
source_code_uri: https://github.com/ntkme/sass-embedded-host-ruby/tree/v0.9.0
|
160
162
|
funding_uri: https://github.com/sponsors/ntkme
|
161
163
|
post_install_message:
|
162
164
|
rdoc_options: []
|
@@ -1,131 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'open3'
|
4
|
-
require 'observer'
|
5
|
-
require_relative 'compiler'
|
6
|
-
require_relative 'error'
|
7
|
-
require_relative 'protocol'
|
8
|
-
|
9
|
-
module Sass
|
10
|
-
class Embedded
|
11
|
-
# The {::Observable} {Transport} for low level communication with
|
12
|
-
# `dart-sass-embedded` using protocol buffers via stdio. Received messages
|
13
|
-
# can be observed by an {Observer}.
|
14
|
-
class Transport
|
15
|
-
include Observable
|
16
|
-
|
17
|
-
ONEOF_MESSAGE = EmbeddedProtocol::InboundMessage
|
18
|
-
.descriptor
|
19
|
-
.lookup_oneof('message')
|
20
|
-
.collect do |field_descriptor|
|
21
|
-
[field_descriptor.subtype, field_descriptor.name]
|
22
|
-
end.to_h
|
23
|
-
|
24
|
-
private_constant :ONEOF_MESSAGE
|
25
|
-
|
26
|
-
def initialize
|
27
|
-
@observerable_mutex = Mutex.new
|
28
|
-
@stdin_mutex = Mutex.new
|
29
|
-
@stdin, @stdout, @stderr, @wait_thread = Open3.popen3(Compiler::DART_SASS_EMBEDDED)
|
30
|
-
|
31
|
-
[@stdin, @stdout].each(&:binmode)
|
32
|
-
|
33
|
-
poll do
|
34
|
-
warn(@stderr.readline, uplevel: 1)
|
35
|
-
end
|
36
|
-
poll do
|
37
|
-
receive_proto read
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def add_observer(*args)
|
42
|
-
@observerable_mutex.synchronize do
|
43
|
-
super(*args)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def send_message(message)
|
48
|
-
write EmbeddedProtocol::InboundMessage.new(
|
49
|
-
ONEOF_MESSAGE[message.class.descriptor] => message
|
50
|
-
).to_proto
|
51
|
-
end
|
52
|
-
|
53
|
-
def close
|
54
|
-
delete_observers
|
55
|
-
@stdin.close unless @stdin.closed?
|
56
|
-
@stdout.close unless @stdout.closed?
|
57
|
-
@stderr.close unless @stderr.closed?
|
58
|
-
nil
|
59
|
-
end
|
60
|
-
|
61
|
-
def closed?
|
62
|
-
@stdin.closed?
|
63
|
-
end
|
64
|
-
|
65
|
-
private
|
66
|
-
|
67
|
-
def poll
|
68
|
-
Thread.new do
|
69
|
-
loop do
|
70
|
-
yield
|
71
|
-
rescue StandardError => e
|
72
|
-
notify_observers(e, nil)
|
73
|
-
close
|
74
|
-
break
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def notify_observers(*args)
|
80
|
-
@observerable_mutex.synchronize do
|
81
|
-
changed
|
82
|
-
super(*args)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def receive_proto(proto)
|
87
|
-
payload = EmbeddedProtocol::OutboundMessage.decode(proto)
|
88
|
-
message = payload[payload.message.to_s]
|
89
|
-
case message
|
90
|
-
when EmbeddedProtocol::ProtocolError
|
91
|
-
raise ProtocolError, message.message
|
92
|
-
else
|
93
|
-
notify_observers(nil, message)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def read
|
98
|
-
length = read_varint(@stdout)
|
99
|
-
@stdout.read(length)
|
100
|
-
end
|
101
|
-
|
102
|
-
def write(payload)
|
103
|
-
@stdin_mutex.synchronize do
|
104
|
-
write_varint(@stdin, payload.length)
|
105
|
-
@stdin.write payload
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
def read_varint(readable)
|
110
|
-
value = bits = 0
|
111
|
-
loop do
|
112
|
-
byte = readable.readbyte
|
113
|
-
value |= (byte & 0x7f) << bits
|
114
|
-
bits += 7
|
115
|
-
break if byte < 0x80
|
116
|
-
end
|
117
|
-
value
|
118
|
-
end
|
119
|
-
|
120
|
-
def write_varint(writeable, value)
|
121
|
-
bytes = []
|
122
|
-
until value < 0x80
|
123
|
-
bytes << (0x80 | (value & 0x7f))
|
124
|
-
value >>= 7
|
125
|
-
end
|
126
|
-
bytes << value
|
127
|
-
writeable.write bytes.pack('C*')
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|