all files / src/hyperscript/ h.ts

92.65% Statements 63/68
78.85% Branches 41/52
100% Functions 8/8
91.23% Lines 52/57
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143    597× 597×   597× 597× 597×   597× 489×   489× 108× 108×   108× 106× 27×     597×                         597×   597×       597×   533×   533×     597×   597×   591×   591×   483×     1484×   1484×     1096× 611× 887×       611×     887×     485× 761× 485×   485×   485× 761×   761× 304× 457×   761×   761×     421×                                                                                
import { HtmlTagNames, SvgTagNames, VNode, VNodeProps } from '../types'
import { MostlyVNode, addSvgNamespace } from './VNode'
import { isPrimitive, isString } from '../helpers'
 
export const h: HyperscriptFn = function(): VNode {
  const tagName: string | ComponentFn = arguments[0] // required
  const childrenOrText: HyperscriptChildren = slice(2, arguments) // optional
 
  let props: VNodeProps = {}
  let children: Array<VNode> | undefined
  let text: string | undefined
 
  if (childrenOrText) {
    props = arguments[1] || {}
 
    if (isArrayLike(childrenOrText)) children = flattenArrayLike(childrenOrText) as Array<VNode>
    else Eif (isPrimitive(childrenOrText)) text = String(childrenOrText)
  } else Eif (arguments[1]) {
    const childrenOrTextOrProps = arguments[1]
 
    if (isArrayLike(childrenOrTextOrProps))
      children = flattenArrayLike(childrenOrTextOrProps) as Array<VNode>
    else if (isPrimitive(childrenOrTextOrProps)) text = String(childrenOrTextOrProps)
    else props = childrenOrTextOrProps || {}
  }
 
  Iif (typeof tagName === 'function') {
    const childVNodes = Array.isArray(children)
      ? children
      : text ? [ MostlyVNode.createText(text) ] : []
    const computedVNode = tagName(props, childVNodes)
 
    if (Array.isArray(computedVNode.children)) {
      computedVNode.children = sanitizeChildren(computedVNode.children, computedVNode)
    }
 
    return computedVNode
  }
 
  const isSvg = tagName === 'svg'
 
  const vNode = isSvg
    ? MostlyVNode.createSvg(tagName, props, undefined, text)
    : MostlyVNode.create(tagName, props, undefined, text)
 
  if (Array.isArray(children)) vNode.children = sanitizeChildren(children, vNode)
 
  if (isSvg) addSvgNamespace(vNode)
 
  return vNode
}
 
function slice<A>(from: number, arrLike: ArrayLike<VNode | string | number>): HyperscriptChildren {
  const arr = [] as Array<VNode | string | number>
 
  if (arrLike.length === 1) return arrLike[0]
 
  for (let i = from; i < arrLike.length; ++i) arr.push(arrLike[i])
 
  if (arr.length === 0) return null
 
  return arr
}
 
function isArrayLike<T>(x: any): x is ArrayLike<T> {
  const typeOf = typeof x
 
  return x && typeof x.length === 'number' && typeOf !== 'function' && typeOf !== 'string'
}
 
function flattenArrayLike<A>(arrayLike: ArrayLike<A | ArrayLike<A>>, arr: Array<A> = []): Array<A> {
  forEach(
    (x: A | ArrayLike<A>) => (isArrayLike(x) ? flattenArrayLike(x, arr) : arr.push(x)),
    arrayLike
  )
 
  return arr
}
 
function forEach<A>(fn: (value: A) => void, list: ArrayLike<A>): void {
  for (let i = 0; i < list.length; ++i) fn(list[i])
}
 
function sanitizeChildren(childrenOrText: Array<VNode>, parent: VNode): Array<VNode> {
  childrenOrText = childrenOrText
    .filter((x) => x !== null || x !== undefined) // remove possible null values
  const childCount: number = childrenOrText.length
 
  const children: Array<VNode> = Array(childCount)
 
  for (let i = 0; i < childCount; ++i) {
    const vNodeOrText = childrenOrText[i]
 
    if (isString(vNodeOrText) || typeof vNodeOrText === 'number')
      children[i] = MostlyVNode.createText(String(vNodeOrText))
    else children[i] = vNodeOrText
 
    if (parent.scope && !children[i].scope) children[i].scope = parent.scope
 
    children[i].parent = parent
  }
 
  return children
}
 
export type VNodeChildren = string | number | null | VNode
export type HyperscriptChildren =
  | VNodeChildren
  | ArrayLike<VNodeChildren>
  | ArrayLike<VNodeChildren | ArrayLike<VNodeChildren>>
  | ArrayLike<ArrayLike<VNodeChildren>>
 
export interface ComponentFn {
  (props: VNodeProps, children: Array<string | null | VNode>): VNode
}
 
export type ValidTagNames = HtmlTagNames | SvgTagNames | ComponentFn
 
export interface HyperscriptFn {
  (tagName: ValidTagNames): VNode
  (tagName: ValidTagNames, props: VNodeProps<any>): VNode
  (tagName: ValidTagNames, children: HyperscriptChildren): VNode
  (tagName: ValidTagNames, props: VNodeProps<any>, children: HyperscriptChildren): VNode
 
  <T extends Node, Props extends VNodeProps<Element> = VNodeProps<Element>>(
    tagName: ValidTagNames
  ): VNode<T, Props>
  <T extends Node, Props extends VNodeProps<Element> = VNodeProps<Element>>(
    tagName: ValidTagNames,
    props: Props
  ): VNode<T>
  <T extends Node, Props extends VNodeProps<Element> = VNodeProps<Element>>(
    tagName: ValidTagNames,
    children: HyperscriptChildren
  ): VNode<T, Props>
 
  <T extends Node, Props extends VNodeProps<Element> = VNodeProps<Element>>(
    tagName: ValidTagNames,
    props: Props,
    children: HyperscriptChildren
  ): VNode<T, Props>
}