sass-embedded 0.1.2 → 0.2.4

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,15 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sass
4
-
5
4
  class BaseError < StandardError; end
5
+
6
6
  class ProtocolError < BaseError; end
7
+
7
8
  class NotRenderedError < BaseError; end
9
+
8
10
  class InvalidStyleError < BaseError; end
11
+
9
12
  class UnsupportedValue < BaseError; end
10
13
 
11
14
  class CompilationError < BaseError
12
-
13
15
  attr_accessor :formatted, :file, :line, :column, :status
14
16
 
15
17
  def initialize(message, formatted, file, line, column, status)
@@ -23,6 +25,7 @@ module Sass
23
25
 
24
26
  def backtrace
25
27
  return nil if super.nil?
28
+
26
29
  ["#{@file}:#{@line}:#{@column}"] + super
27
30
  end
28
31
  end
data/lib/sass/platform.rb CHANGED
@@ -1,27 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
-
4
-
5
3
  module Sass
6
4
  module Platform
7
-
8
5
  OS = case RbConfig::CONFIG['host_os'].downcase
9
6
  when /linux/
10
- "linux"
7
+ 'linux'
11
8
  when /darwin/
12
- "darwin"
9
+ 'darwin'
13
10
  when /freebsd/
14
- "freebsd"
11
+ 'freebsd'
15
12
  when /netbsd/
16
- "netbsd"
13
+ 'netbsd'
17
14
  when /openbsd/
18
- "openbsd"
15
+ 'openbsd'
19
16
  when /dragonfly/
20
- "dragonflybsd"
17
+ 'dragonflybsd'
21
18
  when /sunos|solaris/
22
- "solaris"
19
+ 'solaris'
23
20
  when /mingw|mswin/
24
- "windows"
21
+ 'windows'
25
22
  else
26
23
  RbConfig::CONFIG['host_os'].downcase
27
24
  end
@@ -32,22 +29,22 @@ module Sass
32
29
 
33
30
  ARCH = case CPU.downcase
34
31
  when /amd64|x86_64|x64/
35
- "x86_64"
32
+ 'x86_64'
36
33
  when /i\d86|x86|i86pc/
37
- "i386"
34
+ 'i386'
38
35
  when /ppc64|powerpc64/
39
- "powerpc64"
36
+ 'powerpc64'
40
37
  when /ppc|powerpc/
41
- "powerpc"
38
+ 'powerpc'
42
39
  when /sparcv9|sparc64/
43
- "sparcv9"
40
+ 'sparcv9'
44
41
  when /arm64|aarch64/ # MacOS calls it "arm64", other operating systems "aarch64"
45
- "aarch64"
42
+ 'aarch64'
46
43
  when /^arm/
47
- if OS == "darwin" # Ruby before 3.0 reports "arm" instead of "arm64" as host_cpu on darwin
48
- "aarch64"
44
+ if OS == 'darwin' # Ruby before 3.0 reports "arm" instead of "arm64" as host_cpu on darwin
45
+ 'aarch64'
49
46
  else
50
- "arm"
47
+ 'arm'
51
48
  end
52
49
  else
53
50
  RbConfig::CONFIG['host_cpu']
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open3'
4
+ require 'observer'
5
+ require_relative '../../ext/embedded_sass_pb'
6
+
7
+ module Sass
8
+ class Transport
9
+ include Observable
10
+
11
+ DART_SASS_EMBEDDED = File.absolute_path(
12
+ "../../ext/sass_embedded/dart-sass-embedded#{Sass::Platform::OS == 'windows' ? '.bat' : ''}", __dir__
13
+ )
14
+
15
+ PROTOCOL_ERROR_ID = 4_294_967_295
16
+
17
+ def initialize
18
+ @stdin, @stdout, @stderr, @wait_thread = Open3.popen3(DART_SASS_EMBEDDED)
19
+ @stdin_semaphore = Mutex.new
20
+ @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
58
+ end
59
+
60
+ def send(req, id)
61
+ mutex = Mutex.new
62
+ resource = ConditionVariable.new
63
+
64
+ req_name = req.class.name.split('::').last.gsub(/\B(?=[A-Z])/, '_').downcase
65
+
66
+ message = Sass::EmbeddedProtocol::InboundMessage.new(req_name.to_sym => req)
67
+
68
+ error = nil
69
+ res = nil
70
+
71
+ @observerable_semaphore.synchronize do
72
+ MessageObserver.new self, id do |e, r|
73
+ mutex.synchronize do
74
+ error = e
75
+ res = r
76
+
77
+ resource.signal
78
+ end
79
+ end
80
+ end
81
+
82
+ mutex.synchronize do
83
+ write message.to_proto
84
+
85
+ resource.wait(mutex)
86
+ end
87
+
88
+ raise error if error
89
+
90
+ res
91
+ end
92
+
93
+ def close
94
+ delete_observers
95
+ @stdin.close unless @stdin.closed?
96
+ @stdout.close unless @stdout.closed?
97
+ @stderr.close unless @stderr.closed?
98
+ nil
99
+ end
100
+
101
+ private
102
+
103
+ def write(proto)
104
+ @stdin_semaphore.synchronize do
105
+ length = proto.length
106
+ while length.positive?
107
+ @stdin.write ((length > 0x7f ? 0x80 : 0) | (length & 0x7f)).chr
108
+ length >>= 7
109
+ end
110
+ @stdin.write proto
111
+ end
112
+ end
113
+
114
+ class MessageObserver
115
+ def initialize(obs, id, &block)
116
+ @obs = obs
117
+ @id = id
118
+ @block = block
119
+ @obs.add_observer self
120
+ end
121
+
122
+ def update(error, message)
123
+ if error
124
+ @obs.delete_observer self
125
+ @block.call error, nil
126
+ elsif message.error&.id == Sass::Transport::PROTOCOL_ERROR_ID
127
+ @obs.delete_observer self
128
+ @block.call Sass::ProtocolError.new(message.error.message), nil
129
+ else
130
+ res = message[message.message.to_s]
131
+ if (res['compilation_id'] || res['id']) == @id
132
+ @obs.delete_observer self
133
+ @block.call error, res
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
data/lib/sass/util.rb CHANGED
@@ -2,18 +2,18 @@
2
2
 
3
3
  module Sass
4
4
  module Util
5
- extend self
5
+ module_function
6
6
 
7
- def file_uri path
7
+ def file_uri(path)
8
8
  absolute_path = File.absolute_path(path)
9
9
 
10
- if !absolute_path.start_with?('/')
10
+ unless absolute_path.start_with?('/')
11
11
  components = absolute_path.split File::SEPARATOR
12
12
  components[0] = components[0].split(':').first.downcase
13
13
  absolute_path = components.join File::SEPARATOR
14
14
  end
15
15
 
16
- 'file://' + absolute_path
16
+ "file://#{absolute_path}"
17
17
  end
18
18
 
19
19
  def now
data/lib/sass/version.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sass
4
- VERSION = "0.1.2"
4
+ VERSION = '0.2.4'
5
5
  end
6
-
@@ -1,37 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path("../lib", __FILE__)
3
+ lib = File.expand_path('lib', __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
- require "sass/version"
5
+ require 'sass/version'
6
6
 
7
7
  Gem::Specification.new do |spec|
8
-
9
- spec.name = "sass-embedded"
8
+ spec.name = 'sass-embedded'
10
9
  spec.version = Sass::VERSION
11
- spec.authors = ["なつき"]
12
- spec.email = ["i@ntk.me"]
13
- spec.summary = "Use dart-sass with Ruby!"
14
- spec.description = "Use dart-sass with Ruby!"
15
- spec.homepage = "https://github.com/ntkme/embedded-host-ruby"
16
- spec.license = "MIT"
10
+ spec.authors = ['なつき']
11
+ spec.email = ['i@ntk.me']
12
+ spec.summary = 'Use dart-sass with Ruby!'
13
+ spec.description = 'Use dart-sass with Ruby!'
14
+ spec.homepage = 'https://github.com/ntkme/embedded-host-ruby'
15
+ spec.license = 'MIT'
17
16
 
18
17
  spec.files = `git ls-files -z`.split("\x0")
19
- spec.extensions = ['ext/sass_embedded/extconf.rb']
18
+ spec.extensions = ['ext/extconf.rb']
20
19
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
20
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
22
21
 
22
+ spec.required_ruby_version = '>= 2.0.0'
23
23
 
24
- spec.required_ruby_version = ">= 2.0.0"
25
-
26
- spec.require_paths = ["lib"]
24
+ spec.require_paths = ['lib']
27
25
 
28
26
  spec.platform = Gem::Platform::RUBY
29
27
 
30
- spec.add_dependency "google-protobuf", "~> 3.17.0"
28
+ spec.add_dependency 'google-protobuf', '~> 3.17.0'
31
29
 
32
- spec.add_development_dependency "bundler"
33
- spec.add_development_dependency "minitest", "~> 5.14.4"
34
- spec.add_development_dependency "minitest-around"
35
- spec.add_development_dependency "rake"
36
- spec.add_development_dependency "rake-compiler"
30
+ spec.add_development_dependency 'bundler'
31
+ spec.add_development_dependency 'minitest', '~> 5.14.4'
32
+ spec.add_development_dependency 'minitest-around'
33
+ spec.add_development_dependency 'rake'
34
+ spec.add_development_dependency 'rake-compiler'
35
+ spec.add_development_dependency 'rubocop'
37
36
  end
@@ -1,119 +1,120 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "test_helper"
3
+ require_relative 'test_helper'
4
4
 
5
5
  module Sass
6
- class CompilerTest < MiniTest::Test
6
+ class EmbeddedTest < MiniTest::Test
7
7
  include TempFileTest
8
8
 
9
9
  def setup
10
- @compiler = Embedded::Compiler.new
10
+ @embedded = Embedded.new
11
11
  end
12
12
 
13
13
  def teardown
14
+ @embedded.close
14
15
  end
15
16
 
16
17
  def render(data)
17
- @compiler.render({ data: data })[:css]
18
+ @embedded.render({ data: data })[:css]
18
19
  end
19
20
 
20
21
  def test_line_comments
21
22
  skip 'not supported'
22
23
 
23
- template = <<-SCSS
24
- .foo {
25
- baz: bang; }
24
+ template = <<~SCSS
25
+ .foo {
26
+ baz: bang; }
26
27
  SCSS
27
- expected_output = <<-CSS
28
- /* line 1, stdin */
29
- .foo {
30
- baz: bang;
31
- }
28
+ expected_output = <<~CSS
29
+ /* line 1, stdin */
30
+ .foo {
31
+ baz: bang;
32
+ }
32
33
  CSS
33
- output = @compiler.render({
34
- data: template,
35
- source_comments: true
36
- })
34
+ output = @embedded.render({
35
+ data: template,
36
+ source_comments: true
37
+ })
37
38
  assert_equal expected_output, output[:css]
38
39
  end
39
40
 
40
41
  def test_one_line_comments
41
- assert_equal <<CSS.chomp, render(<<SCSS)
42
- .foo {
43
- baz: bang;
44
- }
45
- CSS
46
- .foo {// bar: baz;}
47
- baz: bang; //}
48
- }
49
- SCSS
50
- assert_equal <<CSS.chomp, render(<<SCSS)
51
- .foo bar[val="//"] {
52
- baz: bang;
53
- }
54
- CSS
55
- .foo bar[val="//"] {
56
- baz: bang; //}
57
- }
58
- SCSS
59
- end
42
+ assert_equal <<~CSS.chomp, render(<<~SCSS)
43
+ .foo {
44
+ baz: bang;
45
+ }
46
+ CSS
47
+ .foo {// bar: baz;}
48
+ baz: bang; //}
49
+ }
50
+ SCSS
51
+ assert_equal <<~CSS.chomp, render(<<~SCSS)
52
+ .foo bar[val="//"] {
53
+ baz: bang;
54
+ }
55
+ CSS
56
+ .foo bar[val="//"] {
57
+ baz: bang; //}
58
+ }
59
+ SCSS
60
+ end
60
61
 
61
62
  def test_variables
62
- assert_equal <<CSS.chomp, render(<<SCSS)
63
- blat {
64
- a: foo;
65
- }
66
- CSS
67
- $var: foo;
68
-
69
- blat {a: $var}
70
- SCSS
71
-
72
- assert_equal <<CSS.chomp, render(<<SCSS)
73
- foo {
74
- a: 2;
75
- b: 6;
76
- }
77
- CSS
78
- foo {
79
- $var: 2;
80
- $another-var: 4;
81
- a: $var;
82
- b: $var + $another-var;}
83
- SCSS
63
+ assert_equal <<~CSS.chomp, render(<<~SCSS)
64
+ blat {
65
+ a: foo;
66
+ }
67
+ CSS
68
+ $var: foo;
69
+ #{' '}
70
+ blat {a: $var}
71
+ SCSS
72
+
73
+ assert_equal <<~CSS.chomp, render(<<~SCSS)
74
+ foo {
75
+ a: 2;
76
+ b: 6;
77
+ }
78
+ CSS
79
+ foo {
80
+ $var: 2;
81
+ $another-var: 4;
82
+ a: $var;
83
+ b: $var + $another-var;}
84
+ SCSS
84
85
  end
85
86
 
86
87
  def test_precision
87
88
  skip 'not supported'
88
89
 
89
- template = <<-SCSS
90
- $var: 1;
91
- .foo {
92
- baz: $var / 3; }
93
- SCSS
94
- expected_output = <<-CSS.chomp
95
- .foo {
96
- baz: 0.33333333;
97
- }
98
- CSS
99
- output = @compiler.render({
100
- data: template,
101
- precision: 8
102
- })
90
+ template = <<~SCSS
91
+ $var: 1;
92
+ .foo {
93
+ baz: $var / 3; }
94
+ SCSS
95
+ expected_output = <<~CSS.chomp
96
+ .foo {
97
+ baz: 0.33333333;
98
+ }
99
+ CSS
100
+ output = @embedded.render({
101
+ data: template,
102
+ precision: 8
103
+ })
103
104
  assert_equal expected_output, output
104
105
  end
105
106
 
106
107
  def test_precision_not_specified
107
- template = <<-SCSS
108
- $var: 1;
109
- .foo {
110
- baz: $var / 3; }
111
- SCSS
112
- expected_output = <<-CSS.chomp
113
- .foo {
114
- baz: 0.3333333333;
115
- }
116
- CSS
108
+ template = <<~SCSS
109
+ $var: 1;
110
+ .foo {
111
+ baz: $var / 3; }
112
+ SCSS
113
+ expected_output = <<~CSS.chomp
114
+ .foo {
115
+ baz: 0.3333333333;
116
+ }
117
+ CSS
117
118
  output = render(template)
118
119
  assert_equal expected_output, output
119
120
  end
@@ -121,63 +122,65 @@ CSS
121
122
  def test_source_map
122
123
  temp_dir('admin')
123
124
 
124
- temp_file('admin/text-color.scss', <<SCSS)
125
- p {
126
- color: red;
127
- }
128
- SCSS
129
- temp_file('style.scss', <<SCSS)
130
- @use 'admin/text-color';
131
-
132
- p {
133
- padding: 20px;
134
- }
135
- SCSS
136
- output = @compiler.render({
137
- data: File.read("style.scss"),
138
- source_map: "style.scss.map"
139
- })
125
+ temp_file('admin/text-color.scss', <<~SCSS)
126
+ p {
127
+ color: red;
128
+ }
129
+ SCSS
130
+ temp_file('style.scss', <<~SCSS)
131
+ @use 'admin/text-color';
132
+ #{' '}
133
+ p {
134
+ padding: 20px;
135
+ }
136
+ SCSS
137
+ output = @embedded.render({
138
+ data: File.read('style.scss'),
139
+ source_map: 'style.scss.map'
140
+ })
140
141
 
141
142
  assert output[:map].start_with? '{"version":3,'
142
143
  end
143
144
 
144
145
  def test_no_source_map
145
- output = @compiler.render({
146
- data: "$size: 30px;"
147
- })
148
- assert_equal "", output[:map]
146
+ output = @embedded.render({
147
+ data: '$size: 30px;'
148
+ })
149
+ assert_equal '', output[:map]
149
150
  end
150
151
 
151
152
  def test_include_paths
152
- temp_dir("included_1")
153
- temp_dir("included_2")
154
-
155
- temp_file("included_1/import_parent.scss", "$s: 30px;")
156
- temp_file("included_2/import.scss", "@use 'import_parent' as *; $size: $s;")
157
- temp_file("styles.scss", "@use 'import.scss' as *; .hi { width: $size; }")
158
-
159
- assert_equal ".hi {\n width: 30px;\n}", @compiler.render({
160
- data: File.read("styles.scss"),
161
- include_paths: [ "included_1", "included_2" ]
162
- })[:css]
153
+ temp_dir('included_1')
154
+ temp_dir('included_2')
155
+
156
+ temp_file('included_1/import_parent.scss', '$s: 30px;')
157
+ temp_file('included_2/import.scss', "@use 'import_parent' as *; $size: $s;")
158
+ temp_file('styles.scss', "@use 'import.scss' as *; .hi { width: $size; }")
159
+
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]
163
166
  end
164
167
 
165
168
  def test_global_include_paths
166
- temp_dir("included_1")
167
- temp_dir("included_2")
169
+ temp_dir('included_1')
170
+ temp_dir('included_2')
168
171
 
169
- temp_file("included_1/import_parent.scss", "$s: 30px;")
170
- temp_file("included_2/import.scss", "@use 'import_parent' as *; $size: $s;")
171
- temp_file("styles.scss", "@use 'import.scss' as *; .hi { width: $size; }")
172
+ temp_file('included_1/import_parent.scss', '$s: 30px;')
173
+ temp_file('included_2/import.scss', "@use 'import_parent' as *; $size: $s;")
174
+ temp_file('styles.scss', "@use 'import.scss' as *; .hi { width: $size; }")
172
175
 
173
- ::Sass.include_paths << "included_1"
174
- ::Sass.include_paths << "included_2"
176
+ ::Sass.include_paths << 'included_1'
177
+ ::Sass.include_paths << 'included_2'
175
178
 
176
- assert_equal ".hi {\n width: 30px;\n}", render(File.read("styles.scss"))
179
+ assert_equal ".hi {\n width: 30px;\n}", render(File.read('styles.scss'))
177
180
  end
178
181
 
179
182
  def test_env_include_paths
180
- expected_include_paths = [ "included_3", "included_4" ]
183
+ expected_include_paths = %w[included_3 included_4]
181
184
 
182
185
  ::Sass.instance_eval { @include_paths = nil }
183
186
 
@@ -189,58 +192,53 @@ SCSS
189
192
  end
190
193
 
191
194
  def test_include_paths_not_configured
192
- temp_dir("included_5")
193
- temp_dir("included_6")
194
- temp_file("included_5/import_parent.scss", "$s: 30px;")
195
- temp_file("included_6/import.scss", "@use 'import_parent' as *; $size: $s;")
196
- temp_file("styles.scss", "@use 'import.scss' as *; .hi { width: $size; }")
195
+ temp_dir('included_5')
196
+ temp_dir('included_6')
197
+ temp_file('included_5/import_parent.scss', '$s: 30px;')
198
+ temp_file('included_6/import.scss', "@use 'import_parent' as *; $size: $s;")
199
+ temp_file('styles.scss', "@use 'import.scss' as *; .hi { width: $size; }")
197
200
 
198
201
  assert_raises(CompilationError) do
199
- render(File.read("styles.scss"))
202
+ render(File.read('styles.scss'))
200
203
  end
201
204
  end
202
205
 
203
206
  def test_sass_variation
204
- sass = <<SASS
205
- $size: 30px
206
- .foo
207
- width: $size
208
- SASS
209
-
210
- css = <<CSS.chomp
211
- .foo {
212
- width: 30px;
213
- }
214
- CSS
215
-
216
- assert_equal css, @compiler.render({ data: sass, indented_syntax: true })[:css]
207
+ sass = <<~SASS
208
+ $size: 30px
209
+ .foo
210
+ width: $size
211
+ SASS
212
+
213
+ css = <<~CSS.chomp
214
+ .foo {
215
+ width: 30px;
216
+ }
217
+ CSS
218
+
219
+ assert_equal css, @embedded.render({ data: sass, indented_syntax: true })[:css]
217
220
  assert_raises(CompilationError) do
218
- @compiler.render({ data: sass, indented_syntax: false })
221
+ @embedded.render({ data: sass, indented_syntax: false })
219
222
  end
220
223
  end
221
224
 
222
225
  def test_inline_source_maps
223
226
  skip 'not supported'
224
227
 
225
- template = <<-SCSS
226
- .foo {
227
- baz: bang; }
228
+ template = <<~SCSS
229
+ .foo {
230
+ baz: bang; }
228
231
  SCSS
229
- expected_output = <<-CSS
230
- /* line 1, stdin */
231
- .foo {
232
- baz: bang; }
233
- CSS
234
232
 
235
- output = @compiler.render({
236
- data: template,
237
- source_map: ".",
238
- source_map_embed: true,
239
- source_map_contents: true
240
- })[:css]
233
+ output = @embedded.render({
234
+ data: template,
235
+ source_map: '.',
236
+ source_map_embed: true,
237
+ source_map_contents: true
238
+ })[:css]
241
239
 
242
- assert_match /sourceMappingURL/, output
243
- assert_match /.foo/, output
240
+ assert_match(/sourceMappingURL/, output)
241
+ assert_match(/.foo/, output)
244
242
  end
245
243
 
246
244
  def test_empty_template
@@ -249,12 +247,12 @@ CSS
249
247
  end
250
248
 
251
249
  def test_import_plain_css
252
- temp_file("test.css", ".something{color: red}")
253
- expected_output = <<-CSS.chomp
254
- .something {
255
- color: red;
256
- }
257
- CSS
250
+ temp_file('test.css', '.something{color: red}')
251
+ expected_output = <<~CSS.chomp
252
+ .something {
253
+ color: red;
254
+ }
255
+ CSS
258
256
 
259
257
  output = render("@use 'test';")
260
258
  assert_equal expected_output, output
@@ -265,10 +263,10 @@ CSS
265
263
  threads = []
266
264
  10.times do |i|
267
265
  threads << Thread.new(i) do |id|
268
- output = @compiler.render({
269
- data: "div { width: #{id} }",
270
- })[:css]
271
- assert_match /#{id}/, output
266
+ output = @embedded.render({
267
+ data: "div { width: #{id} }"
268
+ })[:css]
269
+ assert_match(/#{id}/, output)
272
270
  end
273
271
  end
274
272
  threads.each(&:join)