phlexing 0.2.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: 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