sass-embedded 0.8.1 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []