phlexing 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ffc28265f871c2d98adcd231e5c97677d186261c1f7770fa809b26bfc72490de
4
- data.tar.gz: 9a1aad812fab3bd2c355a5a768a68feae857b38af09075225ee0be4da5764c35
3
+ metadata.gz: 0a1f00bbf43b734cd75f369ace78ec7a8e782192fc65b9729c345b16dfb25dd6
4
+ data.tar.gz: 10985bfa447d6e052b2bfe932ff796bc2465458ca5a7b8f0e77bb089f0372371
5
5
  SHA512:
6
- metadata.gz: bd78f6266b1fc40d5d61631b261867141b3cdfbc876f20ec0b78605f583e8969aff2d540010842c76e9376531daa6cd51ed7b35990aabd78209001104368cf6b
7
- data.tar.gz: f2dbaddeab5395082db642e60e8d95983af5e5a39f0687aeb2d9e1ec07761a7a4a6edac6d86fff18e206f9fe68c5bb6bb999428371b5a7e4a856501640fc6bee
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.1.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.11)
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,201 +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
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
- @options = options
26
- handle_node
11
+ def self.convert(source, **options)
12
+ new(**options).call(source)
27
13
  end
28
14
 
29
- def handle_text(node, level, newline: true)
30
- text = node.text
31
-
32
- if text.squish.empty? && text.length.positive?
33
- @buffer << indent(level)
34
- @buffer << whitespace(@options)
35
-
36
- text.strip!
37
- end
38
-
39
- if text.length.positive?
40
- @buffer << indent(level)
41
-
42
- if siblings?(node)
43
- @buffer << "text "
44
- end
45
-
46
- @buffer << quote(text)
47
- @buffer << "\n" if newline
48
- end
49
- end
50
-
51
- def handle_erb_element(node, level, newline: true)
52
- if erb_safe_output?(node)
53
- @buffer << "raw "
54
- @buffer << node.text.from(1)
55
- @buffer << "\n" if newline
56
- return
57
- end
58
-
59
- if erb_interpolation?(node) && node.parent.children.count > 1
60
- if node.text.length >= 24
61
- @buffer << "text("
62
- @buffer << node.text
63
- @buffer << ")"
64
- else
65
- @buffer << "text "
66
- @buffer << node.text
67
- end
68
- elsif erb_comment?(node)
69
- @buffer << "#"
70
- @buffer << node.text
71
- else
72
- @buffer << node.text
73
- end
74
-
75
- @buffer << "\n" if newline
76
- end
77
-
78
- def handle_element(node, level)
79
- @buffer << (indent(level) + node_name(node) + handle_attributes(node))
80
-
81
- if node.children.any?
82
- if node.children.one? && text_node?(node.children.first) && node.text.length <= 32
83
- single_line_block {
84
- handle_text(node.children.first, 0, newline: false)
85
- }
86
- elsif node.children.one? && erb_interpolation?(node.children.first) && node.text.length <= 32
87
- single_line_block {
88
- handle_erb_element(node.children.first, 0, newline: false)
89
- }
90
- else
91
- multi_line_block(level) {
92
- handle_children(node, level)
93
- }
94
- end
95
- else
96
- @buffer << "\n"
97
- end
98
- end
15
+ def call(source)
16
+ @source = source
99
17
 
100
- def handle_comment_node(node, level)
101
- @buffer << indent(level)
102
- @buffer << "comment "
103
- @buffer << quote(node.text.strip)
104
- @buffer << "\n"
18
+ code
105
19
  end
106
20
 
107
- def handle_children(node, level)
108
- node.children.each do |child|
109
- handle_node(child, level + 1)
110
- end
111
- end
112
-
113
- def handle_attributes(node)
114
- return "" if node.attributes.keys.none?
115
-
116
- b = StringIO.new
117
-
118
- node.attributes.each_value do |attribute|
119
- b << attribute.name.gsub("-", "_")
120
- b << ": "
121
- b << double_quote(attribute.value)
122
- b << ", " if node.attributes.values.last != attribute
123
- end
124
-
125
- if node.children.any?
126
- "(#{b.string.strip}) "
127
- else
128
- " #{b.string.strip}"
129
- end
130
- end
131
-
132
- def handle_node(node = parsed, level = 0)
133
- case node
134
- when Nokogiri::XML::Text
135
- handle_text(node, level)
136
- when Nokogiri::XML::Element
137
- if erb_node?(node)
138
- handle_erb_element(node, level)
139
- else
140
- handle_element(node, level)
141
- end
142
-
143
- @buffer << "\n" if level == 1
144
- when Nokogiri::HTML4::DocumentFragment
145
- handle_children(node, level)
146
- when Nokogiri::XML::Comment
147
- handle_comment_node(node, level)
148
- else
149
- @buffer << ("UNKNOWN#{node.class}")
150
- end
151
-
152
- @buffer.string
153
- end
21
+ def initialize(source = nil, **options)
22
+ @custom_elements = Set.new
23
+ @options = Options.new(**options)
154
24
 
155
- def parsed
156
- @parsed ||= Nokogiri::HTML.fragment(minified_erb)
25
+ call(source)
157
26
  end
158
27
 
159
- def buffer
160
- Rufo::Formatter.format(@buffer.string.strip)
161
- rescue Rufo::SyntaxError
162
- @buffer.string.strip
28
+ def code
29
+ options.component? ? component_code : template_code
163
30
  end
164
31
 
165
- def output
166
- buffer = StringIO.new
167
-
168
- if @options.fetch(:phlex_class, false)
169
- buffer << "class #{@options.fetch(:component_name, 'MyComponent')}"
170
- buffer << "< #{@options.fetch(:parent_component, 'Phlex::HTML')}\n"
171
-
172
- @custom_elements.each do |element|
173
- buffer << (indent(1) + "register_element :#{element}\n")
174
- end
175
-
176
- buffer << ("#{indent(1)}def template\n")
177
- buffer << (indent(2) + @buffer.string)
178
- buffer << ("#{indent(1)}end\n")
179
- buffer << "end\n"
180
- else
181
- buffer << @buffer.string
182
- end
183
-
184
- Rufo::Formatter.format(buffer.string.strip)
185
- rescue Rufo::SyntaxError
186
- buffer.string.strip
187
- end
32
+ # private
188
33
 
189
- def converted_erb
190
- ErbParser.transform_xml(html).gsub("\n", "").gsub("\r", "")
191
- rescue StandardError
192
- html
34
+ def template_code
35
+ TemplateGenerator.call(self, source)
193
36
  end
194
37
 
195
- def minified_erb
196
- HtmlPress.press(converted_erb)
197
- rescue StandardError
198
- converted_erb
38
+ def component_code
39
+ ComponentGenerator.call(self)
199
40
  end
200
41
  end
201
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
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "syntax_tree"
4
+
5
+ module Phlexing
6
+ class Formatter
7
+ def self.call(...)
8
+ new(...).call
9
+ end
10
+
11
+ def initialize(source, max: 80)
12
+ @source = source.to_s.dup
13
+ @max = max
14
+ end
15
+
16
+ def call
17
+ SyntaxTree.format(@source, @max).strip
18
+ rescue SyntaxTree::Parser::ParseError, NoMethodError
19
+ @source
20
+ end
21
+ end
22
+ end