deadfire 0.2.0 → 0.4.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.
@@ -1,387 +0,0 @@
1
- # frozen_string_literal: true
2
- module Deadfire
3
- class Parser
4
- singleton_class.attr_accessor :cached_mixins
5
- self.cached_mixins = Hash.new { |h, k| h[k] = {} }
6
-
7
- singleton_class.attr_accessor :import_path_cache
8
- self.import_path_cache = []
9
-
10
- ROOT_SELECTOR = ":root {"
11
- OPENING_SELECTOR_PATTERN = /\s*\{/
12
- CLOSING_SELECTOR_PATTERN = /\s*\}/
13
- NEST_SELECTOR = "&"
14
- START_BLOCK_CHAR = "{"
15
- END_BLOCK_CHAR = "}"
16
- OPENING_SELECTOR_PATTERN_OTHER = /\..*\{/
17
- IMPORT_SELECTOR = "@import"
18
- CSS_FILE_EXTENSION = ".css"
19
- APPLY_SELECTOR = "@apply"
20
- NEWLINE = "\n"
21
-
22
- def self.parse(content, options = {})
23
- new(content, options).parse
24
- end
25
-
26
- attr_reader :output
27
-
28
- def initialize(content, options = {})
29
- @content = content
30
- @filename = options[:filename]
31
- @output = []
32
- @imports = []
33
- end
34
-
35
- def buffer
36
- @buffer ||= CssBuffer.new(@content)
37
- end
38
-
39
- class Line
40
- attr_accessor :content, :line_number
41
-
42
- def initialize(content, line_number)
43
- @content = content
44
- @line_number = line_number
45
- end
46
-
47
- def to_s
48
- content
49
- end
50
- end
51
-
52
- class Root < Line
53
- def initialize(content, lineno, buffer)
54
- super(content, lineno)
55
- @end_tag = false
56
- @output_current_line = true
57
- @output = []
58
- @buffer = buffer
59
- end
60
-
61
- def parse
62
- line = @content
63
- if line.include? ROOT_SELECTOR
64
- @output << Line.new(line, @line_number)
65
- end
66
-
67
- while !@end_tag && line = @buffer.gets
68
- if line =~ OPENING_SELECTOR_PATTERN
69
- @output_current_line = false
70
- name = extract_mixin_name(line)
71
- properties = extract_properties_from_mixin(@buffer, line)
72
- Parser.cached_mixins[name] = properties
73
- elsif line =~ CLOSING_SELECTOR_PATTERN
74
- @end_tag = true
75
- end
76
-
77
- @output << Line.new(line, @buffer.lineno) if @output_current_line
78
- @output_current_line = true
79
- end
80
-
81
- to_s
82
- end
83
-
84
- def to_s
85
- return "" if @output.size <= 1
86
-
87
- @output.map(&:to_s)
88
- end
89
-
90
- private
91
-
92
- def extract_mixin_name(line)
93
- line.tr("{", "").tr(".", "").tr(":", "").strip
94
- end
95
-
96
- def extract_properties_from_mixin(buffer, line)
97
- properties = {}
98
- line = buffer.gets # skip opening {
99
- while line !~ CLOSING_SELECTOR_PATTERN && !buffer.eof?
100
- name, value = extract_name_and_values(line)
101
- properties[name] = value
102
- line = buffer.gets
103
- end
104
- properties
105
- end
106
-
107
- def extract_name_and_values(line)
108
- name, value = line.split(":")
109
- value = value.gsub(";", "")
110
- [name, value].map(&:strip)
111
- end
112
- end
113
-
114
- class Import < Line
115
- attr_accessor :import_path
116
-
117
- def initialize(content, lineno)
118
- super
119
- @import_path = self.class.resolve_import_path(content, lineno)
120
- end
121
-
122
- def parse
123
- Parser.new(File.read(import_path), filename: import_path).parse
124
- end
125
-
126
- class << self
127
- def resolve_import_path(line, lineno = 0)
128
- path = normalize_import_path(line)
129
- unless path.end_with?(Parser::CSS_FILE_EXTENSION)
130
- path += Parser::CSS_FILE_EXTENSION
131
- end
132
- import_path = File.join(Deadfire.configuration.root_path, path)
133
-
134
- unless File.exist?(import_path)
135
- raise Deadfire::ImportException.new(import_path, lineno)
136
- end
137
-
138
- import_path
139
- end
140
-
141
- def normalize_import_path(line)
142
- path = line.split.last
143
- path.gsub!("\"", "")
144
- path.gsub!("\'", "")
145
- path.gsub!(";", "")
146
- path
147
- end
148
- end
149
- end
150
-
151
- class Apply < Line
152
- def initialize(...)
153
- super
154
- @current_line = @content.dup
155
- @space = " "
156
- @space_counter = 0
157
- @import_start_tag = "@"
158
- @output = []
159
- end
160
-
161
- def parse
162
- raise Deadfire::EarlyApplyException.new(@content, @lineno) if Parser.cached_mixins.empty?
163
-
164
- @current_line.each_char do |char|
165
- break if char == @import_start_tag
166
- @space_counter += 1
167
- end
168
-
169
- @current_line.split(" ").each do |css|
170
- next if css.include?(APPLY_SELECTOR)
171
- css.gsub!(";", "")
172
-
173
- fetch_cached_mixin(css).each_pair do |key, value|
174
- @output << "#{@space * @space_counter}#{key}: #{value};"
175
- end
176
- end
177
- @output
178
- end
179
-
180
- private
181
-
182
- # find css class key/val from hash, otherwise throw because the mixin is not defined
183
- def fetch_cached_mixin(key)
184
- raise Deadfire::EarlyApplyException.new(key, @lineno) unless Parser.cached_mixins.include?(key)
185
-
186
- Parser.cached_mixins[key]
187
- end
188
- end
189
-
190
- class Nesting < Line
191
- attr_accessor :block_names
192
-
193
- def initialize(content, lineno, buffer, output)
194
- super(content, lineno)
195
- @buffer = buffer
196
- @output = output
197
- @block_names = []
198
- @nested_level = 0
199
- end
200
-
201
- def parse
202
- line = content.dup.strip
203
- @block_names << find_block_name(@output, @lineno)
204
- tmp = []
205
-
206
- while @nested_level > 0 || !@buffer.eof?
207
- spaces = calculate_spaces_to_add(line)
208
-
209
- if line.start_with?(NEST_SELECTOR)
210
- add_end_block_when_no_end_block_on_prev_line(arr: tmp)
211
- add_selector_to_block_name(line)
212
- @nested_level += 1
213
- tmp << rewrite_line(spaces, line, @block_names[0...-1].join(" "))
214
- else
215
- tmp << "#{spaces}#{line.lstrip}"
216
- end
217
-
218
- remove_last_block_name_entry if line.end_with?(END_BLOCK_CHAR)
219
-
220
- if line.end_with?(END_BLOCK_CHAR)
221
- result = @buffer.peek
222
- if result.strip == END_BLOCK_CHAR
223
- @buffer.gets(skip_buffer: true)
224
- break
225
- end
226
- end
227
-
228
- line = @buffer.gets
229
-
230
- if line.nil? || @buffer.eof? || line.empty?
231
- break
232
- else
233
- line.strip!
234
- end
235
- end
236
-
237
- tmp.join("\n").concat("\n")
238
- end
239
-
240
- private
241
-
242
- def remove_last_block_name_entry
243
- @nested_level -= 1
244
- @block_names.pop
245
- end
246
-
247
- def add_selector_to_block_name(line)
248
- line = extract_selector(line)
249
- line = line_without_nested_block(line)
250
- @block_names << line unless @block_names.include?(line)
251
- end
252
-
253
- def add_end_block_when_no_end_block_on_prev_line(arr: @output)
254
- unless arr[-1]&.strip&.end_with?("}")
255
- arr << "}"
256
- end
257
- end
258
-
259
- def calculate_spaces_to_add(line)
260
- unless line =~ OPENING_SELECTOR_PATTERN || line =~ CLOSING_SELECTOR_PATTERN
261
- " "
262
- else
263
- ""
264
- end
265
- end
266
-
267
- def extract_selector(line)
268
- line.split(START_BLOCK_CHAR).first.strip
269
- end
270
-
271
- def line_without_nested_block(line)
272
- line.split(NEST_SELECTOR).last.strip
273
- end
274
-
275
- def rewrite_line(spaces, line, selector)
276
- case number_of_selectors_in(line)
277
- when 0
278
- line
279
- when 1
280
- "#{spaces}#{line.strip.gsub("&", selector)}"
281
- else
282
- line.strip.each_char.map do |s|
283
- if s == NEST_SELECTOR
284
- selector
285
- else
286
- s
287
- end
288
- end.join
289
- end
290
- end
291
-
292
- def number_of_selectors_in(line)
293
- line.split.count do |s|
294
- # break if s == "{" # early exit, no need to read every char
295
- s.start_with?(NEST_SELECTOR)
296
- end
297
- end
298
-
299
- def find_block_name(output, lineno = nil)
300
- lineno = output.size unless lineno
301
- if lineno < 0
302
- raise "Cannot find block name"
303
- end
304
-
305
- line = output[lineno]
306
-
307
- if line.to_s =~ OPENING_SELECTOR_PATTERN
308
- extract_selector(line)
309
- else
310
- find_block_name(output, lineno - 1)
311
- end
312
- end
313
- end
314
-
315
- def parse
316
- while ! buffer.eof?
317
- process_line(buffer.gets)
318
- end
319
-
320
- @output << NEWLINE
321
-
322
- @output.join
323
- end
324
-
325
- private
326
-
327
- # this method returns void, and modifies the output array directly
328
- def process_line(line)
329
- if line.strip.start_with?("/*")
330
- handle_comment(line)
331
- elsif line.strip.start_with?("@import")
332
- handle_import(line)
333
- elsif line.strip.start_with?(":root {")
334
- handle_mixins(line)
335
- elsif line.strip.start_with?("@apply") # or line.include?("@apply")
336
- handle_apply(line)
337
- elsif line.strip.start_with?("&")
338
- handle_nestings(line)
339
- else
340
- @output << line
341
- end
342
- end
343
-
344
- def keep_comments?
345
- Deadfire.configuration.keep_comments
346
- end
347
-
348
- def handle_comment(line)
349
- @output << Line.new(line, buffer.lineno) if keep_comments?
350
-
351
- while ! line.include?("*/") && ! buffer.eof?
352
- line = buffer.gets
353
- @output << Line.new(line, buffer.lineno) if keep_comments?
354
- end
355
- end
356
-
357
- def handle_import(line)
358
- import = Import.new(line, buffer.lineno)
359
-
360
- if self.class.import_path_cache.include?(import.import_path)
361
- raise DuplicateImportException.new(import.import_path, buffer.lineno)
362
- end
363
-
364
- self.class.import_path_cache << import.import_path
365
-
366
- # TODO:
367
- # - decide on how many levels of imports we want to allow
368
- # - make async??
369
- @output << import.parse
370
- end
371
-
372
- def handle_apply(line)
373
- @apply = Apply.new(line, buffer.lineno)
374
- @output << @apply.parse.join(NEWLINE)
375
- end
376
-
377
- def handle_mixins(line)
378
- @root = Root.new(line, buffer.lineno, buffer)
379
- @output << @root.parse
380
- end
381
-
382
- def handle_nestings(line)
383
- nesting = Nesting.new(line, buffer.lineno, buffer, @output)
384
- @output << nesting.parse
385
- end
386
- end
387
- end
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Deadfire::Transformers
4
- class Transformer
5
- def name
6
- self.class.name
7
- end
8
-
9
- def matches?(line)
10
- false
11
- end
12
-
13
- def transform(line, buffer, output); end
14
-
15
- def reset; end
16
- end
17
- end