sass-embedded 0.9.3 → 0.13.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.
data/lib/sass/embedded.rb CHANGED
@@ -1,14 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'base64'
4
- require 'json'
3
+ require_relative 'compile_error'
4
+ require_relative 'compile_result'
5
+ require_relative 'importer_result'
5
6
  require_relative 'embedded/channel'
6
7
  require_relative 'embedded/compile_context'
7
- require_relative 'embedded/error'
8
- require_relative 'embedded/result'
9
- require_relative 'embedded/util'
8
+ require_relative 'embedded/render'
9
+ require_relative 'embedded/url'
10
10
  require_relative 'embedded/version'
11
11
  require_relative 'embedded/version_context'
12
+ require_relative 'logger'
12
13
 
13
14
  module Sass
14
15
  # The {Embedded} host for using dart-sass-embedded. Each instance creates
@@ -16,109 +17,113 @@ module Sass
16
17
  #
17
18
  # @example
18
19
  # embedded = Sass::Embedded.new
19
- # result = embedded.render(data: 'h1 { font-size: 40px; }')
20
- # result = embedded.render(file: 'style.css')
20
+ # result = embedded.compile_string('h1 { font-size: 40px; }')
21
+ # result = embedded.compile('style.scss')
21
22
  # embedded.close
22
23
  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
-
31
24
  def initialize
32
25
  @channel = Channel.new
33
26
  end
34
27
 
35
- # The {Embedded#info} method.
28
+ # The {Embedded#compile} method.
36
29
  #
30
+ # @return [CompileResult]
31
+ # @raise [CompileError]
37
32
  # @raise [ProtocolError]
38
- def info
39
- @info ||= VersionContext.new(@channel).receive_message
40
- end
33
+ def compile(path,
34
+ load_paths: [],
41
35
 
42
- # The {Embedded#render} method.
43
- #
44
- # See {file:README.md#options} for supported options.
45
- #
46
- # @return [RenderResult]
47
- # @raise [ProtocolError]
48
- # @raise [RenderError]
49
- def render(data: nil,
50
- file: nil,
51
- indented_syntax: false,
52
- include_paths: [],
53
- output_style: :expanded,
54
- indent_type: :space,
55
- indent_width: 2,
56
- linefeed: :lf,
57
- source_map: false,
58
- out_file: nil,
59
- omit_source_map_url: false,
60
- source_map_contents: false,
61
- source_map_embed: false,
62
- source_map_root: '',
63
- functions: {},
64
- importer: [])
65
- start = Util.now
36
+ source_map: false,
37
+ source_map_include_sources: false,
38
+ style: :expanded,
66
39
 
67
- indent_type = parse_indent_type(indent_type)
68
- indent_width = parse_indent_width(indent_width)
69
- linefeed = parse_linefeed(linefeed)
40
+ functions: {},
41
+ importers: [],
42
+
43
+ alert_ascii: false,
44
+ alert_color: $stderr.tty?,
45
+ logger: nil,
46
+ quiet_deps: false,
47
+ verbose: false)
48
+
49
+ raise ArgumentError, 'path must be set' if path.nil?
70
50
 
71
51
  message = CompileContext.new(@channel,
72
- data: data,
73
- file: file,
74
- indented_syntax: indented_syntax,
75
- include_paths: include_paths,
76
- output_style: output_style,
52
+ path: path,
53
+ source: nil,
54
+ importer: nil,
55
+ load_paths: load_paths,
56
+ syntax: nil,
57
+ url: nil,
77
58
  source_map: source_map,
78
- out_file: out_file,
59
+ source_map_include_sources: source_map_include_sources,
60
+ style: style,
79
61
  functions: functions,
80
- importer: importer).receive_message
62
+ importers: importers,
63
+ alert_color: alert_color,
64
+ alert_ascii: alert_ascii,
65
+ logger: logger,
66
+ quiet_deps: quiet_deps,
67
+ verbose: verbose).receive_message
68
+
69
+ raise CompileError.from_proto(message.failure) if message.failure
81
70
 
82
- if message.failure
83
- raise RenderError.new(
84
- message.failure.message,
85
- message.failure.formatted,
86
- if message.failure.span.nil?
87
- nil
88
- elsif message.failure.span.url == ''
89
- 'stdin'
90
- else
91
- Util.path_from_file_uri(message.failure.span.url)
92
- end,
93
- message.failure.span ? message.failure.span.start.line + 1 : nil,
94
- message.failure.span ? message.failure.span.start.column + 1 : nil,
95
- 1
96
- )
97
- end
71
+ CompileResult.from_proto(message.success)
72
+ end
98
73
 
99
- map, source_map = post_process_map(map: message.success.source_map,
100
- data: data,
101
- file: file,
102
- out_file: out_file,
103
- source_map: source_map,
104
- source_map_contents: source_map_contents,
105
- source_map_root: source_map_root)
74
+ # The {Embedded#compile_string} method.
75
+ #
76
+ # @return [CompileResult]
77
+ # @raise [CompileError]
78
+ # @raise [ProtocolError]
79
+ def compile_string(source,
80
+ importer: nil,
81
+ load_paths: [],
82
+ syntax: :scss,
83
+ url: nil,
84
+
85
+ source_map: false,
86
+ source_map_include_sources: false,
87
+ style: :expanded,
88
+
89
+ functions: {},
90
+ importers: [],
91
+
92
+ alert_ascii: false,
93
+ alert_color: $stderr.tty?,
94
+ logger: nil,
95
+ quiet_deps: false,
96
+ verbose: false)
97
+ raise ArgumentError, 'source must be set' if source.nil?
106
98
 
107
- css = post_process_css(css: message.success.css,
108
- indent_type: indent_type,
109
- indent_width: indent_width,
110
- linefeed: linefeed,
111
- map: map,
112
- out_file: out_file,
113
- omit_source_map_url: omit_source_map_url,
114
- source_map: source_map,
115
- source_map_embed: source_map_embed)
99
+ message = CompileContext.new(@channel,
100
+ path: nil,
101
+ source: source,
102
+ importer: importer,
103
+ load_paths: load_paths,
104
+ syntax: syntax,
105
+ url: url,
106
+ source_map: source_map,
107
+ source_map_include_sources: source_map_include_sources,
108
+ style: style,
109
+ functions: functions,
110
+ importers: importers,
111
+ alert_color: alert_color,
112
+ alert_ascii: alert_ascii,
113
+ logger: logger,
114
+ quiet_deps: quiet_deps,
115
+ verbose: verbose).receive_message
116
116
 
117
- finish = Util.now
117
+ raise CompileError.from_proto(message.failure) if message.failure
118
118
 
119
- stats = RenderResultStats.new(file.nil? ? 'data' : file, start, finish, finish - start)
119
+ CompileResult.from_proto(message.success)
120
+ end
120
121
 
121
- RenderResult.new(css, map, stats)
122
+ # The {Embedded#info} method.
123
+ #
124
+ # @raise [ProtocolError]
125
+ def info
126
+ @info ||= VersionContext.new(@channel).receive_message
122
127
  end
123
128
 
124
129
  def close
@@ -128,129 +133,5 @@ module Sass
128
133
  def closed?
129
134
  @channel.closed?
130
135
  end
131
-
132
- private
133
-
134
- def post_process_map(map:,
135
- data:,
136
- file:,
137
- out_file:,
138
- source_map:,
139
- source_map_contents:,
140
- source_map_root:)
141
- return if map.nil? || map.empty?
142
-
143
- map_data = JSON.parse(map)
144
-
145
- map_data['sourceRoot'] = source_map_root
146
-
147
- source_map_path = if source_map.is_a? String
148
- source_map
149
- else
150
- "#{out_file}.map"
151
- end
152
-
153
- source_map_dir = File.dirname(source_map_path)
154
-
155
- if out_file
156
- map_data['file'] = Util.relative_path(source_map_dir, out_file)
157
- elsif file
158
- ext = File.extname(file)
159
- map_data['file'] = "#{file[0..(ext.empty? ? -1 : -ext.length - 1)]}.css"
160
- else
161
- map_data['file'] = 'stdin.css'
162
- end
163
-
164
- map_data['sourcesContent'] = [] if source_map_contents
165
-
166
- file = File.absolute_path(file) unless file.nil?
167
-
168
- map_data['sources'].map! do |source|
169
- if source.start_with? 'file://'
170
- path = Util.path_from_file_uri(source)
171
- content = if path == file && !data.nil?
172
- data
173
- else
174
- begin
175
- File.read(path)
176
- rescue StandardError
177
- nil
178
- end
179
- end
180
- map_data['sourcesContent'].push(content) if source_map_contents
181
- Util.relative_path(source_map_dir, path)
182
- else
183
- map_data['sourcesContent'].push(nil) if source_map_contents
184
- source
185
- end
186
- end
187
-
188
- [-JSON.generate(map_data), source_map_path]
189
- end
190
-
191
- def post_process_css(css:,
192
- indent_type:,
193
- indent_width:,
194
- linefeed:,
195
- map:,
196
- omit_source_map_url:,
197
- out_file:,
198
- source_map:,
199
- source_map_embed:)
200
- css = +css
201
- if indent_width != 2 || indent_type.to_sym != :space
202
- indent = indent_type * indent_width
203
- css.gsub!(/^ +/) do |space|
204
- indent * (space.length / 2)
205
- end
206
- end
207
- css.gsub!("\n", linefeed) if linefeed != "\n"
208
-
209
- unless map.nil? || omit_source_map_url == true
210
- url = if source_map_embed
211
- "data:application/json;base64,#{Base64.strict_encode64(map)}"
212
- elsif out_file
213
- Util.relative_path(File.dirname(out_file), source_map)
214
- else
215
- source_map
216
- end
217
- css += "#{linefeed}#{linefeed}/*# sourceMappingURL=#{url} */"
218
- end
219
-
220
- -css
221
- end
222
-
223
- def parse_indent_type(indent_type)
224
- case indent_type.to_sym
225
- when :space
226
- ' '
227
- when :tab
228
- "\t"
229
- else
230
- raise ArgumentError, 'indent_type must be one of :space, :tab'
231
- end
232
- end
233
-
234
- def parse_indent_width(indent_width)
235
- raise ArgumentError, 'indent_width must be an integer' unless indent_width.is_a? Integer
236
- raise RangeError, 'indent_width must be in between 0 and 10 (inclusive)' unless indent_width.between? 0, 10
237
-
238
- indent_width
239
- end
240
-
241
- def parse_linefeed(linefeed)
242
- case linefeed.to_sym
243
- when :lf
244
- "\n"
245
- when :lfcr
246
- "\n\r"
247
- when :cr
248
- "\r"
249
- when :crlf
250
- "\r\n"
251
- else
252
- raise ArgumentError, 'linefeed must be one of :lf, :lfcr, :cr, :crlf'
253
- end
254
- end
255
136
  end
256
137
  end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ # The {FileImporter} interface.
5
+ class FileImporter
6
+ def find_file_url(url, from_import:) # rubocop:disable Lint/UnusedMethodArgument
7
+ raise NotImplementedError, 'FileImporter#find_file_url must be implemented'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ # The {Importer} interface.
5
+ class Importer
6
+ def canonicalize(url) # rubocop:disable Lint/UnusedMethodArgument
7
+ raise NotImplementedError, 'Importer#canonicalize must be implemented'
8
+ end
9
+
10
+ def load(canonical_url) # rubocop:disable Lint/UnusedMethodArgument
11
+ raise NotImplementedError, 'Importer#load must be implemented'
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ # The {ImporterResult} of {Importer#load}.
5
+ class ImporterResult
6
+ attr_reader :contents, :syntax, :source_map_url
7
+
8
+ def initialize(contents, syntax, source_map_url = nil)
9
+ @contents = contents
10
+ @syntax = syntax
11
+ @source_map_url = source_map_url
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ module Logger
5
+ # The {SourceLocation} in {SourceSpan}.
6
+ class SourceLocation
7
+ attr_reader :offset, :line, :column
8
+
9
+ def initialize(offset, line, column)
10
+ @offset = offset
11
+ @line = line
12
+ @column = column
13
+ end
14
+
15
+ def self.from_proto(source_location)
16
+ return nil if source_location.nil?
17
+
18
+ SourceLocation.new(source_location.offset,
19
+ source_location.line,
20
+ source_location.column)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'source_location'
4
+
5
+ module Sass
6
+ module Logger
7
+ # The {SourceSpan} in {CompileError}.
8
+ class SourceSpan
9
+ attr_reader :start, :end, :text, :url, :context
10
+
11
+ def initialize(start, end_, text, url, context)
12
+ @start = start
13
+ @end = end_
14
+ @text = text
15
+ @url = url == '' ? nil : url
16
+ @context = context == '' ? nil : context
17
+ end
18
+
19
+ def self.from_proto(source_span)
20
+ return nil if source_span.nil?
21
+
22
+ SourceSpan.new(SourceLocation.from_proto(source_span.start),
23
+ SourceLocation.from_proto(source_span.end),
24
+ source_span.text,
25
+ source_span.url,
26
+ source_span.context)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ # The {Logger} module.
5
+ module Logger
6
+ module_function
7
+
8
+ # The instance of a silent {Logger}.
9
+ def silent
10
+ Silent
11
+ end
12
+
13
+ # The silent {Logger}.
14
+ module Silent
15
+ module_function
16
+
17
+ def warn(message, deprecation: false, span: nil, stack: nil); end
18
+
19
+ def debug(message, span: nil); end
20
+ end
21
+
22
+ private_constant :Silent
23
+ end
24
+ end
data/lib/sass.rb CHANGED
@@ -6,6 +6,35 @@ require_relative 'sass/embedded'
6
6
  # the Embedded Sass protocol.
7
7
  module Sass
8
8
  class << self
9
+ # The global {.compile} method. This instantiates a global {Embedded} instance
10
+ # and calls {Embedded#compile}.
11
+ #
12
+ # See {Embedded#compile} for supported options.
13
+ #
14
+ # @example
15
+ # Sass.compile('style.scss')
16
+ # @return [CompileResult]
17
+ # @raise [CompileError]
18
+ # @raise [ProtocolError]
19
+ def compile(path, **kwargs)
20
+ instance.compile(path, **kwargs)
21
+ end
22
+
23
+ # The global {.compile_string} method. This instantiates a global {Embedded} instance
24
+ # and calls {Embedded#compile_string}.
25
+ #
26
+ # See {Embedded#compile_string} for supported options.
27
+ #
28
+ # @example
29
+ # Sass.compile_string('h1 { font-size: 40px; }')
30
+ # @return [CompileResult]
31
+ # @raise [CompileError]
32
+ # @raise [ProtocolError]
33
+ def compile_string(source, **kwargs)
34
+ instance.compile_string(source, **kwargs)
35
+ end
36
+
37
+ # @deprecated
9
38
  # The global {.include_paths} for Sass files. This is meant for plugins and
10
39
  # libraries to register the paths to their Sass stylesheets to that they may
11
40
  # be included via `@import` or `@use`. This include path is used by every
@@ -32,10 +61,11 @@ module Sass
32
61
  instance.info
33
62
  end
34
63
 
64
+ # @deprecated
35
65
  # The global {.render} method. This instantiates a global {Embedded} instance
36
66
  # and calls {Embedded#render}.
37
67
  #
38
- # See {file:README.md#options} for supported options.
68
+ # See {Embedded#render} for supported options.
39
69
  #
40
70
  # @example
41
71
  # Sass.render(data: 'h1 { font-size: 40px; }')