keep replace replace keep keep keep keep keep

dom = diff(dom, parentDom, vnode, vnode, this.context, parentDom.ownerSVGElement!==undefined, null, mounts, this._ancestorComponent);

if (dom!=null && dom.parentNode!==parentDom) {

import { assign } from './util';
import { diff, commitRoot } from './diff/index';

/**
 * Base Component class. Provides `setState()` and `forceUpdate()`, which
 * trigger rendering
 * @param {object} props The initial component props
 * @param {object} context The initial context from parent components'
 * getChildContext
 */
export function Component(props, context) {
	this.props = props;
	this.context = context;
	// if (this.state==null) this.state = {};
	// this.state = {};
	// this._dirty = true;
	// this._renderCallbacks = []; // Only class components

	// Other properties that Component will have set later,
	// shown here as commented out for quick reference
	// this.base = null;
	// this._ancestorComponent = null; // Always set right after instantiation
	// this._vnode = null;
	// this._nextState = null; // Only class components
	// this._prevVNode = null;
	// this._processingException = null; // Always read, set only when handling error
	// this._constructor = null; // Only functional components, always set right after instantiation
}

/**
 * Update component state and schedule a re-render.
 * @param {object | ((s: object, p: object) => object)} update A hash of state
 * properties to update with new values or a function that given the current
 * state and props returns a new partial state
 * @param {() => void} [callback] A function to be called once component state is
 * updated
 */
Component.prototype.setState = function(update, callback) {

	// only clone state when copying to nextState the first time.
	let s = (this._nextState!==this.state && this._nextState) || (this._nextState = assign({}, this.state));

	// Needed for the devtools to check if state has changed after the tree
	// has been committed
	this._prevState = assign({}, s);

	// if update() mutates state in-place, skip the copy:
	if (typeof update!=='function' || (update = update(s, this.props))) {
		assign(s, update);
	}

	// Skip update if updater function returned null
	if (update==null) return;

	if (callback!=null) this._renderCallbacks.push(callback);

	this._force = false;
	enqueueRender(this);
};

/**
 * Immediately perform a synchronous re-render of the component
 * @param {() => void} [callback] A function to be called after component is
 * re-renderd
 */
Component.prototype.forceUpdate = function(callback) {
	if (this._parentDom!=null) {
		// Set render mode so that we can differantiate where the render request
		// is coming from. We need this because forceUpdate should never call
		// shouldComponentUpdate
		if (this._force==null) this._force = true;

		let mounts = [];
		diff(this._vnode._dom, this._parentDom, this._vnode, this._vnode, this.context, this._parentDom.ownerSVGElement!==undefined, true, null, mounts, this._ancestorComponent);
		commitRoot(mounts, this._vnode);

		// Reset mode to its initial value for the next render
		this._force = null;
	}
	if (callback!=null) callback();
};

/**
 * Accepts `props` and `state`, and returns a new Virtual DOM tree to build.
 * Virtual DOM is generally constructed via [JSX](http://jasonformat.com/wtf-is-jsx).
 * @param {object} props Props (eg: JSX attributes) received from parent
 * element/component
 * @param {object} state The component's current state
 * @param {object} context Context object, as returned by the nearest
 * ancestor's `getChildContext()`
 * @returns {import('./index').ComponentChildren | void}
 */
Component.prototype.render = function () {};

/**
 * The render queue
 * @type {Array<import('./internal').Component>}
 */
let q = [];

/**
 * Asynchronously schedule a callback
 * @type {(cb) => void}
 */
const defer = typeof Promise=='function' ? Promise.resolve().then.bind(Promise.resolve()) : setTimeout;

/*
 * The value of `Component.debounce` must asynchronously invoke the passed in callback. It is
 * important that contributors to Preact can consistenly reason about what calls to `setState`, etc.
 * do, and when their effects will be applied. See the links below for some further reading on designing
 * asynchronous APIs.
 * * [Designing APIs for Asynchrony](https://blog.izs.me/2013/08/designing-apis-for-asynchrony)
 * * [Callbacks synchronous and asynchronous](https://blog.ometer.com/2011/07/24/callbacks-synchronous-and-asynchronous/)
 */

/**
 * Enqueue a rerender of a component
 * @param {import('./internal').Component} c The component to rerender
 */
export function enqueueRender(c) {
	if (!c._dirty && (c._dirty = true) && q.push(c) === 1) {
		(Component.debounce || defer)(process);
	}
}

/** Flush the render queue by rerendering all queued components */
function process() {
	let p;
	while ((p=q.pop())) {
		if (p._dirty) p.forceUpdate();
	}
}


import { createElement, Component, options } from 'ceviche';

function getPrimes(max) {
	let sieve = [], i, j, primes = [];
	for (i = 2; i <= max; ++i) {
		if (!sieve[i]) {
			// i has not been marked -- it is prime
			primes.push(i);
			for (j = i << 1; j <= max; j += i) {
				sieve[j] = true;
			}
		}
	}
	return primes.join('');
}

function Foo(props) {
	return
{props.children}
;
}

function Bar() {
	getPrimes(10000);
	return (

			...yet another component

	);
}

function PrimeNumber(props) {
	// Slow down rendering of this component
	getPrimes(10);

	return (

			I'm a slow component

			{props.children}

	);
}

export default class ProfilerDemo extends Component {
	constructor() {
		super();
		this.onClick = this.onClick.bind(this);
		this.state = { counter: 0 };
	}

	componentDidMount() {
		options.beforeDiff = vnode => vnode.startTime = performance.now();
		options.afterDiff = vnode => vnode.endTime = performance.now();
	}

	componentWillUnmount() {
		delete options.beforeDiff;
		delete options.afterDiff;
	}

	onClick() {
		this.setState(prev => ({ counter: ++prev.counter }));
	}

	render() {
		return (

⚛ Preact

Devtools Profiler integration 🕒

				<Foo><PrimeNumber><Foo>I'm a fast component</Foo><Bar /></PrimeNumber></Foo>
				<Foo>I'm the fastest component   🎉</Foo>
				Counter: {this.state.counter}

				<button onClick={this.onClick}>Force re-render</button>

		);
	}
}


import { createElement as h, render, Component, Fragment } from '../../src/index';
import { setupScratch, teardown } from '../_util/helpers';

/** @jsx h */

describe('context', () => {
	let scratch;

	beforeEach(() => {
		scratch = setupScratch();
	});

	afterEach(() => {
		teardown(scratch);
	});

	it('should pass context to grandchildren', () => {
		const CONTEXT = { a: 'a' };
		const PROPS = { b: 'b' };
		// let inner;

		class Outer extends Component {
			getChildContext() {
				return CONTEXT;
			}
			render(props) {
				return
<Inner {...props} />
;
			}
		}
		sinon.spy(Outer.prototype, 'getChildContext');

		class Inner extends Component {
			// constructor() {
			// 	super();
			// 	inner = this;
			// }
			shouldComponentUpdate() { return true; }
			componentWillReceiveProps() {}
			componentWillUpdate() {}
			componentDidUpdate() {}
			render(props, state, context) {
				return
{ context && context.a }
;
			}
		}
		sinon.spy(Inner.prototype, 'shouldComponentUpdate');
		sinon.spy(Inner.prototype, 'componentWillReceiveProps');
		sinon.spy(Inner.prototype, 'componentWillUpdate');
		sinon.spy(Inner.prototype, 'componentDidUpdate');
		sinon.spy(Inner.prototype, 'render');

		render(<Outer />, scratch);

		expect(Outer.prototype.getChildContext).to.have.been.calledOnce;

		// initial render does not invoke anything but render():
		expect(Inner.prototype.render).to.have.been.calledWith({}, {}, CONTEXT);

		CONTEXT.foo = 'bar';
		render(<Outer {...PROPS} />, scratch);

		expect(Outer.prototype.getChildContext).to.have.been.calledTwice;

		expect(Inner.prototype.shouldComponentUpdate).to.have.been.calledOnce.and.calledWith(PROPS, {}, CONTEXT);
		expect(Inner.prototype.componentWillReceiveProps).to.have.been.calledWith(PROPS, CONTEXT);
		expect(Inner.prototype.componentWillUpdate).to.have.been.calledWith(PROPS, {}, CONTEXT);
		expect(Inner.prototype.componentDidUpdate).to.have.been.calledWith({}, {}, CONTEXT);
		expect(Inner.prototype.render).to.have.been.calledWith(PROPS, {}, CONTEXT);

		/* Future:
		 *  Newly created context objects are *not* currently cloned.
		 *  This test checks that they *are* cloned.
		 */
		// Inner.prototype.render.resetHistory();
		// CONTEXT.foo = 'baz';
		// inner.forceUpdate();
		// expect(Inner.prototype.render).to.have.been.calledWith(PROPS, {}, { a:'a', foo:'bar' });
	});

	it('should pass context to direct children', () => {
		const CONTEXT = { a: 'a' };
		const PROPS = { b: 'b' };

		class Outer extends Component {
			getChildContext() {
				return CONTEXT;
			}
			render(props) {
				return <Inner {...props} />;
			}
		}
		sinon.spy(Outer.prototype, 'getChildContext');

		class Inner extends Component {
			shouldComponentUpdate() { return true; }
			componentWillReceiveProps() {}
			componentWillUpdate() {}
			componentDidUpdate() {}
			render(props, state, context) {
				return
{ context && context.a }
;
			}
		}
		sinon.spy(Inner.prototype, 'shouldComponentUpdate');
		sinon.spy(Inner.prototype, 'componentWillReceiveProps');
		sinon.spy(Inner.prototype, 'componentWillUpdate');
		sinon.spy(Inner.prototype, 'componentDidUpdate');
		sinon.spy(Inner.prototype, 'render');

		render(<Outer />, scratch);

		expect(Outer.prototype.getChildContext).to.have.been.calledOnce;

		// initial render does not invoke anything but render():
		expect(Inner.prototype.render).to.have.been.calledWith({}, {}, CONTEXT);

		CONTEXT.foo = 'bar';
		render(<Outer {...PROPS} />, scratch);

		expect(Outer.prototype.getChildContext).to.have.been.calledTwice;

		expect(Inner.prototype.shouldComponentUpdate).to.have.been.calledOnce.and.calledWith(PROPS, {}, CONTEXT);
		expect(Inner.prototype.componentWillReceiveProps).to.have.been.calledWith(PROPS, CONTEXT);
		expect(Inner.prototype.componentWillUpdate).to.have.been.calledWith(PROPS, {}, CONTEXT);
		expect(Inner.prototype.componentDidUpdate).to.have.been.calledWith({}, {}, CONTEXT);
		expect(Inner.prototype.render).to.have.been.calledWith(PROPS, {}, CONTEXT);

		// make sure render() could make use of context.a
		expect(Inner.prototype.render).to.have.returned(sinon.match({ props: { children: 'a' } }));
	});

	it('should preserve existing context properties when creating child contexts', () => {
		let outerContext = { outer: true },
			innerContext = { inner: true };
		class Outer extends Component {
			getChildContext() {
				return { outerContext };
			}
			render() {
				return
<Inner />
;
			}
		}

		class Inner extends Component {
			getChildContext() {
				return { innerContext };
			}
			render() {
				return <InnerMost />;
			}
		}

		class InnerMost extends Component {
			render() {
				return test;
			}
		}

		sinon.spy(Inner.prototype, 'render');
		sinon.spy(InnerMost.prototype, 'render');

		render(<Outer />, scratch);

		expect(Inner.prototype.render).to.have.been.calledWith({}, {}, { outerContext });
		expect(InnerMost.prototype.render).to.have.been.calledWith({}, {}, { outerContext, innerContext });
	});

	it('should pass context through Fragments', () => {
		const context = { foo: 'bar' };

		const Foo = sinon.spy(() =>
);

		class Wrapper extends Component {
			getChildContext() {
				return context;
			}

			render() {
				return (
					<Fragment>
						<Foo />
						<Foo />
					</Fragment>
				);
			}
		}

		render(<Wrapper />, scratch);
		expect(Foo.args[0][1]).to.deep.equal(context);
	});
});


import { IS_NON_DIMENSIONAL } from '../constants';

/**
 * Diff the old and new properties of a VNode and apply changes to the DOM node
 * @param {import('../internal').PreactElement} dom The DOM node to apply
 * changes to
 * @param {object} newProps The new props
 * @param {object} oldProps The old props
 * @param {boolean} isSvg Whether or not this node is an SVG node
 */
export function diffProps(dom, newProps, oldProps, isSvg) {
	for (let i in newProps) {
		if (i!=='children' && i!=='key' && (!oldProps || oldProps[i]!=newProps[i])) {
			setProperty(dom, i, newProps[i], oldProps[i], isSvg);
		}
	}
	for (let i in oldProps) {
		if (i!=='children' && i!=='key' && (!newProps || !(i in newProps))) {
			setProperty(dom, i, null, oldProps[i], isSvg);
		}
	}
}

/**
 * Set a property value on a DOM node
 * @param {import('../internal').PreactElement} dom The DOM node to modify
 * @param {string} name The name of the property to set
 * @param {*} value The value to set the property to
 * @param {*} oldValue The old value the property had
 * @param {boolean} isSvg Whether or not this DOM node is an SVG node or not
 */
function setProperty(dom, name, value, oldValue, isSvg) {
	if (name==='class' || name==='className') name = isSvg ? 'class' : 'className';

	if (name==='style') {

		/* Possible golfing activities for setting styles:
		 *   - we could just drop String style values. They're not supported in other VDOM libs.
		 *   - assigning to .style sets .style.cssText - TODO: benchmark this, might not be worth the bytes.
		 *   - assigning also casts to String, and ignores invalid values. This means assigning an Object clears all styles.
		 */
		let s = dom.style;

		if (typeof value==='string') return s.cssText = value;
		if (typeof oldValue==='string') s.cssText = '';
		else {
			// remove values not in the new list
			for (let i in oldValue) {
				if (value==null || !(i in value)) s[i] = '';
			}
		}

		for (let i in value) {
			let v = value[i];
			if (oldValue==null || v!==oldValue[i]) {
				s[i] = typeof v==='number' && IS_NON_DIMENSIONAL.test(i)===false ? (v + 'px') : v;
			}
		}
	}
	else if (name==='dangerouslySetInnerHTML') {
		dom.innerHTML = value && value.__html || '';
	}
	// Benchmark for comparison: https://esbench.com/bench/574c954bdb965b9a00965ac6
	else if (name[0]==='o' && name[1]==='n') {
		let useCapture = name !== (name=name.replace(/Capture$/, ''));
		let nameLower = name.toLowerCase();
		name = (nameLower in dom ? nameLower : name).substring(2);

		if (value) {
			if (!oldValue) dom.addEventListener(name, eventProxy, useCapture);
		}
		else {
			dom.removeEventListener(name, eventProxy, useCapture);
		}
		(dom._listeners || (dom._listeners = {}))[name] = value;
	}
	else if (name!=='list' && !isSvg && (name in dom)) {
		dom[name] = value==null ? '' : value;
	}
	else if (value==null || value===false) {
		dom.removeAttribute(name);
	}
	else if (typeof value!=='function') {
		dom.setAttribute(name, value);
	}
}

/**
 * Proxy an event to hooked event handlers
 * @param {Event} e The event object from the browser
 * @private
 */
function eventProxy(e) {
	return this._listeners[e.type](e);
}


// TODO: Ideally be consistent with the oldParentVNode. We should only check for null or EMPTY_OBJ

let newChildren = getVNodeChildren(newParentVNode);

let oldChildren = oldParentVNode==null || oldParentVNode==EMPTY_OBJ ? EMPTY_ARR : getVNodeChildren(oldParentVNode);

keep keep keep replace replace keep keep keep keep

import { EMPTY_OBJ, EMPTY_ARR } from '../constants';
import { Component, enqueueRender } from '../component';
import { coerceToVNode, Fragment } from '../create-element';
import { diffChildren } from './children';
import { diffProps } from './props';
import { assign } from '../util';
import options from '../options';

/**
 * Diff two virtual nodes and apply proper changes to the DOM
 * @param {import('../internal').PreactElement | Text} dom The DOM element representing
 * the virtual nodes under diff
 * @param {import('../internal').PreactElement} parentDom The parent of the DOM element
 * @param {import('../internal').VNode | null} newVNode The new virtual node
 * @param {import('../internal').VNode | null} oldVNode The old virtual node
 * @param {object} context The current context object
 * @param {boolean} isSvg Whether or not this element is an SVG node
 * @param {boolean} append Whether or not to immediately append the new DOM
 * element after diffing
 * @param {Array<import('../internal').PreactElement>} excessDomChildren
 * @param {Array<import('../internal').Component>} mounts A list of newly
 * mounted components
 * @param {import('../internal').Component | null} ancestorComponent The direct
 * parent component
 */
export function diff(dom, parentDom, newVNode, oldVNode, context, isSvg, append, excessDomChildren, mounts, ancestorComponent) {

	// If the previous type doesn't match the new type we drop the whole subtree
	if (oldVNode==null || newVNode==null || oldVNode.type!==newVNode.type) {
		if (oldVNode!=null) unmount(oldVNode, ancestorComponent);
		if (newVNode==null) return null;
		dom = null;
		oldVNode = EMPTY_OBJ;
	}

	if (options.beforeDiff) options.beforeDiff(newVNode);

	let c, p, isNew = false, oldProps, oldState, oldContext,
		newType = newVNode.type;

	/** @type {import('../internal').Component | null} */
	let clearProcessingException;

	try {
		// const isOldVNodeFragment = oldVNode.type===Fragment;

		// outer: if (isOldVNodeFragment || newType===Fragment) {
		outer: if (oldVNode.type===Fragment || newType===Fragment) {
			// const oldVNodeChildren = oldVNode===EMPTY_OBJ ? EMPTY_ARR : !isOldVNodeFragment ? [oldVNode] : getVNodeChildren(oldVNode);

			// let childDom = oldVNode._dom;
			// if (excessDomChildren!=null) {
			// 	for (let i = 0; i < excessDomChildren.length; i++) {
			// 		if (excessDomChildren[i]!=null) {
			// 			childDom = excessDomChildren[i];
			// 			break;
			// 		}
			// 	}
			// }

			// diffChildren(parentDom, getVNodeChildren(newVNode), oldVNodeChildren, context, isSvg, excessDomChildren, mounts, c, newVNode, childDom);
			// diffChildren(parentDom, getVNodeChildren(newVNode), oldVNodeChildren, context, isSvg, excessDomChildren, mounts, c, newVNode);
			diffChildren(parentDom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts, c);

			// 3133 B
			if (newVNode._children.length) {
				dom = newVNode._children[0]._dom;
				newVNode._lastDomChild = newVNode._children[newVNode._children.length - 1]._dom;
			}

			// 3133 B
			// dom = newVNode._children.length ? newVNode._children[0]._dom : null;
			// newVNode._lastDomChild = newVNode._children.length ? newVNode._children[newVNode._children.length - 1]._dom : null;

			// 3136 B
			// p = newVNode._children;
			// dom = p.length ? p[0]._dom : null;
			// newVNode._lastDomChild = p.length ? p[p.length - 1]._dom : null;

			// 3135 B
			// if ((p = newVNode._children).length) {
			// 	dom = p[0]._dom;
			// 	newVNode._lastDomChild = p[p.length - 1]._dom;
			// }
		}
		else if (typeof newType==='function') {

			// Get component and set it to `c`
			if (oldVNode._component) {
				c = newVNode._component = oldVNode._component;
				clearProcessingException = c._processingException;
			}
			else {
				isNew = true;

				// Instantiate the new component
				if (newType.prototype && newType.prototype.render) {
					newVNode._component = c = new newType(newVNode.props, context); // eslint-disable-line new-cap
				}
				else {
					newVNode._component = c = new Component(newVNode.props, context);
					c._constructor = newType;
					c.render = doRender;
				}
				c._ancestorComponent = ancestorComponent;

				c.props = newVNode.props;
				if (!c.state) c.state = {};
				c.context = context;
				c._dirty = true;
				c._renderCallbacks = [];
			}

			c._vnode = newVNode;

			// Invoke getDerivedStateFromProps
			let s = c._nextState || c.state;
			if (newType.getDerivedStateFromProps!=null) {
				oldState = assign({}, c.state);
				if (s===c.state) s = assign({}, s);
				assign(s, newType.getDerivedStateFromProps(newVNode.props, s));
			}

			// Invoke pre-render lifecycle methods
			if (isNew) {
				if (newType.getDerivedStateFromProps==null && c.componentWillMount!=null) c.componentWillMount();
				if (c.componentDidMount!=null) mounts.push(c);
			}
			else {
				if (!c._force && c.shouldComponentUpdate!=null && c.shouldComponentUpdate(newVNode.props, s, context)===false) {
					c._dirty = false;
					break outer;
				}
				if (newType.getDerivedStateFromProps==null && c._force==null && c.componentWillReceiveProps!=null) {
					c.componentWillReceiveProps(newVNode.props, context);
				}

				if (c.componentWillUpdate!=null) {
					c.componentWillUpdate(newVNode.props, s, context);
				}
			}

			oldProps = c.props;
			if (!oldState) oldState = c.state;

			oldContext = c.context = context;
			c.props = newVNode.props;
			c.state = s;

			if (options.beforeRender) options.beforeRender(newVNode);

			let prev = c._prevVNode;
			let vnode = c._prevVNode = coerceToVNode(c.render(c.props, c.state, c.context));
			c._dirty = false;

			if (c.getChildContext!=null) {
				context = assign(assign({}, context), c.getChildContext());
			}

			if (!isNew && c.getSnapshotBeforeUpdate!=null) {
				oldContext = c.getSnapshotBeforeUpdate(oldProps, oldState);
			}

			c.base = dom = diff(dom, parentDom, vnode, prev, context, isSvg, append, excessDomChildren, mounts, c);

			if (vnode!=null) {
				newVNode._lastDomChild = vnode._lastDomChild;
			}

			c._parentDom = parentDom;

			if (newVNode.ref) applyRef(newVNode.ref, c, ancestorComponent);
		}
		else {
			dom = diffElementNodes(dom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts, ancestorComponent);

			if (newVNode.ref && (oldVNode.ref !== newVNode.ref)) {
				applyRef(newVNode.ref, dom, ancestorComponent);
			}
		}

		// TODO: Look to remove...
		if (parentDom && append!==false && dom!=null && dom.parentNode!==parentDom) {
			parentDom.appendChild(dom);
		}

		newVNode._dom = dom;

		if (c!=null) {
			while (p=c._renderCallbacks.pop()) p();

			// Don't call componentDidUpdate on mount or when we bailed out via
			// `shouldComponentUpdate`
			if (!isNew && oldProps!=null && c.componentDidUpdate!=null) {
				c.componentDidUpdate(oldProps, oldState, oldContext);
			}
		}

		if (clearProcessingException) {
			c._processingException = null;
		}

		if (options.afterDiff) options.afterDiff(newVNode);
	}
	catch (e) {
		catchErrorInComponent(e, ancestorComponent);
	}

	return dom;
}

export function commitRoot(mounts, root) {
	let c;
	while ((c = mounts.pop())) {
		try {
			c.componentDidMount();
		}
		catch (e) {
			catchErrorInComponent(e, c._ancestorComponent);
		}
	}

	if (options.commitRoot) options.commitRoot(root);
}

/**
 * Diff two virtual nodes representing DOM element
 * @param {import('../internal').PreactElement} dom The DOM element representing
 * the virtual nodes being diffed
 * @param {import('../internal').VNode} newVNode The new virtual node
 * @param {import('../internal').VNode} oldVNode The old virtual node
 * @param {object} context The current context object
 * @param {boolean} isSvg Whether or not this DOM node is an SVG node
 * @param {*} excessDomChildren
 * @param {Array<import('../internal').Component>} mounts An array of newly
 * mounted components
 * @param {import('../internal').Component} ancestorComponent The parent
 * component to the ones being diffed
 * @returns {import('../internal').PreactElement}
 */
function diffElementNodes(dom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts, ancestorComponent) {
	let d = dom;

	// Tracks entering and exiting SVG namespace when descending through the tree.
	isSvg = isSvg ? newVNode.type !== 'foreignObject' : newVNode.type === 'svg';

	if (dom==null && excessDomChildren!=null) {
		for (let i=0; i<excessDomChildren.length; i++) {
			const child = excessDomChildren[i];
			if (child!=null && (newVNode.type===null ? child.nodeType===3 : child.localName===newVNode.type)) {
				dom = child;
				excessDomChildren[i] = null;
				break;
			}
		}
	}

	if (dom==null) {
		dom = newVNode.type===null ? document.createTextNode(newVNode.text) : isSvg ? document.createElementNS('http://www.w3.org/2000/svg', newVNode.type) : document.createElement(newVNode.type);

		// we created a new parent, so none of the previously attached children can be reused:
		excessDomChildren = null;
	}
	newVNode._dom = dom;

	if (newVNode.type===null) {
		if (dom===d && newVNode.text!==oldVNode.text) {
			dom.data = newVNode.text;
		}
	}
	else {
		if (excessDomChildren!=null && dom.childNodes!=null) {
			excessDomChildren = EMPTY_ARR.slice.call(dom.childNodes);
		}
		if (newVNode!==oldVNode) {
			let oldProps = oldVNode.props;
			// if we're hydrating, use the element's attributes as its current props:
			if (oldProps==null) {
				oldProps = {};
				if (excessDomChildren!=null) {
					for (let i=0; i<dom.attributes.length; i++) {
						oldProps[dom.attributes[i].name] = dom.attributes[i].value;
					}
				}
			}
			diffProps(dom, newVNode.props, oldProps, isSvg);
		}

		// diffChildren(dom, getVNodeChildren(newVNode), oldVNode==EMPTY_OBJ ? EMPTY_ARR : getVNodeChildren(oldVNode), context, isSvg, excessDomChildren, mounts, ancestorComponent, newVNode);
		diffChildren(dom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts, ancestorComponent);
	}

	return dom;
}

/**
 * Invoke or update a ref, depending on whether it is a function or object ref.
 * @param {object|function} [ref=null]
 * @param {any} [value]
 */
export function applyRef(ref, value, ancestorComponent) {
	try {
		if (typeof ref=='function') ref(value);
		else ref.current = value;
	}
	catch (e) {
		catchErrorInComponent(e, ancestorComponent);
	}
}

/**
 * Unmount a virtual node from the tree and apply DOM changes
 * @param {import('../internal').VNode} vnode The virtual node to unmount
 * @param {import('../internal').Component} ancestorComponent The parent
 * component to this virtual node
 */
export function unmount(vnode, ancestorComponent) {
	let r;
	if (options.beforeUnmount) options.beforeUnmount(vnode);

	if (r = vnode.ref) {
		applyRef(r, null, ancestorComponent);
	}

	if ((r = vnode._dom)!=null) r.remove();

	vnode._dom = vnode._lastDomChild = null;

	if ((r = vnode._component)!=null) {
		if (r.componentWillUnmount) {
			try {
				r.componentWillUnmount();
			}
			catch (e) {
				catchErrorInComponent(e, ancestorComponent);
			}
		}

		r.base = r._parentDom = null;
		if (r = r._prevVNode) unmount(r, ancestorComponent);
	}
	else if (r = vnode._children) {
		for (let i = 0; i < r.length; i++) {
			unmount(r[i], ancestorComponent);
		}
	}
}

/** The `.render()` method for a PFC backing instance. */
function doRender(props, state, context) {
	return this._constructor(props, context);
}

/**
 * Find the closest error boundary to a thrown error and call it
 * @param {object} error The thrown value
 * @param {import('../internal').Component} component The first ancestor
 * component check for error boundary behaviors
 */
function catchErrorInComponent(error, component) {
	for (; component; component = component._ancestorComponent) {
		if (!component._processingException) {
			try {
				if (component.constructor.getDerivedStateFromError!=null) {
					component.setState(component.constructor.getDerivedStateFromError(error));
				}
				else if (component.componentDidCatch!=null) {
					component.componentDidCatch(error);
				}
				else {
					continue;
				}
				return enqueueRender(component._processingException = component);
			}
			catch (e) {
				error = e;
			}
		}
	}
	throw error;
}


import { createElement as h, render, Component, Fragment } from '../../src/index';
import { setupScratch, teardown, setupRerender } from '../_util/helpers';
/* eslint-disable react/jsx-boolean-value */

/** @jsx h */
describe('focus', () => {

	/** @type {HTMLDivElement} */
	let scratch;

	/** @type {() => void} */
	let rerender;

	beforeEach(() => {
		scratch = setupScratch();
		rerender = setupRerender();
	});

	afterEach(() => {
		teardown(scratch);
	});

	it('should not lose focus', () => {
		function Foo({ condition }) {
			return (

					{condition ? <input /> : fooo}
					{condition ? fooo : <input />}

			);
		}

		render(<Foo condition={true} />, scratch);
		let input = scratch.querySelector('input');

		input.focus();
		expect(document.activeElement).to.equal(input);

		render(<Foo condition={false} />, scratch);
		expect(document.activeElement).to.equal(input);
	});

	it('should keep focus in Fragments', () => {

		/** @type {HTMLInputElement} */
		let input;
		let updateState;

		class App extends Component {
			constructor() {
				super();
				this.state = { active: false };
				updateState = () => this.setState(prev => ({ active: !prev.active }));
			}

			render() {
				return (

Heading

						{!this.state.active ? (
							<Fragment>
								foobar
								<Fragment>
									Hello World

yo

								</Fragment>
								<input type="text" ref={i => input = i} />
							</Fragment>
						) : (
							<Fragment>
								<Fragment>
									Hello World

yo

								</Fragment>
								foobar
								<input type="text" ref={i => input = i} />
							</Fragment>
						)}

				);
			}
		}

		render(<App />, scratch);

		input.focus();
		updateState();

		expect(document.activeElement).to.equal(input, 'Before rerender');
		rerender();

		expect(document.activeElement).to.equal(input, 'After rerender');
	});

	it('should keep text selection', () => {

		/** @type {HTMLInputElement} */
		let input;
		let updateState;

		class App extends Component {
			constructor() {
				super();
				this.state = { active: false };
				updateState = () => this.setState(prev => ({ active: !prev.active }));
			}

			render() {
				return (

Heading

						{!this.state.active ? (
							<Fragment>
								foobar
								<Fragment>
									Hello World

yo

								</Fragment>
								<input type="text" ref={i => input = i} value="foobar" />
							</Fragment>
						) : (
							<Fragment>
								<Fragment>
									Hello World

yo

								</Fragment>
								foobar
								<input type="text" ref={i => input = i} value="foobar" />
							</Fragment>
						)}

				);
			}
		}

		render(<App />, scratch);

		input.focus();
		input.setSelectionRange(2, 5);
		updateState();

		expect(document.activeElement).to.equal(input, 'Before rerender');
		rerender();

		expect(input.selectionStart).to.equal(2);
		expect(input.selectionEnd).to.equal(5);
		expect(document.activeElement).to.equal(input, 'After rerender');
	});
});


// function createRoot(title) {
// 	let div = document.createElement('div');
// 	let h2 = document.createElement('h2');
// 	h2.textContent = title;
// 	div.appendChild(h2);
// 	document.body.appendChild(div);
// 	return div;
// }

/*
function logCall(obj, method, name) {
	let old = obj[method];
	obj[method] = function(...args) {
		console.log(`<${this.localName}>.`+(name || `${method}(${args})`));
		return old.apply(this, args);
	};
}

logCall(HTMLElement.prototype, 'appendChild');
logCall(HTMLElement.prototype, 'removeChild');
logCall(HTMLElement.prototype, 'insertBefore');
logCall(HTMLElement.prototype, 'replaceChild');
logCall(HTMLElement.prototype, 'setAttribute');
logCall(HTMLElement.prototype, 'removeAttribute');
let d = Object.getOwnPropertyDescriptor(Node.prototype, 'nodeValue');
Object.defineProperty(Text.prototype, 'nodeValue', {
	get() {
		let value = d.get.call(this);
		console.log('get Text#nodeValue: ', value);
		return value;
	},
	set(v) {
		console.log('set Text#nodeValue', v);
		return d.set.call(this, v);
	}
});

render((

This is a test.

		<Foo />
		<time>...</time>

), createRoot('Stateful component update demo:'));

class Foo extends Component {
	componentDidMount() {
		console.log('mounted');
		this.timer = setInterval( () => {
			this.setState({ time: Date.now() });
		}, 5000);
	}
	componentWillUnmount() {
		clearInterval(this.timer);
	}
	render(props, state, context) {
		// console.log('rendering', props, state, context);
		return <time>test: {state.time}</time>
	}
}

render((

This is a test.

		<Foo />
		<time>...</time>

), createRoot('Stateful component update demo:'));

let items = [];
let count = 0;
let three = createRoot('Top-level render demo:');

setInterval( () => {
	if (count++ %20 < 10 ) {
		items.push(
item #{items.length}
);
	}
	else {
		items.shift();
	}

	render((

This is a test.

			<time>{Date.now()}</time>

{items}

	), three);
}, 5000);

// Mount the top-level component to the DOM:
render(, document.body);
*/


!function() {
    'use strict';
    function h(nodeName, attributes) {
        var lastSimple, child, simple, i, children = EMPTY_CHILDREN;
        for (i = arguments.length; i-- > 2; ) stack.push(arguments[i]);
        if (attributes && null != attributes.children) {
            if (!stack.length) stack.push(attributes.children);
            delete attributes.children;
        }
        while (stack.length) if ((child = stack.pop()) && void 0 !== child.pop) for (i = child.length; i--; ) stack.push(child[i]); else {
            if ('boolean' == typeof child) child = null;
            if (simple = 'function' != typeof nodeName) if (null == child) child = ''; else if ('number' == typeof child) child = String(child); else if ('string' != typeof child) simple = !1;
            if (simple && lastSimple) children[children.length - 1] += child; else if (children === EMPTY_CHILDREN) children = [ child ]; else children.push(child);
            lastSimple = simple;
        }
        var p = new VNode();
        p.nodeName = nodeName;
        p.children = children;
        p.attributes = null == attributes ? void 0 : attributes;
        p.key = null == attributes ? void 0 : attributes.key;
        if (void 0 !== options.vnode) options.vnode(p);
        return p;
    }
    function extend(obj, props) {
        for (var i in props) obj[i] = props[i];
        return obj;
    }
    function applyRef(ref, value) {
        if ('function' == typeof ref) ref(value); else if (null != ref) ref.current = value;
    }
    function cloneElement(vnode, props) {
        return h(vnode.nodeName, extend(extend({}, vnode.attributes), props), arguments.length > 2 ? [].slice.call(arguments, 2) : vnode.children);
    }
    function enqueueRender(component) {
        if (!component.__d && (component.__d = !0) && 1 == items.push(component)) (options.debounceRendering || defer)(rerender);
    }
    function rerender() {
        var p;
        while (p = items.pop()) if (p.__d) renderComponent(p);
    }
    function isSameNodeType(node, vnode, hydrating) {
        if ('string' == typeof vnode || 'number' == typeof vnode) return void 0 !== node.splitText;
        if ('string' == typeof vnode.nodeName) return !node._componentConstructor && isNamedNode(node, vnode.nodeName); else return hydrating || node._componentConstructor === vnode.nodeName;
    }
    function isNamedNode(node, nodeName) {
        return node.__n === nodeName || node.nodeName.toLowerCase() === nodeName.toLowerCase();
    }
    function getNodeProps(vnode) {
        var props = extend({}, vnode.attributes);
        props.children = vnode.children;
        var defaultProps = vnode.nodeName.defaultProps;
        if (void 0 !== defaultProps) for (var i in defaultProps) if (void 0 === props[i]) props[i] = defaultProps[i];
        return props;
    }
    function createNode(nodeName, isSvg) {
        var node = isSvg ? document.createElementNS('http://www.w3.org/2000/svg', nodeName) : document.createElement(nodeName);
        node.__n = nodeName;
        return node;
    }
    function removeNode(node) {
        var parentNode = node.parentNode;
        if (parentNode) parentNode.removeChild(node);
    }
    function setAccessor(node, name, old, value, isSvg) {
        if ('className' === name) name = 'class';
        if ('key' === name) ; else if ('ref' === name) {
            applyRef(old, null);
            applyRef(value, node);
        } else if ('class' === name && !isSvg) node.className = value || ''; else if ('style' === name) {
            if (!value || 'string' == typeof value || 'string' == typeof old) node.style.cssText = value || '';
            if (value && 'object' == typeof value) {
                if ('string' != typeof old) for (var i in old) if (!(i in value)) node.style[i] = '';
                for (var i in value) node.style[i] = 'number' == typeof value[i] && !1 === IS_NON_DIMENSIONAL.test(i) ? value[i] + 'px' : value[i];
            }
        } else if ('dangerouslySetInnerHTML' === name) {
            if (value) node.innerHTML = value.__html || '';
        } else if ('o' == name[0] && 'n' == name[1]) {
            var useCapture = name !== (name = name.replace(/Capture$/, ''));
            name = name.toLowerCase().substring(2);
            if (value) {
                if (!old) node.addEventListener(name, eventProxy, useCapture);
            } else node.removeEventListener(name, eventProxy, useCapture);
            (node.__l || (node.__l = {}))[name] = value;
        } else if ('list' !== name && 'type' !== name && !isSvg && name in node) {
            try {
                node[name] = null == value ? '' : value;
            } catch (e) {}
            if ((null == value || !1 === value) && 'spellcheck' != name) node.removeAttribute(name);
        } else {
            var ns = isSvg && name !== (name = name.replace(/^xlink:?/, ''));
            if (null == value || !1 === value) if (ns) node.removeAttributeNS('http://www.w3.org/1999/xlink', name.toLowerCase()); else node.removeAttribute(name); else if ('function' != typeof value) if (ns) node.setAttributeNS('http://www.w3.org/1999/xlink', name.toLowerCase(), value); else node.setAttribute(name, value);
        }
    }
    function eventProxy(e) {
        return this.__l[e.type](options.event && options.event(e) || e);
    }
    function flushMounts() {
        var c;
        while (c = mounts.pop()) {
            if (options.afterMount) options.afterMount(c);
            if (c.componentDidMount) c.componentDidMount();
        }
    }
    function diff(dom, vnode, context, mountAll, parent, componentRoot) {
        if (!diffLevel++) {
            isSvgMode = null != parent && void 0 !== parent.ownerSVGElement;
            hydrating = null != dom && !('__preactattr_' in dom);
        }
        var ret = idiff(dom, vnode, context, mountAll, componentRoot);
        if (parent && ret.parentNode !== parent) parent.appendChild(ret);
        if (!--diffLevel) {
            hydrating = !1;
            if (!componentRoot) flushMounts();
        }
        return ret;
    }
    function idiff(dom, vnode, context, mountAll, componentRoot) {
        var out = dom, prevSvgMode = isSvgMode;
        if (null == vnode || 'boolean' == typeof vnode) vnode = '';
        if ('string' == typeof vnode || 'number' == typeof vnode) {
            if (dom && void 0 !== dom.splitText && dom.parentNode && (!dom._component || componentRoot)) {
                if (dom.nodeValue != vnode) dom.nodeValue = vnode;
            } else {
                out = document.createTextNode(vnode);
                if (dom) {
                    if (dom.parentNode) dom.parentNode.replaceChild(out, dom);
                    recollectNodeTree(dom, !0);
                }
            }
            out.__preactattr_ = !0;
            return out;
        }
        var vnodeName = vnode.nodeName;
        if ('function' == typeof vnodeName) return buildComponentFromVNode(dom, vnode, context, mountAll);
        isSvgMode = 'svg' === vnodeName ? !0 : 'foreignObject' === vnodeName ? !1 : isSvgMode;
        vnodeName = String(vnodeName);
        if (!dom || !isNamedNode(dom, vnodeName)) {
            out = createNode(vnodeName, isSvgMode);
            if (dom) {
                while (dom.firstChild) out.appendChild(dom.firstChild);
                if (dom.parentNode) dom.parentNode.replaceChild(out, dom);
                recollectNodeTree(dom, !0);
            }
        }
        var fc = out.firstChild, props = out.__preactattr_, vchildren = vnode.children;
        if (null == props) {
            props = out.__preactattr_ = {};
            for (var a = out.attributes, i = a.length; i--; ) props[a[i].name] = a[i].value;
        }
        if (!hydrating && vchildren && 1 === vchildren.length && 'string' == typeof vchildren[0] && null != fc && void 0 !== fc.splitText && null == fc.nextSibling) {
            if (fc.nodeValue != vchildren[0]) fc.nodeValue = vchildren[0];
        } else if (vchildren && vchildren.length || null != fc) innerDiffNode(out, vchildren, context, mountAll, hydrating || null != props.dangerouslySetInnerHTML);
        diffAttributes(out, vnode.attributes, props);
        isSvgMode = prevSvgMode;
        return out;
    }
    function innerDiffNode(dom, vchildren, context, mountAll, isHydrating) {
        var j, c, f, vchild, child, originalChildren = dom.childNodes, children = [], keyed = {}, keyedLen = 0, min = 0, len = originalChildren.length, childrenLen = 0, vlen = vchildren ? vchildren.length : 0;
        if (0 !== len) for (var i = 0; i < len; i++) {
            var _child = originalChildren[i], props = _child.__preactattr_, key = vlen && props ? _child._component ? _child._component.__k : props.key : null;
            if (null != key) {
                keyedLen++;
                keyed[key] = _child;
            } else if (props || (void 0 !== _child.splitText ? isHydrating ? _child.nodeValue.trim() : !0 : isHydrating)) children[childrenLen++] = _child;
        }
        if (0 !== vlen) for (var i = 0; i < vlen; i++) {
            vchild = vchildren[i];
            child = null;
            var key = vchild.key;
            if (null != key) {
                if (keyedLen && void 0 !== keyed[key]) {
                    child = keyed[key];
                    keyed[key] = void 0;
                    keyedLen--;
                }
            } else if (min < childrenLen) for (j = min; j < childrenLen; j++) if (void 0 !== children[j] && isSameNodeType(c = children[j], vchild, isHydrating)) {
                child = c;
                children[j] = void 0;
                if (j === childrenLen - 1) childrenLen--;
                if (j === min) min++;
                break;
            }
            child = idiff(child, vchild, context, mountAll);
            f = originalChildren[i];
            if (child && child !== dom && child !== f) if (null == f) dom.appendChild(child); else if (child === f.nextSibling) removeNode(f); else dom.insertBefore(child, f);
        }
        if (keyedLen) for (var i in keyed) if (void 0 !== keyed[i]) recollectNodeTree(keyed[i], !1);
        while (min <= childrenLen) if (void 0 !== (child = children[childrenLen--])) recollectNodeTree(child, !1);
    }
    function recollectNodeTree(node, unmountOnly) {
        var component = node._component;
        if (component) unmountComponent(component); else {
            if (null != node.__preactattr_) applyRef(node.__preactattr_.ref, null);
            if (!1 === unmountOnly || null == node.__preactattr_) removeNode(node);
            removeChildren(node);
        }
    }
    function removeChildren(node) {
        node = node.lastChild;
        while (node) {
            var next = node.previousSibling;
            recollectNodeTree(node, !0);
            node = next;
        }
    }
    function diffAttributes(dom, attrs, old) {
        var name;
        for (name in old) if ((!attrs || null == attrs[name]) && null != old[name]) setAccessor(dom, name, old[name], old[name] = void 0, isSvgMode);
        for (name in attrs) if (!('children' === name || 'innerHTML' === name || name in old && attrs[name] === ('value' === name || 'checked' === name ? dom[name] : old[name]))) setAccessor(dom, name, old[name], old[name] = attrs[name], isSvgMode);
    }
    function createComponent(Ctor, props, context) {
        var inst, i = recyclerComponents.length;
        if (Ctor.prototype && Ctor.prototype.render) {
            inst = new Ctor(props, context);
            Component.call(inst, props, context);
        } else {
            inst = new Component(props, context);
            inst.constructor = Ctor;
            inst.render = doRender;
        }
        while (i--) if (recyclerComponents[i].constructor === Ctor) {
            inst.__b = recyclerComponents[i].__b;
            recyclerComponents.splice(i, 1);
            return inst;
        }
        return inst;
    }
    function doRender(props, state, context) {
        return this.constructor(props, context);
    }
    function setComponentProps(component, props, renderMode, context, mountAll) {
        if (!component.__x) {
            component.__x = !0;
            component.__r = props.ref;
            component.__k = props.key;
            delete props.ref;
            delete props.key;
            if (void 0 === component.constructor.getDerivedStateFromProps) if (!component.base || mountAll) {
                if (component.componentWillMount) component.componentWillMount();
            } else if (component.componentWillReceiveProps) component.componentWillReceiveProps(props, context);
            if (context && context !== component.context) {
                if (!component.__c) component.__c = component.context;
                component.context = context;
            }
            if (!component.__p) component.__p = component.props;
            component.props = props;
            component.__x = !1;
            if (0 !== renderMode) if (1 === renderMode || !1 !== options.syncComponentUpdates || !component.base) renderComponent(component, 1, mountAll); else enqueueRender(component);
            applyRef(component.__r, component);
        }
    }
    function renderComponent(component, renderMode, mountAll, isChild) {
        if (!component.__x) {
            var rendered, inst, cbase, props = component.props, state = component.state, context = component.context, previousProps = component.__p || props, previousState = component.__s || state, previousContext = component.__c || context, isUpdate = component.base, nextBase = component.__b, initialBase = isUpdate || nextBase, initialChildComponent = component._component, skip = !1, snapshot = previousContext;
            if (component.constructor.getDerivedStateFromProps) {
                state = extend(extend({}, state), component.constructor.getDerivedStateFromProps(props, state));
                component.state = state;
            }
            if (isUpdate) {
                component.props = previousProps;
                component.state = previousState;
                component.context = previousContext;
                if (2 !== renderMode && component.shouldComponentUpdate && !1 === component.shouldComponentUpdate(props, state, context)) skip = !0; else if (component.componentWillUpdate) component.componentWillUpdate(props, state, context);
                component.props = props;
                component.state = state;
                component.context = context;
            }
            component.__p = component.__s = component.__c = component.__b = null;
            component.__d = !1;
            if (!skip) {
                rendered = component.render(props, state, context);
                if (component.getChildContext) context = extend(extend({}, context), component.getChildContext());
                if (isUpdate && component.getSnapshotBeforeUpdate) snapshot = component.getSnapshotBeforeUpdate(previousProps, previousState);
                var toUnmount, base, childComponent = rendered && rendered.nodeName;
                if ('function' == typeof childComponent) {
                    var childProps = getNodeProps(rendered);
                    inst = initialChildComponent;
                    if (inst && inst.constructor === childComponent && childProps.key == inst.__k) setComponentProps(inst, childProps, 1, context, !1); else {
                        toUnmount = inst;
                        component._component = inst = createComponent(childComponent, childProps, context);
                        inst.__b = inst.__b || nextBase;
                        inst.__u = component;
                        setComponentProps(inst, childProps, 0, context, !1);
                        renderComponent(inst, 1, mountAll, !0);
                    }
                    base = inst.base;
                } else {
                    cbase = initialBase;
                    toUnmount = initialChildComponent;
                    if (toUnmount) cbase = component._component = null;
                    if (initialBase || 1 === renderMode) {
                        if (cbase) cbase._component = null;
                        base = diff(cbase, rendered, context, mountAll || !isUpdate, initialBase && initialBase.parentNode, !0);
                    }
                }
                if (initialBase && base !== initialBase && inst !== initialChildComponent) {
                    var baseParent = initialBase.parentNode;
                    if (baseParent && base !== baseParent) {
                        baseParent.replaceChild(base, initialBase);
                        if (!toUnmount) {
                            initialBase._component = null;
                            recollectNodeTree(initialBase, !1);
                        }
                    }
                }
                if (toUnmount) unmountComponent(toUnmount);
                component.base = base;
                if (base && !isChild) {
                    var componentRef = component, t = component;
                    while (t = t.__u) (componentRef = t).base = base;
                    base._component = componentRef;
                    base._componentConstructor = componentRef.constructor;
                }
            }
            if (!isUpdate || mountAll) mounts.unshift(component); else if (!skip) {
                if (component.componentDidUpdate) component.componentDidUpdate(previousProps, previousState, snapshot);
                if (options.afterUpdate) options.afterUpdate(component);
            }
            while (component.__h.length) component.__h.pop().call(component);
            if (!diffLevel && !isChild) flushMounts();
        }
    }
    function buildComponentFromVNode(dom, vnode, context, mountAll) {
        var c = dom && dom._component, originalComponent = c, oldDom = dom, isDirectOwner = c && dom._componentConstructor === vnode.nodeName, isOwner = isDirectOwner, props = getNodeProps(vnode);
        while (c && !isOwner && (c = c.__u)) isOwner = c.constructor === vnode.nodeName;
        if (c && isOwner && (!mountAll || c._component)) {
            setComponentProps(c, props, 3, context, mountAll);
            dom = c.base;
        } else {
            if (originalComponent && !isDirectOwner) {
                unmountComponent(originalComponent);
                dom = oldDom = null;
            }
            c = createComponent(vnode.nodeName, props, context);
            if (dom && !c.__b) {
                c.__b = dom;
                oldDom = null;
            }
            setComponentProps(c, props, 1, context, mountAll);
            dom = c.base;
            if (oldDom && dom !== oldDom) {
                oldDom._component = null;
                recollectNodeTree(oldDom, !1);
            }
        }
        return dom;
    }
    function unmountComponent(component) {
        if (options.beforeUnmount) options.beforeUnmount(component);
        var base = component.base;
        component.__x = !0;
        if (component.componentWillUnmount) component.componentWillUnmount();
        component.base = null;
        var inner = component._component;
        if (inner) unmountComponent(inner); else if (base) {
            if (base.__preactattr_ && base.__preactattr_.ref) base.__preactattr_.ref(null);
            component.__b = base;
            removeNode(base);
            recyclerComponents.push(component);
            removeChildren(base);
        }
        applyRef(component.__r, null);
    }
    function Component(props, context) {
        this.__d = !0;
        this.context = context;
        this.props = props;
        this.state = this.state || {};
        this.__h = [];
    }
    function render(vnode, parent, merge) {
        return diff(merge, vnode, {}, !1, parent, !1);
    }
    function createRef() {
        return {};
    }
    var VNode = function() {};
    var options = {};
    var stack = [];
    var EMPTY_CHILDREN = [];
    var defer = 'function' == typeof Promise ? Promise.resolve().then.bind(Promise.resolve()) : setTimeout;
    var IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|ows|mnc|ntw|ine[ch]|zoo|^ord/i;
    var items = [];
    var mounts = [];
    var diffLevel = 0;
    var isSvgMode = !1;
    var hydrating = !1;
    var recyclerComponents = [];
    extend(Component.prototype, {
        setState: function(state, callback) {
            if (!this.__s) this.__s = this.state;
            this.state = extend(extend({}, this.state), 'function' == typeof state ? state(this.state, this.props) : state);
            if (callback) this.__h.push(callback);
            enqueueRender(this);
        },
        forceUpdate: function(callback) {
            if (callback) this.__h.push(callback);
            renderComponent(this, 2);
        },
        render: function() {}
    });
    var preact = {
        h: h,
        createElement: h,
        cloneElement: cloneElement,
        createRef: createRef,
        Component: Component,
        render: render,
        rerender: rerender,
        options: options
    };
    if ('undefined' != typeof module) module.exports = preact; else self.preact = preact;
}();
//# sourceMappingURL=preact.js.map


nextDom = childDom!=null && childDom.nextSibling;

// Morph the old element into the new one, but don't append it to the dom yet

newDom = diff(oldVNode==null ? null : oldVNode._dom, parentDom, childVNode, oldVNode, context, isSvg, false, excessDomChildren, mounts, ancestorComponent);

// Only proceed if the vnode has not been unmounted by `diff()` above.

if (childVNode!=null && newDom !=null) {

keep keep keep keep keep replace keep keep keep

newDom = diff(oldVNode==null ? null : oldVNode._dom, parentDom, childVNode, oldVNode, context, isSvg, excessDomChildren, mounts, ancestorComponent);


import "mocha";
import { expect } from "chai";
import {
	createElement,
	Component,
	RenderableProps,
	Fragment
} from "../../src/";

export class ContextComponent extends Component<{ foo: string }> {
	getChildContext() {
		return { something: 2 };
	}

	render() {
		return null;
	}
}

export interface SimpleComponentProps {
	initialName: string | null;
}

export interface SimpleState {
	name: string | null;
}

export class SimpleComponent extends Component<
	SimpleComponentProps,
	SimpleState
> {
	constructor(props: SimpleComponentProps) {
		super(props);
		this.state = {
			name: props.initialName
		};
	}

	render() {
		if (!this.state.name) {
			return null;
		}
		const { initialName, children } = this.props;
		return (

					{initialName} / {this.state.name}

				{children}

		);
	}
}

class DestructuringRenderPropsComponent extends Component<
	SimpleComponentProps,
	SimpleState
> {
	constructor(props: SimpleComponentProps) {
		super(props);
		this.state = {
			name: props.initialName
		};
	}

	render({ initialName, children }: RenderableProps<SimpleComponentProps>) {
		if (!this.state.name) {
			return null;
		}
		return (

				{this.props.initialName} / {this.state.name}

		);
	}
}

interface RandomChildrenComponenProps {
	num?: number;
	val?: string;
	span?: boolean;
}

class RandomChildrenComponent extends Component<RandomChildrenComponenProps> {
	render() {
		const { num, val, span } = this.props;
		if (num) {
			return num;
		}
		if (val) {
			return val;
		}
		if (span) {
			return hi
		}
		return null;
	}
}

describe("Component", () => {
	const component = new SimpleComponent({ initialName: "da name" });

	it("has state", () => {
		expect(component.state.name).to.eq("da name");
	});

	it("has props", () => {
		expect(component.props.initialName).to.eq("da name");
	});

	it("has no base when not mounted", () => {
		expect(component.base).to.not.exist;
	});

	describe("setState", () => {
		it("can be used with an object", () => {
			component.setState({ name: "another name" });
		});

		it("can be used with a function", () => {
			const updater = (state: any, props: any) => ({
				name: `${state.name} - ${props.initialName}`
			});
			component.setState(updater);
		});
	});

	describe("render", () => {
		it("can return null", () => {
			const comp = new SimpleComponent({ initialName: null });
			const actual = comp.render();

			expect(actual).to.eq(null);
		});
	});

	describe("Fragment", () => {
		it('should render nested Fragments', () => {
			var vnode = <Fragment>
					<Fragment>foo</Fragment>
					bar
				</Fragment>

			expect(vnode.type).to.be.equal(Fragment);
		});
	});
});


It controls protected/private property mangling so that minified builds have consistent property names.

Most properties are only used on one type of objects, so they can have the same name since they will never collide. Doing this reduces size.


import { createElement, render, hydrate, Component, options } from 'ceviche';
import * as preact from 'preact';
import renderToString from 'preact-render-to-string';
import './style.scss';
import { Router, Link } from './router';
import Pythagoras from './pythagoras';
import Spiral from './spiral';
import Reorder from './reorder';
import Todo from './todo';
import Fragments from './fragments';
import installLogger from './logger';
import ProfilerDemo from './profiler';
import KeyBug from './key_bug';
import { initDevTools } from 'ceviche/debug/src/devtools';

let isBenchmark = /(\/spiral|\/pythagoras|[#&]bench)/g.test(window.location.href);
if (!isBenchmark) {
	// eslint-disable-next-line no-console
	console.log('Enabling devtools');
	initDevTools();
}

window.ceviche = { createElement, render, hydrate, Component, options };

class Home extends Component {
	a = 1;
	render() {
		return (

Hello

		);
	}
}

class DevtoolsWarning extends Component {
	onClick = () => {
		window.location.reload();
	}

	render() {
		return (
			<button onClick={this.onClick}>Start Benchmark (disables devtools)</button>
		);
	}
}

class App extends Component {
	render({ url }) {
		return (

						<Link href="/" activeClassName="active">Home</Link>
						<Link href="/reorder" activeClassName="active">Reorder</Link>
						<Link href="/spiral" activeClassName="active">Spiral</Link>
						<Link href="/pythagoras" activeClassName="active">Pythagoras</Link>
						<Link href="/todo" activeClassName="active">ToDo</Link>
						<Link href="/fragments" activeClassName="active">Fragments</Link>
						<Link href="/key_bug" activeClassName="active">Key Bug</Link>
						<Link href="/profiler" activeClassName="active">Profiler</Link>

					<Router url={url}>
						<Home path="/" />
						<Reorder path="/reorder" />

							{!isBenchmark
								? <DevtoolsWarning />
								: <Spiral />
							}

							{!isBenchmark
								? <DevtoolsWarning />
								: <Pythagoras />
							}

						<Todo path="/todo" />
						<Fragments path="/fragments" />
						<ProfilerDemo path="/profiler" />
						<KeyBug path="/key_bug" />
					</Router>

		);
	}
}

document.body.innerHTML = renderToString(<App url={location.href.match(/[#&]ssr/) ? undefined : '/'} />);
// document.body.firstChild.setAttribute('is-ssr', 'true');

installLogger(
	String(localStorage.LOG)==='true' || location.href.match(/logger/),
	String(localStorage.CONSOLE)==='true' || location.href.match(/console/)
);

render(<App />, document.body);


* @param {import('../internal').VNode | null} newVNode The new virtual node

* @param {import('../internal').VNode | null} oldVNode The old virtual node

* @param {object} context The current context object

* @param {boolean} isSvg Whether or not this element is an SVG node

* @param {boolean} append Whether or not to immediately append the new DOM

* @param {Array<import('../internal').PreactElement>} excessDomChildren

* @param {Array<import('../internal').Component>} mounts A list of newly

* @param {import('../internal').Component | null} ancestorComponent The direct

keep keep keep keep replace replace keep keep keep keep keep


import { assign } from './util';
import { EMPTY_ARR } from './constants';
import { createElement } from './create-element';

/**
 * Clones the given VNode, optionally adding attributes/props and replacing its children.
 * @param {import('./internal').VNode} vnode The virtual DOM element to clone
 * @param {object} props Attributes/props to add when cloning
 * @param {Array<import('./index').ComponentChildren>} rest Any additional arguments will be used as replacement children.
 */
export function cloneElement(vnode, props) {
	let out = createElement(vnode.type, assign(assign({}, vnode.props), props));
	out.key = out.key || vnode.key;
	out.ref = out.ref || vnode.ref;
	if (arguments.length>2) out.props.children = EMPTY_ARR.slice.call(arguments, 2);
	return out;
}


export function diff(dom, parentDom, newVNode, oldVNode, context, isSvg, append, excessDomChildren, mounts, ancestorComponent) {

// If the previous type doesn't match the new type we drop the whole subtree

if (oldVNode==null || newVNode==null || oldVNode.type!==newVNode.type) {

if (oldVNode!=null) unmount(oldVNode, ancestorComponent);

keep keep keep keep replace keep keep keep keep

export function diff(dom, parentDom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts, ancestorComponent) {


import { createElement as h, render, Component, Fragment } from '../../src/index';
import { setupScratch, teardown, setupRerender, getMixedArray, mixedArrayHTML } from '../_util/helpers';

/** @jsx h */

let spyAll = obj => Object.keys(obj).forEach( key => sinon.spy(obj,key) );

function getAttributes(node) {
	let attrs = {};
	if (node.attributes) {
		for (let i=node.attributes.length; i--; ) {
			attrs[node.attributes[i].name] = node.attributes[i].value;
		}
	}
	return attrs;
}

// hacky normalization of attribute order across browsers.
function sortAttributes(html) {
	return html.replace(/<([a-z0-9-]+)((?:\s[a-z0-9:_.-]+=".*?")+)((?:\s*\/)?>)/gi, (s, pre, attrs, after) => {
		let list = attrs.match(/\s[a-z0-9:_.-]+=".*?"/gi).sort( (a, b) => a>b ? 1 : -1 );
		if (~after.indexOf('/')) after = '></'+pre+'>';
		return '<' + pre + list.join('') + after;
	});
}

describe('Components', () => {

	/** @type {HTMLDivElement} */
	let scratch;

	/** @type {() => void} */
	let rerender;

	beforeEach(() => {
		scratch = setupScratch();
		rerender = setupRerender();
	});

	afterEach(() => {
		teardown(scratch);
	});

	describe('Component construction', () => {

		/** @type {object} */
		let instance;
		let PROPS;
		let STATE;

		beforeEach(() => {
			instance = null;
			PROPS = { foo: 'bar', onBaz: () => {} };
			STATE = { text: 'Hello' };
		});

		it('should render components', () => {
			class C1 extends Component {
				render() {
					return
C1
;
				}
			}
			sinon.spy(C1.prototype, 'render');
			render(<C1 />, scratch);

			expect(C1.prototype.render)
				.to.have.been.calledOnce
				.and.to.have.been.calledWithMatch({}, {})
				.and.to.have.returned(sinon.match({ type: 'div' }));

			expect(scratch.innerHTML).to.equal('
C1
');
		});

		it('should render functional components', () => {
			const C3 = sinon.spy( props =>
 );

			render(<C3 {...PROPS} />, scratch);

			expect(C3)
				.to.have.been.calledOnce
				.and.to.have.been.calledWithMatch(PROPS)
				.and.to.have.returned(sinon.match({
					type: 'div',
					props: PROPS
				}));

			expect(scratch.innerHTML).to.equal('

');
		});

		it('should render components with props', () => {
			let constructorProps;

			class C2 extends Component {
				constructor(props) {
					super(props);
					constructorProps = props;
				}
				render(props) {
					return
;
				}
			}
			sinon.spy(C2.prototype, 'render');

			render(<C2 {...PROPS} />, scratch);

			expect(constructorProps).to.deep.equal(PROPS);

			expect(C2.prototype.render)
				.to.have.been.calledOnce
				.and.to.have.been.calledWithMatch(PROPS, {})
				.and.to.have.returned(sinon.match({
					type: 'div',
					props: PROPS
				}));

			expect(scratch.innerHTML).to.equal('

');
		});

		it('should initialize props & context but not state in Component constructor', () => {
			// Not initializing state matches React behavior: https://codesandbox.io/s/rml19v8o2q
			class Foo extends Component {
				constructor(props, context) {
					super(props, context);
					expect(this.props).to.equal(props);
					expect(this.state).to.deep.equal(undefined);
					expect(this.context).to.equal(context);

					instance = this;
				}
				render(props) {
					return
Hello
;
				}
			}

			sinon.spy(Foo.prototype, 'render');

			render(<Foo {...PROPS} />, scratch);

			expect(Foo.prototype.render)
				.to.have.been.calledOnce
				.and.to.have.been.calledWithMatch(PROPS, {}, {})
				.and.to.have.returned(sinon.match({ type: 'div', props: PROPS }));
			expect(instance.props).to.deep.equal(PROPS);
			expect(instance.state).to.deep.equal({});
			expect(instance.context).to.deep.equal({});

			expect(scratch.innerHTML).to.equal('
Hello
');
		});

		it('should render Component classes that don\'t pass args into the Component constructor', () => {
			function Foo () {
				Component.call(this);
				instance = this;
				this.state = STATE;
			}
			Foo.prototype.render = sinon.spy((props, state) =>
{state.text}
);

			render(<Foo {...PROPS} />, scratch);

			expect(Foo.prototype.render)
				.to.have.been.calledOnce
				.and.to.have.been.calledWithMatch(PROPS, STATE, {})
				.and.to.have.returned(sinon.match({ type: 'div', props: PROPS }));
			expect(instance.props).to.deep.equal(PROPS);
			expect(instance.state).to.deep.equal(STATE);
			expect(instance.context).to.deep.equal({});

			expect(scratch.innerHTML).to.equal('
Hello
');
		});

		it('should render components that don\'t pass args into the Component constructor (unistore pattern)', () => {
			// Pattern unistore uses for connect: https://git.io/fxRqu
			function Wrapper() {
				instance = this;
				this.state = STATE;
				this.render = sinon.spy((props, state) =>
{state.text}
);
			}
			(Wrapper.prototype = new Component()).constructor = Wrapper;

			render(<Wrapper {...PROPS} />, scratch);

			expect(instance.render)
				.to.have.been.calledOnce
				.and.to.have.been.calledWithMatch(PROPS, STATE, {})
				.and.to.have.returned(sinon.match({ type: 'div', props: PROPS }));
			expect(instance.props).to.deep.equal(PROPS);
			expect(instance.state).to.deep.equal(STATE);
			expect(instance.context).to.deep.equal({});

			expect(scratch.innerHTML).to.equal('
Hello
');
		});

		it('should render components that don\'t call Component constructor', () => {
			function Foo() {
				instance = this;
				this.state = STATE;
			}
			Foo.prototype = Object.create(Component);
			Foo.prototype.render = sinon.spy((props, state) =>
{state.text}
);

			render(<Foo {...PROPS} />, scratch);

			expect(Foo.prototype.render)
				.to.have.been.calledOnce
				.and.to.have.been.calledWithMatch(PROPS, STATE, {})
				.and.to.have.returned(sinon.match({ type: 'div', props: PROPS }));
			expect(instance.props).to.deep.equal(PROPS);
			expect(instance.state).to.deep.equal(STATE);
			expect(instance.context).to.deep.equal({});

			expect(scratch.innerHTML).to.equal('
Hello
');
		});

		it('should render components that don\'t call Component constructor and don\'t initialize state', () => {
			function Foo () {
				instance = this;
			}
			Foo.prototype.render = sinon.spy((props) =>
Hello
);

			render(<Foo {...PROPS} />, scratch);

			expect(Foo.prototype.render)
				.to.have.been.calledOnce
				.and.to.have.been.calledWithMatch(PROPS, {}, {})
				.and.to.have.returned(sinon.match({ type: 'div', props: PROPS }));
			expect(instance.props).to.deep.equal(PROPS);
			expect(instance.state).to.deep.equal({});
			expect(instance.context).to.deep.equal({});

			expect(scratch.innerHTML).to.equal('
Hello
');
		});

		it('should render components that don\'t inherit from Component', () => {
			class Foo {
				constructor() {
					instance = this;
					this.state = STATE;
				}
				render(props, state) {
					return
{state.text}
;
				}
			}
			sinon.spy(Foo.prototype, 'render');

			render(<Foo {...PROPS} />, scratch);

			expect(Foo.prototype.render)
				.to.have.been.calledOnce
				.and.to.have.been.calledWithMatch(PROPS, STATE, {})
				.and.to.have.returned(sinon.match({ type: 'div', props: PROPS }));
			expect(instance.props).to.deep.equal(PROPS);
			expect(instance.state).to.deep.equal(STATE);
			expect(instance.context).to.deep.equal({});

			expect(scratch.innerHTML).to.equal('
Hello
');
		});

		it('should render components that don\'t inherit from Component (unistore pattern)', () => {
			// Pattern unistore uses for Provider: https://git.io/fxRqR
			function Provider() {
				instance = this;
				this.state = STATE;
			}
			Provider.prototype.render = sinon.spy((props, state) =>
{state.text}
);

			render(<Provider {...PROPS} />, scratch);

			expect(Provider.prototype.render)
				.to.have.been.calledOnce
				.and.to.have.been.calledWithMatch(PROPS, STATE, {})
				.and.to.have.returned(sinon.match({ type: 'div', props: PROPS }));
			expect(instance.props).to.deep.equal(PROPS);
			expect(instance.state).to.deep.equal(STATE);
			expect(instance.context).to.deep.equal({});

			expect(scratch.innerHTML).to.equal('
Hello
');
		});

		it('should render components that don\'t inherit from Component and don\'t initialize state', () => {
			class Foo {
				constructor() {
					instance = this;
				}
				render(props, state) {
					return
Hello
;
				}
			}
			sinon.spy(Foo.prototype, 'render');

			render(<Foo {...PROPS} />, scratch);

			expect(Foo.prototype.render)
				.to.have.been.calledOnce
				.and.to.have.been.calledWithMatch(PROPS, {}, {})
				.and.to.have.returned(sinon.match({ type: 'div', props: PROPS }));
			expect(instance.props).to.deep.equal(PROPS);
			expect(instance.state).to.deep.equal({});
			expect(instance.context).to.deep.equal({});

			expect(scratch.innerHTML).to.equal('
Hello
');
		});

		it('should render class components that inherit from Component without a render method', () => {
			class Foo extends Component {
				constructor(props, context) {
					super(props, context);
					instance = this;
				}
			}

			sinon.spy(Foo.prototype, 'render');

			render(<Foo {...PROPS} />, scratch);

			expect(Foo.prototype.render)
				.to.have.been.calledOnce
				.and.to.have.been.calledWithMatch(PROPS, {}, {})
				.and.to.have.returned(undefined);
			expect(instance.props).to.deep.equal(PROPS);
			expect(instance.state).to.deep.equal({});
			expect(instance.context).to.deep.equal({});

			expect(scratch.innerHTML).to.equal('');
		});
	});

	it('should render string', () => {
		class StringComponent extends Component {
			render() {
				return 'Hi there';
			}
		}

		render(<StringComponent />, scratch);
		expect(scratch.innerHTML).to.equal('Hi there');
	});

	it('should render number as string', () => {
		class NumberComponent extends Component {
			render() {
				return 42;
			}
		}

		render(<NumberComponent />, scratch);
		expect(scratch.innerHTML).to.equal('42');
	});

	it('should render null as empty string', () => {
		class NullComponent extends Component {
			render() {
				return null;
			}
		}

		render(<NullComponent />, scratch);
		expect(scratch.innerHTML).to.equal('');
	});

	// Test for Issue #73
	it('should remove orphaned elements replaced by Components', () => {
		class Comp extends Component {
			render() {
				return span in a component;
			}
		}

		let root;
		function test(content) {
			root = render(content, scratch, root);
		}

		test(<Comp />);
		test(
just a div
);
		test(<Comp />);

		expect(scratch.innerHTML).to.equal('span in a component');
	});

	// Test for Issue developit/preact#176
	it('should remove children when root changes to text node', () => {
		let comp;

		class Comp extends Component {
			constructor() {
				super();
				comp = this;
			}
			render(_, { alt }) {
				return alt ? 'asdf' :
test
;
			}
		}

		render(<Comp />, scratch);

		comp.setState({ alt: true });
		comp.forceUpdate();
		expect(scratch.innerHTML, 'switching to textnode').to.equal('asdf');

		comp.setState({ alt: false });
		comp.forceUpdate();
		expect(scratch.innerHTML, 'switching to element').to.equal('
test
');

		comp.setState({ alt: true });
		comp.forceUpdate();
		expect(scratch.innerHTML, 'switching to textnode 2').to.equal('asdf');
	});

	// Test for Issue developit/preact#254
	it('should not recycle common class children with different keys', () => {
		let idx = 0;
		let msgs = ['A','B','C','D','E','F','G','H'];
		let sideEffect = sinon.spy();

		class Comp extends Component {
			componentWillMount() {
				this.innerMsg = msgs[(idx++ % 8)];
				sideEffect();
			}
			render() {
				return
{this.innerMsg}
;
			}
		}
		sinon.spy(Comp.prototype, 'componentWillMount');

		let good, bad;
		class GoodContainer extends Component {
			constructor(props) {
				super(props);
				this.state = { alt: false };
				good = this;
			}

			render(_, { alt }) {
				return (

						{alt ? null : (<Comp key={1} alt={alt} />)}
						{alt ? null : (<Comp key={2} alt={alt} />)}
						{alt ? (<Comp key={3} alt={alt} />) : null}

				);
			}
		}

		class BadContainer extends Component {
			constructor(props) {
				super(props);
				this.state = { alt: false };
				bad = this;
			}

			render(_, { alt }) {
				return (

						{alt ? null : (<Comp alt={alt} />)}
						{alt ? null : (<Comp alt={alt} />)}
						{alt ? (<Comp alt={alt} />) : null}

				);
			}
		}

		render(<GoodContainer />, scratch);
		expect(scratch.textContent, 'new component with key present').to.equal('AB');
		expect(Comp.prototype.componentWillMount).to.have.been.calledTwice;
		expect(sideEffect).to.have.been.calledTwice;

		sideEffect.resetHistory();
		Comp.prototype.componentWillMount.resetHistory();
		good.setState({ alt: true });
		rerender();
		expect(scratch.textContent, 'new component with key present re-rendered').to.equal('C');
		//we are recycling the first 2 components already rendered, just need a new one
		expect(Comp.prototype.componentWillMount).to.have.been.calledOnce;
		expect(sideEffect).to.have.been.calledOnce;

		sideEffect.resetHistory();
		Comp.prototype.componentWillMount.resetHistory();
		render(<BadContainer />, scratch);
		expect(scratch.textContent, 'new component without key').to.equal('DE');
		expect(Comp.prototype.componentWillMount).to.have.been.calledTwice;
		expect(sideEffect).to.have.been.calledTwice;

		sideEffect.resetHistory();
		Comp.prototype.componentWillMount.resetHistory();
		bad.setState({ alt: true });
		rerender();
		expect(scratch.textContent, 'new component without key re-rendered').to.equal('D');
		expect(Comp.prototype.componentWillMount).to.not.have.been.called;
		expect(sideEffect).to.not.have.been.called;
	});

	describe('array children', () => {
		it('should render DOM element\'s array children', () => {
			render(
{getMixedArray()}
, scratch);
			expect(scratch.firstChild.innerHTML).to.equal(mixedArrayHTML);
		});

		it('should render Component\'s array children', () => {
			const Foo = () => getMixedArray();

			render(<Foo />, scratch);

			expect(scratch.innerHTML).to.equal(mixedArrayHTML);
		});

		it('should render Fragment\'s array children', () => {
			const Foo = () => (
				<Fragment>
					{getMixedArray()}
				</Fragment>
			);

			render(<Foo />, scratch);

			expect(scratch.innerHTML).to.equal(mixedArrayHTML);
		});

		it('should render sibling array children', () => {
			const Todo = () => (

A header

					{['a','b'].map(value =>
{value}
)}

A divider

					{['c', 'd'].map(value =>
{value}
)}

A footer

			);

			render(<Todo />, scratch);

			let ul = scratch.firstChild;
			expect(ul.childNodes.length).to.equal(7);
			expect(ul.childNodes[0].textContent).to.equal('A header');
			expect(ul.childNodes[1].textContent).to.equal('a');
			expect(ul.childNodes[2].textContent).to.equal('b');
			expect(ul.childNodes[3].textContent).to.equal('A divider');
			expect(ul.childNodes[4].textContent).to.equal('c');
			expect(ul.childNodes[5].textContent).to.equal('d');
			expect(ul.childNodes[6].textContent).to.equal('A footer');
		});
	});

	describe('props.children', () => {
		let children;

		let Foo = props => {
			children = props.children;
			return
{props.children}
;
		};

		let FunctionFoo = props => {
			children = props.children;
			return
{props.children(2)}
;
		};

		let Bar = () => Bar;

		beforeEach(() => {
			children = undefined;
		});

		it('should support passing children as a prop', () => {
			const Foo = props =>
;

			render(<Foo a="b" children={[
				bar,
				'123',
				456
			]}
			       />, scratch);

			expect(scratch.innerHTML).to.equal('
bar123456
');
		});

		it('should be ignored when explicit children exist', () => {
			const Foo = props =>
a
;

			render(<Foo children={'b'} />, scratch);

			expect(scratch.innerHTML).to.equal('
a
');
		});

		it('should be undefined with no child', () => {
			render(<Foo />, scratch);

			expect(children).to.be.undefined;
			expect(scratch.innerHTML).to.equal('

');
		});

		it('should be undefined with null as a child', () => {
			render(<Foo>{null}</Foo>, scratch);

			expect(children).to.be.undefined;
			expect(scratch.innerHTML).to.equal('

');
		});

		it('should be false with false as a child', () => {
			render(<Foo>{false}</Foo>, scratch);

			expect(children).to.be.false;
			expect(scratch.innerHTML).to.equal('

');
		});

		it('should be true with true as a child', () => {
			render(<Foo>{true}</Foo>, scratch);

			expect(children).to.be.true;
			expect(scratch.innerHTML).to.equal('

');
		});

		it('should be a string with a text child', () => {
			render(<Foo>text</Foo>, scratch);

			expect(children).to.be.a('string');
			expect(children).to.equal('text');
			expect(scratch.innerHTML).to.equal('
text
');
		});

		it('should be a string with a number child', () => {
			render(<Foo>1</Foo>, scratch);

			expect(children).to.be.a('string');
			expect(children).to.equal('1');
			expect(scratch.innerHTML).to.equal('
1
');
		});

		it('should be a VNode with a DOM node child', () => {
			render(<Foo></Foo>, scratch);

			expect(children).to.be.an('object');
			expect(children.type).to.equal('span');
			expect(scratch.innerHTML).to.equal('

');
		});

		it('should be a VNode with a Component child', () => {
			render(<Foo><Bar /></Foo>, scratch);

			expect(children).to.be.an('object');
			expect(children.type).to.equal(Bar);
			expect(scratch.innerHTML).to.equal('
Bar
');
		});

		it('should be a function with a function child', () => {
			const child = num => num.toFixed(2);
			render(<FunctionFoo>{child}</FunctionFoo>, scratch);

			expect(children).to.be.an('function');
			expect(children).to.equal(child);
			expect(scratch.innerHTML).to.equal('
2.00
');
		});

		it('should be an array with multiple children', () => {
			render(<Foo>0<input />
1</Foo>, scratch);

			expect(children).to.be.an('array');
			expect(children[0]).to.equal('0');
			expect(children[1].type).to.equal('span');
			expect(children[2].type).to.equal('input');
			expect(children[3].type).to.equal('div');
			expect(children[4]).to.equal('1');
			expect(scratch.innerHTML).to.equal(`<div>0<span></span><input><div></div>1</div>`);
		});

		it('should be an array with an array as children', () => {
			const mixedArray = getMixedArray();
			render(<Foo>{mixedArray}</Foo>, scratch);

			expect(children).to.be.an('array');
			expect(children).to.deep.equal(mixedArray);
			expect(scratch.innerHTML).to.equal(`<div>${mixedArrayHTML}</div>`);
		});

		it('should not flatten sibling and nested arrays', () => {
			const list1 = [0, 1];
			const list2 = [2, 3];
			const list3 = [4, 5];
			const list4 = [6, 7];
			const list5 = [8, 9];

			render(<Foo>{[list1, list2]}{[list3, list4]}{list5}</Foo>, scratch);

			expect(children).to.be.an('array');
			expect(children).to.deep.equal([
				[list1, list2],
				[list3, list4],
				list5
			]);
			expect(scratch.innerHTML).to.equal('
0123456789
');
		});
	});

	describe('High-Order Components', () => {
		it('should render wrapper HOCs', () => {
			const text = 'We\'ll throw some happy little limbs on this tree.';

			function withBobRoss(ChildComponent) {
				return class BobRossIpsum extends Component {
					getChildContext() {
						return { text };
					}

					render(props) {
						return <ChildComponent {...props} />;
					}
				};
			}

			const PaintSomething = (props, context) =>
{context.text}
;
			const Paint = withBobRoss(PaintSomething);

			render(<Paint />, scratch);
			expect(scratch.innerHTML).to.equal(`<div>${text}</div>`);
		});

		it('should render HOCs with generic children', () => {
			const text = 'Let your imagination just wonder around when you\'re doing these things.';

			class BobRossProvider extends Component {
				getChildContext() {
					return { text };
				}

				render(props) {
					return props.children;
				}
			}

			function BobRossConsumer(props, context) {
				return props.children(context.text);
			}

			const Say = props =>
{props.text}
;

			const Speak = () => (
				<BobRossProvider>
					A span
					<BobRossConsumer>
						{ text => <Say text={text} /> }
					</BobRossConsumer>
					A final span
				</BobRossProvider>
			);

			render(<Speak />, scratch);

			expect(scratch.innerHTML).to.equal(`<span>A span</span><div>${text}</div><span>A final span</span>`);
		});

		it('should render nested functional components', () => {
			const PROPS = { foo: 'bar', onBaz: () => {} };

			const Outer = sinon.spy(
				props => <Inner {...props} />
			);

			const Inner = sinon.spy(
				props =>
inner

			);

			render(<Outer {...PROPS} />, scratch);

			expect(Outer)
				.to.have.been.calledOnce
				.and.to.have.been.calledWithMatch(PROPS)
				.and.to.have.returned(sinon.match({
					type: Inner,
					props: PROPS
				}));

			expect(Inner)
				.to.have.been.calledOnce
				.and.to.have.been.calledWithMatch(PROPS)
				.and.to.have.returned(sinon.match({
					type: 'div',
					props: { ...PROPS, children: 'inner' }
				}));

			expect(scratch.innerHTML).to.equal('
inner
');
		});

		it('should re-render nested functional components', () => {
			let doRender = null;
			class Outer extends Component {
				componentDidMount() {
					let i = 1;
					doRender = () => this.setState({ i: ++i });
				}
				componentWillUnmount() {}
				render(props, { i }) {
					return <Inner i={i} {...props} />;
				}
			}
			sinon.spy(Outer.prototype, 'render');
			sinon.spy(Outer.prototype, 'componentWillUnmount');

			let j = 0;
			const Inner = sinon.spy(
				props =>
inner

			);

			render(<Outer foo="bar" />, scratch);

			// update & flush
			doRender();
			rerender();

			expect(Outer.prototype.componentWillUnmount)
				.not.to.have.been.called;

			expect(Inner).to.have.been.calledTwice;

			expect(Inner.secondCall)
				.to.have.been.calledWithMatch({ foo: 'bar', i: 2 })
				.and.to.have.returned(sinon.match({
					props: {
						j: 2,
						i: 2,
						foo: 'bar'
					}
				}));

			expect(getAttributes(scratch.firstElementChild)).to.eql({
				j: '2',
				i: '2',
				foo: 'bar'
			});

			// update & flush
			doRender();
			rerender();

			expect(Inner).to.have.been.calledThrice;

			expect(Inner.thirdCall)
				.to.have.been.calledWithMatch({ foo: 'bar', i: 3 })
				.and.to.have.returned(sinon.match({
					props: {
						j: 3,
						i: 3,
						foo: 'bar'
					}
				}));

			expect(getAttributes(scratch.firstElementChild)).to.eql({
				j: '3',
				i: '3',
				foo: 'bar'
			});
		});

		it('should re-render nested components', () => {
			let doRender = null,
				alt = false;

			class Outer extends Component {
				componentDidMount() {
					let i = 1;
					doRender = () => this.setState({ i: ++i });
				}
				componentWillUnmount() {}
				render(props, { i }) {
					if (alt) return
;
					return <Inner i={i} {...props} />;
				}
			}
			sinon.spy(Outer.prototype, 'render');
			sinon.spy(Outer.prototype, 'componentDidMount');
			sinon.spy(Outer.prototype, 'componentWillUnmount');

			let j = 0;
			class Inner extends Component {
				constructor(...args) {
					super();
					this._constructor(...args);
				}
				_constructor() {}
				componentWillMount() {}
				componentDidMount() {}
				componentWillUnmount() {}
				render(props) {
					return
inner
;
				}
			}
			sinon.spy(Inner.prototype, '_constructor');
			sinon.spy(Inner.prototype, 'render');
			sinon.spy(Inner.prototype, 'componentWillMount');
			sinon.spy(Inner.prototype, 'componentDidMount');
			sinon.spy(Inner.prototype, 'componentWillUnmount');

			render(<Outer foo="bar" />, scratch);

			expect(Outer.prototype.componentDidMount).to.have.been.calledOnce;

			// update & flush
			doRender();
			rerender();

			expect(Outer.prototype.componentWillUnmount).not.to.have.been.called;

			expect(Inner.prototype._constructor).to.have.been.calledOnce;
			expect(Inner.prototype.componentWillUnmount).not.to.have.been.called;
			expect(Inner.prototype.componentWillMount).to.have.been.calledOnce;
			expect(Inner.prototype.componentDidMount).to.have.been.calledOnce;
			expect(Inner.prototype.render).to.have.been.calledTwice;

			expect(Inner.prototype.render.secondCall)
				.to.have.been.calledWithMatch({ foo: 'bar', i: 2 })
				.and.to.have.returned(sinon.match({
					props: {
						j: 2,
						i: 2,
						foo: 'bar'
					}
				}));

			expect(getAttributes(scratch.firstElementChild)).to.eql({
				j: '2',
				i: '2',
				foo: 'bar'
			});

			expect(sortAttributes(scratch.innerHTML)).to.equal(sortAttributes('
inner
'));

			// update & flush
			doRender();
			rerender();

			expect(Inner.prototype.componentWillUnmount).not.to.have.been.called;
			expect(Inner.prototype.componentWillMount).to.have.been.calledOnce;
			expect(Inner.prototype.componentDidMount).to.have.been.calledOnce;
			expect(Inner.prototype.render).to.have.been.calledThrice;

			expect(Inner.prototype.render.thirdCall)
				.to.have.been.calledWithMatch({ foo: 'bar', i: 3 })
				.and.to.have.returned(sinon.match({
					props: {
						j: 3,
						i: 3,
						foo: 'bar'
					}
				}));

			expect(getAttributes(scratch.firstElementChild)).to.eql({
				j: '3',
				i: '3',
				foo: 'bar'
			});

			// update & flush
			alt = true;
			doRender();
			rerender();

			expect(Inner.prototype.componentWillUnmount).to.have.been.calledOnce;

			expect(scratch.innerHTML).to.equal('

');

			// update & flush
			alt = false;
			doRender();
			rerender();

			expect(sortAttributes(scratch.innerHTML)).to.equal(sortAttributes('
inner
'));
		});

		it('should resolve intermediary functional component', () => {
			let ctx = {};
			class Root extends Component {
				getChildContext() {
					return { ctx };
				}
				render() {
					return <Func />;
				}
			}
			const Func = () => <Inner />;
			class Inner extends Component {
				componentWillMount() {}
				componentDidMount() {}
				componentWillUnmount() {}
				render() {
					return
inner
;
				}
			}

			spyAll(Inner.prototype);

			render(<Root />, scratch);

			expect(Inner.prototype.componentWillMount).to.have.been.calledOnce;
			expect(Inner.prototype.componentDidMount).to.have.been.calledOnce;
			expect(Inner.prototype.componentWillMount).to.have.been.calledBefore(Inner.prototype.componentDidMount);

			render(<asdf />, scratch);

			expect(Inner.prototype.componentWillUnmount).to.have.been.calledOnce;
		});

		it('should unmount children of high-order components without unmounting parent', () => {
			let outer, inner2, counter=0;

			class Outer extends Component {
				constructor(props, context) {
					super(props, context);
					outer = this;
					this.state = {
						child: this.props.child
					};
				}
				componentWillUnmount(){}
				componentWillMount(){}
				componentDidMount(){}
				render(_, { child: C }) {
					return <C />;
				}
			}
			spyAll(Outer.prototype);

			class Inner extends Component {
				componentWillUnmount(){}
				componentWillMount(){}
				componentDidMount(){}
				render() {
					return h('element'+(++counter));
				}
			}
			spyAll(Inner.prototype);

			class Inner2 extends Component {
				constructor(props, context) {
					super(props, context);
					inner2 = this;
				}
				componentWillUnmount(){}
				componentWillMount(){}
				componentDidMount(){}
				render() {
					return h('element'+(++counter));
				}
			}
			spyAll(Inner2.prototype);

			render(<Outer child={Inner} />, scratch);

			// outer should only have been mounted once
			expect(Outer.prototype.componentWillMount, 'outer initial').to.have.been.calledOnce;
			expect(Outer.prototype.componentDidMount, 'outer initial').to.have.been.calledOnce;
			expect(Outer.prototype.componentWillUnmount, 'outer initial').not.to.have.been.called;

			// inner should only have been mounted once
			expect(Inner.prototype.componentWillMount, 'inner initial').to.have.been.calledOnce;
			expect(Inner.prototype.componentDidMount, 'inner initial').to.have.been.calledOnce;
			expect(Inner.prototype.componentWillUnmount, 'inner initial').not.to.have.been.called;

			outer.setState({ child: Inner2 });
			outer.forceUpdate();

			expect(Inner2.prototype.render).to.have.been.calledOnce;

			// outer should still only have been mounted once
			expect(Outer.prototype.componentWillMount, 'outer swap').to.have.been.calledOnce;
			expect(Outer.prototype.componentDidMount, 'outer swap').to.have.been.calledOnce;
			expect(Outer.prototype.componentWillUnmount, 'outer swap').not.to.have.been.called;

			// inner should only have been mounted once
			expect(Inner2.prototype.componentWillMount, 'inner2 swap').to.have.been.calledOnce;
			expect(Inner2.prototype.componentDidMount, 'inner2 swap').to.have.been.calledOnce;
			expect(Inner2.prototype.componentWillUnmount, 'inner2 swap').not.to.have.been.called;

			inner2.forceUpdate();

			expect(Inner2.prototype.render, 'inner2 update').to.have.been.calledTwice;
			expect(Inner2.prototype.componentWillMount, 'inner2 update').to.have.been.calledOnce;
			expect(Inner2.prototype.componentDidMount, 'inner2 update').to.have.been.calledOnce;
			expect(Inner2.prototype.componentWillUnmount, 'inner2 update').not.to.have.been.called;
		});

		it('should remount when swapping between HOC child types', () => {
			class Outer extends Component {
				render({ child: Child }) {
					return <Child />;
				}
			}

			class Inner extends Component {
				componentWillMount() {}
				componentWillUnmount() {}
				render() {
					return
foo
;
				}
			}
			spyAll(Inner.prototype);

			const InnerFunc = () => (

bar

			);

			render(<Outer child={Inner} />, scratch);

			expect(Inner.prototype.componentWillMount, 'initial mount').to.have.been.calledOnce;
			expect(Inner.prototype.componentWillUnmount, 'initial mount').not.to.have.been.called;

			Inner.prototype.componentWillMount.resetHistory();
			render(<Outer child={InnerFunc} />, scratch);

			expect(Inner.prototype.componentWillMount, 'unmount').not.to.have.been.called;
			expect(Inner.prototype.componentWillUnmount, 'unmount').to.have.been.calledOnce;

			Inner.prototype.componentWillUnmount.resetHistory();
			render(<Outer child={Inner} />, scratch);

			expect(Inner.prototype.componentWillMount, 'remount').to.have.been.calledOnce;
			expect(Inner.prototype.componentWillUnmount, 'remount').not.to.have.been.called;
		});
	});

	describe('Component Nesting', () => {
		let useIntermediary = false;

		let createComponent = (Intermediary) => {
			class C extends Component {
				componentWillMount() {}
				render({ children }) {
					if (!useIntermediary) return children;
					let I = useIntermediary===true ? Intermediary : useIntermediary;
					return {children};
				}
			}
			spyAll(C.prototype);
			return C;
		};

		let createFunction = () => sinon.spy( ({ children }) => children );

		let F1 = createFunction();
		let F2 = createFunction();
		let F3 = createFunction();

		let C1 = createComponent(F1);
		let C2 = createComponent(F2);
		let C3 = createComponent(F3);

		let reset = () => [C1, C2, C3].reduce(
			(acc, c) => acc.concat( Object.keys(c.prototype).map(key => c.prototype[key]) ),
			[F1, F2, F3]
		).forEach( c => c.resetHistory() );

		it('should handle lifecycle for no intermediary in component tree', () => {
			reset();
			render(<C1><C2><C3>Some Text</C3></C2></C1>, scratch);

			expect(C1.prototype.componentWillMount, 'initial mount').to.have.been.calledOnce;
			expect(C2.prototype.componentWillMount, 'initial mount').to.have.been.calledOnce;
			expect(C3.prototype.componentWillMount, 'initial mount').to.have.been.calledOnce;

			reset();
			render(<C1><C2>Some Text</C2></C1>, scratch);

			expect(C1.prototype.componentWillMount, 'unmount innermost, C1').not.to.have.been.called;
			expect(C2.prototype.componentWillMount, 'unmount innermost, C2').not.to.have.been.called;

			reset();
			render(<C1><C3>Some Text</C3></C1>, scratch);

			expect(C1.prototype.componentWillMount, 'swap innermost').not.to.have.been.called;
			expect(C3.prototype.componentWillMount, 'swap innermost').to.have.been.calledOnce;

			reset();
			render(<C1><C2><C3>Some Text</C3></C2></C1>, scratch);

			expect(C1.prototype.componentWillMount, 'inject between, C1').not.to.have.been.called;
			expect(C2.prototype.componentWillMount, 'inject between, C2').to.have.been.calledOnce;
			expect(C3.prototype.componentWillMount, 'inject between, C3').to.have.been.calledOnce;
		});

		it('should handle lifecycle for nested intermediary functional components', () => {
			useIntermediary = true;

			render(
, scratch);
			reset();
			render(<C1><C2><C3>Some Text</C3></C2></C1>, scratch);

			expect(C1.prototype.componentWillMount, 'initial mount w/ intermediary fn, C1').to.have.been.calledOnce;
			expect(C2.prototype.componentWillMount, 'initial mount w/ intermediary fn, C2').to.have.been.calledOnce;
			expect(C3.prototype.componentWillMount, 'initial mount w/ intermediary fn, C3').to.have.been.calledOnce;

			reset();
			render(<C1><C2>Some Text</C2></C1>, scratch);

			expect(C1.prototype.componentWillMount, 'unmount innermost w/ intermediary fn, C1').not.to.have.been.called;
			expect(C2.prototype.componentWillMount, 'unmount innermost w/ intermediary fn, C2').not.to.have.been.called;

			reset();
			render(<C1><C3>Some Text</C3></C1>, scratch);

			expect(C1.prototype.componentWillMount, 'swap innermost w/ intermediary fn').not.to.have.been.called;
			expect(C3.prototype.componentWillMount, 'swap innermost w/ intermediary fn').to.have.been.calledOnce;

			reset();
			render(<C1><C2><C3>Some Text</C3></C2></C1>, scratch);

			expect(C1.prototype.componentWillMount, 'inject between, C1 w/ intermediary fn').not.to.have.been.called;
			expect(C2.prototype.componentWillMount, 'inject between, C2 w/ intermediary fn').to.have.been.calledOnce;
			expect(C3.prototype.componentWillMount, 'inject between, C3 w/ intermediary fn').to.have.been.calledOnce;
		});

		it('should handle lifecycle for nested intermediary elements', () => {
			useIntermediary = 'div';

			render(
, scratch);
			reset();
			render(<C1><C2><C3>Some Text</C3></C2></C1>, scratch);

			expect(C1.prototype.componentWillMount, 'initial mount w/ intermediary div, C1').to.have.been.calledOnce;
			expect(C2.prototype.componentWillMount, 'initial mount w/ intermediary div, C2').to.have.been.calledOnce;
			expect(C3.prototype.componentWillMount, 'initial mount w/ intermediary div, C3').to.have.been.calledOnce;

			reset();
			render(<C1><C2>Some Text</C2></C1>, scratch);

			expect(C1.prototype.componentWillMount, 'unmount innermost w/ intermediary div, C1').not.to.have.been.called;
			expect(C2.prototype.componentWillMount, 'unmount innermost w/ intermediary div, C2').not.to.have.been.called;

			reset();
			render(<C1><C3>Some Text</C3></C1>, scratch);

			expect(C1.prototype.componentWillMount, 'swap innermost w/ intermediary div').not.to.have.been.called;
			expect(C3.prototype.componentWillMount, 'swap innermost w/ intermediary div').to.have.been.calledOnce;

			reset();
			render(<C1><C2><C3>Some Text</C3></C2></C1>, scratch);

			expect(C1.prototype.componentWillMount, 'inject between, C1 w/ intermediary div').not.to.have.been.called;
			expect(C2.prototype.componentWillMount, 'inject between, C2 w/ intermediary div').to.have.been.calledOnce;
			expect(C3.prototype.componentWillMount, 'inject between, C3 w/ intermediary div').to.have.been.calledOnce;
		});
	});
});


import { createElement as h, render } from 'preact';
import { setupScratch, teardown, setupRerender } from '../../../test/_util/helpers';
import { useRef } from '../../src';

/** @jsx h */

describe('useRef', () => {

	/** @type {HTMLDivElement} */
	let scratch;

	/** @type {() => void} */
	let rerender;

	beforeEach(() => {
		scratch = setupScratch();
		rerender = setupRerender();
	});

	afterEach(() => {
		teardown(scratch);
	});

	it('provides a stable reference', () => {
		const values = [];

		function Comp() {
			const ref = useRef(1);
			values.push(ref.current);
			ref.current = 2;
			return null;
		}

		render(<Comp />, scratch);
		render(<Comp />, scratch);

		expect(values).to.deep.equal([1, 2]);
	});

});