lib/preact.rb in isomorfeus-preact-22.9.0.rc9 vs lib/preact.rb in isomorfeus-preact-22.10.0.rc1

- old
+ new

@@ -1,36 +1,10 @@ if RUBY_ENGINE == 'opal' class VNode # just a empty place holder to make is_a?(VNode) work # internally using the js implementation end -else - class VNode - # full ruby implementation - attr_accessor :__c - attr_reader :_children - attr_reader :_component - attr_reader :_depth - attr_reader :_dom - attr_reader :_hydrating - attr_reader :_nextDom - attr_reader :_original - attr_reader :constructor - attr_reader :key - attr_reader :props - attr_reader :ref - attr_reader :type - - def initialize(type, props, key, ref, original) - @type = type - @props = props - @key = key - @ref = ref - @_depth = 0 - @_original = original.nil? ? Preact._vnode_id += 1 : original - end - end end class Fragment def initialize(props, _context) @props = props @@ -494,15 +468,15 @@ } else { if (typeof oldValue === 'string') { dom.style.cssText = oldValue = ''; } - if (value && value !== nil && value["$is_a?"](Opal.Hash)) { + if (value && value !== nil && value.$$is_hash) { value = value.$to_n(); } - if (oldValue && oldValue !== nil && oldValue["$is_a?"](Opal.Hash)) { + if (oldValue && oldValue !== nil && oldValue.$$is_hash) { oldValue = oldValue.$to_n(); } if (oldValue && oldValue !== nil) { for (name in oldValue) { @@ -1031,11 +1005,12 @@ } } return true; }, "$eql?": eql, - "$nil?": is_nil + "$nil?": is_nil, + "$$is_vnode": true }; return vnode; } @@ -1140,15 +1115,13 @@ } } self.process._rerenderCount = 0; } else - IGNORED_PROPS = %i[key ref __self __source].freeze IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|^--/i JS_TO_CSS = {} ENCODED_ENTITIES = /[&<>"]/ - VOID_ELEMENTS = /^(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/ end class << self def _ctxi @_ctxi ||= 0 @@ -1182,11 +1155,11 @@ normalized_props[:children] = children unless children.nil? if RUBY_ENGINE == 'opal' `self.createVNode(vnode.type, normalized_props, #{key || `vnode.key`}, #{ref || `vnode.ref`}, null)` else - _create_vnode(vnode.type, normalized_props, key || vnode.key, ref || vnode.ref, nil) + VNode.new(vnode.type, normalized_props, key || vnode.key, ref || vnode.ref) end end def create_context(const_name, default_value = nil) context = Preact::Context.new(default_value) @@ -1197,24 +1170,24 @@ self.render_buffer = [] self.rerender_queue = [] Isomorfeus.reset_something_loading end - def is_renderable?(block_result) - block_result && - (block_result.is_a?(VNode) || block_result.is_a?(String) || block_result.is_a?(Numeric) || - (block_result.is_a?(Array) && block_result.length > 0 && is_renderable?(block_result[0]))) - end - if RUBY_ENGINE == 'opal' attr_accessor :_vnode_id attr_accessor :render_buffer attr_accessor :rerender_queue - def create_element(type, props = nil, children = nil, &block) + def is_renderable?(block_result) + block_result && + (block_result.JS['$$is_vnode'] || block_result.JS['$$is_string'] || block_result.is_a?(Numeric) || + (block_result.JS['$$is_array'] && `block_result.length > 0` && is_renderable?(block_result[0]))) + end + + def create_element(type, props = nil, children = nil) if props - if props.is_a?(Hash) + if props.JS['$$is_hash'] normalized_props = props.dup key = normalized_props.delete(:key) ref = normalized_props.delete(:ref) else children = props @@ -1227,21 +1200,22 @@ key = nil ref = nil end if block_given? - pr = render_buffer + pr = `self.render_buffer` pr.JS.push([]) - block_result = block.call - children = pr.JS.pop() - if Preact.is_renderable?(block_result) - children.JS.push(block_result) - end + block_result = yield + c = pr.JS.pop() + %x{ + if (self["$is_renderable?"](block_result)) { c.push(block_result); } + if (c.length > 0) { children = c; } + } end %x{ - if (children !== nil && children !== null) { #{normalized_props[:children] = children} } + if (children !== nil && children !== null) { normalized_props["$[]="]("children", children); } return self.createVNode(type, normalized_props, key, ref, null); } end def _enqueue_render(c) @@ -1251,11 +1225,11 @@ end def _render_element(component, props, &block) %x{ let opr = Opal.Preact.render_buffer; - opr[opr.length-1].push(#{self.create_element(component, props, nil, &block)}); + opr[opr.length-1].push(#{create_element(component, props, nil, &block)}); } nil end def hydrate(vnode, container_node) @@ -1288,18 +1262,10 @@ def unmount_component_at_node(element_or_query) element_or_query = element_or_query_to_n(element_or_query) `self.render(null, element_or_query)` end else # RUBY_ENGINE - def _vnode_id - Thread.current[:@_isomorfeus_preact_vnode_id] ||= 0 - end - - def _vnode_id=(i) - Thread.current[:@_isomorfeus_preact_vnode_id] = i - end - def render_buffer Thread.current[:@_isomorfeus_preact_render_buffer] end def render_buffer=(i) @@ -1312,50 +1278,19 @@ def rerender_queue=(i) Thread.current[:@_isomorfeus_preact_rerender_queue] = i end - def _create_vnode(type, props, key, ref, original) - VNode.new(type, props, key, ref, original) + def is_renderable?(block_result) + block_result && + (block_result.is_a?(VNode) || block_result.is_a?(String) || block_result.is_a?(Numeric) || + (block_result.is_a?(Array) && block_result.length > 0 && is_renderable?(block_result[0]))) end - def create_element(type, props = nil, children = nil, &block) - if props - if props.is_a?(Hash) - normalized_props = props.dup - key = normalized_props.delete(:key) - ref = normalized_props.delete(:ref) - else - children = props - normalized_props = {} - key = nil - ref = nil - end - else - normalized_props = {} - key = nil - ref = nil - end - - if block_given? - pr = render_buffer - pr.push([]) - block_result = block.call - children = pr.pop - if Preact.is_renderable?(block_result) - children.push(block_result) - end - end - - normalized_props[:children] = children unless children.nil? - _create_vnode(type, normalized_props, key, ref, nil) - end - def _encode_entities(input) s = input.to_s return s unless ENCODED_ENTITIES.match?(s) - s.gsub(/&/, '&amp;').gsub(/</, '&lt;').gsub(/>/, '&gt;').gsub(/"/, '&quot;') # TODO performance maybe, maybe similar to new js way, need to measure # for (; i<str.length; i++) { # switch (str.charCodeAt(i)) { # case 60: ch = '&lt;'; break; # case 62: ch = '&gt;'; break; @@ -1365,21 +1300,13 @@ # } # if (i > start) out += str.slice(start, i); # out += ch; # start = i + 1; # } + s.gsub(/&/, '&amp;').gsub(/</, '&lt;').gsub(/>/, '&gt;').gsub(/"/, '&quot;') end - def _get_children(accumulator, children) - if children.is_a?(Array) - children.reduce(accumulator) { |accumulator, child| _get_children(accumulator, child) } - elsif children != nil && children != false - accumulator.push(children) - end - accumulator - end - def _style_obj_to_css(v) str = '' v.each do |prop, val| if val != nil && val != '' str << ' ' if !str.empty? @@ -1398,220 +1325,17 @@ pr = Preact.render_buffer pr[pr.length-1].push(create_element(element, props, nil, &block)) nil end - def _render_to_string(vnode, context, opts, inner = nil, is_svg_mode = false, select_value = nil) - return '' if !vnode || vnode == true - - if vnode.is_a?(String) # text nodes - return _encode_entities(vnode) - elsif vnode.is_a?(Numeric) # numeric nodes - return vnode.to_s - end - - if vnode.is_a?(Array) # array of nodes - rendered = '' - vnode.each_index do |i| - rendered << _render_to_string(vnode[i], context, opts, inner, is_svg_mode, select_value) - end - return rendered - end - - node_name = vnode.type - props = vnode.props - is_component = false - is_fragment = false - - # components - if !node_name.is_a?(String) && (node_name.ancestors.include?(Preact::Component) || is_fragment = node_name.ancestors.include?(Fragment)) - is_component = true - if opts && opts[:shallow] && (inner || opts[:render_root_component] == false) - node_name = node_name.class.to_s - elsif is_fragment - children = [] - _get_children(children, vnode.props[:children]) - return _render_to_string(children, context, opts, opts&.fetch(:shallow_high_order) != false, is_svg_mode, select_value) - else - rendered = nil - - vnode.__c = { - __v: vnode, - context: context, - props: vnode.props, - __d: true - } - - c = vnode.__c - # silently drop state updates - mark_as_dirty = proc { c[:__d] = true } - c[:set_state] = mark_as_dirty, - c[:force_update] = mark_as_dirty, - - # class-based components - cx_type = node_name.context_type - provider = cx_type && context[cx_type.context_id] - cctx = cx_type ? (provider ? provider.props[:value] : cx_type.value) : context - - # validate props - declared_props = node_name.instance_variable_get(:@declared_props) - if declared_props - declared_props.each do |prop, value| - props[prop] = value[:default] if value.key?(:default) && !props.key?(prop) - end - node_name.validate_props(props) if Isomorfeus.development? - end - - c = vnode.__c = node_name.new(props, cctx) - c.__v = vnode - # turn off stateful re-rendering: - c._dirty = c.__d = true - c.instance_variable_set(:@props, props) if c.props.nil? - c.instance_variable_set(:@state, {}) if c.state.nil? - - c._next_state = c.__s = c.state if c._next_state == nil && c.__s == nil - - c.instance_variable_set(:@context, cctx) - - if c.respond_to?(:get_derived_state_from_props) - c.instance_variable_set(:@state, {}.merge!(c.state, c.get_derived_state_from_props(c.props, c.state))) - end - - rendered = c.render - - context = {}.merge!(context, c.get_child_context) if c.respond_to?(:get_child_context) - - return _render_to_string(rendered, context, opts, opts&.fetch(:shallow_high_order) != false, is_svg_mode, select_value) - end - end - - # render JSX to HTML - node_name_s = node_name.to_s - s = "<#{node_name_s}" - prop_children = nil - html = nil - - if props - attrs = props.keys - - # allow sorting lexicographically for more determinism (useful for tests, such as via preact-jsx-chai) - attrs.sort! if opts && opts[:sort_attributes] == true - - attrs.each do |name| - v = props[name] - if name == :children - prop_children = v - next - end - - next if UNSAFE_NAME.match?(name.to_s) - next if !(opts && opts[:all_attributes]) && IGNORED_PROPS.include?(name) - - if name == :default_value - name = :value - elsif name == :class_name - next if props[:class] != nil - name = :class - elsif is_svg_mode && name.to_s.match?(/^xlink:?./) - name = name.to_s.downcase.gsub(/^xlink:?/, 'xlink:') - end - - if name == :html_for - next if props.key?(:for) - name = :for - end - - v = _style_obj_to_css(v) if name == :style && v && v.is_a?(Hash) - - # always use string values instead of booleans for aria attributes - # also see https://github.com/preactjs/preact/pull/2347/files - v = v.to_s if name.to_s.start_with?('aria') && (v == true || v == false) - - if name == :dangerouslySetInnerHTML - html = v && v[:__html] - elsif node_name == :textarea && name == :value - # <textarea value="a&b"> --> <textarea>a&amp;b</textarea> - prop_children = v - elsif name.to_s.start_with?('on_') - next - elsif v || v == 0 || v == '' && !v.is_a?(Proc) - if v == true || v == '' - v = name - # in non-xml mode, allow boolean attributes - if !opts || !opts[:xml] - s << " #{name}" - next - end - end - - if name == :value - if node_name == :select - select_value = v - next - elsif ( - # If we're looking at an <option> and it's the currently selected one - node_name == :option && - select_value == v && - # and the <option> doesn't already have a selected attribute on it - props[:selected] == nil - ) - s << ' selected' - end - end - s << " #{name}=\"#{_encode_entities(v)}\"" - end - end - end - - s << '>' - - raise "#{node_name_s} is not a valid HTML tag name in #{s}" if (UNSAFE_NAME.match?(node_name_s)) - - is_void = VOID_ELEMENTS.match?(node_name_s) - pieces = [] - - if html - s << html - elsif prop_children - children = [] - if _get_children(children, prop_children).size > 0 - last_was_text = false - - children.each do |child| - if child != nil && child != false - child_svg_mode = node_name == :svg ? true : ((node_name == :foreignObject) ? false : is_svg_mode) - ret = _render_to_string(child, context, opts, true, child_svg_mode, select_value) - - # Skip if we received an empty string - if ret - pieces.push(ret) - end - end - end - end - end - - if (pieces.length > 0) || html - s << pieces.join('') - elsif opts && opts[:xml] - return s.chop! << ' />' - end - - if is_void && !children && !html - s = s.sub(/>$/, ' />') - else - s << "</#{node_name}>" - end - - return s - end - - def render_to_string(vnode, context = nil, opts = nil) + def render_to_string(vnode, context = nil) _init_render context = {} unless context - _render_to_string(vnode, context, opts) + _render_to_string(vnode, context, false, nil) end end # RUBY_ENGINE end end -Preact._vnode_id = 0 +if RUBY_ENGINE == 'opal' + Preact._vnode_id = 0 +end