sass-embedded 0.9.0 → 0.10.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.
@@ -0,0 +1,344 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+ require 'json'
5
+ require 'pathname'
6
+ require_relative 'url'
7
+
8
+ module Sass
9
+ # The {Embedded} host for using dart-sass-embedded. Each instance creates
10
+ # its own {Channel}.
11
+ #
12
+ # @example
13
+ # embedded = Sass::Embedded.new
14
+ # result = embedded.compile_string('h1 { font-size: 40px; }')
15
+ # result = embedded.compile('style.scss')
16
+ # embedded.close
17
+ class Embedded
18
+ # @deprecated
19
+ def self.include_paths
20
+ @include_paths ||= if ENV['SASS_PATH']
21
+ ENV['SASS_PATH'].split(File::PATH_SEPARATOR)
22
+ else
23
+ []
24
+ end
25
+ end
26
+
27
+ # @deprecated
28
+ # The {Embedded#render} method.
29
+ #
30
+ # See {file:README.md#options} for supported options.
31
+ #
32
+ # @return [RenderResult]
33
+ # @raise [ProtocolError]
34
+ # @raise [RenderError]
35
+ def render(data: nil,
36
+ file: nil,
37
+ indented_syntax: false,
38
+ include_paths: [],
39
+ output_style: :expanded,
40
+ indent_type: :space,
41
+ indent_width: 2,
42
+ linefeed: :lf,
43
+ source_map: false,
44
+ out_file: nil,
45
+ omit_source_map_url: false,
46
+ source_map_contents: false,
47
+ source_map_embed: false,
48
+ source_map_root: '',
49
+ functions: {},
50
+ importer: [])
51
+ start = now
52
+
53
+ raise ArgumentError, 'either data or file must be set' if file.nil? && data.nil?
54
+
55
+ indent_type = parse_indent_type(indent_type)
56
+ indent_width = parse_indent_width(indent_width)
57
+ linefeed = parse_linefeed(linefeed)
58
+
59
+ load_paths = include_paths + Embedded.include_paths
60
+
61
+ source_map_option = source_map.is_a?(String) || (source_map == true && !out_file.nil?)
62
+
63
+ begin
64
+ compile_result = if data
65
+ compile_string(data, load_paths: load_paths,
66
+ syntax: indented_syntax ? :indented : :scss,
67
+ url: (Url.path_to_file_url(File.absolute_path(file)) unless file.nil?),
68
+ source_map: source_map_option,
69
+ style: output_style,
70
+ functions: functions,
71
+ importers: importer.map do |legacy_importer|
72
+ LegacyImporter.new(legacy_importer, file)
73
+ end)
74
+ else
75
+ compile(file, load_paths: load_paths,
76
+ source_map: source_map_option,
77
+ style: output_style,
78
+ functions: functions,
79
+ importers: importer.map do |legacy_importer|
80
+ LegacyImporter.new(legacy_importer, file)
81
+ end)
82
+ end
83
+ rescue CompileError => e
84
+ raise RenderError.new(
85
+ e.sass_message,
86
+ e.message,
87
+ if e.span.nil?
88
+ nil
89
+ elsif e.span.url.nil?
90
+ 'stdin'
91
+ else
92
+ Url.file_url_to_path(e.span.url)
93
+ end,
94
+ e.span.start.line + 1,
95
+ e.span.start.column + 1,
96
+ 1
97
+ )
98
+ end
99
+
100
+ map, source_map = post_process_map(map: compile_result.source_map,
101
+ data: data,
102
+ file: file,
103
+ out_file: out_file,
104
+ source_map: source_map,
105
+ source_map_contents: source_map_contents,
106
+ source_map_root: source_map_root)
107
+
108
+ css = post_process_css(css: compile_result.css,
109
+ indent_type: indent_type,
110
+ indent_width: indent_width,
111
+ linefeed: linefeed,
112
+ map: map,
113
+ out_file: out_file,
114
+ omit_source_map_url: omit_source_map_url,
115
+ source_map: source_map,
116
+ source_map_embed: source_map_embed)
117
+
118
+ finish = now
119
+
120
+ stats = RenderResultStats.new(file.nil? ? 'data' : file, start, finish, finish - start)
121
+
122
+ RenderResult.new(css, map, stats)
123
+ end
124
+
125
+ # @deprecated
126
+ # The {RenderResult} of {Embedded#render}.
127
+ class RenderResult
128
+ attr_reader :css, :map, :stats
129
+
130
+ def initialize(css, map, stats)
131
+ @css = css
132
+ @map = map
133
+ @stats = stats
134
+ end
135
+ end
136
+
137
+ # @deprecated
138
+ # The {RenderResultStats} of {Embedded#render}.
139
+ class RenderResultStats
140
+ attr_reader :entry, :start, :end, :duration
141
+
142
+ def initialize(entry, start, finish, duration)
143
+ @entry = entry
144
+ @start = start
145
+ @end = finish
146
+ @duration = duration
147
+ end
148
+ end
149
+
150
+ # @deprecated
151
+ # The {RenderError} raised by {Embedded#render}.
152
+ class RenderError < StandardError
153
+ attr_reader :formatted, :file, :line, :column, :status
154
+
155
+ def initialize(message, formatted, file, line, column, status)
156
+ super(message)
157
+ @formatted = formatted
158
+ @file = file
159
+ @line = line
160
+ @column = column
161
+ @status = status
162
+ end
163
+
164
+ def backtrace
165
+ return nil if super.nil?
166
+
167
+ ["#{@file}:#{@line}:#{@column}"] + super
168
+ end
169
+ end
170
+
171
+ private
172
+
173
+ # @deprecated
174
+ def post_process_map(map:,
175
+ data:,
176
+ file:,
177
+ out_file:,
178
+ source_map:,
179
+ source_map_contents:,
180
+ source_map_root:)
181
+ return if map.nil? || map.empty?
182
+
183
+ map_data = JSON.parse(map)
184
+
185
+ map_data['sourceRoot'] = source_map_root
186
+
187
+ source_map_path = if source_map.is_a? String
188
+ source_map
189
+ else
190
+ "#{out_file}.map"
191
+ end
192
+
193
+ source_map_dir = File.dirname(source_map_path)
194
+
195
+ if out_file
196
+ map_data['file'] = relative_path(source_map_dir, out_file)
197
+ elsif file
198
+ ext = File.extname(file)
199
+ map_data['file'] = "#{file[0..(ext.empty? ? -1 : -ext.length - 1)]}.css"
200
+ else
201
+ map_data['file'] = 'stdin.css'
202
+ end
203
+
204
+ map_data['sourcesContent'] = [] if source_map_contents
205
+
206
+ file = File.absolute_path(file) unless file.nil?
207
+
208
+ map_data['sources'].map! do |source|
209
+ if source.start_with? 'file://'
210
+ path = Url.file_url_to_path(source)
211
+ content = if path == file && !data.nil?
212
+ data
213
+ else
214
+ begin
215
+ File.read(path)
216
+ rescue StandardError
217
+ nil
218
+ end
219
+ end
220
+ map_data['sourcesContent'].push(content) if source_map_contents
221
+ relative_path(source_map_dir, path)
222
+ else
223
+ map_data['sourcesContent'].push(nil) if source_map_contents
224
+ source
225
+ end
226
+ end
227
+
228
+ [-JSON.generate(map_data), source_map_path]
229
+ end
230
+
231
+ # @deprecated
232
+ def post_process_css(css:,
233
+ indent_type:,
234
+ indent_width:,
235
+ linefeed:,
236
+ map:,
237
+ omit_source_map_url:,
238
+ out_file:,
239
+ source_map:,
240
+ source_map_embed:)
241
+ css = +css
242
+ if indent_width != 2 || indent_type.to_sym != :space
243
+ indent = indent_type * indent_width
244
+ css.gsub!(/^ +/) do |space|
245
+ indent * (space.length / 2)
246
+ end
247
+ end
248
+ css.gsub!("\n", linefeed) if linefeed != "\n"
249
+
250
+ unless map.nil? || omit_source_map_url == true
251
+ url = if source_map_embed
252
+ "data:application/json;base64,#{Base64.strict_encode64(map)}"
253
+ elsif out_file
254
+ relative_path(File.dirname(out_file), source_map)
255
+ else
256
+ source_map
257
+ end
258
+ css += "#{linefeed}#{linefeed}/*# sourceMappingURL=#{url} */"
259
+ end
260
+
261
+ -css
262
+ end
263
+
264
+ # @deprecated
265
+ def parse_indent_type(indent_type)
266
+ case indent_type.to_sym
267
+ when :space
268
+ ' '
269
+ when :tab
270
+ "\t"
271
+ else
272
+ raise ArgumentError, 'indent_type must be one of :space, :tab'
273
+ end
274
+ end
275
+
276
+ # @deprecated
277
+ def parse_indent_width(indent_width)
278
+ raise ArgumentError, 'indent_width must be an integer' unless indent_width.is_a? Integer
279
+ raise RangeError, 'indent_width must be in between 0 and 10 (inclusive)' unless indent_width.between? 0, 10
280
+
281
+ indent_width
282
+ end
283
+
284
+ # @deprecated
285
+ def parse_linefeed(linefeed)
286
+ case linefeed.to_sym
287
+ when :lf
288
+ "\n"
289
+ when :lfcr
290
+ "\n\r"
291
+ when :cr
292
+ "\r"
293
+ when :crlf
294
+ "\r\n"
295
+ else
296
+ raise ArgumentError, 'linefeed must be one of :lf, :lfcr, :cr, :crlf'
297
+ end
298
+ end
299
+
300
+ # @deprecated
301
+ def now
302
+ (Time.now.to_f * 1000).to_i
303
+ end
304
+
305
+ # @deprecated
306
+ def relative_path(from, to)
307
+ Pathname.new(File.absolute_path(to)).relative_path_from(Pathname.new(File.absolute_path(from))).to_s
308
+ end
309
+
310
+ # @deprecated
311
+ # The {LegacyImporter} for {Embedded#render}.
312
+ class LegacyImporter
313
+ def initialize(importer, file)
314
+ super()
315
+ @file = file
316
+ @importer = importer
317
+ @importer_results = {}
318
+ end
319
+
320
+ def canonicalize(url)
321
+ canonical_url = Url.path_to_file_url(File.absolute_path(url, (@file.nil? ? 'stdin' : @file)))
322
+
323
+ result = @importer.call canonical_url, @file
324
+
325
+ raise result if result.is_a? StandardError
326
+
327
+ if result&.key? :contents
328
+ @importer_results[canonical_url] = ImporterResult.new(result[:contents], :scss)
329
+ canonical_url
330
+ elsif result&.key? :file
331
+ canonical_url = Url.path_to_file_url(File.absolute_path(result[:file]))
332
+ @importer_results[canonical_url] = ImporterResult.new(File.read(result[:file]), :scss)
333
+ canonical_url
334
+ end
335
+ end
336
+
337
+ def load(canonical_url)
338
+ @importer_results[canonical_url]
339
+ end
340
+ end
341
+
342
+ private_constant :LegacyImporter
343
+ end
344
+ end
@@ -1,33 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'pathname'
4
3
  require 'uri'
5
4
 
6
5
  module Sass
7
6
  class Embedded
8
- # The {Util} module.
9
- module Util
7
+ # The {Url} module.
8
+ module Url
9
+ # The {::URI::Parser} that is in consistent with Dart Uri.
10
10
  URI_PARSER = URI::Parser.new({ RESERVED: ';/?:@&=+$,' })
11
11
 
12
12
  private_constant :URI_PARSER
13
13
 
14
14
  module_function
15
15
 
16
- def file_uri_from_path(path)
16
+ def path_to_file_url(path)
17
17
  "file://#{Platform::OS == 'windows' ? File::SEPARATOR : ''}#{URI_PARSER.escape(path)}"
18
18
  end
19
19
 
20
- def path_from_file_uri(file_uri)
20
+ def file_url_to_path(file_uri)
21
21
  URI_PARSER.unescape(file_uri[(Platform::OS == 'windows' ? 8 : 7)..])
22
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
23
  end
32
24
  end
33
25
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Sass
4
4
  class Embedded
5
- VERSION = '0.9.0'
5
+ VERSION = '0.10.0'
6
6
  end
7
7
  end