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