sass-embedded 0.9.0 → 0.10.0

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