phlexing 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.
@@ -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