sassc-embedded 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 01b39ff3df99a2a2c1d9e9aa76ad7d06462a0ba970c8ed907753dc094ac93f54
4
+ data.tar.gz: aa75aa2ff62c0b50c48fd0c7594aadee3ff7180b215df76c9efbc7f3e4112ace
5
+ SHA512:
6
+ metadata.gz: 9eec5413a5ef0da29d752c421889cb0f4bf278f10a48ca7a0ffdd002d7217f04f99d3553dbdf9396077c23e66ed24d5baa7005bb6a7a324f094533300a2d9d20
7
+ data.tar.gz: 59348aae444404fba124bc4717ac51f8433739f5ff6c26ab394b3191483ccc5aa67ffb5df917a3c0643c5ca2fd93da1ab563c8c460fd6c27083e04c1aaeb1551
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 なつき
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # Embedded Sass Polyfill for SassC Ruby
2
+
3
+ [![build](https://github.com/ntkme/sassc-embedded-polyfill-ruby/actions/workflows/build.yml/badge.svg)](https://github.com/ntkme/sassc-embedded-polyfill-ruby/actions/workflows/build.yml)
4
+ [![gem](https://badge.fury.io/rb/sassc-embedded.svg)](https://rubygems.org/gems/sassc-embedded)
5
+
6
+ Use `sass-embedded` with SassC Ruby!
7
+
8
+ This library polyfills [`sassc`](https://github.com/sass/sassc-ruby) with the [`sass-embedded`](https://github.com/ntkme/sass-embedded-host-ruby) implementation.
9
+
10
+
11
+ ## Install
12
+
13
+ ``` sh
14
+ gem install sassc-embedded
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ To compile, `require 'sassc/embedded'` before use a `SassC::Engine`, e.g.:
20
+
21
+ ``` ruby
22
+ require 'sassc/embedded'
23
+
24
+ SassC::Engine.new(sass, style: :compressed).render
25
+ ```
26
+
27
+ See [rubydoc.info/gems/sassc](https://rubydoc.info/gems/sassc) for full API documentation.
28
+
29
+ ## Behavioral Differences from SassC Ruby
30
+
31
+ 1. Option `:style => :nested` behaves as `:expanded`.
32
+ 2. Option `:style => :compact` behaves as `:compressed`.
33
+ 3. Option `:precision` is ignored.
34
+ 4. Option `:line_comments` is ignored.
35
+ 5. Argument `parent_path` in `Importer#imports` is set to value of option `:filename`.
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SassC
4
+ module Embedded
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
@@ -0,0 +1,402 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sassc'
4
+ require 'sass-embedded'
5
+
6
+ require 'base64'
7
+ require 'json'
8
+ require 'pathname'
9
+ require 'uri'
10
+
11
+ require_relative 'embedded/version'
12
+
13
+ module SassC
14
+ class Engine
15
+ def render
16
+ return @template.dup if @template.empty?
17
+
18
+ result = ::Sass.compile_string(
19
+ @template,
20
+ importer: nil,
21
+ load_paths: load_paths,
22
+ syntax: syntax,
23
+ url: file_url,
24
+
25
+ source_map: !source_map_file.nil?,
26
+ source_map_include_sources: source_map_contents?,
27
+ style: output_style,
28
+
29
+ functions: FunctionsHandler.new(@options).setup(nil, functions: @functions),
30
+ importers: ImportHandler.new(@options).setup(nil),
31
+
32
+ alert_ascii: @options.fetch(:alert_ascii, false),
33
+ alert_color: @options.fetch(:alert_color, nil),
34
+ logger: @options.fetch(:logger, nil),
35
+ quiet_deps: @options.fetch(:quiet_deps, false),
36
+ verbose: @options.fetch(:verbose, false)
37
+ )
38
+
39
+ @dependencies = result.loaded_urls
40
+ .filter { |url| url.start_with?('file:') && url != file_url }
41
+ .map { |url| Util.file_url_to_path(url) }
42
+ @source_map = post_process_source_map(result.source_map)
43
+
44
+ return post_process_css(result.css) unless quiet?
45
+ rescue ::Sass::CompileError => e
46
+ line = e.span&.start&.line
47
+ line += 1 unless line.nil?
48
+ path = Util.file_url_to_path(e.span&.url)
49
+ path = relative_path(Dir.pwd, path)
50
+ raise SyntaxError.new(e.message, filename: path, line: line)
51
+ end
52
+
53
+ private
54
+
55
+ def file_url
56
+ @file_url ||= Util.path_to_file_url(filename || 'stdin')
57
+ end
58
+
59
+ def syntax
60
+ syntax = @options.fetch(:syntax, :scss)
61
+ syntax = :indented if syntax.to_sym == :sass
62
+ syntax
63
+ end
64
+
65
+ def output_style
66
+ @output_style ||= begin
67
+ style = @options.fetch(:style, :sass_style_nested).to_s
68
+ style = "sass_style_#{style}" unless style.include?('sass_style_')
69
+ raise InvalidStyleError unless OUTPUT_STYLES.include?(style.to_sym)
70
+
71
+ style = style.delete_prefix('sass_style_').to_sym
72
+ case style
73
+ when :nested
74
+ :expanded
75
+ when :compact
76
+ :compressed
77
+ else
78
+ style
79
+ end
80
+ end
81
+ end
82
+
83
+ def load_paths
84
+ @load_paths ||= (@options[:load_paths] || []) + SassC.load_paths
85
+ end
86
+
87
+ def post_process_source_map(source_map)
88
+ return unless source_map
89
+
90
+ data = JSON.parse(source_map)
91
+
92
+ data['sources'].map! do |source|
93
+ if source.start_with? 'file:'
94
+ relative_path(Dir.pwd, Util.file_url_to_path(source))
95
+ else
96
+ source
97
+ end
98
+ end
99
+
100
+ -JSON.generate(data)
101
+ end
102
+
103
+ def post_process_css(css)
104
+ css += "\n" if css.end_with? '}'
105
+ unless @source_map.nil? || omit_source_map_url?
106
+ url = if source_map_embed?
107
+ "data:application/json;base64,#{Base64.strict_encode64(@source_map)}"
108
+ else
109
+ URI::DEFAULT_PARSER.escape(source_map_file)
110
+ end
111
+ css += "\n/*# sourceMappingURL=#{url} */"
112
+ end
113
+ -css
114
+ end
115
+
116
+ def relative_path(from, to)
117
+ Pathname.new(to).relative_path_from(Pathname.new(from)).to_s
118
+ end
119
+ end
120
+
121
+ class FunctionsHandler
122
+ def setup(_native_options, functions: Script::Functions)
123
+ @callbacks = {}
124
+
125
+ functions_wrapper = Class.new do
126
+ attr_accessor :options
127
+
128
+ include functions
129
+ end.new
130
+ functions_wrapper.options = @options
131
+
132
+ Script.custom_functions(functions: functions).each do |custom_function|
133
+ callback = lambda do |native_argument_list|
134
+ function_arguments = arguments_from_native_list(native_argument_list)
135
+ begin
136
+ result = functions_wrapper.send(custom_function, *function_arguments)
137
+ rescue StandardError
138
+ raise ::Sass::ScriptError, "Error: error in C function #{custom_function}"
139
+ end
140
+ to_native_value(result)
141
+ rescue StandardError => e
142
+ warn "[SassC::FunctionsHandler] #{e.cause.message}"
143
+ raise e
144
+ end
145
+
146
+ @callbacks[Script.formatted_function_name(custom_function, functions: functions)] = callback
147
+ end
148
+
149
+ @callbacks
150
+ end
151
+
152
+ private
153
+
154
+ def arguments_from_native_list(native_argument_list)
155
+ native_argument_list.map do |embedded_value|
156
+ Script::ValueConversion.from_native embedded_value
157
+ end
158
+ end
159
+
160
+ begin
161
+ begin
162
+ raise
163
+ rescue StandardError
164
+ raise ::Sass::ScriptError
165
+ end
166
+ rescue StandardError => e
167
+ unless e.full_message.include? e.cause.full_message
168
+ ::Sass::ScriptError.class_eval do
169
+ def full_message(*args, **kwargs)
170
+ full_message = super(*args, **kwargs)
171
+ if cause
172
+ "#{full_message}\n#{cause.full_message(*args, **kwargs)}"
173
+ else
174
+ full_message
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
181
+
182
+ class ImportHandler
183
+ def setup(_native_options)
184
+ if @importer
185
+ [FileImporter.new, Importer.new(@importer)]
186
+ else
187
+ [FileImporter.new]
188
+ end
189
+ end
190
+
191
+ class FileImporter
192
+ def find_file_url(url, **_kwargs)
193
+ return url if url.start_with?('file:')
194
+ end
195
+ end
196
+
197
+ private_constant :FileImporter
198
+
199
+ class Importer
200
+ def initialize(importer)
201
+ @importer = importer
202
+ @importer_results = {}
203
+ end
204
+
205
+ def canonicalize(url, **)
206
+ path = Util.file_url_to_path(url)
207
+ canonical_url = Util.path_to_file_url(File.absolute_path(path))
208
+
209
+ if @importer_results.key?(canonical_url)
210
+ return if @importer_results[canonical_url].nil?
211
+
212
+ return canonical_url
213
+ end
214
+
215
+ canonical_url = "sass-importer-shim:#{canonical_url}"
216
+
217
+ imports = @importer.imports path, @importer.options[:filename]
218
+ unless imports.is_a? Array
219
+ return if imports.path == path
220
+
221
+ imports = [imports]
222
+ end
223
+
224
+ contents = imports.map do |import|
225
+ import_path = File.absolute_path(import.path)
226
+ import_url = Util.path_to_file_url(import_path)
227
+ @importer_results[import_url] = if import.source
228
+ {
229
+ contents: import.source,
230
+ syntax: case import.path
231
+ when /\.scss$/i
232
+ :scss
233
+ when /\.sass$/i
234
+ :indented
235
+ when /\.css$/i
236
+ :css
237
+ else
238
+ raise ArgumentError
239
+ end,
240
+ source_map_url: if import.source_map_path
241
+ Util.path_to_file_url(
242
+ File.absolute_path(import.source_map_path, path)
243
+ )
244
+ end
245
+ }
246
+ end
247
+ "@import #{import_url.inspect};"
248
+ end.join("\n")
249
+
250
+ @importer_results[canonical_url] = {
251
+ contents: contents,
252
+ syntax: :scss
253
+ }
254
+
255
+ canonical_url
256
+ end
257
+
258
+ def load(canonical_url)
259
+ @importer_results[canonical_url]
260
+ end
261
+ end
262
+
263
+ private_constant :Importer
264
+ end
265
+
266
+ module Script
267
+ module ValueConversion
268
+ def self.to_native(value)
269
+ case value
270
+ when nil
271
+ ::Sass::Value::Null::NULL
272
+ when ::SassC::Script::Value::Bool
273
+ ::Sass::Value::Boolean.new(value.to_bool)
274
+ when ::SassC::Script::Value::Color
275
+ if value.rgba?
276
+ ::Sass::Value::Color.new(
277
+ red: value.red,
278
+ green: value.green,
279
+ blue: value.blue,
280
+ alpha: value.alpha
281
+ )
282
+ elsif value.hlsa?
283
+ ::Sass::Value::Color.new(
284
+ hue: value.hue,
285
+ saturation: value.saturation,
286
+ lightness: value.lightness,
287
+ alpha: value.alpha
288
+ )
289
+ else
290
+ raise ArgumentError
291
+ end
292
+ when ::SassC::Script::Value::List
293
+ ::Sass::Value::List.new(
294
+ value.to_a.map { |element| to_native(element) },
295
+ separator: case value.separator
296
+ when :comma
297
+ ','
298
+ when :space
299
+ ' '
300
+ else
301
+ raise ArgumentError
302
+ end,
303
+ bracketed: value.bracketed
304
+ )
305
+ when ::SassC::Script::Value::Map
306
+ ::Sass::Value::Map.new(
307
+ value.value.to_a.to_h { |k, v| [to_native(k), to_native(v)] }
308
+ )
309
+ when ::SassC::Script::Value::Number
310
+ ::Sass::Value::Number.new(
311
+ value.value, {
312
+ numerator_units: value.numerator_units,
313
+ denominator_units: value.denominator_units
314
+ }
315
+ )
316
+ when ::SassC::Script::Value::String
317
+ ::Sass::Value::String.new(
318
+ value.value,
319
+ quoted: value.type != :identifier
320
+ )
321
+ else
322
+ raise ArgumentError
323
+ end
324
+ end
325
+
326
+ def self.from_native(value)
327
+ case value
328
+ when ::Sass::Value::Null::NULL
329
+ nil
330
+ when ::Sass::Value::Boolean
331
+ ::SassC::Script::Value::Bool.new(value.to_bool)
332
+ when ::Sass::Value::Color
333
+ if value.instance_eval { defined? @hue }
334
+ ::SassC::Script::Value::Color.new(
335
+ hue: value.hue,
336
+ saturation: value.saturation,
337
+ lightness: value.lightness,
338
+ alpha: value.alpha
339
+ )
340
+ else
341
+ ::SassC::Script::Value::Color.new(
342
+ red: value.red,
343
+ green: value.green,
344
+ blue: value.blue,
345
+ alpha: value.alpha
346
+ )
347
+ end
348
+ when ::Sass::Value::List
349
+ ::SassC::Script::Value::List.new(
350
+ value.to_a.map { |element| from_native(element) },
351
+ separator: case value.separator
352
+ when ','
353
+ :comma
354
+ when ' '
355
+ :space
356
+ else
357
+ raise ArgumentError
358
+ end,
359
+ bracketed: value.bracketed?
360
+ )
361
+ when ::Sass::Value::Map
362
+ ::SassC::Script::Value::Map.new(
363
+ value.contents.to_a.to_h { |k, v| [from_native(k), from_native(v)] }
364
+ )
365
+ when ::Sass::Value::Number
366
+ ::SassC::Script::Value::Number.new(
367
+ value.value,
368
+ value.numerator_units,
369
+ value.denominator_units
370
+ )
371
+ when ::Sass::Value::String
372
+ ::SassC::Script::Value::String.new(
373
+ value.text,
374
+ value.quoted? ? :string : :identifier
375
+ )
376
+ else
377
+ raise ArgumentError
378
+ end
379
+ end
380
+ end
381
+ end
382
+
383
+ module Util
384
+ module_function
385
+
386
+ def file_url_to_path(url)
387
+ return if url.nil?
388
+
389
+ path = URI::DEFAULT_PARSER.unescape(URI.parse(url).path)
390
+ path = path[1..] if Gem.win_platform? && path[0].chr == '/' && path[1].chr =~ /[a-z]/i && path[2].chr == ':'
391
+ path
392
+ end
393
+
394
+ def path_to_file_url(path)
395
+ return if path.nil?
396
+
397
+ path = File.absolute_path(path)
398
+ path = "/#{path}" unless path.start_with? '/'
399
+ URI::File.build([nil, URI::DEFAULT_PARSER.escape(path)]).to_s
400
+ end
401
+ end
402
+ end
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative 'sassc/embedded'
metadata ADDED
@@ -0,0 +1,163 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sassc-embedded
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - なつき
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-03-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sassc
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: sass-embedded
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 5.15.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 5.15.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest-around
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.5.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.5.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.26.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.26.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop-minitest
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.18.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.18.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop-performance
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 1.13.0
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 1.13.0
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop-rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.6.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.6.0
125
+ description: An embedded sass polyfill for SassC.
126
+ email:
127
+ - i@ntk.me
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - LICENSE
133
+ - README.md
134
+ - lib/sassc-embedded.rb
135
+ - lib/sassc/embedded.rb
136
+ - lib/sassc/embedded/version.rb
137
+ homepage: https://github.com/ntkme/sassc-embedded-polyfill-ruby
138
+ licenses:
139
+ - MIT
140
+ metadata:
141
+ documentation_uri: https://rubydoc.info/gems/sassc-embedded/0.1.0
142
+ source_code_uri: https://github.com/ntkme/sassc-embedded-polyfill-ruby/tree/v0.1.0
143
+ funding_uri: https://github.com/sponsors/ntkme
144
+ post_install_message:
145
+ rdoc_options: []
146
+ require_paths:
147
+ - lib
148
+ required_ruby_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: 2.6.0
153
+ required_rubygems_version: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ requirements: []
159
+ rubygems_version: 3.3.7
160
+ signing_key:
161
+ specification_version: 4
162
+ summary: Use dart-sass with SassC!
163
+ test_files: []