type Defaultize<Props, Defaults> =
	// Distribute over unions
	Props extends any
		? 	// Make any properties included in Default optional
			& Partial<Pick<Props, Extract<keyof Props, keyof Defaults>>>
			// Include the remaining properties from Props
			& Pick<Props, Exclude<keyof Props, keyof Defaults>>
		: never;

export namespace JSXInternal {

	type LibraryManagedAttributes<Component, Props> =
	Component extends { defaultProps: infer Defaults }
		? Defaultize<Props, Defaults>
		: Props;

	interface IntrinsicAttributes {
		key?: any;
	}

	interface Element extends preact.VNode<any> {
	}

	interface ElementClass extends preact.Component<any, any> {
	}

	interface ElementAttributesProperty {
		props: any;
	}

	interface ElementChildrenAttribute {
		children: any;
	}

	interface SVGAttributes extends HTMLAttributes {
		accentHeight?: number | string;
		accumulate?: "none" | "sum";
		additive?: "replace" | "sum";
		alignmentBaseline?: "auto" | "baseline" | "before-edge" | "text-before-edge" | "middle" | "central" | "after-edge" | "text-after-edge" | "ideographic" | "alphabetic" | "hanging" | "mathematical" | "inherit";
		allowReorder?: "no" | "yes";
		alphabetic?: number | string;
		amplitude?: number | string;
		arabicForm?: "initial" | "medial" | "terminal" | "isolated";
		ascent?: number | string;
		attributeName?: string;
		attributeType?: string;
		autoReverse?: number | string;
		azimuth?: number | string;
		baseFrequency?: number | string;
		baselineShift?: number | string;
		baseProfile?: number | string;
		bbox?: number | string;
		begin?: number | string;
		bias?: number | string;
		by?: number | string;
		calcMode?: number | string;
		capHeight?: number | string;
		clip?: number | string;
		clipPath?: string;
		clipPathUnits?: number | string;
		clipRule?: number | string;
		colorInterpolation?: number | string;
		colorInterpolationFilters?: "auto" | "sRGB" | "linearRGB" | "inherit";
		colorProfile?: number | string;
		colorRendering?: number | string;
		contentScriptType?: number | string;
		contentStyleType?: number | string;
		cursor?: number | string;
		cx?: number | string;
		cy?: number | string;
		d?: string;
		decelerate?: number | string;
		descent?: number | string;
		diffuseConstant?: number | string;
		direction?: number | string;
		display?: number | string;
		divisor?: number | string;
		dominantBaseline?: number | string;
		dur?: number | string;
		dx?: number | string;
		dy?: number | string;
		edgeMode?: number | string;
		elevation?: number | string;
		enableBackground?: number | string;
		end?: number | string;
		exponent?: number | string;
		externalResourcesRequired?: number | string;
		fill?: string;
		fillOpacity?: number | string;
		fillRule?: "nonzero" | "evenodd" | "inherit";
		filter?: string;
		filterRes?: number | string;
		filterUnits?: number | string;
		floodColor?: number | string;
		floodOpacity?: number | string;
		focusable?: number | string;
		fontFamily?: string;
		fontSize?: number | string;
		fontSizeAdjust?: number | string;
		fontStretch?: number | string;
		fontStyle?: number | string;
		fontVariant?: number | string;
		fontWeight?: number | string;
		format?: number | string;
		from?: number | string;
		fx?: number | string;
		fy?: number | string;
		g1?: number | string;
		g2?: number | string;
		glyphName?: number | string;
		glyphOrientationHorizontal?: number | string;
		glyphOrientationVertical?: number | string;
		glyphRef?: number | string;
		gradientTransform?: string;
		gradientUnits?: string;
		hanging?: number | string;
		horizAdvX?: number | string;
		horizOriginX?: number | string;
		ideographic?: number | string;
		imageRendering?: number | string;
		in2?: number | string;
		in?: string;
		intercept?: number | string;
		k1?: number | string;
		k2?: number | string;
		k3?: number | string;
		k4?: number | string;
		k?: number | string;
		kernelMatrix?: number | string;
		kernelUnitLength?: number | string;
		kerning?: number | string;
		keyPoints?: number | string;
		keySplines?: number | string;
		keyTimes?: number | string;
		lengthAdjust?: number | string;
		letterSpacing?: number | string;
		lightingColor?: number | string;
		limitingConeAngle?: number | string;
		local?: number | string;
		markerEnd?: string;
		markerHeight?: number | string;
		markerMid?: string;
		markerStart?: string;
		markerUnits?: number | string;
		markerWidth?: number | string;
		mask?: string;
		maskContentUnits?: number | string;
		maskUnits?: number | string;
		mathematical?: number | string;
		mode?: number | string;
		numOctaves?: number | string;
		offset?: number | string;
		opacity?: number | string;
		operator?: number | string;
		order?: number | string;
		orient?: number | string;
		orientation?: number | string;
		origin?: number | string;
		overflow?: number | string;
		overlinePosition?: number | string;
		overlineThickness?: number | string;
		paintOrder?: number | string;
		panose1?: number | string;
		pathLength?: number | string;
		patternContentUnits?: string;
		patternTransform?: number | string;
		patternUnits?: string;
		pointerEvents?: number | string;
		points?: string;
		pointsAtX?: number | string;
		pointsAtY?: number | string;
		pointsAtZ?: number | string;
		preserveAlpha?: number | string;
		preserveAspectRatio?: string;
		primitiveUnits?: number | string;
		r?: number | string;
		radius?: number | string;
		refX?: number | string;
		refY?: number | string;
		renderingIntent?: number | string;
		repeatCount?: number | string;
		repeatDur?: number | string;
		requiredExtensions?: number | string;
		requiredFeatures?: number | string;
		restart?: number | string;
		result?: string;
		rotate?: number | string;
		rx?: number | string;
		ry?: number | string;
		scale?: number | string;
		seed?: number | string;
		shapeRendering?: number | string;
		slope?: number | string;
		spacing?: number | string;
		specularConstant?: number | string;
		specularExponent?: number | string;
		speed?: number | string;
		spreadMethod?: string;
		startOffset?: number | string;
		stdDeviation?: number | string;
		stemh?: number | string;
		stemv?: number | string;
		stitchTiles?: number | string;
		stopColor?: string;
		stopOpacity?: number | string;
		strikethroughPosition?: number | string;
		strikethroughThickness?: number | string;
		string?: number | string;
		stroke?: string;
		strokeDasharray?: string | number;
		strokeDashoffset?: string | number;
		strokeLinecap?: "butt" | "round" | "square" | "inherit";
		strokeLinejoin?: "miter" | "round" | "bevel" | "inherit";
		strokeMiterlimit?: string;
		strokeOpacity?: number | string;
		strokeWidth?: number | string;
		surfaceScale?: number | string;
		systemLanguage?: number | string;
		tableValues?: number | string;
		targetX?: number | string;
		targetY?: number | string;
		textAnchor?: string;
		textDecoration?: number | string;
		textLength?: number | string;
		textRendering?: number | string;
		to?: number | string;
		transform?: string;
		u1?: number | string;
		u2?: number | string;
		underlinePosition?: number | string;
		underlineThickness?: number | string;
		unicode?: number | string;
		unicodeBidi?: number | string;
		unicodeRange?: number | string;
		unitsPerEm?: number | string;
		vAlphabetic?: number | string;
		values?: string;
		vectorEffect?: number | string;
		version?: string;
		vertAdvY?: number | string;
		vertOriginX?: number | string;
		vertOriginY?: number | string;
		vHanging?: number | string;
		vIdeographic?: number | string;
		viewBox?: string;
		viewTarget?: number | string;
		visibility?: number | string;
		vMathematical?: number | string;
		widths?: number | string;
		wordSpacing?: number | string;
		writingMode?: number | string;
		x1?: number | string;
		x2?: number | string;
		x?: number | string;
		xChannelSelector?: string;
		xHeight?: number | string;
		xlinkActuate?: string;
		xlinkArcrole?: string;
		xlinkHref?: string;
		xlinkRole?: string;
		xlinkShow?: string;
		xlinkTitle?: string;
		xlinkType?: string;
		xmlBase?: string;
		xmlLang?: string;
		xmlns?: string;
		xmlnsXlink?: string;
		xmlSpace?: string;
		y1?: number | string;
		y2?: number | string;
		y?: number | string;
		yChannelSelector?: string;
		z?: number | string;
		zoomAndPan?: string;
	}

	interface PathAttributes {
		d: string;
	}

	interface EventHandler<E extends Event> {
		(event: E): void;
	}

	type ClipboardEventHandler = EventHandler<ClipboardEvent>;
	type CompositionEventHandler = EventHandler<CompositionEvent>;
	type DragEventHandler = EventHandler<DragEvent>;
	type FocusEventHandler = EventHandler<FocusEvent>;
	type KeyboardEventHandler = EventHandler<KeyboardEvent>;
	type MouseEventHandler = EventHandler<MouseEvent>;
	type TouchEventHandler = EventHandler<TouchEvent>;
	type UIEventHandler = EventHandler<UIEvent>;
	type WheelEventHandler = EventHandler<WheelEvent>;
	type AnimationEventHandler = EventHandler<AnimationEvent>;
	type TransitionEventHandler = EventHandler<TransitionEvent>;
	type GenericEventHandler = EventHandler<Event>;
	type PointerEventHandler = EventHandler<PointerEvent>;

	interface DOMAttributes extends preact.PreactDOMAttributes {
		// Image Events
		onLoad?: GenericEventHandler;
		onLoadCapture?: GenericEventHandler;
		onError?: GenericEventHandler;
		onErrorCapture?: GenericEventHandler;

		// Clipboard Events
		onCopy?: ClipboardEventHandler;
		onCopyCapture?: ClipboardEventHandler;
		onCut?: ClipboardEventHandler;
		onCutCapture?: ClipboardEventHandler;
		onPaste?: ClipboardEventHandler;
		onPasteCapture?: ClipboardEventHandler;

		// Composition Events
		onCompositionEnd?: CompositionEventHandler;
		onCompositionEndCapture?: CompositionEventHandler;
		onCompositionStart?: CompositionEventHandler;
		onCompositionStartCapture?: CompositionEventHandler;
		onCompositionUpdate?: CompositionEventHandler;
		onCompositionUpdateCapture?: CompositionEventHandler;

		// Focus Events
		onFocus?: FocusEventHandler;
		onFocusCapture?: FocusEventHandler;
		onBlur?: FocusEventHandler;
		onBlurCapture?: FocusEventHandler;

		// Form Events
		onChange?: GenericEventHandler;
		onChangeCapture?: GenericEventHandler;
		onInput?: GenericEventHandler;
		onInputCapture?: GenericEventHandler;
		onSearch?: GenericEventHandler;
		onSearchCapture?: GenericEventHandler;
		onSubmit?: GenericEventHandler;
		onSubmitCapture?: GenericEventHandler;
		onInvalid?: GenericEventHandler;
		onInvalidCapture?: GenericEventHandler;

		// Keyboard Events
		onKeyDown?: KeyboardEventHandler;
		onKeyDownCapture?: KeyboardEventHandler;
		onKeyPress?: KeyboardEventHandler;
		onKeyPressCapture?: KeyboardEventHandler;
		onKeyUp?: KeyboardEventHandler;
		onKeyUpCapture?: KeyboardEventHandler;

		// Media Events
		onAbort?: GenericEventHandler;
		onAbortCapture?: GenericEventHandler;
		onCanPlay?: GenericEventHandler;
		onCanPlayCapture?: GenericEventHandler;
		onCanPlayThrough?: GenericEventHandler;
		onCanPlayThroughCapture?: GenericEventHandler;
		onDurationChange?: GenericEventHandler;
		onDurationChangeCapture?: GenericEventHandler;
		onEmptied?: GenericEventHandler;
		onEmptiedCapture?: GenericEventHandler;
		onEncrypted?: GenericEventHandler;
		onEncryptedCapture?: GenericEventHandler;
		onEnded?: GenericEventHandler;
		onEndedCapture?: GenericEventHandler;
		onLoadedData?: GenericEventHandler;
		onLoadedDataCapture?: GenericEventHandler;
		onLoadedMetadata?: GenericEventHandler;
		onLoadedMetadataCapture?: GenericEventHandler;
		onLoadStart?: GenericEventHandler;
		onLoadStartCapture?: GenericEventHandler;
		onPause?: GenericEventHandler;
		onPauseCapture?: GenericEventHandler;
		onPlay?: GenericEventHandler;
		onPlayCapture?: GenericEventHandler;
		onPlaying?: GenericEventHandler;
		onPlayingCapture?: GenericEventHandler;
		onProgress?: GenericEventHandler;
		onProgressCapture?: GenericEventHandler;
		onRateChange?: GenericEventHandler;
		onRateChangeCapture?: GenericEventHandler;
		onSeeked?: GenericEventHandler;
		onSeekedCapture?: GenericEventHandler;
		onSeeking?: GenericEventHandler;
		onSeekingCapture?: GenericEventHandler;
		onStalled?: GenericEventHandler;
		onStalledCapture?: GenericEventHandler;
		onSuspend?: GenericEventHandler;
		onSuspendCapture?: GenericEventHandler;
		onTimeUpdate?: GenericEventHandler;
		onTimeUpdateCapture?: GenericEventHandler;
		onVolumeChange?: GenericEventHandler;
		onVolumeChangeCapture?: GenericEventHandler;
		onWaiting?: GenericEventHandler;
		onWaitingCapture?: GenericEventHandler;

		// MouseEvents
		onClick?: MouseEventHandler;
		onClickCapture?: MouseEventHandler;
		onContextMenu?: MouseEventHandler;
		onContextMenuCapture?: MouseEventHandler;
		onDblClick?: MouseEventHandler;
		onDblClickCapture?: MouseEventHandler;
		onDrag?: DragEventHandler;
		onDragCapture?: DragEventHandler;
		onDragEnd?: DragEventHandler;
		onDragEndCapture?: DragEventHandler;
		onDragEnter?: DragEventHandler;
		onDragEnterCapture?: DragEventHandler;
		onDragExit?: DragEventHandler;
		onDragExitCapture?: DragEventHandler;
		onDragLeave?: DragEventHandler;
		onDragLeaveCapture?: DragEventHandler;
		onDragOver?: DragEventHandler;
		onDragOverCapture?: DragEventHandler;
		onDragStart?: DragEventHandler;
		onDragStartCapture?: DragEventHandler;
		onDrop?: DragEventHandler;
		onDropCapture?: DragEventHandler;
		onMouseDown?: MouseEventHandler;
		onMouseDownCapture?: MouseEventHandler;
		onMouseEnter?: MouseEventHandler;
		onMouseEnterCapture?: MouseEventHandler;
		onMouseLeave?: MouseEventHandler;
		onMouseLeaveCapture?: MouseEventHandler;
		onMouseMove?: MouseEventHandler;
		onMouseMoveCapture?: MouseEventHandler;
		onMouseOut?: MouseEventHandler;
		onMouseOutCapture?: MouseEventHandler;
		onMouseOver?: MouseEventHandler;
		onMouseOverCapture?: MouseEventHandler;
		onMouseUp?: MouseEventHandler;
		onMouseUpCapture?: MouseEventHandler;

		// Selection Events
		onSelect?: GenericEventHandler;
		onSelectCapture?: GenericEventHandler;

		// Touch Events
		onTouchCancel?: TouchEventHandler;
		onTouchCancelCapture?: TouchEventHandler;
		onTouchEnd?: TouchEventHandler;
		onTouchEndCapture?: TouchEventHandler;
		onTouchMove?: TouchEventHandler;
		onTouchMoveCapture?: TouchEventHandler;
		onTouchStart?: TouchEventHandler;
		onTouchStartCapture?: TouchEventHandler;

		// Pointer Events
		onPointerOver?: PointerEventHandler;
		onPointerOverCapture?: PointerEventHandler;
		onPointerEnter?: PointerEventHandler;
		onPointerEnterCapture?: PointerEventHandler;
		onPointerDown?: PointerEventHandler;
		onPointerDownCapture?: PointerEventHandler;
		onPointerMove?: PointerEventHandler;
		onPointerMoveCapture?: PointerEventHandler;
		onPointerUp?: PointerEventHandler;
		onPointerUpCapture?: PointerEventHandler;
		onPointerCancel?: PointerEventHandler;
		onPointerCancelCapture?: PointerEventHandler;
		onPointerOut?: PointerEventHandler;
		onPointerOutCapture?: PointerEventHandler;
		onPointerLeave?: PointerEventHandler;
		onPointerLeaveCapture?: PointerEventHandler;
		onGotPointerCapture?: PointerEventHandler;
		onGotPointerCaptureCapture?: PointerEventHandler;
		onLostPointerCapture?: PointerEventHandler;
		onLostPointerCaptureCapture?: PointerEventHandler;

		// UI Events
		onScroll?: UIEventHandler;
		onScrollCapture?: UIEventHandler;

		// Wheel Events
		onWheel?: WheelEventHandler;
		onWheelCapture?: WheelEventHandler;

		// Animation Events
		onAnimationStart?: AnimationEventHandler;
		onAnimationStartCapture?: AnimationEventHandler;
		onAnimationEnd?: AnimationEventHandler;
		onAnimationEndCapture?: AnimationEventHandler;
		onAnimationIteration?: AnimationEventHandler;
		onAnimationIterationCapture?: AnimationEventHandler;

		// Transition Events
		onTransitionEnd?: TransitionEventHandler;
		onTransitionEndCapture?: TransitionEventHandler;
	}

	interface HTMLAttributes extends preact.ClassAttributes<any>, DOMAttributes {
		// Standard HTML Attributes
		accept?: string;
		acceptCharset?: string;
		accessKey?: string;
		action?: string;
		allowFullScreen?: boolean;
		allowTransparency?: boolean;
		alt?: string;
		async?: boolean;
		autocomplete?: string;
		autoComplete?: string;
		autocorrect?: string;
		autoCorrect?: string;
		autofocus?: boolean;
		autoFocus?: boolean;
		autoPlay?: boolean;
		capture?: boolean;
		cellPadding?: number | string;
		cellSpacing?: number | string;
		charSet?: string;
		challenge?: string;
		checked?: boolean;
		class?: string;
		className?: string;
		cols?: number;
		colSpan?: number;
		content?: string;
		contentEditable?: boolean;
		contextMenu?: string;
		controls?: boolean;
		controlsList?: string;
		coords?: string;
		crossOrigin?: string;
		data?: string;
		dateTime?: string;
		default?: boolean;
		defer?: boolean;
		dir?: string;
		disabled?: boolean;
		disableRemotePlayback?: boolean;
		download?: any;
		draggable?: boolean;
		encType?: string;
		form?: string;
		formAction?: string;
		formEncType?: string;
		formMethod?: string;
		formNoValidate?: boolean;
		formTarget?: string;
		frameBorder?: number | string;
		headers?: string;
		height?: number | string;
		hidden?: boolean;
		high?: number;
		href?: string;
		hrefLang?: string;
		for?: string;
		htmlFor?: string;
		httpEquiv?: string;
		icon?: string;
		id?: string;
		inputMode?: string;
		integrity?: string;
		is?: string;
		keyParams?: string;
		keyType?: string;
		kind?: string;
		label?: string;
		lang?: string;
		list?: string;
		loop?: boolean;
		low?: number;
		manifest?: string;
		marginHeight?: number;
		marginWidth?: number;
		max?: number | string;
		maxLength?: number;
		media?: string;
		mediaGroup?: string;
		method?: string;
		min?: number | string;
		minLength?: number;
		multiple?: boolean;
		muted?: boolean;
		name?: string;
		noValidate?: boolean;
		open?: boolean;
		optimum?: number;
		pattern?: string;
		placeholder?: string;
		playsInline?: boolean;
		poster?: string;
		preload?: string;
		radioGroup?: string;
		readOnly?: boolean;
		rel?: string;
		required?: boolean;
		role?: string;
		rows?: number;
		rowSpan?: number;
		sandbox?: string;
		scope?: string;
		scoped?: boolean;
		scrolling?: string;
		seamless?: boolean;
		selected?: boolean;
		shape?: string;
		size?: number;
		sizes?: string;
		slot?: string;
		span?: number;
		spellcheck?: boolean;
		src?: string;
		srcset?: string;
		srcDoc?: string;
		srcLang?: string;
		srcSet?: string;
		start?: number;
		step?: number | string;
		style?: string | {[key: string]: string | number};
		summary?: string;
		tabIndex?: number;
		target?: string;
		title?: string;
		type?: string;
		useMap?: string;
		value?: string | string[] | number;
		volume?: string | number;
		width?: number | string;
		wmode?: string;
		wrap?: string;

		// RDFa Attributes
		about?: string;
		datatype?: string;
		inlist?: any;
		prefix?: string;
		property?: string;
		resource?: string;
		typeof?: string;
		vocab?: string;

		// Microdata Attributes
		itemProp?: string;
		itemScope?: boolean;
		itemType?: string;
		itemID?: string;
		itemRef?: string;
	}

	interface IntrinsicElements {
		// HTML
		a: HTMLAttributes;
		abbr: HTMLAttributes;
		address: HTMLAttributes;
		area: HTMLAttributes;
		article: HTMLAttributes;
		aside: HTMLAttributes;
		audio: HTMLAttributes;
		b: HTMLAttributes;
		base: HTMLAttributes;
		bdi: HTMLAttributes;
		bdo: HTMLAttributes;
		big: HTMLAttributes;
		blockquote: HTMLAttributes;
		body: HTMLAttributes;
		br: HTMLAttributes;
		button: HTMLAttributes;
		canvas: HTMLAttributes;
		caption: HTMLAttributes;
		cite: HTMLAttributes;
		code: HTMLAttributes;
		col: HTMLAttributes;
		colgroup: HTMLAttributes;
		data: HTMLAttributes;
		datalist: HTMLAttributes;
		dd: HTMLAttributes;
		del: HTMLAttributes;
		details: HTMLAttributes;
		dfn: HTMLAttributes;
		dialog: HTMLAttributes;
		div: HTMLAttributes;
		dl: HTMLAttributes;
		dt: HTMLAttributes;
		em: HTMLAttributes;
		embed: HTMLAttributes;
		fieldset: HTMLAttributes;
		figcaption: HTMLAttributes;
		figure: HTMLAttributes;
		footer: HTMLAttributes;
		form: HTMLAttributes;
		h1: HTMLAttributes;
		h2: HTMLAttributes;
		h3: HTMLAttributes;
		h4: HTMLAttributes;
		h5: HTMLAttributes;
		h6: HTMLAttributes;
		head: HTMLAttributes;
		header: HTMLAttributes;
		hgroup: HTMLAttributes;
		hr: HTMLAttributes;
		html: HTMLAttributes;
		i: HTMLAttributes;
		iframe: HTMLAttributes;
		img: HTMLAttributes;
		input: HTMLAttributes;
		ins: HTMLAttributes;
		kbd: HTMLAttributes;
		keygen: HTMLAttributes;
		label: HTMLAttributes;
		legend: HTMLAttributes;
		li: HTMLAttributes;
		link: HTMLAttributes;
		main: HTMLAttributes;
		map: HTMLAttributes;
		mark: HTMLAttributes;
		menu: HTMLAttributes;
		menuitem: HTMLAttributes;
		meta: HTMLAttributes;
		meter: HTMLAttributes;
		nav: HTMLAttributes;
		noscript: HTMLAttributes;
		object: HTMLAttributes;
		ol: HTMLAttributes;
		optgroup: HTMLAttributes;
		option: HTMLAttributes;
		output: HTMLAttributes;
		p: HTMLAttributes;
		param: HTMLAttributes;
		picture: HTMLAttributes;
		pre: HTMLAttributes;
		progress: HTMLAttributes;
		q: HTMLAttributes;
		rp: HTMLAttributes;
		rt: HTMLAttributes;
		ruby: HTMLAttributes;
		s: HTMLAttributes;
		samp: HTMLAttributes;
		script: HTMLAttributes;
		section: HTMLAttributes;
		select: HTMLAttributes;
		slot: HTMLAttributes;
		small: HTMLAttributes;
		source: HTMLAttributes;
		span: HTMLAttributes;
		strong: HTMLAttributes;
		style: HTMLAttributes;
		sub: HTMLAttributes;
		summary: HTMLAttributes;
		sup: HTMLAttributes;
		table: HTMLAttributes;
		tbody: HTMLAttributes;
		td: HTMLAttributes;
		textarea: HTMLAttributes;
		tfoot: HTMLAttributes;
		th: HTMLAttributes;
		thead: HTMLAttributes;
		time: HTMLAttributes;
		title: HTMLAttributes;
		tr: HTMLAttributes;
		track: HTMLAttributes;
		u: HTMLAttributes;
		ul: HTMLAttributes;
		"var": HTMLAttributes;
		video: HTMLAttributes;
		wbr: HTMLAttributes;

		//SVG
		svg: SVGAttributes;
		animate: SVGAttributes;
		circle: SVGAttributes;
		clipPath: SVGAttributes;
		defs: SVGAttributes;
		desc: SVGAttributes;
		ellipse: SVGAttributes;
		feBlend: SVGAttributes;
		feColorMatrix: SVGAttributes;
		feComponentTransfer: SVGAttributes;
		feComposite: SVGAttributes;
		feConvolveMatrix: SVGAttributes;
		feDiffuseLighting: SVGAttributes;
		feDisplacementMap: SVGAttributes;
		feFlood: SVGAttributes;
		feGaussianBlur: SVGAttributes;
		feImage: SVGAttributes;
		feMerge: SVGAttributes;
		feMergeNode: SVGAttributes;
		feMorphology: SVGAttributes;
		feOffset: SVGAttributes;
		feSpecularLighting: SVGAttributes;
		feTile: SVGAttributes;
		feTurbulence: SVGAttributes;
		filter: SVGAttributes;
		foreignObject: SVGAttributes;
		g: SVGAttributes;
		image: SVGAttributes;
		line: SVGAttributes;
		linearGradient: SVGAttributes;
		marker: SVGAttributes;
		mask: SVGAttributes;
		path: SVGAttributes;
		pattern: SVGAttributes;
		polygon: SVGAttributes;
		polyline: SVGAttributes;
		radialGradient: SVGAttributes;
		rect: SVGAttributes;
		stop: SVGAttributes;
		symbol: SVGAttributes;
		text: SVGAttributes;
		tspan: SVGAttributes;
		use: SVGAttributes;
	}
}


// All cleanups then all effects (+4.5 B)

// afterPaintEffects.some(component => {

// 		component.__hooks._pendingEffects.forEach(invokeEffect);

// 		component.__hooks._pendingEffects = [];

keep keep keep keep keep replace replace replace replace replace replace replace replace replace replace replace replace keep keep keep keep keep

// import React from '../../src';
import * as React from '../../src';
class ReactIsh extends React.Component {
	render() {
		return
Text

	}
}


keep keep keep keep add keep keep keep keep keep

// NOTE: this order is (at the time of writing) intentionally different from

// React. React calls all disposes across all components, and then invokes all

// effects across all components. We call disposes and effects in order of components:

// for each component, call its disposes and then its effects. If presented with a

// compelling use case to support inter-component dispose dependencies, then rewrite this

// test to test React's order. In other words, if there is a use case to support calling

// all disposes across components then re-order the lines below to demonstrate the desired behavior.

import { setupRerender, act } from 'preact/test-utils';
import { createElement as h, render, Component } from 'preact';
import { setupScratch, teardown } from '../../../test/_util/helpers';
import { useState, useReducer, useEffect, useLayoutEffect, useRef } from '../../src';
import { scheduleEffectAssert } from '../_util/useEffectUtil';

/** @jsx h */

describe('combinations', () => {

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

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

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

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

	it('can mix useState hooks', () => {
		const states = {};
		const setStates = {};

		function Parent() {
			const [state1, setState1] = useState(1);
			const [state2, setState2] = useState(2);

			Object.assign(states, { state1, state2 });
			Object.assign(setStates, { setState1, setState2 });

			return <Child />;
		}

		function Child() {
			const [state3, setState3] = useState(3);
			const [state4, setState4] = useState(4);

			Object.assign(states, { state3, state4 });
			Object.assign(setStates, { setState3, setState4 });

			return null;
		}

		render(<Parent />, scratch);
		expect(states).to.deep.equal({ state1: 1, state2: 2, state3: 3, state4: 4 });

		setStates.setState2(n => n * 10);
		setStates.setState3(n => n * 10);
		rerender();
		expect(states).to.deep.equal({ state1: 1, state2: 20, state3: 30, state4: 4 });
	});

	it('can rerender asynchronously from within an effect', () => {
		const didRender = sinon.spy();

		function Comp() {
			const [counter, setCounter] = useState(0);

			useEffect(() => { if (counter === 0) setCounter(1); });

			didRender(counter);
			return null;
		}

		render(<Comp />, scratch);

		return scheduleEffectAssert(() => {
			rerender();
			expect(didRender).to.have.been.calledTwice.and.calledWith(1);
		});
	});

	it('can rerender synchronously from within a layout effect', () => {
		const didRender = sinon.spy();

		function Comp() {
			const [counter, setCounter] = useState(0);

			useLayoutEffect(() => { if (counter === 0) setCounter(1); });

			didRender(counter);
			return null;
		}

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

		expect(didRender).to.have.been.calledTwice.and.calledWith(1);
	});

	it('can access refs from within a layout effect callback', () => {
		let refAtLayoutTime;

		function Comp() {
			const input = useRef();

			useLayoutEffect(() => {
				refAtLayoutTime = input.current;
			});

			return <input ref={input} value="hello" />;
		}

		render(<Comp />, scratch);

		expect(refAtLayoutTime.value).to.equal('hello');
	});

	it('can use multiple useState and useReducer hooks', () => {
		let states = [];
		let dispatchState4;

		function reducer1(state, action) {
			switch (action.type) {
				case 'increment': return state + action.count;
			}
		}

		function reducer2(state, action) {
			switch (action.type) {
				case 'increment': return state + action.count * 2;
			}
		}

		function Comp() {
			const [state1] = useState(0);
			const [state2] = useReducer(reducer1, 10);
			const [state3] = useState(1);
			const [state4, dispatch] = useReducer(reducer2, 20);

			dispatchState4 = dispatch;
			states.push(state1, state2, state3, state4);

			return null;
		}

		render(<Comp />, scratch);

		expect(states).to.deep.equal([0, 10, 1, 20]);

		states = [];

		dispatchState4({ type: 'increment', count: 10 });
		rerender();

		expect(states).to.deep.equal([0, 10, 1, 40]);
	});

	it('ensures useEffect always schedule after the next paint following a redraw effect, when using the default debounce strategy', () => {
		let effectCount = 0;

		function Comp() {
			const [counter, setCounter] = useState(0);

			useEffect(() => {
				if (counter === 0) setCounter(1);
				effectCount++;
			});

			return null;
		}

		render(<Comp />, scratch);

		return scheduleEffectAssert(() => {
			expect(effectCount).to.equal(1);
		});
	});

	it('should not reuse functional components with hooks', () => {
		let updater = { first: undefined, second: undefined };
		function Foo(props) {
			let [v, setter] = useState(0);
			updater[props.id] = () => setter(++v);
			return
{v}
;
		}

		let updateParent;
		class App extends Component {
			constructor(props) {
				super(props);
				this.state = { active: true };
				updateParent = () => this.setState(p => ({ active: !p.active }));
			}

			render() {
				return (

						{this.state.active && <Foo id="first" />}
						<Foo id="second" />

				);
			}
		}

		render(<App />, scratch);
		act(() => updater.second());
		expect(scratch.textContent).to.equal('01');

		updateParent();
		rerender();
		expect(scratch.textContent).to.equal('1');

		updateParent();
		rerender();

		expect(scratch.textContent).to.equal('01');
	});

	it('should have a right call order with correct dom ref', () => {
		let i = 0, set;
		const calls = [];

		function Inner() {
			useLayoutEffect(() => {
				calls.push('layout inner call ' + scratch.innerHTML);
				return () => calls.push('layout inner dispose ' + scratch.innerHTML);
			});
			useEffect(() => {
				calls.push('effect inner call ' + scratch.innerHTML);
				return () => calls.push('effect inner dispose ' + scratch.innerHTML);
			});
			return hello {i};
		}

		function Outer() {
			i++;
			const [state, setState] = useState(false);
			set = () => setState(!state);
			useLayoutEffect(() => {
				calls.push('layout outer call ' + scratch.innerHTML);
				return () => calls.push('layout outer dispose ' + scratch.innerHTML);
			});
			useEffect(() => {
				calls.push('effect outer call ' + scratch.innerHTML);
				return () => calls.push('effect outer dispose ' + scratch.innerHTML);
			});
			return <Inner />;
		}

		act(() => render(<Outer />, scratch));
		expect(calls).to.deep.equal([
			'layout inner call hello 1',
			'layout outer call hello 1',
			'effect inner call hello 1',
			'effect outer call hello 1'
		]);

		act(() => set());
		expect(calls).to.deep.equal([
			'layout inner call hello 1',
			'layout outer call hello 1',
			'effect inner call hello 1',
			'effect outer call hello 1',
			'layout inner dispose hello 2',
			'layout outer dispose hello 2',
			'layout inner call hello 2',
			'layout outer call hello 2',
			'effect inner dispose hello 2',
			'effect outer dispose hello 2',
			'effect inner call hello 2',
			'effect outer call hello 2'
		]);
	});
});


import { setupRerender } from 'preact/test-utils';
import { createElement, render, Component } from '../../../src/index';
import { setupScratch, teardown } from '../../_util/helpers';

/** @jsx createElement */

describe('Lifecycle methods', () => {

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

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

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

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

	describe('#componentDidUpdate', () => {
		it('should be passed previous props and state', () => {

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

			let prevPropsArg;
			let prevStateArg;
			let snapshotArg;
			let curProps;
			let curState;

			class Foo extends Component {
				constructor(props) {
					super(props);
					this.state = {
						value: 0
					};
					updateState = () => this.setState({
						value: this.state.value + 1
					});
				}
				static getDerivedStateFromProps(props, state) {
					// NOTE: Don't do this in real production code!
					// https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html
					return {
						value: state.value + 1
					};
				}
				componentDidUpdate(prevProps, prevState, snapshot) {
					// These object references might be updated later so copy
					// object so we can assert their values at this snapshot in time
					prevPropsArg = { ...prevProps };
					prevStateArg = { ...prevState };
					snapshotArg = snapshot;

					curProps = { ...this.props };
					curState = { ...this.state };
				}
				render() {
					return
{this.state.value}
;
				}
			}

			// Expectation:
			// `prevState` in componentDidUpdate should be
			// the state before setState and getDerivedStateFromProps was called.
			// `this.state` in componentDidUpdate should be
			// the updated state after getDerivedStateFromProps was called.

			// Initial render
			// state.value: initialized to 0 in constructor, 0 -> 1 in gDSFP
			render(<Foo foo="foo" />, scratch);
			expect(scratch.firstChild.textContent).to.be.equal('1');
			expect(prevPropsArg).to.be.undefined;
			expect(prevStateArg).to.be.undefined;
			expect(snapshotArg).to.be.undefined;
			expect(curProps).to.be.undefined;
			expect(curState).to.be.undefined;

			// New props
			// state.value: 1 -> 2 in gDSFP
			render(<Foo foo="bar" />, scratch);
			expect(scratch.firstChild.textContent).to.be.equal('2');
			expect(prevPropsArg).to.deep.equal({ foo: 'foo' });
			expect(prevStateArg).to.deep.equal({ value: 1 });
			expect(snapshotArg).to.be.undefined;
			expect(curProps).to.deep.equal({ foo: 'bar' });
			expect(curState).to.deep.equal({ value: 2 });

			// New state
			// state.value: 2 -> 3 in updateState, 3 -> 4 in gDSFP
			updateState();
			rerender();
			expect(scratch.firstChild.textContent).to.be.equal('4');
			expect(prevPropsArg).to.deep.equal({ foo: 'bar' });
			expect(prevStateArg).to.deep.equal({ value: 2 });
			expect(snapshotArg).to.be.undefined;
			expect(curProps).to.deep.equal({ foo: 'bar' });
			expect(curState).to.deep.equal({ value: 4 });
		});

		it('cDU should not be called when sDU returned false', () => {
			let spy = sinon.spy();
			let c;

			class App extends Component {
				constructor() {
					super();
					c = this;
				}

				shouldComponentUpdate() {
					return false;
				}

				componentDidUpdate(prevProps) {
					spy(prevProps);
				}
			}

			render(<App />, scratch);
			c.setState({});
			rerender();

			expect(spy).to.not.be.called;
		});

		it('prevState argument should be the same object if state doesn\'t change', () => {
			let changeProps, cduPrevState, cduCurrentState;

			class PropsProvider extends Component {
				constructor() {
					super();
					this.state = { value: 0 };
					changeProps = this.changeReceiverProps.bind(this);
				}
				changeReceiverProps() {
					let value = (this.state.value + 1) % 2;
					this.setState({
						value
					});
				}
				render() {
					return <PropsReceiver value={this.state.value} />;
				}
			}

			class PropsReceiver extends Component {
				componentDidUpdate(prevProps, prevState) {
					cduPrevState = prevState;
					cduCurrentState = this.state;
				}
				render({ value }) {
					return
{value}
;
				}
			}

			render(<PropsProvider />, scratch);

			changeProps();
			rerender();

			expect(cduPrevState).to.equal(cduCurrentState);
		});

		it('prevState argument should be a different object if state does change', () => {
			let updateState, cduPrevState, cduCurrentState;

			class Foo extends Component {
				constructor() {
					super();
					this.state = { value: 0 };
					updateState = this.updateState.bind(this);
				}
				updateState() {
					let value = (this.state.value + 1) % 2;
					this.setState({
						value
					});
				}
				componentDidUpdate(prevProps, prevState) {
					cduPrevState = prevState;
					cduCurrentState = this.state;
				}
				render() {
					return
{this.state.value}
;
				}
			}

			render(<Foo />, scratch);

			updateState();
			rerender();

			expect(cduPrevState).to.not.equal(cduCurrentState);
		});

		it('prevProps argument should be the same object if props don\'t change', () => {
			let updateState, cduPrevProps, cduCurrentProps;

			class Foo extends Component {
				constructor() {
					super();
					this.state = { value: 0 };
					updateState = this.updateState.bind(this);
				}
				updateState() {
					let value = (this.state.value + 1) % 2;
					this.setState({
						value
					});
				}
				componentDidUpdate(prevProps) {
					cduPrevProps = prevProps;
					cduCurrentProps = this.props;
				}
				render() {
					return
{this.state.value}
;
				}
			}

			render(<Foo />, scratch);

			updateState();
			rerender();

			expect(cduPrevProps).to.equal(cduCurrentProps);
		});

		it('prevProps argument should be a different object if props do change', () => {
			let changeProps, cduPrevProps, cduCurrentProps;

			class PropsProvider extends Component {
				constructor() {
					super();
					this.state = { value: 0 };
					changeProps = this.changeReceiverProps.bind(this);
				}
				changeReceiverProps() {
					let value = (this.state.value + 1) % 2;
					this.setState({
						value
					});
				}
				render() {
					return <PropsReceiver value={this.state.value} />;
				}
			}

			class PropsReceiver extends Component {
				componentDidUpdate(prevProps) {
					cduPrevProps = prevProps;
					cduCurrentProps = this.props;
				}
				render({ value }) {
					return
{value}
;
				}
			}

			render(<PropsProvider />, scratch);

			changeProps();
			rerender();

			expect(cduPrevProps).to.not.equal(cduCurrentProps);
		});

		it('is invoked after refs are set', () => {
			const spy = sinon.spy();
			let inst;
			let i = 0;

			class App extends Component {
				componentDidUpdate() {
					expect(spy).to.have.been.calledOnceWith(scratch.firstChild);
				}

				render() {
					let ref = null;

					if (i > 0) {
						// Add ref after mount (i > 0)
						ref = spy;
					}

					i++;
					inst = this;
					return
;
				}
			}

			render(<App />, scratch);
			expect(spy).not.to.have.been.called;

			inst.setState({});
			rerender();

			expect(spy).to.have.been.calledOnceWith(scratch.firstChild);
		});

		it('should be called after children are mounted', () => {
			let log = [];

			class Inner extends Component {
				componentDidMount() {
					log.push('Inner mounted');

					// Verify that the component is actually mounted when this
					// callback is invoked.
					expect(scratch.querySelector('#inner')).to.equalNode(this.base);
				}

				render() {
					return
;
				}
			}

			class Outer extends Component {
				componentDidUpdate() {
					log.push('Outer updated');
				}

				render(props) {
					return props.renderInner ? <Inner /> :
;
				}
			}

			render(<Outer renderInner={false} />, scratch);
			render(<Outer renderInner />, scratch);

			expect(log).to.deep.equal(['Inner mounted', 'Outer updated']);
		});

		it('should be called after parent DOM elements are updated', () => {
			let setValue;
			let outerChildText;

			class Outer extends Component {
				constructor(p, c) {
					super(p, c);

					this.state = { i: 0 };
					setValue = i => this.setState({ i });
				}

				render(props, { i }) {
					return (

							<Inner i={i} {...props} />

Outer: {i}

					);
				}
			}

			class Inner extends Component {
				componentDidUpdate() {
					// At this point, the parent's
 tag should've been updated with the latest value
					outerChildText = scratch.querySelector('#parent-child').textContent;
				}

				render(props, { i }) {
					return
Inner: {i}
;
				}
			}

			sinon.spy(Inner.prototype, 'componentDidUpdate');

			// Initial render
			render(<Outer />, scratch);
			expect(Inner.prototype.componentDidUpdate).to.not.have.been.called;

			// Set state with a new i
			const newValue = 5;
			setValue(newValue);
			rerender();

			expect(Inner.prototype.componentDidUpdate).to.have.been.called;
			expect(outerChildText).to.equal(`Outer: ${newValue.toString()}`);
		});
	});
});


import { setupRerender } from 'preact/test-utils';
import { createElement, render, Component } from '../../../src/index';
import { setupScratch, teardown } from '../../_util/helpers';

/** @jsx createElement */

describe('Lifecycle methods', () => {

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

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

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

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

	describe('#componentWillReceiveProps', () => {
		it('should update state when called setState in componentWillReceiveProps', () => {
			let componentState;

			class Foo extends Component {
				constructor(props) {
					super(props);
					this.state = {
						dummy: 0
					};
				}
				componentDidMount() {
					// eslint-disable-next-line react/no-did-mount-set-state
					this.setState({ dummy: 1 });
				}
				render() {
					return <Bar dummy={this.state.dummy} />;
				}
			}
			class Bar extends Component {
				constructor(props) {
					super(props);
					this.state = {
						value: 0
					};
				}
				componentWillReceiveProps() {
					this.setState({ value: 1 });
				}
				render() {
					componentState = this.state;
					return
;
				}
			}

			render(<Foo />, scratch);
			rerender();

			expect(componentState).to.deep.equal({ value: 1 });

			const cWRP = Foo.prototype.componentWillReceiveProps;
			delete Foo.prototype.componentWillReceiveProps;

			Foo.prototype.shouldComponentUpdate = cWRP;

			render(null, scratch);
			render(<Foo />, scratch);
			rerender();

			expect(componentState, 'via shouldComponentUpdate').to.deep.equal({ value: 1 });

			delete Foo.prototype.shouldComponentUpdate;
			Foo.prototype.componentWillUpdate = cWRP;

			render(null, scratch);
			render(<Foo />, scratch);
			rerender();

			expect(componentState, 'via componentWillUpdate').to.deep.equal({ value: 1 });
		});

		it('should NOT be called on initial render', () => {
			class ReceivePropsComponent extends Component {
				componentWillReceiveProps() {}
				render() {
					return
;
				}
			}
			sinon.spy(ReceivePropsComponent.prototype, 'componentWillReceiveProps');
			render(<ReceivePropsComponent />, scratch);
			expect(ReceivePropsComponent.prototype.componentWillReceiveProps).not.to.have.been.called;
		});

		// See last paragraph of cWRP section https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops
		it('should not be called on setState or forceUpdate', () => {
			let spy = sinon.spy();
			let spyInner = sinon.spy();
			let c;

			class Inner extends Component {
				componentWillReceiveProps() {
					spyInner();
				}

				render() {
					return
foo
;
				}
			}

			class Outer extends Component {
				constructor() {
					super();
					c = this;
				}

				componentWillReceiveProps() {
					spy();
				}

				render() {
					return <Inner />;
				}
			}

			render(<Outer />, scratch);
			expect(spy).to.not.be.called;

			c.setState({});
			rerender();
			expect(spy).to.not.be.called;
			expect(spyInner).to.be.calledOnce;
			spy.resetHistory();
			spyInner.resetHistory();

			c.forceUpdate();
			rerender();
			expect(spy).to.not.be.called;
			expect(spyInner).to.be.calledOnce;
		});

		it('should be called when rerender with new props from parent', () => {
			let doRender;
			class Outer extends Component {
				constructor(p, c) {
					super(p, c);
					this.state = { i: 0 };
				}
				componentDidMount() {
					doRender = () => this.setState({ i: this.state.i + 1 });
				}
				render(props, { i }) {
					return <Inner i={i} {...props} />;
				}
			}
			class Inner extends Component {
				componentWillMount() {
					expect(this.props.i).to.be.equal(0);
				}
				componentWillReceiveProps(nextProps) {
					expect(nextProps.i).to.be.equal(1);
				}
				render() {
					return
;
				}
			}
			sinon.spy(Inner.prototype, 'componentWillReceiveProps');
			sinon.spy(Outer.prototype, 'componentDidMount');

			// Initial render
			render(<Outer />, scratch);
			expect(Inner.prototype.componentWillReceiveProps).not.to.have.been.called;

			// Rerender inner with new props
			doRender();
			rerender();
			expect(Inner.prototype.componentWillReceiveProps).to.have.been.called;
		});

		it('should be called in right execution order', () => {
			let doRender;
			class Outer extends Component {
				constructor(p, c) {
					super(p, c);
					this.state = { i: 0 };
				}
				componentDidMount() {
					doRender = () => this.setState({ i: this.state.i + 1 });
				}
				render(props, { i }) {
					return <Inner i={i} {...props} />;
				}
			}
			class Inner extends Component {
				componentDidUpdate() {
					expect(Inner.prototype.componentWillReceiveProps).to.have.been.called;
					expect(Inner.prototype.componentWillUpdate).to.have.been.called;
				}
				componentWillReceiveProps() {
					expect(Inner.prototype.componentWillUpdate).not.to.have.been.called;
					expect(Inner.prototype.componentDidUpdate).not.to.have.been.called;
				}
				componentWillUpdate() {
					expect(Inner.prototype.componentWillReceiveProps).to.have.been.called;
					expect(Inner.prototype.componentDidUpdate).not.to.have.been.called;
				}
				shouldComponentUpdate() {
					expect(Inner.prototype.componentWillReceiveProps).to.have.been.called;
					expect(Inner.prototype.componentWillUpdate).not.to.have.been.called;
					return true;
				}
				render() {
					return
;
				}
			}
			sinon.spy(Inner.prototype, 'componentWillReceiveProps');
			sinon.spy(Inner.prototype, 'componentDidUpdate');
			sinon.spy(Inner.prototype, 'componentWillUpdate');
			sinon.spy(Inner.prototype, 'shouldComponentUpdate');
			sinon.spy(Outer.prototype, 'componentDidMount');

			render(<Outer />, scratch);
			doRender();
			rerender();

			expect(Inner.prototype.componentWillReceiveProps).to.have.been.calledBefore(Inner.prototype.componentWillUpdate);
			expect(Inner.prototype.componentWillReceiveProps).to.have.been.calledBefore(Inner.prototype.shouldComponentUpdate);
			expect(Inner.prototype.componentWillUpdate).to.have.been.calledBefore(Inner.prototype.componentDidUpdate);
		});
	});
});


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

/** @jsx h */

describe('useState', () => {

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

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

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

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

	it('serves the same state across render calls', () => {
		const stateHistory = [];

		function Comp() {
			const [state] = useState({ a: 1 });
			stateHistory.push(state);
			return null;
		}

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

		expect(stateHistory).to.deep.equal([{ a: 1 }, { a: 1 }]);
		expect(stateHistory[0]).to.equal(stateHistory[1]);
	});

	it('can initialize the state via a function', () => {
		const initState = sinon.spy(() => { 1; });

		function Comp() {
			useState(initState);
			return null;
		}

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

		expect(initState).to.be.calledOnce;
	});

	it('does not rerender on equal state', () => {
		let lastState;
		let doSetState;

		const Comp = sinon.spy(() => {
			const [state, setState] = useState(0);
			lastState = state;
			doSetState = setState;
			return null;
		});

		render(<Comp />, scratch);
		expect(lastState).to.equal(0);
		expect(Comp).to.be.calledOnce;

		doSetState(0);
		rerender();
		expect(lastState).to.equal(0);
		expect(Comp).to.be.calledOnce;

		doSetState(() => 0);
		rerender();
		expect(lastState).to.equal(0);
		expect(Comp).to.be.calledOnce;
	});

	it('rerenders when setting the state', () => {
		let lastState;
		let doSetState;

		const Comp = sinon.spy(() => {
			const [state, setState] = useState(0);
			lastState = state;
			doSetState = setState;
			return null;
		});

		render(<Comp />, scratch);
		expect(lastState).to.equal(0);
		expect(Comp).to.be.calledOnce;

		doSetState(1);
		rerender();
		expect(lastState).to.equal(1);
		expect(Comp).to.be.calledTwice;

		// Updater function style
		doSetState(current => current * 10);
		rerender();
		expect(lastState).to.equal(10);
		expect(Comp).to.be.calledThrice;
	});

	it('can be set by another component', () => {
		function StateContainer() {
			const [count, setCount] = useState(0);
			return (

Count: {count}

				<Increment increment={() => setCount(c => c + 10)} />

);
		}

		function Increment(props) {
			return <button onClick={props.increment}>Increment</button>;
		}

		render(<StateContainer />, scratch);
		expect(scratch.textContent).to.include('Count: 0');

		const button = scratch.querySelector('button');
		button.click();

		rerender();
		expect(scratch.textContent).to.include('Count: 10');
	});

	it('should correctly initialize', () => {
		let scopedThing = 'hi';
		let arg;

		function useSomething() {
			const args = useState(setup);
			function setup(thing = scopedThing) {
				arg = thing;
				return thing;
			}
			return args;
		}

		const App = () => {
			const [state] = useSomething();
			return
{state}
;
		};

		render(<App />, scratch);

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

	it('should handle queued useState', () => {
		function Message({ message, onClose }) {
			const [isVisible, setVisible] = useState(Boolean(message));
			const [prevMessage, setPrevMessage] = useState(message);

			if (message !== prevMessage) {
				setPrevMessage(message);
				setVisible(Boolean(message));
			}

			if (!isVisible) {
				return null;
			}
			return
{message}
;
		}

		function App() {
			const [message, setMessage] = useState('Click Here!!');
			return (
				<Message
					onClose={() => {
						setMessage('');
					}}
					message={message}
				/>
			);
		}

		render(<App />, scratch);
		expect(scratch.textContent).to.equal('Click Here!!');
		const text = scratch.querySelector('p');
		text.click();
		rerender();
		expect(scratch.innerHTML).to.equal('');
	});
});


import React, {
	render,
	createElement,
	cloneElement,
	findDOMNode,
	Component,
	unmountComponentAtNode,
	createFactory,
	unstable_batchedUpdates
} from '../../src';
import { createElement as preactH } from 'preact';
import { setupScratch, teardown, createEvent } from '../../../test/_util/helpers';

let ce = type => document.createElement(type);
let text = text => document.createTextNode(text);

describe('preact-compat', () => {

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

	beforeEach(() => {
		scratch = setupScratch();
		proto = document.createElement('div').constructor.prototype;
		sinon.spy(proto, 'addEventListener');
		sinon.spy(proto, 'removeEventListener');
	});

	afterEach(() => {
		proto.addEventListener.restore();
		proto.removeEventListener.restore();
		teardown(scratch);
	});

	describe('render()', () => {
		it('should be exported', () => {
			expect(React)
				.to.have.property('render')
				.that.is.a('function')
				.that.equals(render);
		});

		it('should replace isomorphic content', () => {
			let root = ce('div');
			let initialChild = ce('div');
			initialChild.appendChild(text('initial content'));
			root.appendChild(initialChild);

			render(
dynamic content
, root);
			expect(root)
				.to.have.property('textContent')
				.that.is.a('string')
				.that.equals('dynamic content');
		});

		it('should remove extra elements', () => {
			let root = ce('div');
			let inner = ce('div');

			root.appendChild(inner);

			let c1 = ce('div');
			c1.appendChild(text('isomorphic content'));
			inner.appendChild(c1);

			let c2 = ce('div');
			c2.appendChild(text('extra content'));
			inner.appendChild(c2);

			render(
dynamic content
, root);
			expect(root)
				.to.have.property('textContent')
				.that.is.a('string')
				.that.equals('dynamic content');
		});

		// Note: Replacing text nodes inside the root itself is currently unsupported.
		// We do replace them everywhere else, though.
		it('should remove text nodes', () => {
			let root = ce('div');

			let div = ce('div');
			root.appendChild(div);
			div.appendChild(text('Text Content'));
			div.appendChild(text('More Text Content'));

			render(
dynamic content
, root);
			expect(root)
				.to.have.property('textContent')
				.that.is.a('string')
				.that.equals('dynamic content');
		});

		it('should support onAnimationEnd', () => {
			const func = () => { };
			render(
, scratch);

			expect(proto.addEventListener).to.have.been.calledOnce
				.and.to.have.been.calledWithExactly('animationend', sinon.match.func, false);

			expect(scratch.firstChild._listeners).to.deep.equal({
				animationend: func
			});

			render(
, scratch);
			expect(proto.removeEventListener).to.have.been.calledOnce
				.and.to.have.been.calledWithExactly('animationend', sinon.match.func, false);
		});

		it('should support onTransitionEnd', () => {
			const func = () => { };
			render(
, scratch);

			expect(proto.addEventListener).to.have.been.calledOnce
				.and.to.have.been.calledWithExactly('transitionend', sinon.match.func, false);

			expect(scratch.firstChild._listeners).to.deep.equal({
				transitionend: func
			});

			render(
, scratch);
			expect(proto.removeEventListener).to.have.been.calledOnce
				.and.to.have.been.calledWithExactly('transitionend', sinon.match.func, false);
		});

		it('should support defaultValue', () => {
			render(<input defaultValue="foo" />, scratch);
			expect(scratch.firstElementChild).to.have.property('value', 'foo');
		});

		it('should ignore defaultValue when value is 0', () => {
			render(<input defaultValue={2} value={0} />, scratch);
			expect(scratch.firstElementChild.value).to.equal('0');
		});

		it('should call the callback', () => {
			let spy = sinon.spy();
			render(
, scratch, spy);
			expect(spy).to.be.calledOnce;
			expect(spy).to.be.calledWithExactly();
		});

		// Issue #1727
		it('should destroy the any existing DOM nodes inside the container', () => {
			scratch.appendChild(document.createElement('div'));
			scratch.appendChild(document.createElement('div'));

			render(foo, scratch);
			expect(scratch.innerHTML).to.equal('foo');
		});

		it('should only destroy existing DOM nodes on first render', () => {
			scratch.appendChild(document.createElement('div'));
			scratch.appendChild(document.createElement('div'));

			render(<input />, scratch);

			let child = scratch.firstChild;
			child.focus();
			render(<input />, scratch);
			expect(document.activeElement.nodeName).to.equal('INPUT');
		});
	});

	describe('createFactory', () => {
		it('should create a DOM element', () => {
			render(createFactory('span')({ class: 'foo' }, '1'), scratch);
			expect(scratch.innerHTML).to.equal('1');
		});

		it('should create a component', () => {
			const Foo = ({ id, children }) =>
foo {children}
;
			render(createFactory(Foo)({ id: 'value' }, 'bar'), scratch);
			expect(scratch.innerHTML).to.equal('
foo bar
');
		});
	});

	describe('createElement()', () => {
		it('should be exported', () => {
			expect(React)
				.to.have.property('createElement')
				.that.is.a('function')
				.that.equals(createElement);
		});

		it('should normalize vnodes', () => {
			let vnode =
t
;
			let $$typeof = 0xeac7;
			try {
				// eslint-disable-next-line
				if (Function.prototype.toString.call(eval('Sym' + 'bol.for')).match(/\[native code\]/)) {
					// eslint-disable-next-line
					$$typeof = eval('Sym' + 'bol.for("react.element")');
				}
			}
			catch (e) { }
			expect(vnode).to.have.property('$$typeof', $$typeof);
			expect(vnode).to.have.property('type', 'div');
			expect(vnode).to.have.property('props').that.is.an('object');
			expect(vnode.props).to.have.property('children');
			expect(vnode.props.children).to.have.property('$$typeof', $$typeof);
			expect(vnode.props.children).to.have.property('type', 'a');
			expect(vnode.props.children).to.have.property('props').that.is.an('object');
			expect(vnode.props.children.props).to.eql({ children: 't' });
		});

		it('should normalize onChange', () => {
			let props = { onChange() { } };

			function expectToBeNormalized(vnode, desc) {
				expect(vnode, desc)
					.to.have.property('props')
					.with.all.keys(['oninput'].concat(vnode.props.type ? 'type' : []))
					.and.property('oninput').that.is.a('function');
			}

			function expectToBeUnmodified(vnode, desc) {
				expect(vnode, desc).to.have.property('props').eql({
					...props,
					...(vnode.props.type ? { type: vnode.props.type } : {})
				});
			}

			expectToBeUnmodified(
, '
');
			expectToBeUnmodified(<input {...props} type="radio" />, '<input type="radio">');
			expectToBeUnmodified(<input {...props} type="checkbox" />, '<input type="checkbox">');
			expectToBeUnmodified(<input {...props} type="file" />, '<input type="file">');

			expectToBeNormalized(<textarea {...props} />, '<textarea>');
			expectToBeNormalized(<input {...props} />, '<input>');
			expectToBeNormalized(<input {...props} type="text" />, '<input type="text">');

		});

		it('should normalize beforeinput event listener', () => {
			let spy = sinon.spy();
			render(<input onBeforeInput={spy} />, scratch);
			scratch.firstChild.dispatchEvent(createEvent('beforeinput'));
			expect(spy).to.be.calledOnce;
		});
	});

	describe('Component', () => {
		it('should be exported', () => {
			expect(React)
				.to.have.property('Component')
				.that.is.a('function')
				.that.equals(Component);
		});
	});

	describe('cloneElement', () => {
		it('should clone elements', () => {
			let element = <foo a="b" c="d">ab</foo>;
			expect(cloneElement(element)).to.eql(element);
		});

		it('should support props.children', () => {
			let element = <foo children={b} />;
			let clone = cloneElement(element);
			expect(clone).to.eql(element);
			expect(cloneElement(clone).props.children).to.eql(element.props.children);
		});

		it('children take precedence over props.children', () => {
			let element = <foo children={c}>
b
</foo>;
			let clone = cloneElement(element);
			expect(clone).to.eql(element);
			expect(clone.props.children.type).to.eql('div');
		});

		it('should support children in prop argument', () => {
			let element = <foo />;
			let children = [b];
			let clone = cloneElement(element, { children });
			expect(clone.props.children).to.eql(children);
		});

		it('children argument takes precedence over props.children', () => {
			let element = <foo />;
			let childrenA = [b];
			let childrenB = [
c
];
			let clone = cloneElement(element, { children: childrenA }, ...childrenB);
			expect(clone.props.children).to.eql(childrenB);
		});

		it('children argument takes precedence over props.children even if falsey', () => {
			let element = <foo />;
			let childrenA = [b];
			let clone = cloneElement(element, { children: childrenA }, undefined);
			expect(clone.children).to.eql(undefined);
		});

		it('should skip cloning on invalid element', () => {
			let element = { foo: 42 };
			let clone = cloneElement(element);
			expect(clone).to.eql(element);
		});

		it('should work with jsx constructor from core', () => {
			function Foo(props) {
				return
{props.value}
;
			}

			let clone = cloneElement(preactH(Foo), { value: 'foo' });
			render(clone, scratch);
			expect(scratch.textContent).to.equal('foo');
		});
	});

	describe('findDOMNode()', () => {
		class Helper extends React.Component {
			render({ something }) {
				if (something == null) return null;
				if (something === false) return null;
				return
;
			}
		}

		it.skip('should return DOM Node if render is not false nor null', () => {
			const helper = React.render(<Helper />, scratch);
			expect(findDOMNode(helper)).to.be.instanceof(Node);
		});

		it('should return null if given null', () => {
			expect(findDOMNode(null)).to.be.null;
		}),

		it('should return a regular DOM Element if given a regular DOM Element', () => {
			let scratch = document.createElement('div');
			expect(findDOMNode(scratch)).to.equalNode(scratch);
		}),

		// NOTE: React.render() returning false or null has the component pointing
		// 			to no DOM Node, in contrast, Preact always render an empty Text DOM Node.
		it('should return null if render returns false', () => {
			const helper = React.render(<Helper something={false} />, scratch);
			expect(findDOMNode(helper)).to.be.null;
		});

		// NOTE: React.render() returning false or null has the component pointing
		// 			to no DOM Node, in contrast, Preact always render an empty Text DOM Node.
		it('should return null if render returns null', () => {
			const helper = React.render(<Helper something={null} />, scratch);
			expect(findDOMNode(helper)).to.be.null;
		});
	});

	describe('unmountComponentAtNode', () => {
		it('should unmount a root node', () => {
			const App = () =>
foo
;
			render(<App />, scratch);

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

		it('should do nothing if root is not mounted', () => {
			expect(unmountComponentAtNode(scratch)).to.equal(false);
			expect(scratch.innerHTML).to.equal('');
		});
	});

	describe('unstable_batchedUpdates', () => {
		it('should call the callback', () => {
			const spy = sinon.spy();
			unstable_batchedUpdates(spy);
			expect(spy).to.be.calledOnce;
		});

		it('should call callback with only one arg', () => {
			const spy = sinon.spy();
			unstable_batchedUpdates(spy, 'foo', 'bar');
			expect(spy).to.be.calledWithExactly('foo');
		});
	});

	it('should patch events', () => {
		let spy = sinon.spy();
		render(
, scratch);
		scratch.firstChild.click();

		expect(spy).to.be.calledOnce;
		expect(spy.args[0][0]).to.haveOwnProperty('persist');
		expect(spy.args[0][0]).to.haveOwnProperty('nativeEvent');
	});

	it('should normalize ondoubleclick event', () => {
		let vnode =
 null} />;
		expect(vnode.props).to.haveOwnProperty('ondblclick');
	});

	it('should normalize onChange for textarea', () => {
		let vnode = <textarea onChange={() => null} />;
		expect(vnode.props).to.haveOwnProperty('oninput');
		expect(vnode.props).to.not.haveOwnProperty('onchange');

		vnode = <textarea oninput={() => null} onChange={() => null} />;
		expect(vnode.props).to.haveOwnProperty('oninput');
		expect(vnode.props).to.not.haveOwnProperty('onchange');
	});

	it('should not normalize onChange for range', () => {
		render(<input type="range" onChange={() => null} />, scratch);
		expect(scratch.firstChild._listeners).to.haveOwnProperty('change');
		expect(scratch.firstChild._listeners).to.not.haveOwnProperty('input');
	});

	it('should normalize class+className even on components', () => {
		function Foo(props) {
			return
foo
;
		}
		render(<Foo class="foo" />, scratch);
		expect(scratch.firstChild.className).to.equal('foo');
		render(null, scratch);

		render(<Foo className="foo" />, scratch);
		expect(scratch.firstChild.className).to.equal('foo');
	});
});


# Tests skipped to get CI to pass

- Fragment
	- ✖ should not preserve state between array nested in fragment and double nested array
	- ✖ should preserve state between double nested fragment and double nested array
- hydrate
	- ✖ should override incorrect pre-existing DOM with VNodes passed into render

Also:

- Extend Fragment preserving state tests to track unmounting lifecycle callbacks to verify
  components are properly unmounted. I think all 'should not preserve' tests are the ones
  that will have unmount operations.


import { createElement as h, options, render, createRef, Component, Fragment } from 'preact';
import { lazy, Suspense } from 'preact/compat';
import { useState, useEffect, useLayoutEffect, useMemo, useCallback } from 'preact/hooks';
import { act, setupRerender } from 'preact/test-utils';
import { setupScratch, teardown, clearOptions, serializeHtml } from '../../../test/_util/helpers';
import { serializeVNode, initDebug } from '../../src/debug';
import * as PropTypes from 'prop-types';

/** @jsx h */

describe('debug', () => {
	let scratch;
	let errors = [];
	let warnings = [];

	let diffSpy;

	beforeEach(() => {
		errors = [];
		warnings = [];
		scratch = setupScratch();
		sinon.stub(console, 'error').callsFake(e => errors.push(e));
		sinon.stub(console, 'warn').callsFake(w => warnings.push(w));

		clearOptions();
		diffSpy = sinon.spy();
		options._diff = diffSpy;

		initDebug();
	});

	afterEach(() => {

		/** @type {*} */
		(console.error).restore();
		(console.warn).restore();
		teardown(scratch);
	});

	it('should call previous options', () => {
		render(
, scratch);
		expect(diffSpy, 'diff').to.have.been.called;
	});

	it('should print an error on rendering on undefined parent', () => {
		let fn = () => render(
, undefined);
		expect(fn).to.throw(/render/);
	});

	it('should print an error on rendering on invalid parent', () => {
		let fn = () => render(
, 6);
		expect(fn).to.throw(/valid HTML node/);
		expect(fn).to.throw(/
 {
		const App = () =>
;
		let fn = () => render(<App />, 6);
		expect(fn).to.throw(/<App/);
		expect(fn).to.throw(/6/);
		fn = () => render(<App />, {});
		expect(fn).to.throw(/<App/);
		expect(fn).to.throw(/[object Object]/);
	});

	it('should print an error with (class) component name when available', () => {
		class App extends Component {
			render() {
				return
;
			}
		}
		let fn = () => render(<App />, 6);
		expect(fn).to.throw(/<App/);
	});

	it('should print an error on undefined component', () => {
		let fn = () => render(h(undefined), scratch);
		expect(fn).to.throw(/createElement/);
	});

	it('should print an error on invalid object component', () => {
		let fn = () => render(h({}), scratch);
		expect(fn).to.throw(/createElement/);
	});

	it('should add __source to the vnode in debug mode.', () => {
		const vnode = h('div', {
			__source: {
				fileName: 'div.jsx',
				lineNumber: 3
			}
		});
		expect(vnode.__source).to.deep.equal({
			fileName: 'div.jsx',
			lineNumber: 3
		});
		expect(vnode.props.__source).to.be.undefined;
	});

	it('should add __self to the vnode in debug mode.', () => {
		const vnode = h('div', {
			__self: {}
		});
		expect(vnode.__self).to.deep.equal({});
		expect(vnode.props.__self).to.be.undefined;
	});

	// TODO: Fix this test. It only passed before because App was the first component
	// into render so currentComponent in hooks/index.js wasn't set yet. However,
	// any children under App wouldn't have thrown the error if they did what App
	// did because currentComponent would be set to App.
	// In other words, hooks never clear currentComponent so once it is set, it won't
	// be unset
	it.skip('should throw an error when using a hook outside a render', () => {
		const Foo = props => props.children;
		class App extends Component {
			componentWillMount() {
				useState();
			}

			render() {
				return
test
;
			}
		}
		const fn = () => act(() => render(<Foo><App /></Foo>, scratch));
		expect(fn).to.throw(/Hook can only be invoked from render/);
	});

	// TODO: Fix this test. It only passed before because render was never called.
	// Once render is called, currentComponent is set and never unset so calls to
	// hooks outside of components would still work.
	it.skip('should throw an error when invoked outside of a component', () => {
		function Foo(props) {
			useEffect(() => {}); // Pretend to use a hook
			return props.children;
		}

		const fn = () => act(() => {
			render(<Foo>Hello!</Foo>, scratch);
			useState();
		});
		expect(fn).to.throw(/Hook can only be invoked from render/);
	});

	it('should warn for argumentless useEffect hooks', () => {
		const App = () => {
			const [state] = useState('test');
			useEffect(() => 'test');
			return (

{state}

			);
		};
		render(<App />, scratch);
		expect(warnings[0]).to.match(/You should provide an array of arguments/);
		render(<App />, scratch);
		expect(warnings[1]).to.be.undefined;
	});

	it('should warn for argumentless useLayoutEffect hooks', () => {
		const App = () => {
			const [state] = useState('test');
			useLayoutEffect(() => 'test');
			return (

{state}

			);
		};
		render(<App />, scratch);
		expect(warnings[0]).to.match(/You should provide an array of arguments/);
		render(<App />, scratch);
		expect(warnings[1]).to.be.undefined;
	});

	it('should not warn for argumented effect hooks', () => {
		const App = () => {
			const [state] = useState('test');
			useLayoutEffect(() => 'test', []);
			useEffect(() => 'test', [state]);
			return (

{state}

			);
		};
		const fn = () => act(() => render(<App />, scratch));
		expect(fn).to.not.throw();
	});

	it('should print an error on double jsx conversion', () => {
		let Foo =
;
		let fn = () => render(h(<Foo />), scratch);
		expect(fn).to.throw(/createElement/);
	});

	it('should throw errors when accessing certain attributes', () => {
		const vnode = h('div', null);
		expect(() => vnode).to.not.throw();
		expect(() => vnode.attributes).to.throw(/use vnode.props/);
		expect(() => vnode.nodeName).to.throw(/use vnode.type/);
		expect(() => vnode.children).to.throw(/use vnode.props.children/);
		expect(() => vnode.attributes = {}).to.throw(/use vnode.props/);
		expect(() => vnode.nodeName = 'test').to.throw(/use vnode.type/);
		expect(() => vnode.children = [
]).to.throw(/use vnode.props.children/);
	});

	it('should print an error when component is an array', () => {
		let fn = () => render(h([
]), scratch);
		expect(fn).to.throw(/createElement/);
	});

	it('should warn when calling setState inside the constructor', () => {
		class Foo extends Component {
			constructor(props) {
				super(props);
				this.setState({ foo: true });
			}
			render() {
				return
foo
;
			}
		}

		render(<Foo />, scratch);
		expect(console.warn).to.be.calledOnce;
		expect(console.warn.args[0]).to.match(/no-op/);
	});

	it('should print an error when child is a plain object', () => {
		let fn = () => render(
{{}}
, scratch);
		expect(fn).to.throw(/not valid/);
	});

	it('should warn for useless useMemo calls', () => {
		const App = () => {
			const [people] = useState([40, 20, 60, 80]);
			const retiredPeople = useMemo(() => people.filter(x => x >= 60));
			const cb = useCallback(() => () => 'test');
			return
{retiredPeople.map(x => x)}
;
		};
		render(<App />, scratch);
		expect(warnings.length).to.equal(2);
	});

	it('should warn when non-array args is passed', () => {
		const App = () => {
			const foo = useMemo(() => 'foo', 12);
			return
{foo}
;
		};
		render(<App />, scratch);
		expect(warnings[0]).to.match(/without passing arguments/);
	});

	it('should print an error on invalid refs', () => {
		let fn = () => render(
, scratch);
		expect(fn).to.throw(/createRef/);

		// Allow strings for compat
		let vnode =
;

		/** @type {*} */
		(vnode).$$typeof = 'foo';
		render(vnode, scratch);
		expect(console.error).to.not.be.called;
	});

	it('should not print for null as a handler', () => {
		let fn = () => render(
, scratch);
		expect(fn).not.to.throw();
	});

	it('should not print for undefined as a handler', () => {
		let fn = () => render(
, scratch);
		expect(fn).not.to.throw();
	});

	it('should not print for attributes starting with on for Components', () => {
		const Comp = () =>
online
;
		let fn = () => render(<Comp online={false} />, scratch);
		expect(fn).not.to.throw();
	});

	it('should print an error on invalid handler', () => {
		let fn = () => render(
, scratch);
		expect(fn).to.throw(/"onclick" property should be a function/);
	});

	it('should NOT print an error on valid refs', () => {
		let noop = () => {};
		render(
, scratch);

		let ref = createRef();
		render(
, scratch);
		expect(console.error).to.not.be.called;
	});

	it('should throw an error when missing Suspense', () => {
		const Foo = () =>
Foo
;
		const LazyComp = lazy(() => new Promise(resolve => resolve({ default: Foo })));
		const fn = () => {
			render((
				<Fragment>
					<LazyComp />
				</Fragment>
			), scratch);
		};

		expect(fn).to.throw(/Missing Suspense/gi);
	});

	describe('duplicate keys', () => {
		const List = props =>
{props.children}
;
		const ListItem = props =>
{props.children}
;

		it('should print an error on duplicate keys with DOM nodes', () => {
			render(

, scratch);
			expect(console.error).to.be.calledOnce;
		});

		it('should allow distinct object keys', () => {
			const A = { is: 'A' };
			const B = { is: 'B' };
			render(

, scratch);
			expect(console.error).not.to.be.called;
		});

		it('should print an error for duplicate object keys', () => {
			const A = { is: 'A' };
			render(

, scratch);
			expect(console.error).to.be.calledOnce;
		});

		it('should print an error on duplicate keys with Components', () => {
			function App() {
				return (
					<List>
						<ListItem key="a">a</ListItem>
						<ListItem key="b">b</ListItem>
						<ListItem key="b">d</ListItem>
						<ListItem key="d">d</ListItem>
					</List>
				);
			}

			render(<App />, scratch);
			expect(console.error).to.be.calledOnce;
		});

		it('should print an error on duplicate keys with Fragments', () => {
			function App() {
				return (
					<Fragment>
						<List key="list">
							<ListItem key="a">a</ListItem>
							<ListItem key="b">b</ListItem>
							<Fragment key="b">
								{/* Should be okay to duplicate keys since these are inside a Fragment */}
								<ListItem key="a">c</ListItem>
								<ListItem key="b">d</ListItem>
								<ListItem key="c">e</ListItem>
							</Fragment>
							<ListItem key="f">f</ListItem>
						</List>

sibling

					</Fragment>
				);
			}

			render(<App />, scratch);
			expect(console.error).to.be.calledTwice;
		});
	});

	describe('serializeVNode', () => {
		it('should prefer a function component\'s displayName', () => {
			function Foo() {
				return
;
			}
			Foo.displayName = 'Bar';

			expect(serializeVNode(<Foo />)).to.equal('<Bar />');
		});

		it('should prefer a class component\'s displayName', () => {
			class Bar extends Component {
				render() {
					return
;
				}
			}
			Bar.displayName = 'Foo';

			expect(serializeVNode(<Bar />)).to.equal('<Foo />');
		});

		it('should serialize vnodes without children', () => {
			expect(serializeVNode(
)).to.equal('
');
		});

		it('should serialize vnodes with children', () => {
			expect(serializeVNode(
Hello World
)).to.equal('
..
');
		});

		it('should serialize components', () => {
			function Foo() {
				return
;
			}
			expect(serializeVNode(<Foo />)).to.equal('<Foo />');
		});

		it('should serialize props', () => {
			expect(serializeVNode(
)).to.equal('
');

			let noop = () => {};
			expect(serializeVNode(
))
				.to.equal('
');

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

			expect(serializeVNode(<Foo foo={[1, 2, 3]} />))
				.to.equal('<Foo foo="1,2,3" />');
		});
	});

	describe('table markup', () => {
		it('missing
/
/
/
', () => {
			const Table = () => (

hi

			);
			render(
, scratch);
			expect(console.error).to.be.calledOnce;
		});

		it('missing
 with
', () => {
			const Table = () => (

hi

			);
			render(
, scratch);
			expect(console.error).to.be.calledOnce;
		});

		it('missing
 with
', () => {
			const Table = () => (

hi

			);
			render(
, scratch);
			expect(console.error).to.be.calledOnce;
		});

		it('missing
 with
', () => {
			const Table = () => (

hi

			);
			render(
, scratch);
			expect(console.error).to.be.calledOnce;
		});

		it('missing
', () => {
			const Table = () => (

						Hi

			);
			render(
, scratch);
			expect(console.error).to.be.calledOnce;
		});

		it('missing
 with td component', () => {
			const Cell = ({ children }) => {children}	;
			const Table = () => (

						<Cell>Hi</Cell>

			);
			render(
, scratch);
			expect(console.error).to.be.calledOnce;
		});

		it('missing
 with th component', () => {
			const Cell = ({ children }) => {children}	;
			const Table = () => (

						<Cell>Hi</Cell>

			);
			render(
, scratch);
			expect(console.error).to.be.calledOnce;
		});

		it('Should accept  instead of  in
', () => {
			const Table = () => (

							Hi

			);
			render(
, scratch);
			expect(console.error).to.not.be.called;
		});

		it('Accepts well formed table with TD components', () => {
			const Cell = ({ children }) => {children}	;
			const Table = () => (

							Head

							Body

							<Cell>Body</Cell>

			);
			render(
, scratch);
			expect(console.error).to.not.be.called;
		});

		it('Accepts well formed table', () => {
			const Table = () => (

							Head

							Body

							Body

			);
			render(
, scratch);
			expect(console.error).to.not.be.called;
		});

		it('Accepts minimal well formed table', () => {
			const Table = () => (

						Head

						Body

			);
			render(
, scratch);
			expect(console.error).to.not.be.called;
		});
	});

	describe('PropTypes', () => {
		it('should fail if props don\'t match prop-types', () => {
			function Foo(props) {
				return
{props.text}
;
			}

			Foo.propTypes = {
				text: PropTypes.string.isRequired
			};

			render(<Foo />, scratch);

			expect(console.error).to.be.calledOnce;
			expect(errors[0].includes('required')).to.equal(true);
		});

		it('should render with error logged when validator gets signal and throws exception', () => {
			function Baz(props) {
				return
{props.unhappy}
;
			}

			Baz.propTypes = {
				unhappy: function alwaysThrows(obj, key) { if (obj[key] === 'signal') throw Error('got prop'); }
			};

			render(<Baz unhappy={'signal'} />, scratch);

			expect(console.error).to.be.calledOnce;
			expect(errors[0].includes('got prop')).to.equal(true);
			expect(serializeHtml(scratch)).to.equal('
signal
');
		});

		it('should not print to console when types are correct', () => {
			function Bar(props) {
				return
{props.text}
;
			}

			Bar.propTypes = {
				text: PropTypes.string.isRequired
			};

			render(<Bar text="foo" />, scratch);
			expect(console.error).to.not.be.called;
		});

		it('should validate propTypes inside lazy()', () => {
			const rerender = setupRerender();

			function Baz(props) {
				return
{props.unhappy}
;
			}

			Baz.propTypes = {
				unhappy: function alwaysThrows(obj, key) { if (obj[key] === 'signal') throw Error('got prop inside lazy()'); }
			};

			const loader = Promise.resolve({ default: Baz });
			const LazyBaz = lazy(() => loader);

			const suspense = (
				<Suspense fallback={
fallback...
}>
					<LazyBaz unhappy="signal" />
				</Suspense>
			);
			render(suspense, scratch);

			expect(console.error).to.not.be.called;

			return loader
				.then(() => Promise.all(suspense._component._suspensions))
				.then(() => {
					rerender();
					expect(errors.length).to.equal(1);
					expect(errors[0].includes('got prop')).to.equal(true);
					expect(serializeHtml(scratch)).to.equal('
signal
');
				});
		});

		it('should throw on missing <Suspense>', () => {
			function Foo() {
				throw Promise.resolve();
			}

			expect(() => render(<Foo />, scratch)).to.throw;
		});

		describe('warn for PropTypes on lazy()', () => {
			it('should log the function name', () => {
				const loader = Promise.resolve({ default: function MyLazyLoadedComponent() { return
Hi there
; } });
				const FakeLazy = lazy(() => loader);
				FakeLazy.propTypes = {};
				const suspense = (
					<Suspense fallback={
fallback...
} >
						<FakeLazy />
					</Suspense>
				);
				render(suspense, scratch);

				return loader
					.then(() => Promise.all(suspense._component._suspensions))
					.then(() => {
						expect(console.warn).to.be.calledTwice;
						expect(warnings[1].includes('MyLazyLoadedComponent')).to.equal(true);
					});
			});

			it('should log the displayName', () => {
				function MyLazyLoadedComponent() { return
Hi there
; }
				MyLazyLoadedComponent.displayName = 'HelloLazy';
				const loader = Promise.resolve({ default: MyLazyLoadedComponent });
				const FakeLazy = lazy(() => loader);
				FakeLazy.propTypes = {};
				const suspense = (
					<Suspense fallback={
fallback...
} >
						<FakeLazy />
					</Suspense>
				);
				render(suspense, scratch);

				return loader
					.then(() => Promise.all(suspense._component._suspensions))
					.then(() => {
						expect(console.warn).to.be.calledTwice;
						expect(warnings[1].includes('HelloLazy')).to.equal(true);
					});
			});

			it('should not log a component if lazy throws', () => {
				const loader = Promise.reject(new Error('Hey there'));
				const FakeLazy = lazy(() => loader);
				FakeLazy.propTypes = {};
				render(
					<Suspense fallback={
fallback...
} >
						<FakeLazy />
					</Suspense>,
					scratch
				);

				return loader.catch(() => {
					expect(console.warn).to.be.calledOnce;
				});
			});

			it('should not log a component if lazy\'s loader throws', () => {
				const FakeLazy = lazy(() => { throw new Error('Hello'); });
				FakeLazy.propTypes = {};
				let error;
				try {
					render(
						<Suspense fallback={
fallback...
} >
							<FakeLazy />
						</Suspense>,
						scratch
					);
				}
				catch (e) {
					error = e;
				}

				expect(console.warn).to.be.calledOnce;
				expect(error).not.to.be.undefined;
				expect(error.message).to.eql('Hello');
			});
		});
	});
});


import { createElement } from 'preact';
import React from 'react';
import { createStore } from 'redux';
import { connect, Provider } from 'react-redux';

const store = createStore((state = { value: 0 }, action) => {
	switch (action.type) {
		case 'increment':
			return { value: state.value + 1 };
		case 'decrement':
			return { value: state.value - 1 };
		default:
			return state;
	}
});

class Child extends React.Component {
	render() {
		return (

Child #1: {this.props.foo}

				<ConnectedChild2 />

		);
	}
}
const ConnectedChild = connect(store => ({ foo: store.value }))(Child);

class Child2 extends React.Component {
	render() {
		return
Child #2: {this.props.foo}
;
	}
}
const ConnectedChild2 = connect(store => ({ foo: store.value }))(Child2);

export default function Redux() {
	return (

Counter

			<Provider store={store}>
				<ConnectedChild />
			</Provider>

			<button onClick={() => store.dispatch({ type: 'increment' })}>+</button>
			<button onClick={() => store.dispatch({ type: 'decrement' })}>-</button>

	);
}


The MIT License (MIT)

Copyright (c) 2015-present Jason Miller

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


import { createElement, Component } from 'preact';
import { select as d3select, mouse as d3mouse } from 'd3-selection';
import { scaleLinear } from 'd3-scale';
import Pythagoras from './pythagoras';

export default class PythagorasDemo extends Component {
	svg = {
		width: 1280,
		height: 600
	};

	state = {
		currentMax: 0,
		baseW: 80,
		heightFactor: 0,
		lean: 0
	};

	realMax = 11;

	svgRef = c => {
		this.svgElement = c;
	};

	scaleFactor = scaleLinear().domain([this.svg.height, 0]).range([0, .8]);

	scaleLean = scaleLinear().domain([0, this.svg.width/2, this.svg.width]).range([.5, 0, -.5]);

	onMouseMove = event => {
		let [x, y] = d3mouse(this.svgElement);

		this.setState({
			heightFactor: this.scaleFactor(y),
			lean: this.scaleLean(x)
		});
	};

	restart = () => {
		this.setState({ currentMax: 0 });
		this.next();
	};

	next = () => {
		let { currentMax } = this.state;

		if (currentMax < this.realMax) {
			this.setState({ currentMax: currentMax + 1 });
			this.timer = setTimeout(this.next, 500);
		}
	};

	componentDidMount() {
		this.selected = d3select(this.svgElement).on('mousemove', this.onMouseMove);
		this.next();
	}

	componentWillUnmount() {
		this.selected.on('mousemove', null);
		clearTimeout(this.timer);
	}

	render({ }, { currentMax, baseW, heightFactor, lean }) {
		let { width, height } = this.svg;

		return (
			<div class="App">
				<svg width={width} height={height} ref={this.svgRef}>
					<Pythagoras
						w={baseW}
						h={baseW}
						heightFactor={heightFactor}
						lean={lean}
						x={width/2-40}
						y={height-baseW}
						lvl={0}
						maxlvl={currentMax}
					/>
				</svg>
			</div>
		);
	}
}


import { act } from 'preact/test-utils';
import { createElement as h, render } from 'preact';
import { setupScratch, teardown } from '../../../test/_util/helpers';
import { useEffect } from '../../src';
import { useEffectAssertions } from './useEffectAssertions.test';
import { scheduleEffectAssert } from '../_util/useEffectUtil';

/** @jsx h */

describe('useEffect', () => {

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

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

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

	useEffectAssertions(useEffect, scheduleEffectAssert);

	it('calls the effect immediately if another render is about to start', () => {
		const cleanupFunction = sinon.spy();
		const callback = sinon.spy(() => cleanupFunction);

		function Comp() {
			useEffect(callback);
			return null;
		}

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

		expect(cleanupFunction).to.be.not.called;
		expect(callback).to.be.calledOnce;

		render(<Comp />, scratch);

		expect(cleanupFunction).to.be.calledOnce;
		expect(callback).to.be.calledTwice;
	});

	it('cancels the effect when the component get unmounted before it had the chance to run it', () => {
		const cleanupFunction = sinon.spy();
		const callback = sinon.spy(() => cleanupFunction);

		function Comp() {
			useEffect(callback);
			return null;
		}

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

		return scheduleEffectAssert(() => {
			expect(cleanupFunction).to.not.be.called;
			expect(callback).to.not.be.called;
		});
	});

	it('should execute multiple effects in same component in the right order', () => {
		let executionOrder = [];
		const App = ({ i }) => {
			executionOrder = [];
			useEffect(() => {
				executionOrder.push('action1');
				return () => executionOrder.push('cleanup1');
			}, [i]);
			useEffect(() => {
				executionOrder.push('action2');
				return () => executionOrder.push('cleanup2');
			}, [i]);
			return
Test
;
		};
		act(() => render(<App i={0} />, scratch));
		act(() => render(<App i={2} />, scratch));
		expect(executionOrder).to.deep.equal(['cleanup1', 'cleanup2', 'action1', 'action2']);
	});
});


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.