phlexing 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 36dca58e17556b8b085edeb24dbe3fc5f8c8cb976056a6f739a655c5d94d7a36
4
- data.tar.gz: 21f2b6dbec24a29a4dbfe52bb25d77887e52c72d29098e41c5a80a2173e8cc66
3
+ metadata.gz: 0a1f00bbf43b734cd75f369ace78ec7a8e782192fc65b9729c345b16dfb25dd6
4
+ data.tar.gz: 10985bfa447d6e052b2bfe932ff796bc2465458ca5a7b8f0e77bb089f0372371
5
5
  SHA512:
6
- metadata.gz: e52e0c6836f3e701c24f6ccf2338f823b5507497863e3af93d089712a2503213d2e86d5f3516f1f9d3e60bffd907de7ffbfc3661610141dd0f917f43a7cc2008
7
- data.tar.gz: 00730543e38df6d87c1f5a1263ea964bfc27024de00dea885df86a5d60626afd665485899aad1d8cf350fba3df7e914879fda76040953125a4e9842e387c4b7b
6
+ metadata.gz: b9ad394751b8749495198b624ee0b0df792225d62664ef760b98f29ec18f1bc09aeb02b9cd3727e386bcf75fd11409658679e2a69b12709644cfeefc7aea3b3c
7
+ data.tar.gz: '009f8d61b78789d5f78641c85ef37307661bdcdc5bcf3591032766d3de63a9bb8c6fb4b63c74500b94b8084ebccd6e977f1d7abb883a0e5417e2a915edcad9d3'
data/Gemfile CHANGED
@@ -4,6 +4,7 @@ source "https://rubygems.org"
4
4
 
5
5
  gemspec
6
6
 
7
+ gem "maxitest", "~> 4.4"
7
8
  gem "minitest", "~> 5.0"
8
9
  gem "rake", "~> 13.0"
9
10
  gem "rubocop", require: false, github: "joeldrapper/rubocop", branch: "rubocop-user-agent"
data/Gemfile.lock CHANGED
@@ -17,63 +17,215 @@ GIT
17
17
  PATH
18
18
  remote: .
19
19
  specs:
20
- phlexing (0.2.0)
21
- erb_parser (~> 0.0.2)
20
+ phlexing (0.4.0)
21
+ deface (~> 1.9)
22
22
  html_press (~> 0.8.2)
23
23
  nokogiri (~> 1.0)
24
- rufo (~> 0.13.0)
24
+ phlex (~> 1.6)
25
+ phlex-rails (~> 0.9)
26
+ syntax_tree (~> 6.0)
25
27
 
26
28
  GEM
27
29
  remote: https://rubygems.org/
28
30
  specs:
31
+ actioncable (7.0.4.3)
32
+ actionpack (= 7.0.4.3)
33
+ activesupport (= 7.0.4.3)
34
+ nio4r (~> 2.0)
35
+ websocket-driver (>= 0.6.1)
36
+ actionmailbox (7.0.4.3)
37
+ actionpack (= 7.0.4.3)
38
+ activejob (= 7.0.4.3)
39
+ activerecord (= 7.0.4.3)
40
+ activestorage (= 7.0.4.3)
41
+ activesupport (= 7.0.4.3)
42
+ mail (>= 2.7.1)
43
+ net-imap
44
+ net-pop
45
+ net-smtp
46
+ actionmailer (7.0.4.3)
47
+ actionpack (= 7.0.4.3)
48
+ actionview (= 7.0.4.3)
49
+ activejob (= 7.0.4.3)
50
+ activesupport (= 7.0.4.3)
51
+ mail (~> 2.5, >= 2.5.4)
52
+ net-imap
53
+ net-pop
54
+ net-smtp
55
+ rails-dom-testing (~> 2.0)
56
+ actionpack (7.0.4.3)
57
+ actionview (= 7.0.4.3)
58
+ activesupport (= 7.0.4.3)
59
+ rack (~> 2.0, >= 2.2.0)
60
+ rack-test (>= 0.6.3)
61
+ rails-dom-testing (~> 2.0)
62
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
63
+ actiontext (7.0.4.3)
64
+ actionpack (= 7.0.4.3)
65
+ activerecord (= 7.0.4.3)
66
+ activestorage (= 7.0.4.3)
67
+ activesupport (= 7.0.4.3)
68
+ globalid (>= 0.6.0)
69
+ nokogiri (>= 1.8.5)
70
+ actionview (7.0.4.3)
71
+ activesupport (= 7.0.4.3)
72
+ builder (~> 3.1)
73
+ erubi (~> 1.4)
74
+ rails-dom-testing (~> 2.0)
75
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
76
+ activejob (7.0.4.3)
77
+ activesupport (= 7.0.4.3)
78
+ globalid (>= 0.3.6)
79
+ activemodel (7.0.4.3)
80
+ activesupport (= 7.0.4.3)
81
+ activerecord (7.0.4.3)
82
+ activemodel (= 7.0.4.3)
83
+ activesupport (= 7.0.4.3)
84
+ activestorage (7.0.4.3)
85
+ actionpack (= 7.0.4.3)
86
+ activejob (= 7.0.4.3)
87
+ activerecord (= 7.0.4.3)
88
+ activesupport (= 7.0.4.3)
89
+ marcel (~> 1.0)
90
+ mini_mime (>= 1.1.0)
91
+ activesupport (7.0.4.3)
92
+ concurrent-ruby (~> 1.0, >= 1.0.2)
93
+ i18n (>= 1.6, < 2)
94
+ minitest (>= 5.1)
95
+ tzinfo (~> 2.0)
29
96
  ast (2.4.2)
97
+ builder (3.2.4)
98
+ cgi (0.3.6)
99
+ concurrent-ruby (1.2.2)
100
+ crass (1.0.6)
30
101
  css_press (0.3.2)
31
102
  csspool-st (= 3.1.2)
32
103
  json
33
104
  csspool-st (3.1.2)
34
- erb_parser (0.0.2)
35
- treetop
105
+ date (3.3.3)
106
+ deface (1.9.0)
107
+ actionview (>= 5.2)
108
+ nokogiri (>= 1.6)
109
+ polyglot
110
+ railties (>= 5.2)
111
+ rainbow (>= 2.1.0)
112
+ erb (4.0.2)
113
+ cgi (>= 0.3.3)
114
+ erubi (1.12.0)
36
115
  execjs (2.8.1)
116
+ globalid (1.1.0)
117
+ activesupport (>= 5.0)
37
118
  html_press (0.8.2)
38
119
  htmlentities
39
120
  multi_css (>= 0.1.0)
40
121
  multi_js (>= 0.1.0)
41
122
  htmlentities (4.3.4)
123
+ i18n (1.12.0)
124
+ concurrent-ruby (~> 1.0)
42
125
  json (2.6.2)
43
- minitest (5.16.3)
126
+ loofah (2.19.1)
127
+ crass (~> 1.0.2)
128
+ nokogiri (>= 1.5.9)
129
+ mail (2.8.1)
130
+ mini_mime (>= 0.1.1)
131
+ net-imap
132
+ net-pop
133
+ net-smtp
134
+ marcel (1.0.2)
135
+ maxitest (4.4.0)
136
+ minitest (>= 5.0.0, < 5.18.0)
137
+ method_source (1.0.0)
138
+ mini_mime (1.1.2)
139
+ minitest (5.17.0)
44
140
  multi_css (0.1.0)
45
141
  css_press
46
142
  multi_js (0.1.0)
47
143
  uglifier (~> 2)
48
- nokogiri (1.13.10-x86_64-darwin)
144
+ net-imap (0.3.4)
145
+ date
146
+ net-protocol
147
+ net-pop (0.1.2)
148
+ net-protocol
149
+ net-protocol (0.2.1)
150
+ timeout
151
+ net-smtp (0.3.3)
152
+ net-protocol
153
+ nio4r (2.5.8)
154
+ nokogiri (1.14.2-x86_64-darwin)
49
155
  racc (~> 1.4)
50
- nokogiri (1.13.10-x86_64-linux)
156
+ nokogiri (1.14.2-x86_64-linux)
51
157
  racc (~> 1.4)
52
158
  parallel (1.22.1)
53
- parser (3.1.2.1)
159
+ parser (3.2.0.0)
54
160
  ast (~> 2.4.1)
161
+ phlex (1.6.1)
162
+ concurrent-ruby (~> 1.2)
163
+ erb (>= 4)
164
+ zeitwerk (~> 2.6)
165
+ phlex-rails (0.9.0)
166
+ phlex (~> 1.6)
167
+ rails (>= 6.1, < 8)
168
+ zeitwerk (~> 2.6)
55
169
  polyglot (0.3.5)
170
+ prettier_print (1.2.1)
56
171
  racc (1.6.2)
172
+ rack (2.2.6.4)
173
+ rack-test (2.1.0)
174
+ rack (>= 1.3)
175
+ rails (7.0.4.3)
176
+ actioncable (= 7.0.4.3)
177
+ actionmailbox (= 7.0.4.3)
178
+ actionmailer (= 7.0.4.3)
179
+ actionpack (= 7.0.4.3)
180
+ actiontext (= 7.0.4.3)
181
+ actionview (= 7.0.4.3)
182
+ activejob (= 7.0.4.3)
183
+ activemodel (= 7.0.4.3)
184
+ activerecord (= 7.0.4.3)
185
+ activestorage (= 7.0.4.3)
186
+ activesupport (= 7.0.4.3)
187
+ bundler (>= 1.15.0)
188
+ railties (= 7.0.4.3)
189
+ rails-dom-testing (2.0.3)
190
+ activesupport (>= 4.2.0)
191
+ nokogiri (>= 1.6)
192
+ rails-html-sanitizer (1.5.0)
193
+ loofah (~> 2.19, >= 2.19.1)
194
+ railties (7.0.4.3)
195
+ actionpack (= 7.0.4.3)
196
+ activesupport (= 7.0.4.3)
197
+ method_source
198
+ rake (>= 12.2)
199
+ thor (~> 1.0)
200
+ zeitwerk (~> 2.5)
57
201
  rainbow (3.1.1)
58
202
  rake (13.0.6)
59
- regexp_parser (2.6.0)
203
+ regexp_parser (2.6.2)
60
204
  rexml (3.2.5)
61
- rubocop-ast (1.23.0)
205
+ rubocop-ast (1.24.1)
62
206
  parser (>= 3.1.1.0)
63
207
  ruby-progressbar (1.11.0)
64
- rufo (0.13.0)
65
- treetop (1.6.12)
66
- polyglot (~> 0.3)
208
+ syntax_tree (6.1.1)
209
+ prettier_print (>= 1.2.0)
210
+ thor (1.2.1)
211
+ timeout (0.3.2)
212
+ tzinfo (2.0.6)
213
+ concurrent-ruby (~> 1.0)
67
214
  uglifier (2.7.2)
68
215
  execjs (>= 0.3.0)
69
216
  json (>= 1.8.0)
70
- unicode-display_width (2.3.0)
217
+ unicode-display_width (2.4.2)
218
+ websocket-driver (0.7.5)
219
+ websocket-extensions (>= 0.1.0)
220
+ websocket-extensions (0.1.5)
221
+ zeitwerk (2.6.7)
71
222
 
72
223
  PLATFORMS
73
224
  x86_64-darwin-19
74
225
  x86_64-linux
75
226
 
76
227
  DEPENDENCIES
228
+ maxitest (~> 4.4)
77
229
  minitest (~> 5.0)
78
230
  phlexing!
79
231
  rake (~> 13.0)
data/Rakefile CHANGED
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "bundler/gem_tasks"
4
3
  require "rake/testtask"
5
4
 
6
5
  Rake::TestTask.new(:test) do |t|
@@ -9,8 +8,4 @@ Rake::TestTask.new(:test) do |t|
9
8
  t.test_files = FileList["test/**/*_test.rb"]
10
9
  end
11
10
 
12
- require "rubocop/rake_task"
13
-
14
- RuboCop::RakeTask.new
15
-
16
- task default: %i[test rubocop]
11
+ task default: %i[test]
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexing
4
+ class ComponentGenerator
5
+ include Helpers
6
+
7
+ attr_accessor :converter, :analyzer
8
+
9
+ def self.call(converter)
10
+ new(converter).call
11
+ end
12
+
13
+ def initialize(converter)
14
+ @converter = converter
15
+ @analyzer = RubyAnalyzer.new
16
+ @analyzer.analyze(converter.source)
17
+ end
18
+
19
+ def call
20
+ out = StringIO.new
21
+
22
+ out << "class "
23
+ out << options.component_name
24
+ out << " < "
25
+ out << options.parent_component
26
+ out << newline
27
+
28
+ analyzer.includes.sort.each do |inklude|
29
+ out << "include "
30
+ out << inklude
31
+ out << newline
32
+ end
33
+
34
+ out << newline if analyzer.includes.any?
35
+
36
+ if analyzer.locals.any?
37
+ out << "attr_accessor "
38
+ out << build_accessors
39
+ out << newline
40
+ out << newline
41
+ end
42
+
43
+ converter.custom_elements.sort.each do |element|
44
+ out << "register_element :"
45
+ out << element
46
+ out << newline
47
+ end
48
+
49
+ out << newline if converter.custom_elements.any?
50
+
51
+ if kwargs.any?
52
+ out << "def initialize("
53
+ out << build_kwargs
54
+ out << ")"
55
+ out << newline
56
+
57
+ kwargs.each do |dep|
58
+ out << "@#{dep} = #{dep}\n"
59
+ end
60
+
61
+ out << "end"
62
+ out << newline
63
+ out << newline
64
+ end
65
+
66
+ out << "def template"
67
+ out << newline
68
+ out << converter.template_code
69
+ out << newline
70
+ out << "end"
71
+
72
+ if analyzer.instance_methods.any?
73
+ out << newline
74
+ out << newline
75
+ out << "private"
76
+ out << newline
77
+ out << newline
78
+
79
+ analyzer.instance_methods.sort.each do |instance_method|
80
+ out << "def "
81
+ out << instance_method
82
+ out << "(*args, **kwargs)"
83
+ out << newline
84
+ out << "# TODO: Implement me"
85
+ out << newline
86
+ out << "end"
87
+ out << newline
88
+ out << newline
89
+ end
90
+
91
+ out << newline
92
+ end
93
+
94
+ out << newline
95
+ out << "end"
96
+ out << newline
97
+
98
+ Formatter.call(out.string.strip)
99
+ rescue StandardError
100
+ out.string.strip
101
+ end
102
+
103
+ private
104
+
105
+ def kwargs
106
+ Set.new(analyzer.ivars + analyzer.locals).sort
107
+ end
108
+
109
+ def build_kwargs
110
+ kwargs.map { |kwarg| arg(kwarg) }.join(", ")
111
+ end
112
+
113
+ def build_accessors
114
+ analyzer.locals.sort.map { |local| symbol(local) }.join(", ")
115
+ end
116
+
117
+ def options
118
+ converter.options
119
+ end
120
+ end
121
+ end
@@ -1,253 +1,42 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "nokogiri"
4
- require "ostruct"
5
- require "rufo"
6
- require "html_press"
7
- require "erb_parser"
8
-
9
3
  module Phlexing
10
4
  class Converter
11
- include Helpers
12
-
13
- using ::Phlexing::Refinements::StringRefinements
14
-
15
- attr_accessor :html, :custom_elements, :ivars, :locals, :idents
5
+ attr_accessor :source, :custom_elements, :options
16
6
 
17
- def self.convert(html, **options)
18
- new(html, **options).output
7
+ def self.call(source, **options)
8
+ new(**options).call(source)
19
9
  end
20
10
 
21
- def initialize(html, **options)
22
- @html = html
23
- @buffer = StringIO.new
24
- @custom_elements = Set.new
25
- @ivars = Set.new
26
- @locals = Set.new
27
- @includes = Set.new
28
- @idents = Set.new
29
- @options = options
30
- analyze_ruby
31
- handle_node
11
+ def self.convert(source, **options)
12
+ new(**options).call(source)
32
13
  end
33
14
 
34
- def handle_text(node, level, newline: true)
35
- text = node.text
36
-
37
- if text.squish.empty? && text.length.positive?
38
- @buffer << indent(level)
39
- @buffer << whitespace(@options)
40
-
41
- text.strip!
42
- end
15
+ def call(source)
16
+ @source = source
43
17
 
44
- if text.length.positive?
45
- @buffer << indent(level)
46
-
47
- if siblings?(node)
48
- @buffer << "text "
49
- end
50
-
51
- @buffer << quote(text)
52
- @buffer << "\n" if newline
53
- end
18
+ code
54
19
  end
55
20
 
56
- def handle_erb_element(node, level, newline: true)
57
- if erb_safe_output?(node)
58
- @buffer << "unsafe_raw "
59
- @buffer << node.text.from(1)
60
- @buffer << "\n" if newline
61
-
62
- return
63
- end
64
-
65
- if erb_interpolation?(node) && node.parent.children.count > 1
66
- if node.text.length >= 24
67
- @buffer << "text("
68
- @buffer << node.text
69
- @buffer << ")"
70
- else
71
- @buffer << "text "
72
- @buffer << node.text
73
- end
74
- elsif erb_comment?(node)
75
- @buffer << "#"
76
- @buffer << node.text
77
- else
78
- @buffer << node.text
79
- end
80
-
81
- @buffer << "\n" if newline
82
- end
83
-
84
- def handle_element(node, level)
85
- @buffer << (indent(level) + node_name(node) + handle_attributes(node))
86
-
87
- if node.children.any?
88
- if node.children.one? && text_node?(node.children.first) && node.text.length <= 32
89
- single_line_block {
90
- handle_text(node.children.first, 0, newline: false)
91
- }
92
- elsif node.children.one? && erb_interpolation?(node.children.first) && node.text.length <= 32
93
- single_line_block {
94
- handle_erb_element(node.children.first, 0, newline: false)
95
- }
96
- else
97
- multi_line_block(level) {
98
- handle_children(node, level)
99
- }
100
- end
101
- else
102
- @buffer << "\n"
103
- end
104
- end
105
-
106
- def handle_comment_node(node, level)
107
- @buffer << indent(level)
108
- @buffer << "comment "
109
- @buffer << quote(node.text.strip)
110
- @buffer << "\n"
111
- end
112
-
113
- def handle_children(node, level)
114
- node.children.each do |child|
115
- handle_node(child, level + 1)
116
- end
117
- end
118
-
119
- def handle_attributes(node)
120
- return "" if node.attributes.keys.none?
121
-
122
- b = StringIO.new
123
-
124
- node.attributes.each_value do |attribute|
125
- b << attribute.name.gsub("-", "_")
126
- b << ": "
127
- b << double_quote(attribute.value)
128
- b << ", " if node.attributes.values.last != attribute
129
- end
130
-
131
- if node.children.any?
132
- "(#{b.string.strip}) "
133
- else
134
- " #{b.string.strip}"
135
- end
136
- end
137
-
138
- def handle_node(node = parsed, level = 0)
139
- case node
140
- when Nokogiri::XML::Text
141
- handle_text(node, level)
142
- when Nokogiri::XML::Element
143
- if erb_node?(node)
144
- handle_erb_element(node, level)
145
- else
146
- handle_element(node, level)
147
- end
148
-
149
- @buffer << "\n" if level == 1
150
- when Nokogiri::HTML4::DocumentFragment
151
- handle_children(node, level)
152
- when Nokogiri::XML::Comment
153
- handle_comment_node(node, level)
154
- else
155
- @buffer << ("UNKNOWN#{node.class}")
156
- end
157
-
158
- @buffer.string
159
- end
160
-
161
- def parsed
162
- @parsed ||= Nokogiri::HTML.fragment(minified_erb)
163
- end
21
+ def initialize(source = nil, **options)
22
+ @custom_elements = Set.new
23
+ @options = Options.new(**options)
164
24
 
165
- def buffer
166
- Rufo::Formatter.format(@buffer.string.strip)
167
- rescue Rufo::SyntaxError
168
- @buffer.string.strip
25
+ call(source)
169
26
  end
170
27
 
171
- def output
172
- out = StringIO.new
173
-
174
- if @options.fetch(:phlex_class, false)
175
- component_name = @options.fetch(:component_name, "Component")
176
- component_name = "A#{component_name}" if component_name[0] == "0" || component_name[0].to_i != 0
177
-
178
- parent_component = @options.fetch(:parent_component, "Phlex::HTML")
179
- parent_component = "A#{parent_component}" if parent_component[0] == "0" || parent_component[0].to_i != 0
180
-
181
- out << "class #{component_name}"
182
- out << "< #{parent_component}\n"
183
-
184
- if locals.any?
185
- out << indent(1)
186
- out << "attr_accessor "
187
- out << locals.sort.map { |local| ":#{local}" }.join(", ")
188
- out << "\n\n"
189
- end
190
-
191
- @custom_elements.sort.each do |element|
192
- out << indent(1)
193
- out << "register_element :#{element}\n"
194
- end
195
-
196
- kwargs = Set.new(ivars + locals).sort
197
-
198
- if kwargs.any?
199
- out << indent(1)
200
- out << "def initialize("
201
- out << kwargs.map { |kwarg| "#{kwarg}: " }.join(", ")
202
- out << ")\n"
203
-
204
- kwargs.each do |dep|
205
- out << indent(2)
206
- out << "@#{dep} = #{dep}\n"
207
- end
208
-
209
- out << indent(1)
210
- out << "end\n"
211
- end
212
-
213
- out << indent(1)
214
- out << "def template\n"
215
-
216
- out << indent(2)
217
- out << @buffer.string
218
-
219
- out << indent(1)
220
- out << "end\n"
221
- out << "end\n"
222
- else
223
- out << @buffer.string
224
- end
225
-
226
- Rufo::Formatter.format(out.string.strip)
227
- rescue Rufo::SyntaxError
228
- out.string.strip
28
+ def code
29
+ options.component? ? component_code : template_code
229
30
  end
230
31
 
231
- def analyze_ruby
232
- ruby_code = ErbParser.parse(html).tokens.map { |tag| tag.is_a?(ErbParser::ErbTag) && !tag.to_s.start_with?("<%#") ? tag.ruby_code.delete_prefix("=") : nil }.join("\n")
233
- visitor = Phlexing::Visitor.new(self)
234
- program = SyntaxTree.parse(ruby_code)
235
- # puts program.construct_keys
236
- visitor.visit(program)
237
- rescue StandardError => e
238
- puts e.inspect
239
- end
32
+ # private
240
33
 
241
- def converted_erb
242
- ErbParser.transform_xml(html).gsub("\n", "").gsub("\r", "")
243
- rescue StandardError
244
- html
34
+ def template_code
35
+ TemplateGenerator.call(self, source)
245
36
  end
246
37
 
247
- def minified_erb
248
- HtmlPress.press(converted_erb)
249
- rescue StandardError
250
- converted_erb
38
+ def component_code
39
+ ComponentGenerator.call(self)
251
40
  end
252
41
  end
253
42
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "deface"
4
+
5
+ module Phlexing
6
+ # Takes ERB and transforms it to Nokogiri-compatible HTML.
7
+ class ERBTransformer
8
+ def self.call(...)
9
+ new(...).call
10
+ end
11
+
12
+ def initialize(source)
13
+ @source = source.to_s.dup
14
+ end
15
+
16
+ def call
17
+ remove_newlines
18
+ strip_whitespace
19
+ transform_erb_tags
20
+ transform_template_tags
21
+
22
+ @source
23
+ end
24
+
25
+ private
26
+
27
+ def remove_newlines
28
+ @source.tr!("\n\r", "")
29
+ end
30
+
31
+ def strip_whitespace
32
+ @source.strip!
33
+ end
34
+
35
+ # Replace ERB tags with Nokogiri-compatible HTML.
36
+ def transform_erb_tags
37
+ @source = Deface::Parser.erb_markup!(@source)
38
+ end
39
+
40
+ # Phlex uses `template_tag` in place of `template` for `<template>` tags.
41
+ def transform_template_tags
42
+ @source.gsub!(/<template/i, "<template-tag")
43
+ @source.gsub!(%r{</template}i, "</template-tag")
44
+ end
45
+ end
46
+ end