phlex 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of phlex might be problematic. Click here for more details.

Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +21 -1
  3. data/Gemfile +26 -13
  4. data/README.md +1 -1
  5. data/SECURITY.md +1 -1
  6. data/bench.rb +7 -0
  7. data/config/sus.rb +15 -0
  8. data/docs/assets/application.css +6 -0
  9. data/docs/build.rb +2 -0
  10. data/docs/components/callout.rb +1 -1
  11. data/docs/components/code_block.rb +2 -2
  12. data/docs/components/code_span.rb +1 -1
  13. data/docs/components/example.rb +4 -4
  14. data/docs/components/heading.rb +2 -2
  15. data/docs/components/layout.rb +55 -32
  16. data/docs/components/markdown.rb +13 -28
  17. data/docs/components/nav/item.rb +1 -1
  18. data/docs/components/nav.rb +1 -1
  19. data/docs/components/tabs/tab.rb +1 -1
  20. data/docs/components/tabs.rb +1 -1
  21. data/docs/components/title.rb +2 -2
  22. data/docs/pages/application_page.rb +1 -1
  23. data/docs/pages/helpers.rb +5 -5
  24. data/docs/pages/library/collections.rb +4 -22
  25. data/docs/pages/rails/getting_started.rb +1 -1
  26. data/docs/pages/rails/helpers.rb +3 -1
  27. data/docs/pages/rails/layouts.rb +2 -2
  28. data/docs/pages/rails/rendering_views.rb +1 -1
  29. data/docs/pages/templates.rb +6 -6
  30. data/docs/pages/testing/capybara.rb +48 -0
  31. data/docs/pages/testing/getting_started.rb +44 -0
  32. data/docs/pages/testing/nokogiri.rb +83 -0
  33. data/docs/pages/testing/rails.rb +17 -0
  34. data/docs/pages/translations.rb +81 -0
  35. data/docs/pages/views.rb +56 -8
  36. data/fixtures/compiler_test_helpers.rb +19 -0
  37. data/fixtures/content.rb +60 -0
  38. data/fixtures/dummy/app/views/application_view.rb +8 -0
  39. data/fixtures/dummy/app/views/articles/form.rb +1 -1
  40. data/fixtures/dummy/app/views/card.rb +1 -1
  41. data/fixtures/dummy/app/views/comments/comment.rb +1 -1
  42. data/fixtures/dummy/app/views/comments/reaction.rb +1 -1
  43. data/fixtures/dummy/app/views/heading.rb +1 -1
  44. data/fixtures/layout.rb +5 -5
  45. data/fixtures/page.rb +18 -24
  46. data/fixtures/{test_helper.rb → rails_helper.rb} +3 -8
  47. data/fixtures/standard_element.rb +87 -0
  48. data/fixtures/view_helper.rb +1 -1
  49. data/fixtures/void_element.rb +31 -0
  50. data/lib/generators/phlex/collection/templates/collection.rb.erb +2 -1
  51. data/lib/generators/phlex/controller/USAGE +10 -0
  52. data/lib/generators/phlex/controller/controller_generator.rb +54 -0
  53. data/lib/generators/phlex/controller/templates/controller.rb.erb +10 -0
  54. data/lib/generators/phlex/controller/templates/view.rb.erb +14 -0
  55. data/lib/generators/phlex/layout/templates/layout.rb.erb +2 -1
  56. data/lib/generators/phlex/page/templates/page.rb.erb +3 -1
  57. data/lib/generators/phlex/table/templates/table.rb.erb +3 -1
  58. data/lib/generators/phlex/view/templates/view.rb.erb +7 -1
  59. data/lib/generators/phlex/view/view_generator.rb +9 -1
  60. data/lib/install/phlex.rb +10 -1
  61. data/lib/phlex/block.rb +2 -4
  62. data/lib/phlex/buffered.rb +6 -8
  63. data/lib/phlex/callable.rb +9 -0
  64. data/lib/phlex/collection.rb +2 -27
  65. data/lib/phlex/compiler/elements.rb +49 -0
  66. data/lib/phlex/compiler/generators/content.rb +103 -0
  67. data/lib/phlex/compiler/generators/element.rb +61 -0
  68. data/lib/phlex/compiler/nodes/base.rb +19 -0
  69. data/lib/phlex/compiler/nodes/call.rb +9 -0
  70. data/lib/phlex/compiler/nodes/command.rb +13 -0
  71. data/lib/phlex/compiler/nodes/fcall.rb +18 -0
  72. data/lib/phlex/compiler/nodes/method_add_block.rb +33 -0
  73. data/lib/phlex/compiler/nodes/vcall.rb +9 -0
  74. data/lib/phlex/compiler/optimizer.rb +66 -0
  75. data/lib/phlex/compiler/visitors/base.rb +15 -0
  76. data/lib/phlex/compiler/visitors/file.rb +23 -11
  77. data/lib/phlex/compiler/visitors/stable_scope.rb +28 -0
  78. data/lib/phlex/compiler/visitors/statements.rb +36 -0
  79. data/lib/phlex/compiler/visitors/view.rb +19 -0
  80. data/lib/phlex/compiler/visitors/view_method.rb +59 -0
  81. data/lib/phlex/compiler.rb +23 -3
  82. data/lib/phlex/elements.rb +57 -0
  83. data/lib/phlex/helpers.rb +59 -0
  84. data/lib/phlex/html/callbacks.rb +11 -0
  85. data/lib/phlex/html.rb +208 -47
  86. data/lib/phlex/markdown.rb +76 -0
  87. data/lib/phlex/rails/form.rb +67 -0
  88. data/lib/phlex/rails/helpers.rb +39 -2
  89. data/lib/phlex/rails.rb +10 -0
  90. data/lib/phlex/renderable.rb +9 -3
  91. data/lib/phlex/testing/capybara.rb +25 -0
  92. data/lib/phlex/testing/nokogiri.rb +24 -0
  93. data/lib/phlex/testing/rails.rb +19 -0
  94. data/lib/phlex/testing/view_helper.rb +15 -0
  95. data/lib/phlex/translation.rb +23 -0
  96. data/lib/phlex/turbo/frame.rb +21 -0
  97. data/lib/phlex/turbo/stream.rb +18 -0
  98. data/lib/phlex/version.rb +1 -1
  99. data/lib/phlex.rb +22 -24
  100. metadata +62 -14
  101. data/.rspec +0 -1
  102. data/fixtures/compilation/vcall.rb +0 -38
  103. data/lib/phlex/compiler/generators/standard_element.rb +0 -30
  104. data/lib/phlex/compiler/generators/void_element.rb +0 -29
  105. data/lib/phlex/compiler/optimizers/base_optimizer.rb +0 -34
  106. data/lib/phlex/compiler/optimizers/vcall.rb +0 -29
  107. data/lib/phlex/compiler/visitors/base_visitor.rb +0 -19
  108. data/lib/phlex/compiler/visitors/component.rb +0 -28
  109. data/lib/phlex/compiler/visitors/component_method.rb +0 -28
  110. data/lib/phlex/view.rb +0 -229
@@ -1,16 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "phlex"
4
- require "bundler"
5
- require "view_component"
3
+ require "phlex/rails"
6
4
 
7
- Bundler.require :test
5
+ Bundler.require :rails
8
6
 
9
7
  Combustion.path = "fixtures/dummy"
8
+
10
9
  Combustion.initialize! :action_controller do
11
10
  config.autoload_paths << "#{root}/app"
12
11
  end
13
-
14
- require "view_helper"
15
-
16
- Zeitwerk::Loader.eager_load_all
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fixtures
4
+ module StandardElement
5
+ class WithParens < Phlex::HTML
6
+ def template
7
+ h1()
8
+ end
9
+ end
10
+
11
+ class WithoutParens < Phlex::HTML
12
+ def template
13
+ h1
14
+ end
15
+ end
16
+
17
+ module WithAttributes
18
+ class WithParens < Phlex::HTML
19
+ def template
20
+ h1(class: "font-bold")
21
+ end
22
+ end
23
+
24
+ class WithoutParens < Phlex::HTML
25
+ def template
26
+ h1 class: "font-bold"
27
+ end
28
+ end
29
+ end
30
+
31
+ module WithBraceBlock
32
+ class WithParens < Phlex::HTML
33
+ def template
34
+ h1() { "Hi" }
35
+ end
36
+ end
37
+
38
+ class WithoutParens < Phlex::HTML
39
+ def template
40
+ h1 { "Hi" }
41
+ end
42
+ end
43
+
44
+ class WithAttributes < Phlex::HTML
45
+ def template
46
+ h1(class: "font-bold") { "Hi" }
47
+ end
48
+ end
49
+ end
50
+
51
+ module WithDoBlock
52
+ class WithParens < Phlex::HTML
53
+ def template
54
+ h1() do
55
+ "Hi"
56
+ end
57
+ end
58
+ end
59
+
60
+ class WithoutParens < Phlex::HTML
61
+ def template
62
+ h1 do
63
+ "Hi"
64
+ end
65
+ end
66
+ end
67
+
68
+ module WithAttributes
69
+ class WithParens < Phlex::HTML
70
+ def template
71
+ h1(class: "font-bold") do
72
+ "Hi"
73
+ end
74
+ end
75
+ end
76
+
77
+ class WithoutParens < Phlex::HTML
78
+ def template
79
+ h1 class: "font-bold" do
80
+ "Hi"
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -10,7 +10,7 @@ module ViewHelper
10
10
 
11
11
  def view(&block)
12
12
  let :view do
13
- Class.new(Phlex::View, &block)
13
+ Class.new(Phlex::HTML, &block)
14
14
  end
15
15
  end
16
16
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fixtures
4
+ module VoidElement
5
+ class WithParens < Phlex::HTML
6
+ def template
7
+ img()
8
+ end
9
+ end
10
+
11
+ class WithoutParens < Phlex::HTML
12
+ def template
13
+ img
14
+ end
15
+ end
16
+
17
+ module WithAttributes
18
+ class WithParens < Phlex::HTML
19
+ def template
20
+ img(class: "a b c")
21
+ end
22
+ end
23
+
24
+ class WithoutParens < Phlex::HTML
25
+ def template
26
+ img class: "a b c"
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,6 +1,7 @@
1
1
  <% module_namespacing do -%>
2
2
  module Views
3
- class <%= class_name %> < ApplicationView
3
+ class <%= class_name %> < Phlex::HTML
4
+ include ApplicationView
4
5
  include Phlex::Collection
5
6
 
6
7
  def collection_template(&)
@@ -0,0 +1,10 @@
1
+ Description:
2
+ Generates a Rails controller with Phlex views for the supplied actions
3
+
4
+ Example:
5
+ rails generate phlex:controller Articles index show
6
+
7
+ This will create:
8
+ app/controllers/articles_controller.rb
9
+ app/views/articles/index.rb
10
+ app/views/articles/show.rb
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlex
4
+ module Generators
5
+ class ControllerGenerator < ::Rails::Generators::NamedBase # :nodoc:
6
+ source_root File.expand_path("templates", __dir__)
7
+
8
+ argument :actions, type: :array, default: [], banner: "action action"
9
+ class_option :skip_routes, type: :boolean, desc: "Don't add routes to config/routes.rb."
10
+ class_option :parent, type: :string, default: "ApplicationController", desc: "The parent class for the generated controller"
11
+
12
+ check_class_collision suffix: "Controller"
13
+
14
+ def create_controller_files
15
+ template "controller.rb.erb", File.join("app/controllers", class_path, "#{file_name}_controller.rb")
16
+ end
17
+
18
+ def copy_view_files
19
+ base_path = File.join("app/views", class_path, file_name)
20
+ empty_directory base_path
21
+
22
+ actions.each do |action|
23
+ Rails::Generators.invoke("phlex:view", [remove_possible_suffix(name) + "/" + action])
24
+ end
25
+ end
26
+
27
+ def add_routes
28
+ return if options[:skip_routes]
29
+ return if actions.empty?
30
+
31
+ routing_code = actions.map { |action| "get '#{file_name}/#{action}'" }.join("\n")
32
+ route routing_code, namespace: regular_class_path
33
+ end
34
+
35
+ hook_for :test_framework, as: :controller do |generator|
36
+ invoke generator, [remove_possible_suffix(name), actions]
37
+ end
38
+
39
+ private
40
+
41
+ def parent_class_name
42
+ options[:parent]
43
+ end
44
+
45
+ def file_name
46
+ @_file_name ||= remove_possible_suffix(super)
47
+ end
48
+
49
+ def remove_possible_suffix(name)
50
+ name.sub(/_?controller$/i, "")
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,10 @@
1
+ <% module_namespacing do -%>
2
+ class <%= class_name %>Controller < <%= parent_class_name.classify %>
3
+ <% actions.each do |action| -%>
4
+ def <%= action %>
5
+ render Views::<%= class_name %>::<%= action.camelize %>.new
6
+ end
7
+ <%= "\n" unless action == actions.last -%>
8
+ <% end -%>
9
+ end
10
+ <% end -%>
@@ -0,0 +1,14 @@
1
+ <% module_namespacing do -%>
2
+ module Views
3
+ class <%= class_name %>::<%= @action.camelize %> < Phlex::HTML
4
+ include ApplicationView
5
+
6
+ def template
7
+ <%= "# " unless @has_layout %>render Layout.new(title: "<%= class_name.gsub("::", " ").titlecase %> - <%= @action.titlecase %>") do
8
+ h1 { "<%= class_name %>#<%= @action %>" }
9
+ p { "Find me in <%= @path %>" }
10
+ <%= "# " unless @has_layout %>end
11
+ end
12
+ end
13
+ end
14
+ <% end %>
@@ -1,6 +1,7 @@
1
1
  <% module_namespacing do -%>
2
2
  module Views
3
- class <%= class_name %> < ApplicationView
3
+ class <%= class_name %> < Phlex::HTML
4
+ include ApplicationView
4
5
  include Phlex::Rails::Layout
5
6
 
6
7
  def initialize(title:)
@@ -1,6 +1,8 @@
1
1
  <% module_namespacing do -%>
2
2
  module Views
3
- class <%= class_name %> < ApplicationView
3
+ class <%= class_name %> < Phlex::HTML
4
+ include ApplicationView
5
+
4
6
  def template
5
7
  render Layout.new(title: "<%= class_name.gsub("::", " ") %>") do
6
8
  h1 { "👋 Hello World!" }
@@ -1,7 +1,9 @@
1
1
  <% module_namespacing do -%>
2
2
  module Views
3
- class <%= class_name %> < ApplicationView
3
+ class <%= class_name %> < Phlex::HTML
4
+ include ApplicationView
4
5
  include Phlex::Table
6
+
5
7
  <% options["properties"].each do |property| %>
6
8
  property "<%= property.humanize %>", &:<%= property.underscore %><% end %>
7
9
  end
@@ -1,7 +1,13 @@
1
1
  <% module_namespacing do -%>
2
2
  module Views
3
- class <%= class_name %> < ApplicationView
3
+ class <%= class_name %> < Phlex::HTML
4
+ include ApplicationView
5
+
4
6
  def template
7
+ <%= "# " unless @layout %>render Layout.new(title: "<%= class_name.gsub("::", " ").titlecase %>") do
8
+ h1 { "<%= class_name %>" }
9
+ p { "Find me in <%= @path %>" }
10
+ <%= "# " unless @layout %>end
5
11
  end
6
12
  end
7
13
  end
@@ -6,7 +6,15 @@ module Phlex
6
6
  source_root File.expand_path("templates", __dir__)
7
7
 
8
8
  def create_view
9
- template "view.rb.erb", File.join("app/views", class_path, "#{file_name}.rb")
9
+ @layout = layout
10
+ @path = File.join("app/views", class_path, "#{file_name}.rb")
11
+ template "view.rb.erb", @path
12
+ end
13
+
14
+ private
15
+
16
+ def layout
17
+ Rails.root.join("app/views/layout.rb").exist?
10
18
  end
11
19
  end
12
20
  end
data/lib/install/phlex.rb CHANGED
@@ -20,11 +20,20 @@ unless Rails.root.join("app/views/application_view.rb").exist?
20
20
  # frozen_string_literal: true
21
21
 
22
22
  module Views
23
- class ApplicationView < Phlex::View
23
+ module ApplicationView
24
24
  include Rails.application.routes.url_helpers
25
+ include Phlex::Translation
25
26
  end
26
27
  end
27
28
  RUBY
28
29
  end
29
30
 
31
+ tailwind_config_path = Rails.root.join("config/tailwind.config.js")
32
+
33
+ if tailwind_config_path.exist?
34
+ insert_into_file tailwind_config_path, after: "content: [" do
35
+ "\n './app/views/**/*.rb',"
36
+ end
37
+ end
38
+
30
39
  say "Phlex successfully installed!"
data/lib/phlex/block.rb CHANGED
@@ -2,15 +2,13 @@
2
2
 
3
3
  module Phlex
4
4
  class Block
5
+ include Callable
6
+
5
7
  def initialize(context, &block)
6
8
  @context = context
7
9
  @block = block
8
10
  end
9
11
 
10
- def to_proc
11
- method(:call).to_proc
12
- end
13
-
14
12
  def call(*args, **kwargs)
15
13
  @context.instance_exec(*args, **kwargs, &@block)
16
14
  end
@@ -1,19 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Phlex
4
- class Buffered
4
+ class Buffered < SimpleDelegator
5
5
  def initialize(object, buffer:)
6
- @object, @buffer = object, buffer
6
+ super(object)
7
+ @buffer = buffer
7
8
  end
8
9
 
9
- def method_missing(name, *args, **kwargs, &block)
10
- output = @object.public_send(name, *args, **kwargs, &block)
10
+ # Alias output methods to this
11
+ def __output_method__(*args, **kwargs, &block)
12
+ output = __getobj__.public_send(__callee__, *args, **kwargs, &block)
11
13
  @buffer << output if output.is_a? String
12
14
  nil
13
15
  end
14
-
15
- def respond_to_missing?(name)
16
- @object.respond_to?(name)
17
- end
18
16
  end
19
17
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlex
4
+ module Callable
5
+ def to_proc
6
+ method(:call).to_proc
7
+ end
8
+ end
9
+ end
@@ -12,11 +12,7 @@ module Phlex
12
12
  end
13
13
 
14
14
  def template
15
- if @item
16
- item_template
17
- else
18
- collection_template { yield_items }
19
- end
15
+ @item ? item_template : collection_template { yield_items }
20
16
  end
21
17
 
22
18
  private
@@ -26,33 +22,12 @@ module Phlex
26
22
  raise ArgumentError, "You can only yield_items when rendering a collection. You are currently rendering an item."
27
23
  end
28
24
 
29
- @collection.each_with_index do |item, index|
25
+ @collection.each do |item|
30
26
  @item = item
31
- @index = index
32
- @position = (index + 1)
33
- @first = (index == 0)
34
- @last = (@position == @collection.size)
35
-
36
27
  item_template
37
28
  end
38
29
 
39
30
  @item = nil
40
- @index = nil
41
- @first = nil
42
- @last = nil
43
- @position = nil
44
- end
45
-
46
- def first?
47
- raise ArgumentError unless @item
48
-
49
- @first
50
- end
51
-
52
- def last?
53
- raise ArgumentError unless @item
54
-
55
- @last
56
31
  end
57
32
  end
58
33
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlex::Compiler::Elements
4
+ module VCall
5
+ def format(formatter)
6
+ Phlex::Compiler::Generators::Element.new(
7
+ Phlex::Compiler::Nodes::VCall.new(self),
8
+ formatter: formatter
9
+ ).call
10
+ end
11
+ end
12
+
13
+ module FCall
14
+ def format(formatter)
15
+ Phlex::Compiler::Generators::Element.new(
16
+ Phlex::Compiler::Nodes::FCall.new(self),
17
+ formatter: formatter
18
+ ).call
19
+ end
20
+ end
21
+
22
+ module Command
23
+ def format(formatter)
24
+ Phlex::Compiler::Generators::Element.new(
25
+ Phlex::Compiler::Nodes::Command.new(self),
26
+ formatter: formatter
27
+ ).call
28
+ end
29
+ end
30
+
31
+ module MutatingMethodAddBlock
32
+ def format(formatter)
33
+ Phlex::Compiler::Generators::Element.new(
34
+ Phlex::Compiler::Nodes::MethodAddBlock.new(self),
35
+ formatter: formatter,
36
+ mutating: true
37
+ ).call
38
+ end
39
+ end
40
+
41
+ module MethodAddBlock
42
+ def format(formatter)
43
+ Phlex::Compiler::Generators::Element.new(
44
+ Phlex::Compiler::Nodes::MethodAddBlock.new(self),
45
+ formatter: formatter
46
+ ).call
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlex
4
+ class Compiler
5
+ module Generators
6
+ class Content
7
+ def initialize(formatter, content:, mutating: false)
8
+ @formatter = formatter
9
+ @content = content
10
+ @mutating = mutating
11
+ end
12
+
13
+ def call
14
+ return if nil_value?
15
+ return bare_string_value if bare_string_value?
16
+ return symbol_value if symbol_value?
17
+ return numeric_value if numeric_value?
18
+ return variable_value if variable_value?
19
+
20
+ unknown_value
21
+ end
22
+
23
+ private
24
+
25
+ def nil_value?
26
+ case @content
27
+ in SyntaxTree::VarRef[value: SyntaxTree::Kw[value: "nil"]]
28
+ true
29
+ else
30
+ false
31
+ end
32
+ end
33
+
34
+ def bare_string_value?
35
+ case @content
36
+ in SyntaxTree::StringLiteral[parts: [SyntaxTree::TStringContent]]
37
+ true
38
+ else
39
+ false
40
+ end
41
+ end
42
+
43
+ def symbol_value?
44
+ @content.is_a?(SyntaxTree::SymbolLiteral)
45
+ end
46
+
47
+ def numeric_value?
48
+ @content.is_a?(SyntaxTree::Int) || @content.is_a?(SyntaxTree::FloatLiteral)
49
+ end
50
+
51
+ def variable_value?
52
+ @content.is_a?(SyntaxTree::VarRef)
53
+ end
54
+
55
+ def bare_string_value
56
+ @formatter.append do |f|
57
+ f.text Hescape.escape_html(
58
+ @content.parts.first.value
59
+ )
60
+ end
61
+ end
62
+
63
+ def symbol_value
64
+ @formatter.append do |f|
65
+ f.text Hescape.escape_html(
66
+ @content.value.value
67
+ )
68
+ end
69
+ end
70
+
71
+ def numeric_value
72
+ @formatter.append do |f|
73
+ f.text Hescape.escape_html(
74
+ @content.value
75
+ )
76
+ end
77
+ end
78
+
79
+ def variable_value
80
+ @formatter.chain_append do |f|
81
+ f.text "Hescape.escape_html("
82
+ @content.format(f)
83
+ f.text ")"
84
+ end
85
+ end
86
+
87
+ def unknown_value
88
+ @formatter.breakable(force: true)
89
+ if @mutating
90
+ @content.format(@formatter)
91
+ else
92
+ @formatter.text "yield_content {"
93
+ @formatter.breakable(force: true)
94
+ @content.format(@formatter)
95
+ @formatter.breakable(force: true)
96
+ @formatter.text "}"
97
+ end
98
+ @formatter.breakable(force: true)
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlex
4
+ class Compiler
5
+ module Generators
6
+ class Element
7
+ def initialize(node, formatter:, mutating: false)
8
+ @node = node
9
+ @formatter = formatter
10
+ @mutating = mutating
11
+ end
12
+
13
+ def call
14
+ @formatter.append do |f|
15
+ f.text "<"
16
+ f.text tag
17
+ end
18
+
19
+ if @node.arguments&.parts&.any?
20
+ @formatter.chain_append do |f|
21
+ f.text "_attributes("
22
+ @node.arguments.format(@formatter)
23
+ f.text ")"
24
+ end
25
+ end
26
+
27
+ @formatter.append do |f|
28
+ f.text ">"
29
+ end
30
+
31
+ return if void?
32
+
33
+ case @node.content
34
+ in SyntaxTree::Statements[body: [c]]
35
+ Content.new(@formatter, content: c, mutating: @mutating).call
36
+ in nil
37
+ nil
38
+ else
39
+ @node.content.format(@formatter)
40
+ end
41
+
42
+ @formatter.append do |f|
43
+ f.text "</"
44
+ f.text tag
45
+ f.text ">"
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def tag
52
+ HTML::STANDARD_ELEMENTS[@node.name] || HTML::VOID_ELEMENTS[@node.name]
53
+ end
54
+
55
+ def void?
56
+ HTML::VOID_ELEMENTS.key?(@node.name)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlex::Compiler::Nodes
4
+ class Base
5
+ def initialize(node)
6
+ @node = node
7
+ end
8
+
9
+ attr_accessor :node
10
+
11
+ def arguments
12
+ nil
13
+ end
14
+
15
+ def content
16
+ nil
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlex::Compiler::Nodes
4
+ class Call < Base
5
+ def name
6
+ nil
7
+ end
8
+ end
9
+ end