hot_module 1.0.0.alpha1 → 1.0.0.alpha3
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 +25 -23
- data/benchmark.rb +14 -0
- data/hot_module.gemspec +1 -1
- data/lib/hot_module/fragment.rb +4 -2
- data/lib/hot_module/petite.rb +20 -124
- data/lib/hot_module/query_selection.rb +1 -1
- data/lib/hot_module/version.rb +1 -1
- data/lib/hot_module.rb +124 -17
- metadata +6 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bc20758effe540a0a4bbb8627fcdd7b3cf528792795de33dd039542e495ad8f9
|
|
4
|
+
data.tar.gz: e6dd965829f44ec8402c8c3a8664ac6c63a203050c945bba89eec02081629e48
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1600644deca41a43f7e9fdd3a33b880d20867cedbcc3251d025c3d0db9c0a0e63239328f46a57ad23a7e71de1793c5c987acb815023fb7496481dadca65d597f
|
|
7
|
+
data.tar.gz: 8901f9bfccaadbde5655bc5b60b5a03120ad422b8fb664b8305a1b4fe2489453608953bdc67f57a85a6125e55e25811af451e11440cdf873a48f89f905939296
|
data/Gemfile.lock
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
hot_module (1.0.0.
|
|
5
|
-
|
|
4
|
+
hot_module (1.0.0.alpha3)
|
|
5
|
+
nokolexbor (~> 0.4)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
8
8
|
remote: https://rubygems.org/
|
|
9
9
|
specs:
|
|
10
|
-
activesupport (7.0.4)
|
|
10
|
+
activesupport (7.0.4.3)
|
|
11
11
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
12
12
|
i18n (>= 1.6, < 2)
|
|
13
13
|
minitest (>= 5.1)
|
|
14
14
|
tzinfo (~> 2.0)
|
|
15
15
|
ast (2.4.2)
|
|
16
16
|
backport (1.2.0)
|
|
17
|
-
benchmark (0.2.
|
|
18
|
-
concurrent-ruby (1.
|
|
17
|
+
benchmark (0.2.1)
|
|
18
|
+
concurrent-ruby (1.2.2)
|
|
19
19
|
diff-lcs (1.5.0)
|
|
20
20
|
e2mmap (0.1.0)
|
|
21
21
|
hash_with_dot_access (1.2.0)
|
|
@@ -23,43 +23,45 @@ GEM
|
|
|
23
23
|
i18n (1.12.0)
|
|
24
24
|
concurrent-ruby (~> 1.0)
|
|
25
25
|
jaro_winkler (1.5.4)
|
|
26
|
-
json (2.6.
|
|
26
|
+
json (2.6.3)
|
|
27
27
|
kramdown (2.4.0)
|
|
28
28
|
rexml
|
|
29
29
|
kramdown-parser-gfm (1.1.0)
|
|
30
30
|
kramdown (~> 2.0)
|
|
31
|
-
minitest (5.
|
|
32
|
-
nokogiri (1.
|
|
31
|
+
minitest (5.18.0)
|
|
32
|
+
nokogiri (1.14.2-arm64-darwin)
|
|
33
33
|
racc (~> 1.4)
|
|
34
|
-
nokogiri (1.
|
|
34
|
+
nokogiri (1.14.2-x86_64-linux)
|
|
35
35
|
racc (~> 1.4)
|
|
36
|
+
nokolexbor (0.4.0-arm64-darwin)
|
|
37
|
+
nokolexbor (0.4.0-x86_64-linux)
|
|
36
38
|
parallel (1.22.1)
|
|
37
|
-
parser (3.
|
|
39
|
+
parser (3.2.1.1)
|
|
38
40
|
ast (~> 2.4.1)
|
|
39
|
-
racc (1.6.
|
|
41
|
+
racc (1.6.2)
|
|
40
42
|
rainbow (3.1.1)
|
|
41
43
|
rake (13.0.6)
|
|
42
|
-
regexp_parser (2.
|
|
44
|
+
regexp_parser (2.7.0)
|
|
43
45
|
reverse_markdown (2.1.1)
|
|
44
46
|
nokogiri
|
|
45
47
|
rexml (3.2.5)
|
|
46
|
-
rubocop (1.
|
|
48
|
+
rubocop (1.48.1)
|
|
47
49
|
json (~> 2.3)
|
|
48
50
|
parallel (~> 1.10)
|
|
49
|
-
parser (>= 3.
|
|
51
|
+
parser (>= 3.2.0.0)
|
|
50
52
|
rainbow (>= 2.2.2, < 4.0)
|
|
51
53
|
regexp_parser (>= 1.8, < 3.0)
|
|
52
54
|
rexml (>= 3.2.5, < 4.0)
|
|
53
|
-
rubocop-ast (>= 1.
|
|
55
|
+
rubocop-ast (>= 1.26.0, < 2.0)
|
|
54
56
|
ruby-progressbar (~> 1.7)
|
|
55
|
-
unicode-display_width (>=
|
|
56
|
-
rubocop-ast (1.
|
|
57
|
-
parser (>= 3.
|
|
58
|
-
rubocop-minitest (0.22.
|
|
57
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
|
58
|
+
rubocop-ast (1.27.0)
|
|
59
|
+
parser (>= 3.2.1.0)
|
|
60
|
+
rubocop-minitest (0.22.2)
|
|
59
61
|
rubocop (>= 0.90, < 2.0)
|
|
60
62
|
rubocop-rake (0.6.0)
|
|
61
63
|
rubocop (~> 1.0)
|
|
62
|
-
ruby-progressbar (1.
|
|
64
|
+
ruby-progressbar (1.13.0)
|
|
63
65
|
solargraph (0.47.2)
|
|
64
66
|
backport (~> 1.2)
|
|
65
67
|
benchmark
|
|
@@ -76,10 +78,10 @@ GEM
|
|
|
76
78
|
tilt (~> 2.0)
|
|
77
79
|
yard (~> 0.9, >= 0.9.24)
|
|
78
80
|
thor (1.2.1)
|
|
79
|
-
tilt (2.0
|
|
80
|
-
tzinfo (2.0.
|
|
81
|
+
tilt (2.1.0)
|
|
82
|
+
tzinfo (2.0.6)
|
|
81
83
|
concurrent-ruby (~> 1.0)
|
|
82
|
-
unicode-display_width (2.
|
|
84
|
+
unicode-display_width (2.4.2)
|
|
83
85
|
webrick (1.7.0)
|
|
84
86
|
yard (0.9.28)
|
|
85
87
|
webrick (~> 1.7.0)
|
data/benchmark.rb
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path("lib", __dir__)
|
|
2
|
+
require "hot_module"
|
|
3
|
+
|
|
4
|
+
require_relative "test/fixtures/classes"
|
|
5
|
+
|
|
6
|
+
require "benchmark"
|
|
7
|
+
|
|
8
|
+
Benchmark.bmbm do |x|
|
|
9
|
+
x.report("render") do
|
|
10
|
+
1000.times do |i|
|
|
11
|
+
Templated.new(name: i.to_s).()
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
data/hot_module.gemspec
CHANGED
data/lib/hot_module/fragment.rb
CHANGED
|
@@ -22,15 +22,17 @@ module HoTModuLe
|
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
def process_attribute_bindings(node)
|
|
25
|
+
def process_attribute_bindings(node) # rubocop:todo Metrics
|
|
26
26
|
node.attributes.each do |name, attr_node|
|
|
27
27
|
@attribute_bindings.each do |attribute_binding|
|
|
28
|
+
next if attribute_binding.only_for_tag && node.name != attribute_binding.only_for_tag.to_s
|
|
28
29
|
next unless attribute_binding.matcher.match?(name)
|
|
30
|
+
next if attribute_binding.method.receiver._check_stack(node)
|
|
29
31
|
|
|
30
32
|
break unless attribute_binding.method.(attribute: attr_node, node: node)
|
|
31
33
|
end
|
|
32
34
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
33
|
-
raise e.class, e.message.lines.first, ["#{@html_module}:#{attr_node
|
|
35
|
+
raise e.class, e.message.lines.first, ["#{@html_module}:#{attr_node}", *e.backtrace]
|
|
34
36
|
end
|
|
35
37
|
end
|
|
36
38
|
end
|
data/lib/hot_module/petite.rb
CHANGED
|
@@ -2,95 +2,41 @@
|
|
|
2
2
|
|
|
3
3
|
require "hot_module"
|
|
4
4
|
|
|
5
|
-
module JSStrings
|
|
6
|
-
refine Kernel do
|
|
7
|
-
def `(str)
|
|
8
|
-
str
|
|
9
|
-
end
|
|
10
|
-
end
|
|
11
|
-
refine String do
|
|
12
|
-
def underscore
|
|
13
|
-
gsub(/([A-Z]+)(?=[A-Z][a-z])|([a-z\d])(?=[A-Z])/) do
|
|
14
|
-
(::Regexp.last_match(1) || ::Regexp.last_match(2)) << "_"
|
|
15
|
-
end.tr("-", "_").downcase
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
5
|
module HoTModuLe
|
|
21
6
|
module Petite
|
|
22
|
-
using JSStrings
|
|
23
|
-
|
|
24
7
|
# @param klass [Class]
|
|
25
8
|
# @return [void]
|
|
26
9
|
def self.included(klass)
|
|
27
|
-
klass.attribute_binding "v-for", :
|
|
28
|
-
klass.attribute_binding "v-text", :
|
|
29
|
-
klass.attribute_binding "v-html", :
|
|
30
|
-
klass.attribute_binding "v-bind", :
|
|
31
|
-
klass.attribute_binding %r{^:}, :
|
|
10
|
+
klass.attribute_binding "v-for", :_petite_for_binding, only: :template
|
|
11
|
+
klass.attribute_binding "v-text", :_petite_text_binding
|
|
12
|
+
klass.attribute_binding "v-html", :_petite_html_binding
|
|
13
|
+
klass.attribute_binding "v-bind", :_petite_bound_attribute
|
|
14
|
+
klass.attribute_binding %r{^:}, :_petite_bound_attribute
|
|
32
15
|
end
|
|
33
16
|
|
|
34
17
|
protected
|
|
35
18
|
|
|
36
|
-
def
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
@_locals.keys.reverse_each do |name|
|
|
40
|
-
eval_code = "#{name} = @_locals[\"#{name}\"];" + eval_code
|
|
41
|
-
end
|
|
42
|
-
instance_eval(eval_code, self.class.html_module, attribute.line)
|
|
43
|
-
rescue NameError => e
|
|
44
|
-
bad_name = e.message.match(/`(.*?)'/)[1]
|
|
45
|
-
suggestion = bad_name.underscore
|
|
46
|
-
eval_code.gsub!(bad_name, suggestion)
|
|
47
|
-
instance_eval(eval_code, self.class.html_module, attribute.line)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def _locals_stack
|
|
51
|
-
@_locals_stack ||= []
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def _check_stack(node) # rubocop:disable Metrics/AbcSize
|
|
55
|
-
node_and_ancestors = [node, *node.ancestors.to_a]
|
|
56
|
-
stack_misses = 0
|
|
57
|
-
|
|
58
|
-
stack_nodes = _locals_stack.map { _1[:node] }
|
|
59
|
-
stack_nodes.each do |stack_node|
|
|
60
|
-
if node_and_ancestors.none? { _1["v-if"] == "!hydrated" } && node_and_ancestors.none? { _1 == stack_node }
|
|
61
|
-
stack_misses += 1
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
stack_misses.times { _locals_stack.pop }
|
|
66
|
-
|
|
67
|
-
!((node_and_ancestors & _locals_stack.map { _1[:node] }).empty?) # rubocop:disable Style/RedundantParentheses
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def _for_binding(attribute:, node:)
|
|
71
|
-
return unless node.name == "template"
|
|
72
|
-
|
|
73
|
-
return if _check_stack(node)
|
|
19
|
+
def _petite_for_binding(attribute:, node:)
|
|
20
|
+
delimiter = node["v-for"].include?(" of ") ? " of " : " in "
|
|
21
|
+
expression = node["v-for"].split(delimiter)
|
|
74
22
|
|
|
75
|
-
|
|
76
|
-
|
|
23
|
+
process_list(
|
|
24
|
+
attribute: attribute,
|
|
25
|
+
node: node,
|
|
26
|
+
item_node: node.children[0].element_children.first,
|
|
27
|
+
for_in: expression
|
|
28
|
+
)
|
|
77
29
|
end
|
|
78
30
|
|
|
79
|
-
def
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
node.content = evaluate_attribute_expression(attribute)
|
|
31
|
+
def _petite_text_binding(attribute:, node:)
|
|
32
|
+
node.content = evaluate_attribute_expression(attribute).to_s
|
|
83
33
|
end
|
|
84
34
|
|
|
85
|
-
def
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
node.content = evaluate_attribute_expression(attribute)
|
|
35
|
+
def _petite_html_binding(attribute:, node:)
|
|
36
|
+
node.inner_html = evaluate_attribute_expression(attribute).to_s
|
|
89
37
|
end
|
|
90
38
|
|
|
91
|
-
def
|
|
92
|
-
return if _check_stack(node)
|
|
93
|
-
|
|
39
|
+
def _petite_bound_attribute(attribute:, node:) # rubocop:disable Metrics
|
|
94
40
|
return if attribute.name == ":key"
|
|
95
41
|
|
|
96
42
|
real_attribute = if attribute.name.start_with?(":")
|
|
@@ -102,60 +48,10 @@ module HoTModuLe
|
|
|
102
48
|
obj = evaluate_attribute_expression(attribute)
|
|
103
49
|
|
|
104
50
|
if real_attribute == "class"
|
|
105
|
-
|
|
106
|
-
when Hash
|
|
107
|
-
obj.filter { |_k, v| v == true }.keys
|
|
108
|
-
when Array
|
|
109
|
-
# TODO: handle objects inside of an array
|
|
110
|
-
obj
|
|
111
|
-
else
|
|
112
|
-
Array[obj]
|
|
113
|
-
end
|
|
114
|
-
node[real_attribute] = class_names.join(" ")
|
|
51
|
+
node[real_attribute] = class_list_for(obj)
|
|
115
52
|
elsif real_attribute != "style" # style bindings aren't SSRed
|
|
116
53
|
node[real_attribute] = obj if obj
|
|
117
54
|
end
|
|
118
55
|
end
|
|
119
|
-
|
|
120
|
-
def _process_list(attribute:, node:) # rubocop:disable Metrics
|
|
121
|
-
item_node = node.element_children.first
|
|
122
|
-
|
|
123
|
-
delimiter = node["v-for"].include?(" of ") ? " of " : " in "
|
|
124
|
-
expression = node["v-for"].split(delimiter)
|
|
125
|
-
lh = expression[0].strip.delete_prefix("(").delete_suffix(")").split(",").map!(&:strip)
|
|
126
|
-
rh = expression[1].strip
|
|
127
|
-
|
|
128
|
-
list_items = evaluate_attribute_expression(attribute, rh)
|
|
129
|
-
|
|
130
|
-
# TODO: handle object style
|
|
131
|
-
# https://vuejs.org/guide/essentials/list.html#v-for-with-an-object
|
|
132
|
-
|
|
133
|
-
return unless list_items
|
|
134
|
-
|
|
135
|
-
_in_locals_stack do
|
|
136
|
-
list_items.each_with_index do |list_item, index|
|
|
137
|
-
new_node = item_node.clone
|
|
138
|
-
node.parent << new_node
|
|
139
|
-
new_node["v-if"] = "!hydrated"
|
|
140
|
-
|
|
141
|
-
local_items = { **(prev_items || {}) }
|
|
142
|
-
local_items[lh[0]] = list_item
|
|
143
|
-
local_items[lh[1]] = index if lh[1]
|
|
144
|
-
|
|
145
|
-
@_locals = local_items
|
|
146
|
-
|
|
147
|
-
Fragment.new(
|
|
148
|
-
new_node, self.class.attribute_bindings,
|
|
149
|
-
html_module: self.class.html_module
|
|
150
|
-
).process
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
def _in_locals_stack
|
|
156
|
-
prev_items = @_locals
|
|
157
|
-
yield
|
|
158
|
-
@_locals = prev_items
|
|
159
|
-
end
|
|
160
56
|
end
|
|
161
57
|
end
|
|
@@ -12,5 +12,5 @@ module HoTModuLe
|
|
|
12
12
|
def query_selector_all(selector) = css(selector)
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Nokolexbor::Element.include QuerySelection unless Nokolexbor::Element.instance_methods.include?(:query_selector)
|
|
16
16
|
end
|
data/lib/hot_module/version.rb
CHANGED
data/lib/hot_module.rb
CHANGED
|
@@ -2,13 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "hot_module/version"
|
|
4
4
|
|
|
5
|
-
require "
|
|
5
|
+
require "nokolexbor"
|
|
6
6
|
|
|
7
7
|
# Include this module into your own component class
|
|
8
8
|
module HoTModuLe
|
|
9
9
|
class Error < StandardError; end
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
module JSTemplateLiterals
|
|
12
|
+
refine Kernel do
|
|
13
|
+
def `(str)
|
|
14
|
+
str
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
using JSTemplateLiterals
|
|
20
|
+
|
|
21
|
+
AttributeBinding = Struct.new(:matcher, :method_name, :method, :only_for_tag, keyword_init: true) # rubocop:disable Lint/StructNewOverride
|
|
12
22
|
|
|
13
23
|
require_relative "hot_module/fragment"
|
|
14
24
|
require_relative "hot_module/query_selection"
|
|
@@ -25,7 +35,11 @@ module HoTModuLe
|
|
|
25
35
|
|
|
26
36
|
# Extends the component class
|
|
27
37
|
module ClassMethods
|
|
28
|
-
def
|
|
38
|
+
def camelcased(method_symbol)
|
|
39
|
+
alias_method(method_symbol.to_s.gsub(/(?!^)_[a-z0-9]/) { |match| match[1].upcase }, method_symbol)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def html_file_extensions = %w[module.html tmpl.html html].freeze
|
|
29
43
|
def processed_css_extension = "css-local"
|
|
30
44
|
|
|
31
45
|
# @param tag_name [String]
|
|
@@ -76,19 +90,20 @@ module HoTModuLe
|
|
|
76
90
|
|
|
77
91
|
# @return [Nokogiri::XML::Element]
|
|
78
92
|
def doc
|
|
79
|
-
@doc ||=
|
|
80
|
-
"<#{tag_name}>#{File.read(html_module)}</#{tag_name}>"
|
|
81
|
-
).
|
|
93
|
+
@doc ||= Nokolexbor::DocumentFragment.parse(
|
|
94
|
+
"<#{tag_name}>#{File.read(html_module).strip}</#{tag_name}>"
|
|
95
|
+
).element_children.first
|
|
82
96
|
end
|
|
83
97
|
|
|
84
98
|
def attribute_bindings
|
|
85
99
|
@attribute_bindings ||= []
|
|
86
100
|
end
|
|
87
101
|
|
|
88
|
-
def attribute_binding(matcher, method_name)
|
|
102
|
+
def attribute_binding(matcher, method_name, only: nil)
|
|
89
103
|
attribute_bindings << AttributeBinding.new(
|
|
90
104
|
matcher: Regexp.new(matcher),
|
|
91
|
-
method_name: method_name
|
|
105
|
+
method_name: method_name,
|
|
106
|
+
only_for_tag: only
|
|
92
107
|
)
|
|
93
108
|
end
|
|
94
109
|
end
|
|
@@ -110,12 +125,13 @@ module HoTModuLe
|
|
|
110
125
|
# @param return_node [Boolean]
|
|
111
126
|
def render_element(attributes: self.attributes, content: self.content, return_node: false) # rubocop:disable Metrics
|
|
112
127
|
doc = self.class.doc.clone
|
|
128
|
+
|
|
113
129
|
tmpl_el = doc.css("> template").find { _1.attributes.length.zero? }
|
|
114
130
|
|
|
115
131
|
unless tmpl_el
|
|
116
132
|
tmpl_el = doc.document.create_element("template")
|
|
117
133
|
immediate_children = doc.css("> :not(style):not(script)")
|
|
118
|
-
tmpl_el << immediate_children
|
|
134
|
+
tmpl_el.children[0] << immediate_children
|
|
119
135
|
doc.prepend_child(tmpl_el)
|
|
120
136
|
end
|
|
121
137
|
|
|
@@ -123,7 +139,7 @@ module HoTModuLe
|
|
|
123
139
|
process_fragment(tmpl_el)
|
|
124
140
|
|
|
125
141
|
# Set attributes on the custom element
|
|
126
|
-
attributes.each { |k, v| doc[k.to_s.tr("_", "-")] = v }
|
|
142
|
+
attributes.each { |k, v| doc[k.to_s.tr("_", "-")] = value_to_attribute(v) if v }
|
|
127
143
|
|
|
128
144
|
# Look for external and internal styles
|
|
129
145
|
output_styles = ""
|
|
@@ -160,13 +176,13 @@ module HoTModuLe
|
|
|
160
176
|
|
|
161
177
|
if self.class.shadow_root
|
|
162
178
|
# Guess what? We can reuse the same template tag! =)
|
|
163
|
-
tmpl_el["
|
|
164
|
-
tmpl_el << style_tag if style_tag
|
|
179
|
+
tmpl_el["shadowrootmode"] = "open"
|
|
180
|
+
tmpl_el.children[0] << style_tag if style_tag
|
|
165
181
|
doc << content if content
|
|
166
182
|
else
|
|
167
|
-
tmpl_el << style_tag if style_tag
|
|
168
|
-
tmpl_el.at_css("slot:not([name])")&.swap(content) if content
|
|
169
|
-
tmpl_el.children.each do |node|
|
|
183
|
+
tmpl_el.children[0] << style_tag if style_tag
|
|
184
|
+
tmpl_el.children[0].at_css("slot:not([name])")&.swap(content) if content
|
|
185
|
+
tmpl_el.children[0].children.each do |node|
|
|
170
186
|
doc << node
|
|
171
187
|
end
|
|
172
188
|
tmpl_el.remove
|
|
@@ -180,6 +196,21 @@ module HoTModuLe
|
|
|
180
196
|
render_element(...)
|
|
181
197
|
end
|
|
182
198
|
|
|
199
|
+
def inspect
|
|
200
|
+
"#<#{self.class.name} #{attributes}>"
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def value_to_attribute(val)
|
|
204
|
+
case val
|
|
205
|
+
when String, Numeric
|
|
206
|
+
val
|
|
207
|
+
when TrueClass
|
|
208
|
+
""
|
|
209
|
+
else
|
|
210
|
+
val.to_json
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
183
214
|
# Override in component if need be, otherwise we'll use the node walker/binding pipeline
|
|
184
215
|
#
|
|
185
216
|
# @param fragment [Nokogiri::XML::Element]
|
|
@@ -191,7 +222,83 @@ module HoTModuLe
|
|
|
191
222
|
).process
|
|
192
223
|
end
|
|
193
224
|
|
|
194
|
-
def
|
|
195
|
-
|
|
225
|
+
def process_list(attribute:, node:, item_node:, for_in:) # rubocop:disable Metrics
|
|
226
|
+
_context_nodes.push(node)
|
|
227
|
+
|
|
228
|
+
lh = for_in[0].strip.delete_prefix("(").delete_suffix(")").split(",").map!(&:strip)
|
|
229
|
+
rh = for_in[1].strip
|
|
230
|
+
|
|
231
|
+
list_items = evaluate_attribute_expression(attribute, rh)
|
|
232
|
+
|
|
233
|
+
# TODO: handle object style
|
|
234
|
+
# https://vuejs.org/guide/essentials/list.html#v-for-with-an-object
|
|
235
|
+
|
|
236
|
+
return unless list_items
|
|
237
|
+
|
|
238
|
+
_in_context_nodes do |previous_context|
|
|
239
|
+
list_items.each_with_index do |list_item, index|
|
|
240
|
+
new_node = item_node.clone
|
|
241
|
+
node.parent << new_node
|
|
242
|
+
new_node["hmod-added"] = ""
|
|
243
|
+
|
|
244
|
+
@_context_locals = { **(previous_context || {}) }
|
|
245
|
+
_context_locals[lh[0]] = list_item
|
|
246
|
+
_context_locals[lh[1]] = index if lh[1]
|
|
247
|
+
|
|
248
|
+
Fragment.new(
|
|
249
|
+
new_node, self.class.attribute_bindings,
|
|
250
|
+
html_module: self.class.html_module
|
|
251
|
+
).process
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def evaluate_attribute_expression(attribute, eval_code = attribute.value)
|
|
257
|
+
eval_code = eval_code.gsub(/\${(.*)}/, "\#{\\1}")
|
|
258
|
+
_context_locals.keys.reverse_each do |name|
|
|
259
|
+
eval_code = "#{name} = _context_locals[\"#{name}\"];" + eval_code
|
|
260
|
+
end
|
|
261
|
+
instance_eval(eval_code, self.class.html_module) # , attribute.line)
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def class_list_for(obj)
|
|
265
|
+
case obj
|
|
266
|
+
when Hash
|
|
267
|
+
obj.filter { |_k, v| v }.keys
|
|
268
|
+
when Array
|
|
269
|
+
# TODO: handle objects inside of an array?
|
|
270
|
+
obj
|
|
271
|
+
else
|
|
272
|
+
Array[obj]
|
|
273
|
+
end.join(" ")
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def _context_nodes
|
|
277
|
+
@_context_nodes ||= []
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def _context_locals
|
|
281
|
+
@_context_locals ||= {}
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def _check_stack(node)
|
|
285
|
+
node_and_ancestors = [node, *node.ancestors.to_a]
|
|
286
|
+
stack_misses = 0
|
|
287
|
+
|
|
288
|
+
_context_nodes.each do |stack_node|
|
|
289
|
+
if node_and_ancestors.none? { _1["hmod-added"] } && node_and_ancestors.none? { _1 == stack_node }
|
|
290
|
+
stack_misses += 1
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
stack_misses.times { _context_nodes.pop }
|
|
295
|
+
|
|
296
|
+
node_and_ancestors.any? { _context_nodes.include?(_1) }
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def _in_context_nodes
|
|
300
|
+
previous_context = _context_locals
|
|
301
|
+
yield previous_context
|
|
302
|
+
@_context_locals = previous_context
|
|
196
303
|
end
|
|
197
304
|
end
|
metadata
CHANGED
|
@@ -1,29 +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.alpha3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jared White
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2023-03-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
14
|
+
name: nokolexbor
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
19
|
+
version: '0.4'
|
|
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: '
|
|
26
|
+
version: '0.4'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: hash_with_dot_access
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -53,6 +53,7 @@ files:
|
|
|
53
53
|
- LICENSE.txt
|
|
54
54
|
- README.md
|
|
55
55
|
- Rakefile
|
|
56
|
+
- benchmark.rb
|
|
56
57
|
- hot_module.gemspec
|
|
57
58
|
- lib/hot_module.rb
|
|
58
59
|
- lib/hot_module/fragment.rb
|