sass-embedded 0.2.4 → 0.3.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.
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