lib/hot_module/petite.rb in hot_module-1.0.0.alpha1 vs lib/hot_module/petite.rb in hot_module-1.0.0.alpha2
- old
+ new
@@ -1,98 +1,44 @@
# frozen_string_literal: true
require "hot_module"
-module JSStrings
- refine Kernel do
- def `(str)
- str
- end
- end
- refine String do
- def underscore
- gsub(/([A-Z]+)(?=[A-Z][a-z])|([a-z\d])(?=[A-Z])/) do
- (::Regexp.last_match(1) || ::Regexp.last_match(2)) << "_"
- end.tr("-", "_").downcase
- end
- end
-end
-
module HoTModuLe
module Petite
- using JSStrings
-
# @param klass [Class]
# @return [void]
def self.included(klass)
- klass.attribute_binding "v-for", :_for_binding
- klass.attribute_binding "v-text", :_text_binding
- klass.attribute_binding "v-html", :_html_binding
- klass.attribute_binding "v-bind", :_handle_bound_attribute
- klass.attribute_binding %r{^:}, :_handle_bound_attribute
+ klass.attribute_binding "v-for", :_petite_for_binding, only: :template
+ klass.attribute_binding "v-text", :_petite_text_binding
+ klass.attribute_binding "v-html", :_petite_html_binding
+ klass.attribute_binding "v-bind", :_petite_bound_attribute
+ klass.attribute_binding %r{^:}, :_petite_bound_attribute
end
protected
- def evaluate_attribute_expression(attribute, eval_code = attribute.value) # rubocop:disable Metrics/AbcSize
- eval_code = eval_code.gsub(/\${(.*)}/, "\#{\\1}")
- @_locals ||= {}
- @_locals.keys.reverse_each do |name|
- eval_code = "#{name} = @_locals[\"#{name}\"];" + eval_code
- end
- instance_eval(eval_code, self.class.html_module, attribute.line)
- rescue NameError => e
- bad_name = e.message.match(/`(.*?)'/)[1]
- suggestion = bad_name.underscore
- eval_code.gsub!(bad_name, suggestion)
- instance_eval(eval_code, self.class.html_module, attribute.line)
- end
+ def _petite_for_binding(attribute:, node:)
+ delimiter = node["v-for"].include?(" of ") ? " of " : " in "
+ expression = node["v-for"].split(delimiter)
- def _locals_stack
- @_locals_stack ||= []
+ process_list(
+ attribute: attribute,
+ node: node,
+ item_node: node.children[0].children.find(&:element?),
+ for_in: expression
+ )
end
- def _check_stack(node) # rubocop:disable Metrics/AbcSize
- node_and_ancestors = [node, *node.ancestors.to_a]
- stack_misses = 0
-
- stack_nodes = _locals_stack.map { _1[:node] }
- stack_nodes.each do |stack_node|
- if node_and_ancestors.none? { _1["v-if"] == "!hydrated" } && node_and_ancestors.none? { _1 == stack_node }
- stack_misses += 1
- end
- end
-
- stack_misses.times { _locals_stack.pop }
-
- !((node_and_ancestors & _locals_stack.map { _1[:node] }).empty?) # rubocop:disable Style/RedundantParentheses
+ def _petite_text_binding(attribute:, node:)
+ node.content = evaluate_attribute_expression(attribute).to_s
end
- def _for_binding(attribute:, node:)
- return unless node.name == "template"
-
- return if _check_stack(node)
-
- @_locals_stack.push({ node: node })
- _process_list(attribute: attribute, node: node)
+ def _petite_html_binding(attribute:, node:)
+ node.inner_html = evaluate_attribute_expression(attribute).to_s
end
- def _text_binding(attribute:, node:)
- return if _check_stack(node)
-
- node.content = evaluate_attribute_expression(attribute)
- end
-
- def _html_binding(attribute:, node:)
- return if _check_stack(node)
-
- node.content = evaluate_attribute_expression(attribute)
- end
-
- def _handle_bound_attribute(attribute:, node:) # rubocop:disable Metrics
- return if _check_stack(node)
-
+ def _petite_bound_attribute(attribute:, node:) # rubocop:disable Metrics
return if attribute.name == ":key"
real_attribute = if attribute.name.start_with?(":")
attribute.name.delete_prefix(":")
elsif attribute.name.start_with?("v-bind:")
@@ -100,62 +46,12 @@
end
obj = evaluate_attribute_expression(attribute)
if real_attribute == "class"
- class_names = case obj
- when Hash
- obj.filter { |_k, v| v == true }.keys
- when Array
- # TODO: handle objects inside of an array
- obj
- else
- Array[obj]
- end
- node[real_attribute] = class_names.join(" ")
+ node[real_attribute] = class_list_for(obj)
elsif real_attribute != "style" # style bindings aren't SSRed
node[real_attribute] = obj if obj
end
- end
-
- def _process_list(attribute:, node:) # rubocop:disable Metrics
- item_node = node.element_children.first
-
- delimiter = node["v-for"].include?(" of ") ? " of " : " in "
- expression = node["v-for"].split(delimiter)
- lh = expression[0].strip.delete_prefix("(").delete_suffix(")").split(",").map!(&:strip)
- rh = expression[1].strip
-
- list_items = evaluate_attribute_expression(attribute, rh)
-
- # TODO: handle object style
- # https://vuejs.org/guide/essentials/list.html#v-for-with-an-object
-
- return unless list_items
-
- _in_locals_stack do
- list_items.each_with_index do |list_item, index|
- new_node = item_node.clone
- node.parent << new_node
- new_node["v-if"] = "!hydrated"
-
- local_items = { **(prev_items || {}) }
- local_items[lh[0]] = list_item
- local_items[lh[1]] = index if lh[1]
-
- @_locals = local_items
-
- Fragment.new(
- new_node, self.class.attribute_bindings,
- html_module: self.class.html_module
- ).process
- end
- end
- end
-
- def _in_locals_stack
- prev_items = @_locals
- yield
- @_locals = prev_items
end
end
end