sass-embedded 0.8.1 → 0.8.2

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.
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Embedded
5
+ class Error < StandardError; end
6
+
7
+ class ProtocolError < Error; end
8
+
9
+ # The {Error} raised by {Embedded#render}.
10
+ class RenderError < Error
11
+ attr_reader :formatted, :file, :line, :column, :status
12
+
13
+ def initialize(message, formatted, file, line, column, status)
14
+ super(message)
15
+ @formatted = formatted
16
+ @file = file
17
+ @line = line
18
+ @column = column
19
+ @status = status
20
+ end
21
+
22
+ def backtrace
23
+ return nil if super.nil?
24
+
25
+ ["#{@file}:#{@line}:#{@column}"] + super
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'observer'
4
+ require_relative 'protocol'
5
+
6
+ module Sass
7
+ class Embedded
8
+ # The {Observer} for {Embedded#info}.
9
+ class VersionContext
10
+ include Observer
11
+
12
+ def initialize(transport, id)
13
+ @id = id
14
+
15
+ super(transport)
16
+
17
+ send_message EmbeddedProtocol::InboundMessage::VersionRequest.new(id: @id)
18
+ end
19
+
20
+ def update(error, message)
21
+ raise error unless error.nil?
22
+
23
+ case message
24
+ when EmbeddedProtocol::OutboundMessage::VersionResponse
25
+ return unless message.id == @id
26
+
27
+ Thread.new do
28
+ super(nil, message)
29
+ end
30
+ end
31
+ rescue StandardError => e
32
+ Thread.new do
33
+ super(e, nil)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Embedded
5
+ # The {Observer} module for receiving messages from {Transport}.
6
+ module Observer
7
+ def initialize(transport)
8
+ @transport = transport
9
+ @mutex = Mutex.new
10
+ @condition_variable = ConditionVariable.new
11
+ @error = nil
12
+ @message = nil
13
+ @transport.add_observer self
14
+ end
15
+
16
+ def receive_message
17
+ @mutex.synchronize do
18
+ @condition_variable.wait(@mutex) if @error.nil? && @message.nil?
19
+ end
20
+
21
+ raise @error unless @error.nil?
22
+
23
+ @message
24
+ end
25
+
26
+ def update(error, message)
27
+ @transport.delete_observer self
28
+ @mutex.synchronize do
29
+ @error = error
30
+ @message = message
31
+ @condition_variable.broadcast
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def send_message(message)
38
+ @transport.send_message(message)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Embedded
5
+ module Platform
6
+ OS = case RbConfig::CONFIG['host_os'].downcase
7
+ when /linux/
8
+ 'linux'
9
+ when /darwin/
10
+ 'darwin'
11
+ when /freebsd/
12
+ 'freebsd'
13
+ when /netbsd/
14
+ 'netbsd'
15
+ when /openbsd/
16
+ 'openbsd'
17
+ when /dragonfly/
18
+ 'dragonflybsd'
19
+ when /sunos|solaris/
20
+ 'solaris'
21
+ when /mingw|mswin/
22
+ 'windows'
23
+ else
24
+ RbConfig::CONFIG['host_os'].downcase
25
+ end
26
+
27
+ OSVERSION = RbConfig::CONFIG['host_os'].gsub(/[^\d]/, '').to_i
28
+
29
+ CPU = RbConfig::CONFIG['host_cpu']
30
+
31
+ ARCH = case CPU.downcase
32
+ when /amd64|x86_64|x64/
33
+ 'x86_64'
34
+ when /i\d86|x86|i86pc/
35
+ 'i386'
36
+ when /ppc64|powerpc64/
37
+ 'powerpc64'
38
+ when /ppc|powerpc/
39
+ 'powerpc'
40
+ when /sparcv9|sparc64/
41
+ 'sparcv9'
42
+ when /arm64|aarch64/ # MacOS calls it "arm64", other operating systems "aarch64"
43
+ 'aarch64'
44
+ when /^arm/
45
+ if OS == 'darwin' # Ruby before 3.0 reports "arm" instead of "arm64" as host_cpu on darwin
46
+ 'aarch64'
47
+ else
48
+ 'arm'
49
+ end
50
+ else
51
+ RbConfig::CONFIG['host_cpu']
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../ext/sass/embedded_sass_pb'
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'struct'
4
+
5
+ module Sass
6
+ class Embedded
7
+ # The {RenderResult} of {Embedded#render}.
8
+ class RenderResult
9
+ include Struct
10
+
11
+ attr_reader :css, :map, :stats
12
+
13
+ def initialize(css, map, stats)
14
+ @css = css
15
+ @map = map
16
+ @stats = stats
17
+ end
18
+ end
19
+
20
+ # The {RenderResultStats} of {Embedded#render}.
21
+ class RenderResultStats
22
+ include Struct
23
+
24
+ attr_reader :entry, :start, :end, :duration
25
+
26
+ def initialize(entry, start, finish, duration)
27
+ @entry = entry
28
+ @start = start
29
+ @end = finish
30
+ @duration = duration
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Embedded
5
+ # The {Struct} module.
6
+ module Struct
7
+ def [](key)
8
+ instance_variable_get("@#{key}".to_sym)
9
+ end
10
+
11
+ def to_h
12
+ instance_variables.map do |variable|
13
+ [variable[1..].to_sym, instance_variable_get(variable)]
14
+ end.to_h
15
+ end
16
+
17
+ def to_s
18
+ to_h.to_s
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,131 @@
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
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+ require 'uri'
5
+
6
+ module Sass
7
+ class Embedded
8
+ # The {Util} module.
9
+ module Util
10
+ URI_PARSER = URI::Parser.new({ RESERVED: ';/?:@&=+$,' })
11
+
12
+ private_constant :URI_PARSER
13
+
14
+ module_function
15
+
16
+ def file_uri_from_path(path)
17
+ "file://#{Platform::OS == 'windows' ? File::SEPARATOR : ''}#{URI_PARSER.escape(path)}"
18
+ end
19
+
20
+ def path_from_file_uri(file_uri)
21
+ URI_PARSER.unescape(file_uri[(Platform::OS == 'windows' ? 8 : 7)..])
22
+ end
23
+
24
+ def relative_path(from, to)
25
+ Pathname.new(File.absolute_path(to)).relative_path_from(Pathname.new(File.absolute_path(from))).to_s
26
+ end
27
+
28
+ def now
29
+ (Time.now.to_f * 1000).to_i
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sass
4
- VERSION = '0.8.1'
4
+ class Embedded
5
+ VERSION = '0.8.2'
6
+ end
5
7
  end
data/lib/sass/embedded.rb CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  require 'base64'
4
4
  require 'json'
5
+ require_relative 'embedded/compile'
6
+ require_relative 'embedded/error'
7
+ require_relative 'embedded/info'
8
+ require_relative 'embedded/result'
9
+ require_relative 'embedded/transport'
10
+ require_relative 'embedded/util'
11
+ require_relative 'embedded/version'
5
12
 
6
13
  module Sass
7
14
  # The {Embedded} host for using dart-sass-embedded. Each instance creates
@@ -13,6 +20,14 @@ module Sass
13
20
  # result = embedded.render(file: 'style.css')
14
21
  # embedded.close
15
22
  class Embedded
23
+ def self.include_paths
24
+ @include_paths ||= if ENV['SASS_PATH']
25
+ ENV['SASS_PATH'].split(File::PATH_SEPARATOR)
26
+ else
27
+ []
28
+ end
29
+ end
30
+
16
31
  def initialize
17
32
  @transport = Transport.new
18
33
  @id_semaphore = Mutex.new
data/lib/sass.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'sass/embedded'
4
+
3
5
  # The Sass module. This communicates with Embedded Dart Sass using
4
6
  # the Embedded Sass protocol.
5
7
  module Sass
@@ -19,11 +21,7 @@ module Sass
19
21
  # Sass.include_paths << File.dirname(__FILE__) + '/sass'
20
22
  # @return [Array]
21
23
  def include_paths
22
- @include_paths ||= if ENV['SASS_PATH']
23
- ENV['SASS_PATH'].split(File::PATH_SEPARATOR)
24
- else
25
- []
26
- end
24
+ Embedded.include_paths
27
25
  end
28
26
 
29
27
  # The global {.info} method. This instantiates a global {Embedded} instance
@@ -31,7 +29,7 @@ module Sass
31
29
  #
32
30
  # @raise [ProtocolError]
33
31
  def info
34
- embedded.info
32
+ instance.info
35
33
  end
36
34
 
37
35
  # The global {.render} method. This instantiates a global {Embedded} instance
@@ -47,28 +45,15 @@ module Sass
47
45
  # @raise [ProtocolError]
48
46
  # @raise [RenderError]
49
47
  def render(**kwargs)
50
- embedded.render(**kwargs)
48
+ instance.render(**kwargs)
51
49
  end
52
50
 
53
51
  private
54
52
 
55
- def embedded
56
- return @embedded if defined?(@embedded) && !@embedded.closed?
53
+ def instance
54
+ return @instance if defined?(@instance) && !@instance.closed?
57
55
 
58
- @embedded = Sass::Embedded.new
56
+ @instance = Embedded.new
59
57
  end
60
58
  end
61
59
  end
62
-
63
- require_relative 'sass/version'
64
- require_relative 'sass/platform'
65
- require_relative 'sass/compiler'
66
- require_relative 'sass/util'
67
- require_relative 'sass/struct'
68
- require_relative 'sass/result'
69
- require_relative 'sass/error'
70
- require_relative 'sass/transport'
71
- require_relative 'sass/observer'
72
- require_relative 'sass/info'
73
- require_relative 'sass/compile'
74
- require_relative 'sass/embedded'
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.8.1
4
+ version: 0.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - なつき
@@ -138,24 +138,25 @@ files:
138
138
  - ext/sass/unzip.vbs
139
139
  - lib/sass-embedded.rb
140
140
  - lib/sass.rb
141
- - lib/sass/compile.rb
142
- - lib/sass/compiler.rb
143
141
  - lib/sass/embedded.rb
144
- - lib/sass/error.rb
145
- - lib/sass/info.rb
146
- - lib/sass/observer.rb
147
- - lib/sass/platform.rb
148
- - lib/sass/result.rb
149
- - lib/sass/struct.rb
150
- - lib/sass/transport.rb
151
- - lib/sass/util.rb
152
- - lib/sass/version.rb
142
+ - lib/sass/embedded/compile.rb
143
+ - lib/sass/embedded/compiler.rb
144
+ - lib/sass/embedded/error.rb
145
+ - lib/sass/embedded/info.rb
146
+ - lib/sass/embedded/observer.rb
147
+ - lib/sass/embedded/platform.rb
148
+ - lib/sass/embedded/protocol.rb
149
+ - lib/sass/embedded/result.rb
150
+ - lib/sass/embedded/struct.rb
151
+ - lib/sass/embedded/transport.rb
152
+ - lib/sass/embedded/util.rb
153
+ - lib/sass/embedded/version.rb
153
154
  homepage: https://github.com/ntkme/sass-embedded-host-ruby
154
155
  licenses:
155
156
  - MIT
156
157
  metadata:
157
- documentation_uri: https://www.rubydoc.info/gems/sass-embedded/0.8.1
158
- source_code_uri: https://github.com/ntkme/sass-embedded-host-ruby/tree/v0.8.1
158
+ documentation_uri: https://www.rubydoc.info/gems/sass-embedded/0.8.2
159
+ source_code_uri: https://github.com/ntkme/sass-embedded-host-ruby/tree/v0.8.2
159
160
  funding_uri: https://github.com/sponsors/ntkme
160
161
  post_install_message:
161
162
  rdoc_options: []