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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +4 -1
- data/.gitignore +1 -0
- data/Gemfile.lock +96 -4
- data/README.md +6 -31
- data/benchmarks/basic_benchmark.rb +39 -7
- data/changelog.md +25 -0
- data/deadfire.gemspec +6 -1
- data/lib/.keep +0 -0
- data/lib/deadfire/asset_loader.rb +67 -0
- data/lib/deadfire/asset_registry.rb +88 -0
- data/lib/deadfire/ast_printer.rb +6 -1
- data/lib/deadfire/configuration.rb +25 -7
- data/lib/deadfire/css_generator.rb +2 -3
- data/lib/deadfire/error_reporter.rb +8 -2
- data/lib/deadfire/errors.rb +1 -42
- data/lib/deadfire/filename_helper.rb +30 -10
- data/lib/deadfire/front_end/apply_node.rb +1 -2
- data/lib/deadfire/front_end/parser.rb +13 -17
- data/lib/deadfire/front_end/scanner.rb +54 -40
- data/lib/deadfire/front_end/stylesheet_node.rb +1 -1
- data/lib/deadfire/interpreter.rb +6 -14
- data/lib/deadfire/mixin_parser.rb +51 -0
- data/lib/deadfire/parser_engine.rb +11 -1
- data/lib/deadfire/railtie.rb +38 -0
- data/lib/deadfire/spec.rb +28 -27
- data/lib/deadfire/version.rb +1 -1
- data/lib/deadfire.rb +13 -4
- metadata +66 -7
- data/lib/deadfire/css_buffer.rb +0 -37
- data/lib/deadfire/parser.rb +0 -258
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.
|
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:
|
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/
|
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: '
|
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.
|
146
|
+
rubygems_version: 3.5.9
|
88
147
|
signing_key:
|
89
148
|
specification_version: 4
|
90
149
|
summary: Deadfire - lightweight css preprocessor
|
data/lib/deadfire/css_buffer.rb
DELETED
@@ -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
|
data/lib/deadfire/parser.rb
DELETED
@@ -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
|