import { JSXNode, JSXElement, isJSXPrimitive } from '../jsx' import { intoObservable } from './observable' import { JSXAsync, JSXElementAsync } from './jsx' export const jsxAsyncToSync = (jsx: JSXAsync): Observable => { const jsx$ = intoObservable(jsx) return new Observable(observer => { let completeCount = 1 const complete = () => { completeCount -= 1 if (completeCount === 0) { unsubscribeChildren() observer.complete() } } let childSubscriptions: Subscription[] = [] const unsubscribeChildren = () => { childSubscriptions.forEach(subscription => subscription.unsubscribe()) childSubscriptions = [] } const subscription = jsx$.subscribe( node => { // Reset complete count to one and unsubscribe all of our children. completeCount = 1 unsubscribeChildren() if (isJSXPrimitive(node)) { observer.next(node) } else if (!node.children || node.children.length === 0) { observer.next(node as JSXElement) } else { const { elementName, attributes } = node // We don’t want to emit any values while we are still `map`ing over // the array. let ready = false // Map all of the children to void. `childrenSync` is not immutable. // Therefore whenever we call `observer.next` we need to make a // shallow copy. const childrenSync: JSXNode[] = node.children.map(() => {}) node.children.forEach((childAsync, i) => { completeCount += 1 childSubscriptions.push(jsxAsyncToSync(childAsync).subscribe( childSync => { // Update our child for the `i` position. childrenSync[i] = childSync // If we are not yet ready, return so we don’t send an // intermediate value to our observer. if (!ready) return observer.next({ elementName, attributes, children: arrayShallowCopy(childrenSync), }) }, // Propagate the error. error => observer.error(error), complete )) }) // Send an initial version. observer.next({ elementName, attributes, children: arrayShallowCopy(childrenSync), }) // Ok, we can send values now. ready = true } }, error => observer.error(error), complete ) return () => { subscription.unsubscribe() unsubscribeChildren() } }) } const arrayShallowCopy = (array: T[]): T[] => array.map(item => item)