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(/&/, '&').gsub(/</, '<').gsub(/>/, '>').gsub(/"/, '"')
# TODO performance maybe, maybe similar to new js way, need to measure
# for (; i<str.length; i++) {
# switch (str.charCodeAt(i)) {
# case 60: ch = '<'; break;
# case 62: ch = '>'; break;
@@ -1365,21 +1300,13 @@
# }
# if (i > start) out += str.slice(start, i);
# out += ch;
# start = i + 1;
# }
+ s.gsub(/&/, '&').gsub(/</, '<').gsub(/>/, '>').gsub(/"/, '"')
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&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