rbexy 1.0.2 → 2.0.0.beta4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +3 -1
  3. data/README.md +18 -0
  4. data/lib/rbexy.rb +27 -9
  5. data/lib/rbexy/ast_transformer.rb +21 -0
  6. data/lib/rbexy/component.rb +10 -18
  7. data/lib/rbexy/component_context.rb +18 -0
  8. data/lib/rbexy/component_resolver.rb +60 -0
  9. data/lib/rbexy/configuration.rb +18 -1
  10. data/lib/rbexy/lexer.rb +21 -14
  11. data/lib/rbexy/nodes.rb +15 -135
  12. data/lib/rbexy/nodes/abstract_attr.rb +12 -0
  13. data/lib/rbexy/nodes/abstract_element.rb +13 -0
  14. data/lib/rbexy/nodes/abstract_node.rb +58 -0
  15. data/lib/rbexy/nodes/component_element.rb +69 -0
  16. data/lib/rbexy/nodes/component_prop.rb +29 -0
  17. data/lib/rbexy/nodes/declaration.rb +15 -0
  18. data/lib/rbexy/nodes/expression.rb +15 -0
  19. data/lib/rbexy/nodes/expression_group.rb +55 -0
  20. data/lib/rbexy/nodes/html_attr.rb +13 -0
  21. data/lib/rbexy/nodes/html_element.rb +48 -0
  22. data/lib/rbexy/nodes/newline.rb +9 -0
  23. data/lib/rbexy/nodes/raw.rb +23 -0
  24. data/lib/rbexy/nodes/root.rb +19 -0
  25. data/lib/rbexy/nodes/text.rb +15 -0
  26. data/lib/rbexy/nodes/util.rb +9 -0
  27. data/lib/rbexy/parser.rb +22 -16
  28. data/lib/rbexy/rails/component_template_resolver.rb +3 -3
  29. data/lib/rbexy/rails/controller_helper.rb +5 -4
  30. data/lib/rbexy/rails/engine.rb +1 -11
  31. data/lib/rbexy/refinements.rb +5 -0
  32. data/lib/rbexy/refinements/array.rb +9 -0
  33. data/lib/rbexy/refinements/array/find_map.rb +13 -0
  34. data/lib/rbexy/refinements/array/insert_between_types.rb +26 -0
  35. data/lib/rbexy/refinements/array/map_type_when_neighboring_type.rb +26 -0
  36. data/lib/rbexy/runtime.rb +16 -23
  37. data/lib/rbexy/template.rb +12 -0
  38. data/lib/rbexy/version.rb +1 -1
  39. data/rbexy.gemspec +1 -0
  40. metadata +42 -11
  41. data/example.rb +0 -113
  42. data/lib/rbexy/component_providers/namespaced_rbexy_provider.rb +0 -20
  43. data/lib/rbexy/component_providers/rbexy_provider.rb +0 -21
  44. data/lib/rbexy/component_providers/view_component_provider.rb +0 -21
  45. data/lib/rbexy/component_tag_builder.rb +0 -19
  46. data/lib/rbexy/hash_mash.rb +0 -15
  47. data/lib/rbexy/view_context_helper.rb +0 -23
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f8c0ebb095f668e7b85fbd14fe770be07fc9f42565b7f71a02e4001a5a0c88c6
4
- data.tar.gz: 4670b8929fba14f2cdb197eca4c9d72f37ecdef6d1e84d33d43f18bdb09b0a07
3
+ metadata.gz: d9cb20a821099f8fd51a0abb4b80ad3b2d8d936afcfe240a4b99ea29f4dea92a
4
+ data.tar.gz: aab32903c9632f245857c917b0b99743e5e58ddb8356fce1274c5dd2e4791b37
5
5
  SHA512:
6
- metadata.gz: 8e9920411ff9198828488d984cfd9e69c67e1ced6600a48d701c01153c4bc06419aaac58d2395ab61676798e9bb0a45137f40788d293fade0c0344c596e60c06
7
- data.tar.gz: 3da407f7258c3d2f2ef33a157747f6bf0f9ac945ebf801ec064430d80237f802ae7deac36de07a905b055dc9bd7f1958e80774a57f73d036a931a1c4cdac8b81
6
+ metadata.gz: a849303b1f41e2e6295558b09f1a608a3b24bca8317bde650c579c6a0da2206c67f1ea9bb49e4b7bf15e66497960fff70703845544a57271c4947e4abe16d5ef
7
+ data.tar.gz: 5f08d1838d39cf38d3f3d727e5c1ea098255e81e4a2ab4affd2b67a77a10d7f55dccdff52114f5ce9d544238d81d09a66f548ed74961298026f7887ad30339fd
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rbexy (1.0.2)
4
+ rbexy (2.0.0.beta4)
5
5
  actionview (>= 6.0, < 7.0)
6
6
  activesupport (>= 6.0, < 7.0)
7
7
 
@@ -101,6 +101,7 @@ GEM
101
101
  mini_mime (>= 0.1.1)
102
102
  marcel (0.3.3)
103
103
  mimemagic (~> 0.3.2)
104
+ memory_profiler (0.9.14)
104
105
  method_source (1.0.0)
105
106
  mimemagic (0.3.5)
106
107
  mini_mime (1.0.2)
@@ -199,6 +200,7 @@ PLATFORMS
199
200
 
200
201
  DEPENDENCIES
201
202
  guard-rspec (~> 4.7, >= 4.7.3)
203
+ memory_profiler (~> 0.9.14)
202
204
  pry-byebug
203
205
  rails (>= 6.0, < 7.0)
204
206
  rake
data/README.md CHANGED
@@ -173,6 +173,14 @@ Loops:
173
173
  </ul>
174
174
  ```
175
175
 
176
+ Blocks:
177
+
178
+ ```jsx
179
+ {link_to "/" do
180
+ <span>Click me</span>
181
+ end}
182
+ ```
183
+
176
184
  As an attribute:
177
185
 
178
186
  ```jsx
@@ -189,6 +197,16 @@ Pass a lambda to a prop, that when called returns a tag:
189
197
  </Hero>
190
198
  ```
191
199
 
200
+ _Note that when using tags inside blocks, the block must evaluate to a single root element. Rbexy behaves similar to JSX in this way. E.g.:_
201
+
202
+ ```
203
+ # Do
204
+ -> { <span><i>Hello</i> World</span> }
205
+
206
+ # Don't
207
+ -> { <i>Hello</i> World }
208
+ ```
209
+
192
210
  ### Tags
193
211
 
194
212
  You can put standard HTML tags anywhere.
@@ -1,14 +1,22 @@
1
1
  require "rbexy/version"
2
+ require "active_support/inflector"
3
+ require "active_support/concern"
4
+ require "action_view/helpers/output_safety_helper"
5
+ require "action_view/helpers/capture_helper"
6
+ require "action_view/helpers/tag_helper"
7
+ require "action_view/context"
2
8
 
3
9
  module Rbexy
4
10
  autoload :Lexer, "rbexy/lexer"
5
11
  autoload :Parser, "rbexy/parser"
6
12
  autoload :Nodes, "rbexy/nodes"
7
13
  autoload :Runtime, "rbexy/runtime"
8
- autoload :HashMash, "rbexy/hash_mash"
9
- autoload :ComponentTagBuilder, "rbexy/component_tag_builder"
10
- autoload :ViewContextHelper, "rbexy/view_context_helper"
14
+ autoload :ComponentContext, "rbexy/component_context"
11
15
  autoload :Configuration, "rbexy/configuration"
16
+ autoload :ComponentResolver, "rbexy/component_resolver"
17
+ autoload :Template, "rbexy/template"
18
+ autoload :Refinements, "rbexy/refinements"
19
+ autoload :ASTTransformer, "rbexy/ast_transformer"
12
20
 
13
21
  ContextNotFound = Class.new(StandardError)
14
22
 
@@ -21,14 +29,24 @@ module Rbexy
21
29
  @configuration ||= Configuration.new
22
30
  end
23
31
 
24
- def compile(template_string)
25
- tokens = Rbexy::Lexer.new(template_string).tokenize
26
- template = Rbexy::Parser.new(tokens).parse
27
- template.compile
32
+ def compile(template, context = build_default_compile_context(template))
33
+ tokens = Lexer.new(template, context.element_resolver).tokenize
34
+ root = Parser.new(tokens).parse
35
+ root.inject_compile_context(context)
36
+ root.transform!
37
+ root.precompile.compile
28
38
  end
29
39
 
30
- def evaluate(template_string, runtime)
31
- runtime.evaluate compile(template_string)
40
+ def evaluate(template_string, runtime = Rbexy::Runtime.new)
41
+ runtime.evaluate compile(Template.new(template_string))
42
+ end
43
+
44
+ def build_default_compile_context(template)
45
+ OpenStruct.new(
46
+ template: template,
47
+ element_resolver: configuration.element_resolver,
48
+ ast_transformer: configuration.transforms
49
+ )
32
50
  end
33
51
  end
34
52
  end
@@ -0,0 +1,21 @@
1
+ module Rbexy
2
+ class ASTTransformer
3
+ attr_reader :registry
4
+
5
+ def initialize
6
+ clear!
7
+ end
8
+
9
+ def register(*node_classes, &block)
10
+ node_classes.each { |k| (registry[k] ||= []) << block }
11
+ end
12
+
13
+ def transform(node, context)
14
+ registry[node.class]&.each { |t| t.call(node, context) }
15
+ end
16
+
17
+ def clear!
18
+ @registry = {}
19
+ end
20
+ end
21
+ end
@@ -1,5 +1,4 @@
1
1
  require "action_view"
2
- require "active_support/core_ext/class/attribute"
3
2
 
4
3
  module Rbexy
5
4
  class Component < ActionView::Base
@@ -28,7 +27,7 @@ module Rbexy
28
27
 
29
28
  @view_context = view_context
30
29
 
31
- setup(**props)
30
+ after_initialize(**props)
32
31
  end
33
32
 
34
33
  # Override in your subclass to handle props, setup your component, etc.
@@ -54,17 +53,6 @@ module Rbexy
54
53
  content_block ? content_block.call : ""
55
54
  end
56
55
 
57
- def create_context(name, value)
58
- rbexy_context.last[name] = value
59
- end
60
-
61
- def use_context(name)
62
- index = rbexy_context.rindex { |c| c.has_key?(name) }
63
- index ?
64
- rbexy_context[index][name] :
65
- raise(ContextNotFound, "no parent context `#{name}`")
66
- end
67
-
68
56
  def compiled_method_container
69
57
  Rbexy::Component
70
58
  end
@@ -74,16 +62,20 @@ module Rbexy
74
62
  attr_reader :view_context, :content_block
75
63
 
76
64
  def method_missing(meth, *args, &block)
77
- if view_context.respond_to?(meth)
78
- view_context.send(meth, *args, &block)
79
- else
80
- super
81
- end
65
+ view_context.send(meth, *args, &block)
66
+ end
67
+
68
+ def respond_to_missing?(method_name, include_all)
69
+ view_context.respond_to?(method_name, include_all)
82
70
  end
83
71
 
84
72
  def clean_template_backtrace(backtrace)
85
73
  return backtrace if Rbexy.configuration.debug
86
74
  BacktraceCleaner.new(backtrace).call
87
75
  end
76
+
77
+ def after_initialize(**props)
78
+ setup(**props)
79
+ end
88
80
  end
89
81
  end
@@ -0,0 +1,18 @@
1
+ module Rbexy
2
+ module ComponentContext
3
+ def rbexy_context
4
+ @rbexy_context ||= [{}]
5
+ end
6
+
7
+ def create_context(name, value)
8
+ rbexy_context.last[name] = value
9
+ end
10
+
11
+ def use_context(name)
12
+ index = rbexy_context.rindex { |c| c.has_key?(name) }
13
+ index ?
14
+ rbexy_context[index][name] :
15
+ raise(ContextNotFound, "no parent context `#{name}`")
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,60 @@
1
+ module Rbexy
2
+ class ComponentResolver
3
+ using Rbexy::Refinements::Array::FindMap
4
+
5
+ KNOWN_HTML_ELEMENTS = %w(
6
+ a abbr acronym address animate animateMotion animateTransform applet area article aside audio b base basefont
7
+ bdi bdo bgsound big blink blockquote body br button canvas caption center circle cite clipPath code col colgroup
8
+ color-profile command content data datalist dd defs del desc details dfn dialog dir discard div dl dt element
9
+ ellipse em embed feBlend feColorMatrix feComponentTransfer feComposite feConvolveMatrix feDiffuseLighting
10
+ feDisplacementMap feDistantLight feDropShadow feFlood feFuncA feFuncB feFuncG feFuncR feGaussianBlur feImage
11
+ feMerge feMergeNode feMorphology feOffset fePointLight feSpecularLighting feSpotLight feTile feTurbulence
12
+ fieldset figcaption figure filter font footer foreignObject form frame frameset g h1 h2 h3 h4 h5 h6 hatch
13
+ hatchpath head header hgroup hr html i iframe image img input ins isindex kbd keygen label legend li line
14
+ linearGradient link listing main map mark marker marquee mask menu menuitem mesh meshgradient meshpatch meshrow
15
+ meta metadata meter mpath multicol nav nextid nobr noembed noframes noscript object ol optgroup option output p
16
+ param path pattern picture plaintext polygon polyline pre progress q radialGradient rb rect rp rt rtc ruby s
17
+ samp script section select set shadow slot small solidcolor source spacer span stop strike strong style sub
18
+ summary sup svg switch symbol table tbody td template text textarea textPath tfoot th thead time title tr track
19
+ tspan tt u ul unknown use var video view wbr xmp
20
+ ).to_set
21
+
22
+ attr_reader :component_namespaces
23
+
24
+ def initialize
25
+ self.component_namespaces = {}
26
+ end
27
+
28
+ def component_namespaces=(hash)
29
+ @component_namespaces = hash.transform_keys(&:to_s)
30
+ end
31
+
32
+ def component?(name, template)
33
+ return false if KNOWN_HTML_ELEMENTS.include?(name)
34
+ return true if component_class(name, template)
35
+ false
36
+ end
37
+
38
+ def component_class(name, template)
39
+ possible_names = matching_namespaces(template).map { |ns| "#{ns}.#{name}" } << name
40
+ possible_names.find_map(&method(:find))
41
+ end
42
+
43
+ private
44
+
45
+ def find(name)
46
+ find!(name)
47
+ rescue NameError => e
48
+ raise e unless e.message =~ /wrong constant name/ || e.message =~ /uninitialized constant/
49
+ nil
50
+ end
51
+
52
+ def matching_namespaces(template)
53
+ component_namespaces.select { |path, ns| template.identifier.start_with?(path) }.values.flatten.uniq
54
+ end
55
+
56
+ def find!(name)
57
+ ActiveSupport::Inflector.constantize("#{name.gsub(".", "::")}Component")
58
+ end
59
+ end
60
+ end
@@ -1,12 +1,29 @@
1
1
  module Rbexy
2
2
  class Configuration
3
- attr_accessor :component_provider
3
+ attr_accessor :element_resolver
4
4
  attr_accessor :template_paths
5
5
  attr_accessor :enable_context
6
6
  attr_accessor :debug
7
+ attr_accessor :component_rendering_templates
8
+ attr_accessor :transforms
7
9
 
8
10
  def template_paths
9
11
  @template_paths ||= []
10
12
  end
13
+
14
+ def element_resolver
15
+ @element_resolver ||= ComponentResolver.new
16
+ end
17
+
18
+ def transforms
19
+ @transforms ||= ASTTransformer.new
20
+ end
21
+
22
+ def component_rendering_templates
23
+ @component_rendering_templates ||= {
24
+ children: "{capture{%{children}}}",
25
+ component: "::%{component_class}.new(%{view_context},%{kwargs}).render%{children_block}"
26
+ }
27
+ end
11
28
  end
12
29
  end
@@ -10,7 +10,7 @@ module Rbexy
10
10
  end
11
11
  end
12
12
 
13
- Patterns = HashMash.new(
13
+ Patterns = OpenStruct.new(
14
14
  open_expression: /{/,
15
15
  close_expression: /}/,
16
16
  expression_content: /[^}{"'<]+/,
@@ -29,23 +29,23 @@ module Rbexy
29
29
  single_quote: /'/,
30
30
  double_quoted_text_content: /[^"]+/,
31
31
  single_quoted_text_content: /[^']+/,
32
- expression_internal_tag_prefixes: /(\s+(&&|\?|:|do|do\s*\|[^\|]+\||{|{\s*\|[^\|]+\|)\s+\z|\A\s*\z)/,
32
+ expression_internal_tag_prefixes: /(\s+(&&|\|\||\?|:|do|do\s*\|[^\|]+\||{|{\s*\|[^\|]+\|)\s+\z|\A\s*\z)/,
33
33
  declaration: /<![^>]*>/
34
34
  )
35
35
 
36
- attr_reader :stack, :tokens, :scanner, :curr_expr_quote_levels
37
- attr_accessor :curr_expr_bracket_levels, :curr_expr, :curr_default_text,
36
+ attr_reader :stack, :tokens, :scanner, :element_resolver, :template
37
+ attr_accessor :curr_expr, :curr_default_text,
38
38
  :curr_quoted_text
39
39
 
40
- def initialize(code)
40
+ def initialize(template, element_resolver)
41
+ @template = template
42
+ @scanner = StringScanner.new(template.source)
43
+ @element_resolver = element_resolver
41
44
  @stack = [:default]
42
- @curr_expr_bracket_levels = 0
43
- @curr_expr_quote_levels = { single: 0, double: 0 }
44
45
  @curr_expr = ""
45
46
  @curr_default_text = ""
46
47
  @curr_quoted_text = ""
47
48
  @tokens = []
48
- @scanner = StringScanner.new(code)
49
49
  end
50
50
 
51
51
  def tokenize
@@ -59,7 +59,7 @@ module Rbexy
59
59
  elsif scanner.scan(Patterns.open_expression)
60
60
  open_expression
61
61
  elsif scanner.scan(Patterns.comment)
62
- tokens << [:SILENT_NEWLINE]
62
+ tokens << [:NEWLINE]
63
63
  elsif scanner.check(Patterns.text_content)
64
64
  stack.push(:default_text)
65
65
  else
@@ -74,7 +74,7 @@ module Rbexy
74
74
  elsif scanner.scan(Patterns.open_expression)
75
75
  open_expression
76
76
  elsif scanner.scan(Patterns.comment)
77
- tokens << [:SILENT_NEWLINE]
77
+ tokens << [:NEWLINE]
78
78
  elsif scanner.check(Patterns.text_content)
79
79
  stack.push(:default_text)
80
80
  else
@@ -163,9 +163,9 @@ module Rbexy
163
163
  tokens << [:CLOSE_TAG_DEF]
164
164
  stack.pop
165
165
  elsif scanner.scan(Patterns.tag_name)
166
- tokens << [:TAG_NAME, scanner.matched]
166
+ tokens << [:TAG_DETAILS, tag_details(scanner.matched)]
167
167
  elsif scanner.scan(Patterns.whitespace)
168
- scanner.matched.count("\n").times { tokens << [:SILENT_NEWLINE] }
168
+ scanner.matched.count("\n").times { tokens << [:NEWLINE] }
169
169
  tokens << [:OPEN_ATTRS]
170
170
  stack.push(:tag_attrs)
171
171
  else
@@ -182,7 +182,7 @@ module Rbexy
182
182
  end
183
183
  when :tag_attrs
184
184
  if scanner.scan(Patterns.whitespace)
185
- scanner.matched.count("\n").times { tokens << [:SILENT_NEWLINE] }
185
+ scanner.matched.count("\n").times { tokens << [:NEWLINE] }
186
186
  elsif scanner.check(Patterns.close_tag)
187
187
  tokens << [:CLOSE_ATTRS]
188
188
  stack.pop
@@ -205,7 +205,7 @@ module Rbexy
205
205
  open_expression
206
206
  elsif scanner.scan(Patterns.whitespace) || scanner.check(Patterns.close_tag)
207
207
  tokens << [:CLOSE_ATTR_VALUE]
208
- scanner.matched.count("\n").times { tokens << [:SILENT_NEWLINE] }
208
+ scanner.matched.count("\n").times { tokens << [:NEWLINE] }
209
209
  stack.pop
210
210
  else
211
211
  raise SyntaxError, self
@@ -286,5 +286,12 @@ module Rbexy
286
286
  # etc).
287
287
  scanner.scan(Patterns.expression_content) || scanner.scan(Patterns.open_tag_end)
288
288
  end
289
+
290
+ def tag_details(name)
291
+ type = element_resolver.component?(name, template) ? :component : :html
292
+ details = { name: scanner.matched, type: type }
293
+ details[:component_class] = element_resolver.component_class(name, template) if type == :component
294
+ details
295
+ end
289
296
  end
290
297
  end
@@ -1,139 +1,19 @@
1
1
  module Rbexy
2
2
  module Nodes
3
- module Util
4
- def self.safe_string(str)
5
- str.gsub('"', '\\"')
6
- end
7
-
8
- def self.safe_tag_name(name)
9
- name.gsub(".", "__")
10
- end
11
- end
12
-
13
- class Template
14
- attr_reader :children
15
-
16
- def initialize(children)
17
- @children = children
18
- end
19
-
20
- def compile
21
- "#{children.map(&:compile).map { |c| "@output_buffer << rbexy_prep_output(#{c})"}.join(";")};@output_buffer"
22
- end
23
- end
24
-
25
- class Text
26
- attr_reader :content
27
-
28
- def initialize(content)
29
- @content = content
30
- end
31
-
32
- def compile
33
- "\"#{Util.safe_string(content)}\""
34
- end
35
- end
36
-
37
- class ExpressionGroup
38
- attr_reader :statements
39
-
40
- def initialize(statements)
41
- @statements = statements
42
- end
43
-
44
- def compile
45
- statements.map(&:compile).join
46
- end
47
- end
48
-
49
- class Expression
50
- attr_reader :content
51
-
52
- def initialize(content)
53
- @content = content
54
- end
55
-
56
- def compile
57
- content
58
- end
59
- end
60
-
61
- class XmlNode
62
- attr_reader :name, :members, :children
63
-
64
- def initialize(name, members, children)
65
- @name = name
66
- @members = members || []
67
- @children = children
68
- end
69
-
70
- def compile
71
- base_tag = "rbexy_tag.#{Util.safe_tag_name(name)}(#{compile_members})"
72
- tag = if children.length > 0
73
- [
74
- "#{base_tag} { capture {",
75
- children.map(&:compile).map { |c| "@output_buffer << rbexy_prep_output(#{c})" }.join(";"),
76
- "} }"
77
- ].join
78
- else
79
- base_tag
80
- end + ".html_safe"
81
-
82
- if Rbexy.configuration.enable_context
83
- [
84
- "(",
85
- "rbexy_context.push({});",
86
- "#{tag}.tap { rbexy_context.pop }",
87
- ")"
88
- ].join
89
- else
90
- tag
91
- end
92
- end
93
-
94
- def compile_members
95
- members.each_with_object("") do |member, result|
96
- case member
97
- when ExpressionGroup
98
- result << "**#{member.compile},"
99
- when SilentNewline
100
- result << member.compile
101
- else
102
- result << "#{member.compile},"
103
- end
104
- end
105
- end
106
- end
107
-
108
- class XmlAttr
109
- attr_reader :name, :value
110
-
111
- def initialize(name, value)
112
- @name = name
113
- @value = value
114
- end
115
-
116
- def compile
117
- "\"#{name}\": #{value.compile}"
118
- end
119
- end
120
-
121
- class SilentNewline
122
- def compile
123
- "\n"
124
- end
125
- end
126
-
127
- class Declaration
128
- attr_reader :content
129
-
130
- def initialize(content)
131
- @content = content
132
- end
133
-
134
- def compile
135
- "\"#{Util.safe_string(content)}\".html_safe"
136
- end
137
- end
3
+ autoload :Util, "rbexy/nodes/util"
4
+ autoload :AbstractNode, "rbexy/nodes/abstract_node"
5
+ autoload :Root, "rbexy/nodes/root"
6
+ autoload :Raw, "rbexy/nodes/raw"
7
+ autoload :Text, "rbexy/nodes/text"
8
+ autoload :ExpressionGroup, "rbexy/nodes/expression_group"
9
+ autoload :Expression, "rbexy/nodes/expression"
10
+ autoload :AbstractElement, "rbexy/nodes/abstract_element"
11
+ autoload :HTMLElement, "rbexy/nodes/html_element"
12
+ autoload :ComponentElement, "rbexy/nodes/component_element"
13
+ autoload :AbstractAttr, "rbexy/nodes/abstract_attr"
14
+ autoload :HTMLAttr, "rbexy/nodes/html_attr"
15
+ autoload :ComponentProp, "rbexy/nodes/component_prop"
16
+ autoload :Newline, "rbexy/nodes/newline"
17
+ autoload :Declaration, "rbexy/nodes/declaration"
138
18
  end
139
19
  end