deadfire 0.3.0 → 0.5.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.
metadata CHANGED
@@ -1,15 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deadfire
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Haroon Ahmed
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-11-15 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2024-12-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: actionpack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 7.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 7.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 7.0.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 7.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: railties
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 7.0.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 7.0.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: propshaft
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 0.9.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 0.9.0
13
69
  description:
14
70
  email:
15
71
  - haroon.ahmed25@gmail.com
@@ -37,10 +93,12 @@ files:
37
93
  - bin/test
38
94
  - changelog.md
39
95
  - deadfire.gemspec
96
+ - lib/.keep
40
97
  - lib/deadfire.rb
98
+ - lib/deadfire/asset_loader.rb
99
+ - lib/deadfire/asset_registry.rb
41
100
  - lib/deadfire/ast_printer.rb
42
101
  - lib/deadfire/configuration.rb
43
- - lib/deadfire/css_buffer.rb
44
102
  - lib/deadfire/css_generator.rb
45
103
  - lib/deadfire/error_reporter.rb
46
104
  - lib/deadfire/errors.rb
@@ -58,8 +116,9 @@ files:
58
116
  - lib/deadfire/front_end/stylesheet_node.rb
59
117
  - lib/deadfire/front_end/token.rb
60
118
  - lib/deadfire/interpreter.rb
61
- - lib/deadfire/parser.rb
119
+ - lib/deadfire/mixin_parser.rb
62
120
  - lib/deadfire/parser_engine.rb
121
+ - lib/deadfire/railtie.rb
63
122
  - lib/deadfire/spec.rb
64
123
  - lib/deadfire/version.rb
65
124
  homepage: https://github.com/hahmed/deadfire
@@ -77,14 +136,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
77
136
  requirements:
78
137
  - - ">="
79
138
  - !ruby/object:Gem::Version
80
- version: '2.7'
139
+ version: '3.0'
81
140
  required_rubygems_version: !ruby/object:Gem::Requirement
82
141
  requirements:
83
142
  - - ">="
84
143
  - !ruby/object:Gem::Version
85
144
  version: '0'
86
145
  requirements: []
87
- rubygems_version: 3.4.10
146
+ rubygems_version: 3.5.9
88
147
  signing_key:
89
148
  specification_version: 4
90
149
  summary: Deadfire - lightweight css preprocessor
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
- require "stringio"
3
-
4
- module Deadfire
5
- class CssBuffer
6
- attr_reader :lineno, :buffer
7
-
8
- def initialize(content)
9
- @content = StringIO.new(content)
10
- @buffer = []
11
- @lineno = 0
12
- end
13
-
14
- def gets(skip_buffer: false)
15
- output = content.gets
16
- if output && !skip_buffer
17
- buffer << output
18
- end
19
- @lineno += 1
20
- output
21
- end
22
-
23
- def peek
24
- output = content.gets
25
- content.ungetc(output)
26
- output
27
- end
28
-
29
- def eof?
30
- content.eof?
31
- end
32
-
33
- private
34
-
35
- attr_reader :content
36
- end
37
- end
@@ -1,258 +0,0 @@
1
- # frozen_string_literal: true
2
- module Deadfire
3
- # NOTE: Legacy parser, will be replaced by ParserEngine
4
- class Parser
5
- singleton_class.attr_accessor :cached_mixins
6
- self.cached_mixins = Hash.new { |h, k| h[k] = {} }
7
-
8
- singleton_class.attr_accessor :import_path_cache
9
- self.import_path_cache = []
10
-
11
- ROOT_SELECTOR = ":root {"
12
- OPENING_SELECTOR_PATTERN = /\s*\{/
13
- CLOSING_SELECTOR_PATTERN = /\s*\}/
14
- NEST_SELECTOR = "&"
15
- START_BLOCK_CHAR = "{"
16
- END_BLOCK_CHAR = "}"
17
- IMPORT_SELECTOR = "@import"
18
- CSS_FILE_EXTENSION = ".css"
19
- APPLY_SELECTOR = "@apply"
20
- NEWLINE = "\n"
21
- OPEN_COMMENT_SELECTOR = "/*"
22
- CLOSE_COMMENT_SELECTOR = "*/"
23
-
24
- def self.parse(content, options = {})
25
- new(content, options).parse
26
- end
27
-
28
- attr_reader :output, :errors_list
29
-
30
- def initialize(content, options = {})
31
- @content = content
32
- @errors_list = ErrorsList.new
33
- @filename = options[:filename]
34
- @output = []
35
- @imports = []
36
- end
37
-
38
- def buffer
39
- @content = preprocess
40
- @buffer ||= CssBuffer.new(@content)
41
- end
42
-
43
- def parse
44
- while ! buffer.eof?
45
- process_line(buffer.gets)
46
- end
47
-
48
- @output << NEWLINE
49
-
50
- @output.join
51
- end
52
-
53
- def errors?
54
- @errors_list.errors.any?
55
- end
56
-
57
- private
58
-
59
- def preprocess
60
- @content.gsub(/\r\n?|\f/, "\n").gsub("\u{0000}", "\u{FFFD}")
61
- end
62
-
63
- # this method returns void, and modifies the output array directly
64
- def process_line(line)
65
- if line.strip.start_with?(OPEN_COMMENT_SELECTOR)
66
- handle_comment(line)
67
- elsif line.strip.start_with?(IMPORT_SELECTOR)
68
- handle_import(line)
69
- elsif line.strip.start_with?(ROOT_SELECTOR)
70
- handle_mixins(line)
71
- elsif line.strip.start_with?(APPLY_SELECTOR)
72
- handle_apply(line)
73
- else
74
- @output << line
75
- end
76
- end
77
-
78
- def keep_comments?
79
- Deadfire.configuration.keep_comments
80
- end
81
-
82
- def handle_comment(line)
83
- @output << Line.new(line, buffer.lineno) if keep_comments?
84
-
85
- while ! line.include?(CLOSE_COMMENT_SELECTOR) && ! buffer.eof?
86
- line = buffer.gets
87
- @output << Line.new(line, buffer.lineno) if keep_comments?
88
- end
89
-
90
- if buffer.eof?
91
- @errors_list.add(message: "Unclosed comment error", lineno: buffer.lineno, original_line: line)
92
- end
93
- end
94
-
95
- def handle_import(line)
96
- import = Import.new(line, buffer.lineno)
97
-
98
- # TODO:
99
- # - decide on how many levels of imports we want to allow
100
- # - make async??
101
- @output << import.parse
102
- end
103
-
104
- def handle_apply(line)
105
- @apply = Apply.new(line, buffer.lineno)
106
- @output << @apply.parse.join(NEWLINE)
107
- end
108
-
109
- def handle_mixins(line)
110
- @root = Root.new(line, buffer.lineno, buffer)
111
- @output << @root.parse
112
- end
113
- end
114
-
115
- class Line
116
- attr_accessor :content, :line_number
117
-
118
- def initialize(content, line_number)
119
- @content = content
120
- @line_number = line_number
121
- end
122
-
123
- def to_s
124
- content
125
- end
126
- end
127
-
128
- class Root < Line
129
- def initialize(content, lineno, buffer)
130
- super(content, lineno)
131
- @end_tag = false
132
- @output_current_line = true
133
- @output = []
134
- @buffer = buffer
135
- end
136
-
137
- def parse
138
- line = @content
139
- if line.include? Parser::ROOT_SELECTOR
140
- @output << Line.new(line, @line_number)
141
- end
142
-
143
- while !@end_tag && line = @buffer.gets
144
- if line =~ Parser::OPENING_SELECTOR_PATTERN
145
- @output_current_line = false
146
- name = extract_mixin_name(line)
147
- properties = extract_properties_from_mixin(@buffer, line)
148
- Parser.cached_mixins[name] = properties
149
- elsif line =~ Parser::CLOSING_SELECTOR_PATTERN
150
- @end_tag = true
151
- end
152
-
153
- @output << Line.new(line, @buffer.lineno) if @output_current_line
154
- @output_current_line = true
155
- end
156
-
157
- to_s
158
- end
159
-
160
- def to_s
161
- return "" if @output.size <= 1
162
-
163
- @output.map(&:to_s)
164
- end
165
-
166
- private
167
-
168
- def extract_mixin_name(line)
169
- line.tr("{", "").tr(".", "").tr(":", "").strip
170
- end
171
-
172
- def extract_properties_from_mixin(buffer, line)
173
- properties = {}
174
- line = buffer.gets # skip opening {
175
- while line !~ Parser::CLOSING_SELECTOR_PATTERN && !buffer.eof?
176
- name, value = extract_name_and_values(line)
177
- properties[name] = value
178
- line = buffer.gets
179
- end
180
- properties
181
- end
182
-
183
- def extract_name_and_values(line)
184
- name, value = line.split(":")
185
- value = value.gsub(";", "")
186
- [name, value].map(&:strip)
187
- end
188
- end
189
-
190
- class Import < Line
191
- def initialize(...)
192
- super(...)
193
- end
194
-
195
- def parse
196
- import_path = FilenameHelper.resolve_import_path(content, @lineno)
197
-
198
- if Parser.import_path_cache.include?(import_path)
199
- raise DuplicateImportException.new(import_path, @lineno)
200
- end
201
-
202
- Parser.import_path_cache << import_path
203
-
204
- Parser.new(File.read(import_path), filename: import_path).parse
205
- end
206
- end
207
-
208
- class Apply < Line
209
- def initialize(...)
210
- super
211
- @current_line = @content.dup
212
- @space = " "
213
- @apply_start_char = "@"
214
- @output = []
215
- end
216
-
217
- def parse
218
- raise Deadfire::EarlyApplyException.new(@content, @lineno) if Parser.cached_mixins.empty?
219
-
220
- space_counter = calculate_number_of_spaces
221
- ends_with_end_block_char = false
222
-
223
- @current_line.split(" ").each do |css|
224
- next if css.include?(Parser::APPLY_SELECTOR)
225
-
226
- css.gsub!(";", "")
227
- if css.end_with?(Parser::END_BLOCK_CHAR)
228
- ends_with_end_block_char = true
229
- css.gsub!(Parser::END_BLOCK_CHAR, "")
230
- end
231
-
232
- fetch_cached_mixin(css).each_pair do |key, value|
233
- @output << "#{@space * space_counter}#{key}: #{value};"
234
- end
235
- end
236
- @output << "#{Parser::END_BLOCK_CHAR}" if ends_with_end_block_char
237
- @output
238
- end
239
-
240
- private
241
-
242
- def calculate_number_of_spaces
243
- space_counter = 0
244
- @current_line.each_char do |char|
245
- break if char == @apply_start_char
246
- space_counter += 1
247
- end
248
- space_counter
249
- end
250
-
251
- # find css class key/val from hash, otherwise throw because the mixin is not defined
252
- def fetch_cached_mixin(key)
253
- raise Deadfire::EarlyApplyException.new(key, @lineno) unless Parser.cached_mixins.include?(key)
254
-
255
- Parser.cached_mixins[key]
256
- end
257
- end
258
- end