phlexing 0.3.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,237 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "nokogiri"
4
+
5
+ module Phlexing
6
+ class TemplateGenerator
7
+ using Refinements::StringRefinements
8
+
9
+ include Helpers
10
+
11
+ attr_accessor :converter, :out, :options
12
+
13
+ def self.call(converter, source)
14
+ new(converter).call(source)
15
+ end
16
+
17
+ def initialize(converter)
18
+ @converter = converter
19
+ @options = @converter.options
20
+ @out = StringIO.new
21
+ end
22
+
23
+ def call(source)
24
+ document = Parser.call(source)
25
+ handle_node(document)
26
+
27
+ Formatter.call(out.string.strip)
28
+ rescue StandardError
29
+ out.string.strip
30
+ end
31
+
32
+ def handle_text_output(text)
33
+ output("text", text)
34
+ end
35
+
36
+ def handle_html_comment_output(text)
37
+ output("comment", braces(quote(text)))
38
+ end
39
+
40
+ def handle_erb_comment_output(text)
41
+ output("#", text)
42
+ end
43
+
44
+ def handle_erb_unsafe_output(text)
45
+ output("unsafe_raw", text)
46
+ end
47
+
48
+ def handle_output(text)
49
+ output("", unescape(text).strip)
50
+ end
51
+
52
+ def handle_attributes(node)
53
+ return "" if node.attributes.keys.none?
54
+
55
+ attributes = []
56
+
57
+ node.attribute_nodes.each do |attribute|
58
+ attributes << handle_attribute(attribute)
59
+ end
60
+
61
+ parens(attributes.join(", "))
62
+ end
63
+
64
+ def handle_attribute(attribute)
65
+ if attribute.name.start_with?(/data-erb-(\d+)+/)
66
+ handle_erb_interpolation_in_tag(attribute)
67
+ elsif attribute.name.start_with?("data-erb-")
68
+ handle_erb_attribute_output(attribute)
69
+ else
70
+ handle_html_attribute_output(attribute)
71
+ end
72
+ end
73
+
74
+ def handle_html_attribute_output(attribute)
75
+ String.new.tap { |s|
76
+ s << arg(attribute.name.underscore)
77
+ if attribute.value.blank? && !attribute.to_html.include?("=")
78
+ # handling boolean attributes
79
+ # eg. <input required> => input(required: true)
80
+ s << "true"
81
+ else
82
+ s << quote(attribute.value)
83
+ end
84
+ }
85
+ end
86
+
87
+ def handle_erb_attribute_output(attribute)
88
+ String.new.tap { |s|
89
+ s << arg(attribute.name.delete_prefix("data-erb-").underscore)
90
+
91
+ s << if attribute.value.start_with?("<%=") && attribute.value.scan("<%").one? && attribute.value.end_with?("%>")
92
+ value = unwrap_erb(attribute.value)
93
+ value.include?(" ") ? parens(value) : value
94
+ else
95
+ transformed = Parser.call(attribute.value)
96
+ attribute = StringIO.new
97
+
98
+ transformed.children.each do |node|
99
+ case node
100
+ when Nokogiri::XML::Text
101
+ attribute << node.text
102
+ when Nokogiri::XML::Node
103
+ if node.attributes["loud"]
104
+ attribute << interpolate(node.text.strip)
105
+ else
106
+ attribute << interpolate("#{node.text.strip} && nil")
107
+ end
108
+ end
109
+ end
110
+
111
+ quote(attribute.string)
112
+ end
113
+ }
114
+ end
115
+
116
+ def handle_erb_interpolation_in_tag(attribute)
117
+ "**#{parens("#{unwrap_erb(unescape(attribute.value))}: true")}"
118
+ end
119
+
120
+ def handle_erb_safe_node(node)
121
+ if siblings?(node) && string_output?(node)
122
+ handle_text_output(node.text.strip)
123
+ else
124
+ handle_output(node.text.strip)
125
+ end
126
+ end
127
+
128
+ def handle_text_node(node)
129
+ text = node.text
130
+
131
+ if text.squish.empty? && text.length.positive?
132
+ out << whitespace
133
+
134
+ text.strip!
135
+ end
136
+
137
+ return if text.length.zero?
138
+
139
+ if siblings?(node)
140
+ handle_text_output(quote(node.text))
141
+ else
142
+ handle_output(quote(text))
143
+ end
144
+ end
145
+
146
+ def handle_html_element_node(node, level)
147
+ out << tag_name(node)
148
+ out << handle_attributes(node)
149
+
150
+ params = node.name == "svg" ? options.svg_param : nil
151
+
152
+ if node.children.any?
153
+ block(params) { handle_children(node, level) }
154
+ end
155
+
156
+ out << newline
157
+ end
158
+
159
+ def handle_loud_erb_node(node)
160
+ if node.text.start_with?("=")
161
+ handle_erb_unsafe_output(node.text.from(1).strip)
162
+ else
163
+ handle_erb_safe_node(node)
164
+ end
165
+ end
166
+
167
+ def handle_silent_erb_node(node)
168
+ if node.text.start_with?("#")
169
+ handle_erb_comment_output(node.text.from(1).strip)
170
+ elsif node.text.start_with?("-")
171
+ handle_output(node.text.from(1).to(-2).strip)
172
+ else
173
+ handle_output(node.text.strip)
174
+ end
175
+ end
176
+
177
+ def handle_html_comment_node(node)
178
+ handle_html_comment_output(node.text.strip)
179
+ end
180
+
181
+ def handle_element_node(node, level)
182
+ case node
183
+ in name: "erb", attributes: [{ name: "loud", value: "" }]
184
+ handle_loud_erb_node(node)
185
+ in name: "erb", attributes: [{ name: "silent", value: "" }]
186
+ handle_silent_erb_node(node)
187
+ else
188
+ handle_html_element_node(node, level)
189
+ end
190
+
191
+ out << newline if level == 1
192
+ end
193
+
194
+ def handle_svg_node(node, level)
195
+ node.children.each do |child|
196
+ child.traverse do |subchild|
197
+ subchild.name = SVG_ELEMENTS[subchild.name] if SVG_ELEMENTS.key?(subchild.name)
198
+ subchild.name = subchild.name.prepend("#{options.svg_param}.") # rubocop:disable Style/RedundantSelfAssignment
199
+ end
200
+ end
201
+
202
+ whitespace_before = options.whitespace
203
+ options.whitespace = false
204
+
205
+ handle_element_node(node, level)
206
+
207
+ options.whitespace = whitespace_before
208
+ end
209
+
210
+ def handle_document_node(node, level)
211
+ handle_children(node, level)
212
+ end
213
+
214
+ def handle_children(node, level)
215
+ node.children.each do |child|
216
+ handle_node(child, level + 1)
217
+ end
218
+ end
219
+
220
+ def handle_node(node, level = 0)
221
+ case node
222
+ in Nokogiri::XML::Text
223
+ handle_text_node(node)
224
+ in Nokogiri::XML::Element
225
+ if node.name == "svg"
226
+ handle_svg_node(node, level)
227
+ else
228
+ handle_element_node(node, level)
229
+ end
230
+ in Nokogiri::HTML4::Document | Nokogiri::HTML4::DocumentFragment | Nokogiri::XML::DTD
231
+ handle_document_node(node, level)
232
+ in Nokogiri::XML::Comment
233
+ handle_html_comment_node(node)
234
+ end
235
+ end
236
+ end
237
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Phlexing
4
- VERSION = "0.3.0"
4
+ VERSION = "0.5.0"
5
5
  end
@@ -4,20 +4,102 @@ require "syntax_tree"
4
4
 
5
5
  module Phlexing
6
6
  class Visitor < SyntaxTree::Visitor
7
- def initialize(converter)
8
- @converter = converter
7
+ using Refinements::StringRefinements
8
+ include Helpers
9
+
10
+ def initialize(analyzer)
11
+ @analyzer = analyzer
9
12
  end
10
13
 
11
14
  def visit_ivar(node)
12
- @converter.ivars << node.value.from(1)
15
+ @analyzer.ivars << node.value.from(1)
16
+ end
17
+
18
+ def visit_const(node)
19
+ @analyzer.consts << node.value
20
+ end
21
+
22
+ def visit_command(node)
23
+ unless rails_helper?(node.message.value)
24
+ @analyzer.instance_methods << node.message.value
25
+ end
26
+ super
27
+ end
28
+
29
+ def visit_call(node)
30
+ if node.receiver
31
+ case node.receiver
32
+ when SyntaxTree::VarRef
33
+ value = node.receiver.value.value
34
+
35
+ case node.receiver.value
36
+ when SyntaxTree::IVar
37
+ @analyzer.ivars << value.from(1)
38
+ when SyntaxTree::Ident
39
+ @analyzer.idents << value
40
+ end
41
+
42
+ @analyzer.calls << value
43
+
44
+ when SyntaxTree::VCall
45
+ case node.receiver.value
46
+ when SyntaxTree::Ident
47
+ @analyzer.calls << node.receiver.value.value
48
+ end
49
+
50
+ when SyntaxTree::Ident
51
+ value = node.receiver.value.value.value
52
+
53
+ @analyzer.idents << value unless value.ends_with?("?")
54
+ @analyzer.calls << value
55
+
56
+ when SyntaxTree::Const
57
+ @analyzer.calls << node.receiver.value
58
+ end
59
+
60
+ elsif node.receiver.nil? && node.operator.nil?
61
+ case node.message
62
+ when SyntaxTree::Ident
63
+ if node.message.value.end_with?("?") || node.child_nodes[3].is_a?(SyntaxTree::ArgParen)
64
+ unless rails_helper?(node.message.value)
65
+ @analyzer.instance_methods << node.message.value
66
+ end
67
+ @analyzer.calls << node.message.value
68
+ else
69
+ @analyzer.idents << node.message.value
70
+ end
71
+ end
72
+ end
73
+
74
+ super
13
75
  end
14
76
 
15
77
  def visit_vcall(node)
16
- @converter.locals << node.value.value
78
+ unless rails_helper?(node.value.value)
79
+ @analyzer.locals << node.value.value
80
+ end
17
81
  end
18
82
 
19
83
  def visit_ident(node)
20
- @converter.idents << node.value
84
+ unless rails_helper?(node.value)
85
+ @analyzer.idents << node.value
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ def rails_helper?(name)
92
+ if known_rails_helpers.keys.include?(name)
93
+ @analyzer.includes << known_rails_helpers[name]
94
+ return true
95
+ end
96
+
97
+ if routes_helpers.map { |regex| name.scan(regex).any? }.reduce(:|)
98
+ @analyzer.includes << "Phlex::Rails::Helpers::Routes"
99
+ return true
100
+ end
101
+
102
+ false
21
103
  end
22
104
  end
23
105
  end
data/lib/phlexing.rb CHANGED
@@ -1,10 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "phlexing/version"
4
- require_relative "phlexing/helpers"
5
4
  require_relative "phlexing/refinements/string_refinements"
5
+ require_relative "phlexing/patches/html_press"
6
+
7
+ require_relative "phlexing/erb_transformer"
8
+ require_relative "phlexing/formatter"
9
+ require_relative "phlexing/helpers"
10
+ require_relative "phlexing/minifier"
11
+ require_relative "phlexing/options"
12
+ require_relative "phlexing/parser"
13
+ require_relative "phlexing/ruby_analyzer"
6
14
  require_relative "phlexing/visitor"
15
+
7
16
  require_relative "phlexing/name_suggestor"
17
+ require_relative "phlexing/component_generator"
18
+ require_relative "phlexing/template_generator"
8
19
  require_relative "phlexing/converter"
9
20
 
10
21
  module Phlexing
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phlexing
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
  - Marco Roth
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-01-20 00:00:00.000000000 Z
11
+ date: 2023-04-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: erb_parser
14
+ name: deface
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.0.2
19
+ version: '1.9'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.0.2
26
+ version: '1.9'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: html_press
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -53,33 +53,47 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '1.0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: rufo
56
+ name: phlex
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 0.13.0
61
+ version: '1.6'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 0.13.0
68
+ version: '1.6'
69
+ - !ruby/object:Gem::Dependency
70
+ name: phlex-rails
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.9'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.9'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: syntax_tree
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
- - - ">="
87
+ - - "~>"
74
88
  - !ruby/object:Gem::Version
75
- version: '0'
89
+ version: '6.0'
76
90
  type: :runtime
77
91
  prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
- - - ">="
94
+ - - "~>"
81
95
  - !ruby/object:Gem::Version
82
- version: '0'
96
+ version: '6.0'
83
97
  description: Simple ERB to Phlex converter
84
98
  email:
85
99
  - marco.roth@hey.com
@@ -95,10 +109,19 @@ files:
95
109
  - README.md
96
110
  - Rakefile
97
111
  - lib/phlexing.rb
112
+ - lib/phlexing/component_generator.rb
98
113
  - lib/phlexing/converter.rb
114
+ - lib/phlexing/erb_transformer.rb
115
+ - lib/phlexing/formatter.rb
99
116
  - lib/phlexing/helpers.rb
117
+ - lib/phlexing/minifier.rb
100
118
  - lib/phlexing/name_suggestor.rb
119
+ - lib/phlexing/options.rb
120
+ - lib/phlexing/parser.rb
121
+ - lib/phlexing/patches/html_press.rb
101
122
  - lib/phlexing/refinements/string_refinements.rb
123
+ - lib/phlexing/ruby_analyzer.rb
124
+ - lib/phlexing/template_generator.rb
102
125
  - lib/phlexing/version.rb
103
126
  - lib/phlexing/visitor.rb
104
127
  - sig/phlexing.rbs