sass-embedded 0.1.3 → 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.
- checksums.yaml +4 -4
- data/.github/workflows/build.yml +2 -2
- data/.rubocop.yml +14 -0
- data/README.md +3 -3
- data/Rakefile +11 -4
- data/ext/{sass_embedded/.gitignore → .gitignore} +0 -0
- data/ext/Makefile +51 -0
- data/ext/extconf.rb +160 -0
- data/lib/sass.rb +18 -10
- data/lib/sass/embedded.rb +317 -0
- data/lib/sass/error.rb +4 -9
- data/lib/sass/transport.rb +150 -0
- data/lib/sass/util.rb +1 -0
- data/lib/sass/version.rb +1 -1
- data/sass-embedded.gemspec +4 -2
- data/test/compiler_test.rb +25 -39
- data/test/custom_importer_test.rb +26 -33
- data/test/error_test.rb +4 -6
- data/test/functions_test.rb +151 -159
- data/test/output_style_test.rb +12 -12
- data/test/sass_test.rb +2 -6
- metadata +38 -9
- data/ext/sass_embedded/Makefile +0 -35
- data/ext/sass_embedded/extconf.rb +0 -124
- data/lib/sass/embedded/compiler.rb +0 -238
- data/lib/sass/embedded/transport.rb +0 -140
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e03d2c1775fdd10b7f9d2b21898ac0a6e31e0be96157139742384c7be9b4275a
|
4
|
+
data.tar.gz: dacdc9589a8da93246b791794f97be2c8818f8a492d77e473c9fa543fd49db9e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3be32cd37a7078fceabd16c661cf985fdfc1aa31fbf8b88e6c768cb8edd57749a30e28a77f367d6cc8c6c505710f402e1970ae6c12210b3f46fc10a42cf8d81d
|
7
|
+
data.tar.gz: 5a122cd7c2e883f28b9401ce70d8fb43fcfcf39e1ff64484f5bf036c6978ca1a01cf8d69b1ebbf22f0fde81f98272127cf5a96d381b0279028d316d90775ebfb
|
data/.github/workflows/build.yml
CHANGED
@@ -27,7 +27,7 @@ jobs:
|
|
27
27
|
bundler-cache: true
|
28
28
|
|
29
29
|
- name: Download dart-sass-embedded
|
30
|
-
run: bundle exec rake
|
30
|
+
run: bundle exec rake extconf
|
31
31
|
env:
|
32
32
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
33
33
|
|
@@ -41,4 +41,4 @@ jobs:
|
|
41
41
|
|
42
42
|
- name: Test Gem
|
43
43
|
run: |-
|
44
|
-
ruby -r sass -e "puts Sass.render(
|
44
|
+
ruby -r sass -e "puts Sass.render(data: 'h1 { color: black; }')[:css]"
|
data/.rubocop.yml
ADDED
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Embedded Sass Host for Ruby
|
2
2
|
|
3
|
+
[](https://github.com/ntkme/embedded-host-ruby/actions/workflows/build.yml)
|
4
|
+
|
3
5
|
This is a Ruby library that implements the host side of the [Embedded Sass protocol](https://github.com/sass/sass-embedded-protocol).
|
4
6
|
|
5
7
|
It exposes a Ruby API for Sass that's backed by a native [Dart Sass](https://sass-lang.com/dart-sass) executable.
|
@@ -9,9 +11,7 @@ It exposes a Ruby API for Sass that's backed by a native [Dart Sass](https://sas
|
|
9
11
|
``` ruby
|
10
12
|
require "sass"
|
11
13
|
|
12
|
-
Sass.render(
|
13
|
-
file: "style.scss"
|
14
|
-
})
|
14
|
+
Sass.render(file: "style.scss")
|
15
15
|
```
|
16
16
|
|
17
17
|
---
|
data/Rakefile
CHANGED
@@ -2,15 +2,22 @@
|
|
2
2
|
|
3
3
|
require 'bundler/gem_tasks'
|
4
4
|
|
5
|
-
task default:
|
5
|
+
task default: %i[rubocop test]
|
6
6
|
|
7
7
|
desc 'Download dart-sass-embedded'
|
8
|
-
task :
|
9
|
-
|
8
|
+
task :extconf do
|
9
|
+
system('make', '-C', 'ext')
|
10
10
|
end
|
11
11
|
|
12
12
|
desc 'Run all tests'
|
13
|
-
task :
|
13
|
+
task test: :extconf do
|
14
14
|
$LOAD_PATH.unshift('lib', 'test')
|
15
15
|
Dir.glob('./test/**/*_test.rb').sort.each { |f| require f }
|
16
16
|
end
|
17
|
+
|
18
|
+
begin
|
19
|
+
require 'rubocop/rake_task'
|
20
|
+
RuboCop::RakeTask.new
|
21
|
+
rescue LoadError
|
22
|
+
nil
|
23
|
+
end
|
File without changes
|
data/ext/Makefile
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
ifeq ($(OS),Windows_NT)
|
2
|
+
RM = cmd /c del /f /q /s
|
3
|
+
else
|
4
|
+
RM = rm -fr
|
5
|
+
endif
|
6
|
+
EXTCONF = ruby -- extconf.rb --without-sass-embedded --without-sass-embedded-protocol --without-protoc
|
7
|
+
|
8
|
+
.PHONY: all
|
9
|
+
all: sass_embedded embedded_sass_pb.rb
|
10
|
+
|
11
|
+
.PHONY: install
|
12
|
+
install:
|
13
|
+
|
14
|
+
.PHONY: clean
|
15
|
+
clean:
|
16
|
+
$(RM) embedded_sass_pb.rb protoc sass_embedded
|
17
|
+
|
18
|
+
.PHONY: distclean
|
19
|
+
distclean: clean
|
20
|
+
$(RM) embedded_sass.proto protoc-*.zip sass_embedded-*.tar.gz
|
21
|
+
|
22
|
+
ifeq ($(OS),Windows_NT)
|
23
|
+
sass_embedded-*.zip:
|
24
|
+
else
|
25
|
+
sass_embedded-*.tar.gz:
|
26
|
+
endif
|
27
|
+
$(EXTCONF) --with-sass-embedded
|
28
|
+
|
29
|
+
ifeq ($(OS),Windows_NT)
|
30
|
+
sass_embedded: sass_embedded-*.zip
|
31
|
+
powershell -c "Get-Item $< | Expand-Archive -DestinationPath . -Force"
|
32
|
+
else
|
33
|
+
sass_embedded: sass_embedded-*.tar.gz
|
34
|
+
tar -vxzf $<
|
35
|
+
endif
|
36
|
+
|
37
|
+
protoc-*.zip:
|
38
|
+
$(EXTCONF) --with-protoc
|
39
|
+
|
40
|
+
protoc: protoc-*.zip
|
41
|
+
ifeq ($(OS),Windows_NT)
|
42
|
+
powershell -c "Get-Item $< | Expand-Archive -DestinationPath $@ -Force"
|
43
|
+
else
|
44
|
+
unzip -od $@ $<
|
45
|
+
endif
|
46
|
+
|
47
|
+
embedded_sass.proto:
|
48
|
+
$(EXTCONF) --with-sass-embedded-protocol
|
49
|
+
|
50
|
+
%_pb.rb: %.proto protoc
|
51
|
+
./protoc/bin/protoc --ruby_out=. $<
|
data/ext/extconf.rb
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'mkmf'
|
5
|
+
require 'json'
|
6
|
+
require 'open-uri'
|
7
|
+
require 'fileutils'
|
8
|
+
require_relative '../lib/sass/platform'
|
9
|
+
|
10
|
+
module Sass
|
11
|
+
# Install dependencies for sass-embedded during gem install
|
12
|
+
class Extconf
|
13
|
+
def initialize
|
14
|
+
get_with_config('sass-embedded', true) { latest_sass_embedded }
|
15
|
+
get_with_config('sass-embedded-protocol', true) { latest_sass_embedded_protocol }
|
16
|
+
get_with_config('protoc', true) { latest_protoc }
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def get_with_config(config, default)
|
22
|
+
val = with_config(config, default)
|
23
|
+
case val
|
24
|
+
when true
|
25
|
+
if block_given?
|
26
|
+
get yield
|
27
|
+
else
|
28
|
+
get default
|
29
|
+
end
|
30
|
+
when false
|
31
|
+
nil
|
32
|
+
else
|
33
|
+
get val
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def get(uri_s)
|
38
|
+
uri = URI.parse(uri_s)
|
39
|
+
path = File.absolute_path(File.basename(uri.path), __dir__)
|
40
|
+
if uri.is_a?(URI::File) || uri.instance_of?(URI::Generic)
|
41
|
+
FileUtils.copy_file uri.path, path
|
42
|
+
elsif uri.respond_to? :open
|
43
|
+
uri.open do |source|
|
44
|
+
File.open(path, 'wb') do |destination|
|
45
|
+
destination.write source.read
|
46
|
+
end
|
47
|
+
end
|
48
|
+
else
|
49
|
+
raise
|
50
|
+
end
|
51
|
+
rescue StandardError
|
52
|
+
raise "Failed to get: #{uri}"
|
53
|
+
end
|
54
|
+
|
55
|
+
def latest_release(repo, prerelease: false)
|
56
|
+
if prerelease
|
57
|
+
headers = {}
|
58
|
+
headers['Authorization'] = "token #{ENV['GITHUB_TOKEN']}" if ENV['GITHUB_TOKEN']
|
59
|
+
URI.parse("https://api.github.com/repos/#{repo}/releases").open(headers) do |file|
|
60
|
+
JSON.parse(file.read)[0]['tag_name']
|
61
|
+
end
|
62
|
+
else
|
63
|
+
URI.parse("https://github.com/#{repo}/releases/latest").open do |file|
|
64
|
+
File.basename file.base_uri.to_s
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def latest_sass_embedded
|
70
|
+
repo = 'sass/dart-sass-embedded'
|
71
|
+
|
72
|
+
# TODO, don't use prerelease once a release is available
|
73
|
+
tag_name = latest_release repo, prerelease: true
|
74
|
+
|
75
|
+
os = case Sass::Platform::OS
|
76
|
+
when 'darwin'
|
77
|
+
'macos'
|
78
|
+
when 'linux'
|
79
|
+
'linux'
|
80
|
+
when 'windows'
|
81
|
+
'windows'
|
82
|
+
else
|
83
|
+
raise "Unsupported OS: #{Sass::Platform::OS}"
|
84
|
+
end
|
85
|
+
|
86
|
+
arch = case Sass::Platform::ARCH
|
87
|
+
when 'x86_64'
|
88
|
+
'x64'
|
89
|
+
when 'i386'
|
90
|
+
'ia32'
|
91
|
+
else
|
92
|
+
raise "Unsupported Arch: #{Sass::Platform::ARCH}"
|
93
|
+
end
|
94
|
+
|
95
|
+
ext = case os
|
96
|
+
when 'windows'
|
97
|
+
'zip'
|
98
|
+
else
|
99
|
+
'tar.gz'
|
100
|
+
end
|
101
|
+
|
102
|
+
"https://github.com/#{repo}/releases/download/#{tag_name}/sass_embedded-#{tag_name}-#{os}-#{arch}.#{ext}"
|
103
|
+
end
|
104
|
+
|
105
|
+
def latest_protoc
|
106
|
+
repo = 'protocolbuffers/protobuf'
|
107
|
+
|
108
|
+
tag_name = latest_release repo
|
109
|
+
|
110
|
+
os = case Sass::Platform::OS
|
111
|
+
when 'darwin'
|
112
|
+
'osx'
|
113
|
+
when 'linux'
|
114
|
+
'linux'
|
115
|
+
when 'windows'
|
116
|
+
'win'
|
117
|
+
else
|
118
|
+
raise "Unsupported OS: #{Sass::Platform::OS}"
|
119
|
+
end
|
120
|
+
|
121
|
+
arch = case Sass::Platform::ARCH
|
122
|
+
when 'aarch64'
|
123
|
+
'aarch_64'
|
124
|
+
when 'sparcv9'
|
125
|
+
's390'
|
126
|
+
when 'i386'
|
127
|
+
'x86_32'
|
128
|
+
when 'x86_64'
|
129
|
+
'x86_64'
|
130
|
+
when 'powerpc64'
|
131
|
+
'ppcle_64'
|
132
|
+
else
|
133
|
+
raise "Unsupported Arch: #{Sass::Platform::ARCH}"
|
134
|
+
end
|
135
|
+
|
136
|
+
os_arch = case os
|
137
|
+
when 'win'
|
138
|
+
os + arch.split('_').last
|
139
|
+
else
|
140
|
+
"#{os}-#{arch}"
|
141
|
+
end
|
142
|
+
|
143
|
+
ext = 'zip'
|
144
|
+
|
145
|
+
"https://github.com/#{repo}/releases/download/#{tag_name}/protoc-#{tag_name[1..]}-#{os_arch}.#{ext}"
|
146
|
+
end
|
147
|
+
|
148
|
+
def latest_sass_embedded_protocol
|
149
|
+
repo = 'sass/embedded-protocol'
|
150
|
+
|
151
|
+
# TODO: use latest release once available
|
152
|
+
# tag_name = latest_release repo
|
153
|
+
tag_name = 'HEAD'
|
154
|
+
|
155
|
+
"https://raw.githubusercontent.com/#{repo}/#{tag_name}/embedded_sass.proto"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
Sass::Extconf.new
|
data/lib/sass.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# The Sass module
|
3
4
|
module Sass
|
4
5
|
# The global include_paths for Sass files. This is meant for plugins and
|
5
6
|
# libraries to register the paths to their Sass stylesheets to that they may
|
6
7
|
# be `@imported`. This include path is used by every instance of
|
7
|
-
# {Sass::Embedded
|
8
|
-
#
|
8
|
+
# {Sass::Embedded}. They are lower-precedence than any include paths passed
|
9
|
+
# in via the `:include_paths` option.
|
9
10
|
#
|
10
11
|
# If the `SASS_PATH` environment variable is set,
|
11
12
|
# the initial value of `include_paths` will be initialized based on that.
|
@@ -13,7 +14,7 @@ module Sass
|
|
13
14
|
# (semicolon-separated on Windows).
|
14
15
|
#
|
15
16
|
# @example
|
16
|
-
# Sass.include_paths << File.dirname(__FILE__ + '/sass'
|
17
|
+
# Sass.include_paths << File.dirname(__FILE__) + '/sass'
|
17
18
|
# @return [Array<String, Pathname>]
|
18
19
|
def self.include_paths
|
19
20
|
@include_paths ||= if ENV['SASS_PATH']
|
@@ -23,14 +24,21 @@ module Sass
|
|
23
24
|
end
|
24
25
|
end
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
27
|
+
# The global render methods. This method automatically instantiates a
|
28
|
+
# global {Sass::Embedded} instance when invoked the first time and call
|
29
|
+
# `:render` method on the instance thereafter. The global {Sass::Embedded}
|
30
|
+
# is automatically closed via {Kernel.at_exit}.
|
31
|
+
# @example
|
32
|
+
# Sass.render(options)
|
33
|
+
# @return [Hash]
|
34
|
+
def self.render(**kwargs)
|
35
|
+
unless defined? @embedded
|
36
|
+
@embedded = Sass::Embedded.new
|
29
37
|
at_exit do
|
30
|
-
@
|
38
|
+
@embedded.close
|
31
39
|
end
|
32
40
|
end
|
33
|
-
@
|
41
|
+
@embedded.render(**kwargs)
|
34
42
|
end
|
35
43
|
end
|
36
44
|
|
@@ -38,5 +46,5 @@ require_relative 'sass/version'
|
|
38
46
|
require_relative 'sass/error'
|
39
47
|
require_relative 'sass/platform'
|
40
48
|
require_relative 'sass/util'
|
41
|
-
require_relative 'sass/
|
42
|
-
require_relative 'sass/embedded
|
49
|
+
require_relative 'sass/transport'
|
50
|
+
require_relative 'sass/embedded'
|
@@ -0,0 +1,317 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sass
|
4
|
+
# The interface for using dart-sass-embedded
|
5
|
+
class Embedded
|
6
|
+
def initialize
|
7
|
+
@transport = Transport.new
|
8
|
+
@id_semaphore = Mutex.new
|
9
|
+
@id = 0
|
10
|
+
end
|
11
|
+
|
12
|
+
# rubocop:disable Lint/UnusedMethodArgument
|
13
|
+
|
14
|
+
def render(data: nil,
|
15
|
+
file: nil,
|
16
|
+
indented_syntax: false,
|
17
|
+
include_paths: [],
|
18
|
+
output_style: :expanded,
|
19
|
+
precision: 5,
|
20
|
+
indent_type: :space,
|
21
|
+
indent_width: 2,
|
22
|
+
linefeed: :lf,
|
23
|
+
source_comments: false,
|
24
|
+
source_map: false,
|
25
|
+
out_file: nil,
|
26
|
+
omit_source_map_url: false,
|
27
|
+
source_map_contents: false,
|
28
|
+
source_map_embed: false,
|
29
|
+
source_map_root: '',
|
30
|
+
functions: {},
|
31
|
+
importer: [])
|
32
|
+
# rubocop:enable Lint/UnusedMethodArgument
|
33
|
+
|
34
|
+
start = Util.now
|
35
|
+
|
36
|
+
compilation_id = next_id
|
37
|
+
|
38
|
+
renderer = Renderer.new(
|
39
|
+
data: data,
|
40
|
+
file: file,
|
41
|
+
indented_syntax: indented_syntax,
|
42
|
+
include_paths: include_paths,
|
43
|
+
output_style: output_style,
|
44
|
+
source_map: source_map,
|
45
|
+
out_file: out_file,
|
46
|
+
functions: functions,
|
47
|
+
importer: importer
|
48
|
+
)
|
49
|
+
|
50
|
+
response = @transport.send renderer.compile_request(compilation_id), compilation_id
|
51
|
+
|
52
|
+
loop do
|
53
|
+
case response
|
54
|
+
when EmbeddedProtocol::OutboundMessage::CompileResponse
|
55
|
+
break
|
56
|
+
when EmbeddedProtocol::OutboundMessage::CanonicalizeRequest
|
57
|
+
response = @transport.send renderer.canonicalize_response(response), compilation_id
|
58
|
+
when EmbeddedProtocol::OutboundMessage::ImportRequest
|
59
|
+
response = @transport.send renderer.import_response(response), compilation_id
|
60
|
+
when EmbeddedProtocol::OutboundMessage::FunctionCallRequest
|
61
|
+
response = @transport.send renderer.function_call_response(response), compilation_id
|
62
|
+
when EmbeddedProtocol::ProtocolError
|
63
|
+
raise ProtocolError, response.message
|
64
|
+
else
|
65
|
+
raise ProtocolError, "Unexpected packet received: #{response}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
if response.failure
|
70
|
+
raise RenderError.new(
|
71
|
+
response.failure.message,
|
72
|
+
response.failure.formatted,
|
73
|
+
if response.failure.span
|
74
|
+
response.failure.span.url == '' ? 'stdin' : URI.parse(response.failure.span.url).path
|
75
|
+
end,
|
76
|
+
response.failure.span ? response.failure.span.start.line + 1 : nil,
|
77
|
+
response.failure.span ? response.failure.span.start.column + 1 : nil,
|
78
|
+
1
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
finish = Util.now
|
83
|
+
|
84
|
+
{
|
85
|
+
css: response.success.css,
|
86
|
+
map: response.success.source_map,
|
87
|
+
stats: {
|
88
|
+
entry: file.nil? ? 'data' : file,
|
89
|
+
start: start,
|
90
|
+
end: finish,
|
91
|
+
duration: finish - start
|
92
|
+
}
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
def close
|
97
|
+
@transport.close
|
98
|
+
nil
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def info
|
104
|
+
version_response = @transport.send EmbeddedProtocol::InboundMessage::VersionRequest.new(
|
105
|
+
id: next_id
|
106
|
+
)
|
107
|
+
{
|
108
|
+
compiler_version: version_response.compiler_version,
|
109
|
+
protocol_version: version_response.protocol_version,
|
110
|
+
implementation_name: version_response.implementation_name,
|
111
|
+
implementation_version: version_response.implementation_version
|
112
|
+
}
|
113
|
+
end
|
114
|
+
|
115
|
+
def next_id
|
116
|
+
@id_semaphore.synchronize do
|
117
|
+
@id += 1
|
118
|
+
@id = 0 if @id == Transport::PROTOCOL_ERROR_ID
|
119
|
+
@id
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Helper class that maintains render state
|
124
|
+
class Renderer
|
125
|
+
def initialize(data:,
|
126
|
+
file:,
|
127
|
+
indented_syntax:,
|
128
|
+
include_paths:,
|
129
|
+
output_style:,
|
130
|
+
source_map:,
|
131
|
+
out_file:,
|
132
|
+
functions:,
|
133
|
+
importer:)
|
134
|
+
raise ArgumentError, 'Either :data or :file must be set.' if file.nil? && data.nil?
|
135
|
+
|
136
|
+
@data = data
|
137
|
+
@file = file
|
138
|
+
@indented_syntax = indented_syntax
|
139
|
+
@include_paths = include_paths
|
140
|
+
@output_style = output_style
|
141
|
+
@source_map = source_map
|
142
|
+
@out_file = out_file
|
143
|
+
@global_functions = functions.keys
|
144
|
+
@functions = functions.transform_keys do |key|
|
145
|
+
key.to_s.split('(')[0].chomp
|
146
|
+
end
|
147
|
+
@importer = importer
|
148
|
+
@import_responses = {}
|
149
|
+
end
|
150
|
+
|
151
|
+
def compile_request(id)
|
152
|
+
EmbeddedProtocol::InboundMessage::CompileRequest.new(
|
153
|
+
id: id,
|
154
|
+
string: string,
|
155
|
+
path: path,
|
156
|
+
style: style,
|
157
|
+
source_map: source_map,
|
158
|
+
importers: importers,
|
159
|
+
global_functions: global_functions,
|
160
|
+
alert_color: $stderr.tty?,
|
161
|
+
alert_ascii: Platform::OS == 'windows'
|
162
|
+
)
|
163
|
+
end
|
164
|
+
|
165
|
+
def canonicalize_response(canonicalize_request)
|
166
|
+
url = Util.file_uri(File.absolute_path(canonicalize_request.url, (@file.nil? ? 'stdin' : @file)))
|
167
|
+
|
168
|
+
begin
|
169
|
+
result = @importer[canonicalize_request.importer_id].call canonicalize_request.url, @file
|
170
|
+
raise result if result.is_a? StandardError
|
171
|
+
rescue StandardError => e
|
172
|
+
return EmbeddedProtocol::InboundMessage::CanonicalizeResponse.new(
|
173
|
+
id: canonicalize_request.id,
|
174
|
+
error: e.message
|
175
|
+
)
|
176
|
+
end
|
177
|
+
|
178
|
+
if result&.key? :contents
|
179
|
+
@import_responses[url] = EmbeddedProtocol::InboundMessage::ImportResponse.new(
|
180
|
+
id: canonicalize_request.id,
|
181
|
+
success: EmbeddedProtocol::InboundMessage::ImportResponse::ImportSuccess.new(
|
182
|
+
contents: result[:contents],
|
183
|
+
syntax: EmbeddedProtocol::Syntax::SCSS,
|
184
|
+
source_map_url: nil
|
185
|
+
)
|
186
|
+
)
|
187
|
+
EmbeddedProtocol::InboundMessage::CanonicalizeResponse.new(
|
188
|
+
id: canonicalize_request.id,
|
189
|
+
url: url
|
190
|
+
)
|
191
|
+
elsif result&.key? :file
|
192
|
+
canonicalized_url = Util.file_uri(result[:file])
|
193
|
+
|
194
|
+
# TODO: FileImportRequest is not supported yet.
|
195
|
+
# Workaround by reading contents and return it when server asks
|
196
|
+
@import_responses[canonicalized_url] = EmbeddedProtocol::InboundMessage::ImportResponse.new(
|
197
|
+
id: canonicalize_request.id,
|
198
|
+
success: EmbeddedProtocol::InboundMessage::ImportResponse::ImportSuccess.new(
|
199
|
+
contents: File.read(result[:file]),
|
200
|
+
syntax: EmbeddedProtocol::Syntax::SCSS,
|
201
|
+
source_map_url: nil
|
202
|
+
)
|
203
|
+
)
|
204
|
+
|
205
|
+
EmbeddedProtocol::InboundMessage::CanonicalizeResponse.new(
|
206
|
+
id: canonicalize_request.id,
|
207
|
+
url: canonicalized_url
|
208
|
+
)
|
209
|
+
else
|
210
|
+
EmbeddedProtocol::InboundMessage::CanonicalizeResponse.new(
|
211
|
+
id: canonicalize_request.id
|
212
|
+
)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def import_response(import_request)
|
217
|
+
url = import_request.url
|
218
|
+
|
219
|
+
if @import_responses.key? url
|
220
|
+
@import_responses[url].id = import_request.id
|
221
|
+
else
|
222
|
+
@import_responses[url] = EmbeddedProtocol::InboundMessage::ImportResponse.new(
|
223
|
+
id: import_request.id,
|
224
|
+
error: "Failed to import: #{url}"
|
225
|
+
)
|
226
|
+
end
|
227
|
+
|
228
|
+
@import_responses[url]
|
229
|
+
end
|
230
|
+
|
231
|
+
def function_call_response(function_call_request)
|
232
|
+
EmbeddedProtocol::InboundMessage::FunctionCallResponse.new(
|
233
|
+
id: function_call_request.id,
|
234
|
+
success: @functions[function_call_request.name].call(*function_call_request.arguments)
|
235
|
+
)
|
236
|
+
rescue StandardError => e
|
237
|
+
EmbeddedProtocol::InboundMessage::FunctionCallResponse.new(
|
238
|
+
id: function_call_request.id,
|
239
|
+
error: e.message
|
240
|
+
)
|
241
|
+
end
|
242
|
+
|
243
|
+
private
|
244
|
+
|
245
|
+
def syntax
|
246
|
+
if @indented_syntax == true
|
247
|
+
EmbeddedProtocol::Syntax::INDENTED
|
248
|
+
else
|
249
|
+
EmbeddedProtocol::Syntax::SCSS
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
def url
|
254
|
+
return if @file.nil?
|
255
|
+
|
256
|
+
Util.file_uri(@file)
|
257
|
+
end
|
258
|
+
|
259
|
+
def string
|
260
|
+
return if @data.nil?
|
261
|
+
|
262
|
+
EmbeddedProtocol::InboundMessage::CompileRequest::StringInput.new(
|
263
|
+
source: @data,
|
264
|
+
url: url,
|
265
|
+
syntax: syntax
|
266
|
+
)
|
267
|
+
end
|
268
|
+
|
269
|
+
def path
|
270
|
+
@file if @data.nil?
|
271
|
+
end
|
272
|
+
|
273
|
+
def style
|
274
|
+
case @output_style.to_sym
|
275
|
+
when :expanded
|
276
|
+
EmbeddedProtocol::OutputStyle::EXPANDED
|
277
|
+
when :compressed
|
278
|
+
EmbeddedProtocol::OutputStyle::COMPRESSED
|
279
|
+
when :nested, :compact
|
280
|
+
raise ArgumentError, "#{@output_style} is not a supported output_style"
|
281
|
+
else
|
282
|
+
raise ArgumentError, "#{@output_style} is not a valid utput_style"
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def source_map
|
287
|
+
@source_map.is_a?(String) || (@source_map == true && !@out_file.nil?)
|
288
|
+
end
|
289
|
+
|
290
|
+
attr_reader :global_functions
|
291
|
+
|
292
|
+
# Order
|
293
|
+
# 1. Loading a file relative to the file in which the @use or @import appeared.
|
294
|
+
# 2. Each custom importer.
|
295
|
+
# 3. Loading a file relative to the current working directory.
|
296
|
+
# 4. Each load path in includePaths
|
297
|
+
# 5. Each load path specified in the SASS_PATH environment variable, which should be semicolon-separated on Windows and colon-separated elsewhere.
|
298
|
+
def importers
|
299
|
+
custom_importers = @importer.map.with_index do |_, id|
|
300
|
+
EmbeddedProtocol::InboundMessage::CompileRequest::Importer.new(
|
301
|
+
importer_id: id
|
302
|
+
)
|
303
|
+
end
|
304
|
+
|
305
|
+
include_path_importers = @include_paths
|
306
|
+
.concat(Sass.include_paths)
|
307
|
+
.map do |include_path|
|
308
|
+
EmbeddedProtocol::InboundMessage::CompileRequest::Importer.new(
|
309
|
+
path: File.absolute_path(include_path)
|
310
|
+
)
|
311
|
+
end
|
312
|
+
|
313
|
+
custom_importers.concat include_path_importers
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|