sass-embedded 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/sass/error.rb CHANGED
@@ -1,17 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sass
4
- class BaseError < StandardError; end
4
+ class SassError < StandardError; end
5
5
 
6
- class ProtocolError < BaseError; end
6
+ class ProtocolError < SassError; end
7
7
 
8
- class NotRenderedError < BaseError; end
9
-
10
- class InvalidStyleError < BaseError; end
11
-
12
- class UnsupportedValue < BaseError; end
13
-
14
- class CompilationError < BaseError
8
+ # The error returned by {Sass.render}
9
+ class RenderError < SassError
15
10
  attr_accessor :formatted, :file, :line, :column, :status
16
11
 
17
12
  def initialize(message, formatted, file, line, column, status)
@@ -5,71 +5,39 @@ 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
11
  class Transport
9
12
  include Observable
10
13
 
11
14
  DART_SASS_EMBEDDED = File.absolute_path(
12
- "../../ext/sass_embedded/dart-sass-embedded#{Sass::Platform::OS == 'windows' ? '.bat' : ''}", __dir__
15
+ "../../ext/sass_embedded/dart-sass-embedded#{Platform::OS == 'windows' ? '.bat' : ''}", __dir__
13
16
  )
14
17
 
15
18
  PROTOCOL_ERROR_ID = 4_294_967_295
16
19
 
17
20
  def initialize
18
- @stdin, @stdout, @stderr, @wait_thread = Open3.popen3(DART_SASS_EMBEDDED)
19
21
  @stdin_semaphore = Mutex.new
20
22
  @observerable_semaphore = Mutex.new
21
-
22
- Thread.new do
23
- loop do
24
- bits = length = 0
25
- loop do
26
- byte = @stdout.readbyte
27
- length += (byte & 0x7f) << bits
28
- bits += 7
29
- break if byte <= 0x7f
30
- end
31
- changed
32
- payload = @stdout.read length
33
- @observerable_semaphore.synchronize do
34
- notify_observers nil, Sass::EmbeddedProtocol::OutboundMessage.decode(payload)
35
- end
36
- rescue Interrupt
37
- break
38
- rescue IOError => e
39
- notify_observers e, nil
40
- close
41
- break
42
- end
43
- end
44
-
45
- Thread.new do
46
- loop do
47
- warn @stderr.read
48
- rescue Interrupt
49
- break
50
- rescue IOError => e
51
- @observerable_semaphore.synchronize do
52
- notify_observers e, nil
53
- end
54
- close
55
- break
56
- end
57
- end
23
+ @stdin, @stdout, @stderr, @wait_thread = Open3.popen3(DART_SASS_EMBEDDED)
24
+ pipe @stderr, $stderr
25
+ receive
58
26
  end
59
27
 
60
- def send(req, id)
28
+ def send(req, res_id)
61
29
  mutex = Mutex.new
62
30
  resource = ConditionVariable.new
63
31
 
64
- req_name = req.class.name.split('::').last.gsub(/\B(?=[A-Z])/, '_').downcase
32
+ req_kind = req.class.name.split('::').last.gsub(/\B(?=[A-Z])/, '_').downcase
65
33
 
66
- message = Sass::EmbeddedProtocol::InboundMessage.new(req_name.to_sym => req)
34
+ message = EmbeddedProtocol::InboundMessage.new(req_kind => req)
67
35
 
68
36
  error = nil
69
37
  res = nil
70
38
 
71
39
  @observerable_semaphore.synchronize do
72
- MessageObserver.new self, id do |e, r|
40
+ MessageObserver.new self, res_id do |e, r|
73
41
  mutex.synchronize do
74
42
  error = e
75
43
  res = r
@@ -100,17 +68,60 @@ module Sass
100
68
 
101
69
  private
102
70
 
103
- def write(proto)
71
+ def receive
72
+ Thread.new do
73
+ loop do
74
+ bits = length = 0
75
+ loop do
76
+ byte = @stdout.readbyte
77
+ length += (byte & 0x7f) << bits
78
+ bits += 7
79
+ break if byte <= 0x7f
80
+ end
81
+ changed
82
+ payload = @stdout.read length
83
+ @observerable_semaphore.synchronize do
84
+ notify_observers nil, EmbeddedProtocol::OutboundMessage.decode(payload)
85
+ end
86
+ rescue Interrupt
87
+ break
88
+ rescue IOError => e
89
+ notify_observers e, nil
90
+ close
91
+ break
92
+ end
93
+ end
94
+ end
95
+
96
+ def pipe(readable, writeable)
97
+ Thread.new do
98
+ loop do
99
+ writeable.write readable.read
100
+ rescue Interrupt
101
+ break
102
+ rescue IOError => e
103
+ @observerable_semaphore.synchronize do
104
+ notify_observers e, nil
105
+ end
106
+ close
107
+ break
108
+ end
109
+ end
110
+ end
111
+
112
+ def write(payload)
104
113
  @stdin_semaphore.synchronize do
105
- length = proto.length
114
+ length = payload.length
106
115
  while length.positive?
107
116
  @stdin.write ((length > 0x7f ? 0x80 : 0) | (length & 0x7f)).chr
108
117
  length >>= 7
109
118
  end
110
- @stdin.write proto
119
+ @stdin.write payload
111
120
  end
112
121
  end
113
122
 
123
+ # The observer used to listen on messages from stdout, check if id
124
+ # matches the given request id, and yield back to the given block.
114
125
  class MessageObserver
115
126
  def initialize(obs, id, &block)
116
127
  @obs = obs
@@ -123,9 +134,9 @@ module Sass
123
134
  if error
124
135
  @obs.delete_observer self
125
136
  @block.call error, nil
126
- elsif message.error&.id == Sass::Transport::PROTOCOL_ERROR_ID
137
+ elsif message.error&.id == Transport::PROTOCOL_ERROR_ID
127
138
  @obs.delete_observer self
128
- @block.call Sass::ProtocolError.new(message.error.message), nil
139
+ @block.call ProtocolError.new(message.error.message), nil
129
140
  else
130
141
  res = message[message.message.to_s]
131
142
  if (res['compilation_id'] || res['id']) == @id
data/lib/sass/util.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sass
4
+ # Utilities functions
4
5
  module Util
5
6
  module_function
6
7
 
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.2.4'
4
+ VERSION = '0.3.0'
5
5
  end
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
20
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
21
 
22
- spec.required_ruby_version = '>= 2.0.0'
22
+ spec.required_ruby_version = '>= 2.6'
23
23
 
24
24
  spec.require_paths = ['lib']
25
25
 
@@ -33,4 +33,6 @@ Gem::Specification.new do |spec|
33
33
  spec.add_development_dependency 'rake'
34
34
  spec.add_development_dependency 'rake-compiler'
35
35
  spec.add_development_dependency 'rubocop'
36
+ spec.add_development_dependency 'rubocop-minitest'
37
+ spec.add_development_dependency 'rubocop-rake'
36
38
  end
@@ -15,7 +15,7 @@ module Sass
15
15
  end
16
16
 
17
17
  def render(data)
18
- @embedded.render({ data: data })[:css]
18
+ @embedded.render(data: data)[:css]
19
19
  end
20
20
 
21
21
  def test_line_comments
@@ -31,10 +31,8 @@ module Sass
31
31
  baz: bang;
32
32
  }
33
33
  CSS
34
- output = @embedded.render({
35
- data: template,
36
- source_comments: true
37
- })
34
+ output = @embedded.render(data: template,
35
+ source_comments: true)
38
36
  assert_equal expected_output, output[:css]
39
37
  end
40
38
 
@@ -97,10 +95,8 @@ module Sass
97
95
  baz: 0.33333333;
98
96
  }
99
97
  CSS
100
- output = @embedded.render({
101
- data: template,
102
- precision: 8
103
- })
98
+ output = @embedded.render(data: template,
99
+ precision: 8)
104
100
  assert_equal expected_output, output
105
101
  end
106
102
 
@@ -134,18 +130,14 @@ module Sass
134
130
  padding: 20px;
135
131
  }
136
132
  SCSS
137
- output = @embedded.render({
138
- data: File.read('style.scss'),
139
- source_map: 'style.scss.map'
140
- })
133
+ output = @embedded.render(data: File.read('style.scss'),
134
+ source_map: 'style.scss.map')
141
135
 
142
136
  assert output[:map].start_with? '{"version":3,'
143
137
  end
144
138
 
145
139
  def test_no_source_map
146
- output = @embedded.render({
147
- data: '$size: 30px;'
148
- })
140
+ output = @embedded.render(data: '$size: 30px;')
149
141
  assert_equal '', output[:map]
150
142
  end
151
143
 
@@ -157,12 +149,10 @@ module Sass
157
149
  temp_file('included_2/import.scss', "@use 'import_parent' as *; $size: $s;")
158
150
  temp_file('styles.scss', "@use 'import.scss' as *; .hi { width: $size; }")
159
151
 
160
- assert_equal ".hi {\n width: 30px;\n}", @embedded.render({
161
- data: File.read('styles.scss'),
162
- include_paths: %w[
163
- included_1 included_2
164
- ]
165
- })[:css]
152
+ assert_equal ".hi {\n width: 30px;\n}", @embedded.render(data: File.read('styles.scss'),
153
+ include_paths: %w[
154
+ included_1 included_2
155
+ ])[:css]
166
156
  end
167
157
 
168
158
  def test_global_include_paths
@@ -186,7 +176,7 @@ module Sass
186
176
 
187
177
  ENV['SASS_PATH'] = expected_include_paths.join(File::PATH_SEPARATOR)
188
178
 
189
- assert_equal expected_include_paths, Sass.include_paths
179
+ assert_equal expected_include_paths, ::Sass.include_paths
190
180
 
191
181
  ::Sass.include_paths.clear
192
182
  end
@@ -198,7 +188,7 @@ module Sass
198
188
  temp_file('included_6/import.scss', "@use 'import_parent' as *; $size: $s;")
199
189
  temp_file('styles.scss', "@use 'import.scss' as *; .hi { width: $size; }")
200
190
 
201
- assert_raises(CompilationError) do
191
+ assert_raises(RenderError) do
202
192
  render(File.read('styles.scss'))
203
193
  end
204
194
  end
@@ -216,9 +206,9 @@ module Sass
216
206
  }
217
207
  CSS
218
208
 
219
- assert_equal css, @embedded.render({ data: sass, indented_syntax: true })[:css]
220
- assert_raises(CompilationError) do
221
- @embedded.render({ data: sass, indented_syntax: false })
209
+ assert_equal css, @embedded.render(data: sass, indented_syntax: true)[:css]
210
+ assert_raises(RenderError) do
211
+ @embedded.render(data: sass, indented_syntax: false)
222
212
  end
223
213
  end
224
214
 
@@ -230,12 +220,10 @@ module Sass
230
220
  baz: bang; }
231
221
  SCSS
232
222
 
233
- output = @embedded.render({
234
- data: template,
235
- source_map: '.',
236
- source_map_embed: true,
237
- source_map_contents: true
238
- })[:css]
223
+ output = @embedded.render(data: template,
224
+ source_map: '.',
225
+ source_map_embed: true,
226
+ source_map_contents: true)[:css]
239
227
 
240
228
  assert_match(/sourceMappingURL/, output)
241
229
  assert_match(/.foo/, output)
@@ -263,9 +251,7 @@ module Sass
263
251
  threads = []
264
252
  10.times do |i|
265
253
  threads << Thread.new(i) do |id|
266
- output = @embedded.render({
267
- data: "div { width: #{id} }"
268
- })[:css]
254
+ output = @embedded.render(data: "div { width: #{id} }")[:css]
269
255
  assert_match(/#{id}/, output)
270
256
  end
271
257
  end
@@ -15,7 +15,7 @@ module Sass
15
15
  end
16
16
 
17
17
  def render(data, importer)
18
- @embedded.render({ data: data, importer: importer })[:css]
18
+ @embedded.render(data: data, importer: importer)[:css]
19
19
  end
20
20
 
21
21
  def test_custom_importer_works
@@ -86,17 +86,16 @@ module Sass
86
86
  end
87
87
 
88
88
  def test_custom_importer_that_does_not_resolve
89
- assert_raises(CompilationError) do
89
+ assert_raises(RenderError) do
90
90
  render("@import 'test.scss';", [
91
91
  lambda { |_url, _prev|
92
- return nil
93
92
  }
94
93
  ])
95
94
  end
96
95
  end
97
96
 
98
97
  def test_custom_importer_that_returns_error
99
- assert_raises(CompilationError) do
98
+ assert_raises(RenderError) do
100
99
  render("@import 'test.scss';", [
101
100
  lambda { |_url, _prev|
102
101
  IOError.new 'test error'
@@ -106,7 +105,7 @@ module Sass
106
105
  end
107
106
 
108
107
  def test_custom_importer_that_raises_error
109
- assert_raises(CompilationError) do
108
+ assert_raises(RenderError) do
110
109
  render("@import 'test.scss';", [
111
110
  lambda { |_url, _prev|
112
111
  raise IOError, 'test error'
@@ -116,15 +115,13 @@ module Sass
116
115
  end
117
116
 
118
117
  def test_parent_path_is_accessible
119
- output = @embedded.render({
120
- data: "@import 'parent.scss';",
121
- file: 'import-parent-filename.scss',
122
- importer: [
123
- lambda { |_url, prev|
124
- { contents: ".#{prev} { color: red; }" }
125
- }
126
- ]
127
- })[:css]
118
+ output = @embedded.render(data: "@import 'parent.scss';",
119
+ file: 'import-parent-filename.scss',
120
+ importer: [
121
+ lambda { |_url, prev|
122
+ { contents: ".#{prev} { color: red; }" }
123
+ }
124
+ ])[:css]
128
125
 
129
126
  assert_equal <<~CSS.chomp, output
130
127
  .import-parent-filename.scss {
@@ -134,23 +131,19 @@ module Sass
134
131
  end
135
132
 
136
133
  def test_call_embedded_importer
137
- output = @embedded.render({
138
- data: "@import 'parent.scss';",
139
- importer: [
140
- lambda { |_url, _prev|
141
- {
142
- contents: @embedded.render({
143
- data: "@import 'parent-parent.scss'",
144
- importer: [
145
- lambda { |_url, _prev|
146
- { contents: 'h1 { color: black; }' }
147
- }
148
- ]
149
- })[:css]
150
- }
134
+ output = @embedded.render(data: "@import 'parent.scss';",
135
+ importer: [
136
+ lambda { |_url, _prev|
137
+ {
138
+ contents: @embedded.render(data: "@import 'parent-parent.scss'",
139
+ importer: [
140
+ lambda { |_url, _prev|
141
+ { contents: 'h1 { color: black; }' }
142
+ }
143
+ ])[:css]
151
144
  }
152
- ]
153
- })[:css]
145
+ }
146
+ ])[:css]
154
147
 
155
148
  assert_equal <<~CSS.chomp, output
156
149
  h1 {
data/test/error_test.rb CHANGED
@@ -20,10 +20,8 @@ module Sass
20
20
  }
21
21
  SCSS
22
22
 
23
- @embedded.render({
24
- data: template
25
- })
26
- rescue Sass::CompilationError => e
23
+ @embedded.render(data: template)
24
+ rescue RenderError => e
27
25
  expected = 'stdin:3:20'
28
26
  assert_equal expected, e.backtrace.first
29
27
  end