hot_module 1.0.0.alpha10 → 1.0.0.alpha11
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +2 -1
- data/hot_module.gemspec +1 -0
- data/lib/hot_module/component_renderer.rb +9 -13
- data/lib/hot_module/fragment.rb +3 -3
- data/lib/hot_module/query_selection.rb +3 -3
- data/lib/hot_module/version.rb +1 -1
- data/lib/hot_module.rb +106 -41
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d90f5f3aee3f096c2b0dc962592cef3ff02563a5a8b5bef47cab9adc9eabf309
|
4
|
+
data.tar.gz: cdda20661fa3aab05d14f793a4b6ef3cf6d9c0bfc116a28a49a1118ab1286d2c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ff109253d98e063cfcf6a79481a3cde7934afca492dcca0edd865266cc1401918ba858f04f1cd7ae125d424332f9cdbfd1e562fe4e9914c55313a08070aa61cc
|
7
|
+
data.tar.gz: bb1864cc03b42c1612bd53aa29ef7278d38ad46b8d3362ab31b6093aa8abe907a7a2d1587b04338b2e2b74b868136235181a6871ef27eecb2690e59765d9e3c7
|
data/Gemfile.lock
CHANGED
data/hot_module.gemspec
CHANGED
@@ -7,21 +7,16 @@ module HoTModuLe
|
|
7
7
|
|
8
8
|
class ComponentRenderer < Bridgetown::Builder
|
9
9
|
def build
|
10
|
-
|
11
|
-
Bridgetown::Component.subclasses.each do |component|
|
12
|
-
next unless component.respond_to?(:tag_name)
|
13
|
-
|
14
|
-
registered_tags[component.tag_name] = component
|
15
|
-
end
|
16
|
-
|
17
|
-
render_html_modules(registered_tags)
|
10
|
+
render_html_modules
|
18
11
|
end
|
19
12
|
|
20
|
-
|
21
|
-
|
13
|
+
# rubocop:todo Metrics
|
14
|
+
def render_html_modules
|
15
|
+
inspect_html do |doc, resource|
|
22
16
|
view_context = HoTModuLe::View.new(resource)
|
23
17
|
|
24
|
-
|
18
|
+
HoTModuLe.registered_elements.each do |component|
|
19
|
+
tag_name = component.tag_name
|
25
20
|
doc.xpath("//#{tag_name}").reverse.each do |node|
|
26
21
|
if node["hmod:ignore"]
|
27
22
|
node.attribute("hmod:ignore").remove
|
@@ -33,9 +28,9 @@ module HoTModuLe
|
|
33
28
|
|
34
29
|
new_attrs = {}
|
35
30
|
attrs.each do |k, v|
|
36
|
-
next unless k.start_with?("
|
31
|
+
next unless k.start_with?("arg:")
|
37
32
|
|
38
|
-
new_key = k.delete_prefix("
|
33
|
+
new_key = k.delete_prefix("arg:")
|
39
34
|
attrs.delete(k)
|
40
35
|
new_attrs[new_key] = resource.instance_eval(v)
|
41
36
|
end
|
@@ -54,5 +49,6 @@ module HoTModuLe
|
|
54
49
|
end
|
55
50
|
end
|
56
51
|
end
|
52
|
+
# rubocop:enable Metrics
|
57
53
|
end
|
58
54
|
end
|
data/lib/hot_module/fragment.rb
CHANGED
@@ -10,9 +10,9 @@ module HoTModuLe
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
# NOTE: for some reason, the
|
14
|
-
# parent node. That doesn't work for our case. We want to go strictly in source order.
|
15
|
-
# is our own implementation of that.
|
13
|
+
# NOTE: for some reason, the traverse method yields node children first, then the
|
14
|
+
# parent node. That doesn't work for our case. We want to go strictly in source order.
|
15
|
+
# So this is our own implementation of that.
|
16
16
|
def traverse(node, &block)
|
17
17
|
yield(node)
|
18
18
|
node.children.each { |child| traverse(child, &block) }
|
@@ -1,14 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module HoTModuLe
|
4
|
-
# Add a couple familar DOM API features to
|
4
|
+
# Add a couple familar DOM API features to Nokolexbor
|
5
5
|
module QuerySelection
|
6
6
|
# @param selector [String]
|
7
|
-
# @return [
|
7
|
+
# @return [Nokolexbor::Element]
|
8
8
|
def query_selector(selector) = at_css(selector)
|
9
9
|
|
10
10
|
# @param selector [String]
|
11
|
-
# @return [
|
11
|
+
# @return [Nokolexbor::Element]
|
12
12
|
def query_selector_all(selector) = css(selector)
|
13
13
|
end
|
14
14
|
|
data/lib/hot_module/version.rb
CHANGED
data/lib/hot_module.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require_relative "hot_module/version"
|
4
4
|
|
5
5
|
require "nokolexbor"
|
6
|
+
require "concurrent"
|
6
7
|
|
7
8
|
# Include this module into your own component class
|
8
9
|
module HoTModuLe
|
@@ -23,10 +24,30 @@ module HoTModuLe
|
|
23
24
|
require_relative "hot_module/fragment"
|
24
25
|
require_relative "hot_module/query_selection"
|
25
26
|
|
27
|
+
def self.registered_elements
|
28
|
+
@registered_elements ||= Concurrent::Set.new
|
29
|
+
|
30
|
+
@registered_elements.each do |component|
|
31
|
+
begin
|
32
|
+
next if Kernel.const_get(component.to_s) == component # thin out unloaded consts
|
33
|
+
rescue NameError; end # rubocop:disable Lint/SuppressedException
|
34
|
+
|
35
|
+
@registered_elements.delete component
|
36
|
+
end
|
37
|
+
|
38
|
+
@registered_elements
|
39
|
+
end
|
40
|
+
|
26
41
|
# @param klass [Class]
|
27
42
|
# @return [void]
|
28
43
|
def self.included(klass)
|
29
44
|
klass.extend ClassMethods
|
45
|
+
|
46
|
+
klass.attribute_binding "hmod:children", :_hmod_children_binding, only: :template
|
47
|
+
klass.attribute_binding "hmod:replace", :_hmod_replace_binding
|
48
|
+
klass.attribute_binding "hmod:text", :_hmod_expr_binding
|
49
|
+
klass.attribute_binding "hmod:html", :_hmod_expr_binding
|
50
|
+
|
30
51
|
# Don't stomp on a superclass's `content` method
|
31
52
|
return if klass.instance_methods.include?(:content)
|
32
53
|
|
@@ -41,7 +62,8 @@ module HoTModuLe
|
|
41
62
|
end
|
42
63
|
end
|
43
64
|
|
44
|
-
def html_file_extensions = %w[module.html
|
65
|
+
def html_file_extensions = %w[module.html mod.html html].freeze
|
66
|
+
|
45
67
|
def processed_css_extension = "css-local"
|
46
68
|
|
47
69
|
# @param tag_name [String]
|
@@ -54,43 +76,42 @@ module HoTModuLe
|
|
54
76
|
raise HoTModuLe::Error, "You must either supply a file path argument or respond to `source_location'"
|
55
77
|
end
|
56
78
|
|
57
|
-
|
79
|
+
self.tag_name tag_name
|
58
80
|
|
59
81
|
if html_module
|
60
|
-
|
82
|
+
self.html_module html_module
|
61
83
|
else
|
62
84
|
basepath = File.join(File.dirname(source_location), File.basename(source_location, ".*"))
|
63
85
|
|
64
|
-
|
86
|
+
self.html_module(html_file_extensions.lazy.filter_map do |ext|
|
65
87
|
path = "#{basepath}.#{ext}"
|
66
88
|
File.exist?(path) ? path : nil
|
67
|
-
end.first
|
89
|
+
end.first)
|
68
90
|
|
69
91
|
raise HoTModuLe::Error, "Cannot find sidecar HTML module for `#{self}'" unless @html_module
|
70
92
|
end
|
71
93
|
|
72
|
-
|
94
|
+
self.shadow_root shadow_root
|
73
95
|
end
|
74
96
|
|
75
97
|
# @param value [String]
|
76
98
|
# @return [String]
|
77
99
|
def tag_name(value = nil)
|
78
|
-
@tag_name ||=
|
100
|
+
@tag_name ||= begin
|
101
|
+
HoTModuLe.registered_elements << self
|
102
|
+
value
|
103
|
+
end
|
79
104
|
end
|
80
105
|
|
81
106
|
# @param value [String]
|
82
107
|
# @return [String]
|
83
|
-
def html_module(value = nil)
|
84
|
-
@html_module ||= value
|
85
|
-
end
|
108
|
+
def html_module(value = nil) = @html_module ||= value
|
86
109
|
|
87
110
|
# @param value [Boolean]
|
88
111
|
# @return [Boolean]
|
89
|
-
def shadow_root(value = nil)
|
90
|
-
@shadow_root ||= value
|
91
|
-
end
|
112
|
+
def shadow_root(value = nil) = @shadow_root ||= value
|
92
113
|
|
93
|
-
# @return [
|
114
|
+
# @return [Nokolexbor::Element]
|
94
115
|
def doc
|
95
116
|
@doc ||= begin
|
96
117
|
@doc_html = "<#{tag_name}>#{File.read(html_module).strip}</#{tag_name}>"
|
@@ -103,9 +124,7 @@ module HoTModuLe
|
|
103
124
|
instance_variable_get(:@doc_html)[0..loc].count("\n") + 1
|
104
125
|
end
|
105
126
|
|
106
|
-
def attribute_bindings
|
107
|
-
@attribute_bindings ||= []
|
108
|
-
end
|
127
|
+
def attribute_bindings = @attribute_bindings ||= []
|
109
128
|
|
110
129
|
def attribute_binding(matcher, method_name, only: nil)
|
111
130
|
attribute_bindings << AttributeBinding.new(
|
@@ -117,22 +136,21 @@ module HoTModuLe
|
|
117
136
|
end
|
118
137
|
|
119
138
|
module ContentMethod
|
120
|
-
# @return [String,
|
121
|
-
def content
|
139
|
+
# @return [String, Nokolexbor::Element]
|
140
|
+
def content = @_content
|
122
141
|
end
|
123
142
|
|
124
143
|
# Override in component
|
125
144
|
#
|
126
145
|
# @return [Hash]
|
127
|
-
def attributes
|
128
|
-
{}
|
129
|
-
end
|
146
|
+
def attributes = {}
|
130
147
|
|
131
148
|
# @param attributes [Hash]
|
132
|
-
# @param content [String,
|
149
|
+
# @param content [String, Nokolexbor::Element]
|
133
150
|
# @param return_node [Boolean]
|
134
151
|
def render_element(attributes: self.attributes, content: self.content, return_node: false) # rubocop:disable Metrics
|
135
152
|
doc = self.class.doc.clone
|
153
|
+
@_content = content
|
136
154
|
|
137
155
|
tmpl_el = doc.css("> template").find { _1.attributes.empty? }
|
138
156
|
|
@@ -146,6 +164,33 @@ module HoTModuLe
|
|
146
164
|
# Process all the template bits
|
147
165
|
process_fragment(tmpl_el)
|
148
166
|
|
167
|
+
HoTModuLe.registered_elements.each do |component|
|
168
|
+
tmpl_el.children[0].css(component.tag_name).reverse.each do |node|
|
169
|
+
if node["hmod:ignore"]
|
170
|
+
node.attribute("hmod:ignore").remove
|
171
|
+
next
|
172
|
+
end
|
173
|
+
|
174
|
+
attrs = node.attributes.transform_values(&:value)
|
175
|
+
attrs.reject! { |k| k.start_with?("hmod:") }
|
176
|
+
new_attrs = {}
|
177
|
+
attrs.each do |k, v|
|
178
|
+
next unless k.start_with?("arg:")
|
179
|
+
|
180
|
+
new_key = k.delete_prefix("arg:")
|
181
|
+
attrs.delete(k)
|
182
|
+
new_attrs[new_key] = instance_eval(v, self.class.html_module, self.class.line_number_of_node(node))
|
183
|
+
end
|
184
|
+
attrs.merge!(new_attrs)
|
185
|
+
attrs.transform_keys!(&:to_sym)
|
186
|
+
|
187
|
+
new_node = node.replace(
|
188
|
+
component.new(**attrs).render_element(content: node.children)
|
189
|
+
)
|
190
|
+
new_node.first.attribute("hmod:ignore")&.remove
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
149
194
|
# Set attributes on the custom element
|
150
195
|
attributes.each { |k, v| doc[k.to_s.tr("_", "-")] = value_to_attribute(v) if v }
|
151
196
|
|
@@ -182,14 +227,15 @@ module HoTModuLe
|
|
182
227
|
style_tag.content = output_styles
|
183
228
|
end
|
184
229
|
|
230
|
+
child_content = @_replaced_children || content
|
185
231
|
if self.class.shadow_root
|
186
232
|
# Guess what? We can reuse the same template tag! =)
|
187
233
|
tmpl_el["shadowrootmode"] = "open"
|
188
234
|
tmpl_el.children[0] << style_tag if style_tag
|
189
|
-
doc <<
|
235
|
+
doc << child_content if child_content
|
190
236
|
else
|
191
237
|
tmpl_el.children[0] << style_tag if style_tag
|
192
|
-
tmpl_el.children[0].at_css("slot:not([name])")&.swap(
|
238
|
+
tmpl_el.children[0].at_css("slot:not([name])")&.swap(child_content) if child_content
|
193
239
|
tmpl_el.children[0].children.each do |node|
|
194
240
|
doc << node
|
195
241
|
end
|
@@ -200,13 +246,9 @@ module HoTModuLe
|
|
200
246
|
return_node ? doc : doc.to_html
|
201
247
|
end
|
202
248
|
|
203
|
-
def call(...)
|
204
|
-
render_element(...)
|
205
|
-
end
|
249
|
+
def call(...) = render_element(...)
|
206
250
|
|
207
|
-
def inspect
|
208
|
-
"#<#{self.class.name} #{attributes}>"
|
209
|
-
end
|
251
|
+
def inspect = "#<#{self.class.name} #{attributes}>"
|
210
252
|
|
211
253
|
def value_to_attribute(val)
|
212
254
|
case val
|
@@ -219,13 +261,15 @@ module HoTModuLe
|
|
219
261
|
end
|
220
262
|
end
|
221
263
|
|
264
|
+
def node_or_string(val)
|
265
|
+
val.is_a?(Nokolexbor::Node) ? val : val.to_s
|
266
|
+
end
|
267
|
+
|
222
268
|
# Override in component if need be, otherwise we'll use the node walker/binding pipeline
|
223
269
|
#
|
224
|
-
# @param fragment [
|
270
|
+
# @param fragment [Nokolexbor::Element]
|
225
271
|
# @return [void]
|
226
|
-
def process_fragment(fragment)
|
227
|
-
Fragment.new(fragment, self).process
|
228
|
-
end
|
272
|
+
def process_fragment(fragment) = Fragment.new(fragment, self).process
|
229
273
|
|
230
274
|
def process_list(attribute:, node:, item_node:, for_in:) # rubocop:disable Metrics
|
231
275
|
_context_nodes.push(node)
|
@@ -275,13 +319,9 @@ module HoTModuLe
|
|
275
319
|
end.join(" ")
|
276
320
|
end
|
277
321
|
|
278
|
-
def _context_nodes
|
279
|
-
@_context_nodes ||= []
|
280
|
-
end
|
322
|
+
def _context_nodes = @_context_nodes ||= []
|
281
323
|
|
282
|
-
def _context_locals
|
283
|
-
@_context_locals ||= {}
|
284
|
-
end
|
324
|
+
def _context_locals = @_context_locals ||= {}
|
285
325
|
|
286
326
|
def _check_stack(node)
|
287
327
|
node_and_ancestors = [node, *node.ancestors.to_a]
|
@@ -303,6 +343,31 @@ module HoTModuLe
|
|
303
343
|
yield previous_context
|
304
344
|
@_context_locals = previous_context
|
305
345
|
end
|
346
|
+
|
347
|
+
def _hmod_children_binding(attribute:, node:) # rubocop:disable Lint/UnusedMethodArgument
|
348
|
+
@_replaced_children = node.children[0]
|
349
|
+
node.remove
|
350
|
+
end
|
351
|
+
|
352
|
+
def _hmod_replace_binding(attribute:, node:)
|
353
|
+
if node.name == "template"
|
354
|
+
node.children[0].inner_html = node_or_string(evaluate_attribute_expression(attribute))
|
355
|
+
node.replace(node.children[0].children)
|
356
|
+
else
|
357
|
+
node.inner_html = node_or_string(evaluate_attribute_expression(attribute))
|
358
|
+
node.replace(node.children)
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
def _hmod_expr_binding(attribute:, node:)
|
363
|
+
if attribute.name.end_with?(":text")
|
364
|
+
node.content = node_or_string(evaluate_attribute_expression(attribute))
|
365
|
+
attribute.parent.delete(attribute.name)
|
366
|
+
elsif attribute.name.end_with?(":html")
|
367
|
+
node.inner_html = node_or_string(evaluate_attribute_expression(attribute))
|
368
|
+
attribute.parent.delete(attribute.name)
|
369
|
+
end
|
370
|
+
end
|
306
371
|
end
|
307
372
|
|
308
373
|
if defined?(Bridgetown)
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hot_module
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.alpha11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jared White
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-04-
|
11
|
+
date: 2023-04-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: concurrent-ruby
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.2'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: nokolexbor
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|