Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@bikeshaving/crank

Package Overview
Dependencies
Maintainers
1
Versions
44
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@bikeshaving/crank - npm Package Compare versions

Comparing version 0.2.0-beta.3 to 0.2.0-beta.4

8

cjs/dom.d.ts

@@ -1,2 +0,2 @@

import { Renderer, TagProps } from "./index";
import { Children, Context, ElementValue, Renderer, TagProps } from "./index";
declare module "index" {

@@ -6,7 +6,7 @@ interface EventMap extends GlobalEventHandlersEventMap {

}
declare class DOMRenderer extends Renderer<Node> {
declare class DOMRenderer extends Renderer<Node, string | undefined> {
render(children: Children, root: Node, ctx?: Context): Promise<ElementValue<Node>> | ElementValue<Node>;
parse(text: string): DocumentFragment;
scope(tag: string | symbol, props: Record<string, any>, scope: string | undefined): string | undefined;
// TODO: cache createElement calls and use cloneNode
create<TTag extends string | symbol>(tag: TTag, props: Record<string, any>, ns: string | undefined): Node;
parse(text: string): DocumentFragment;
patch<TTag extends string | symbol>(tag: TTag, props: TagProps<TTag>, el: Element, ns: string | undefined): void;

@@ -13,0 +13,0 @@ arrange<TTag extends string | symbol>(tag: TTag, props: Record<string, any>, parent: Node, children: Array<Node | string>): void;

@@ -8,4 +8,23 @@ 'use strict';

const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
// TODO: override render to provide a better error message when a node is not passed in as the second argument
class DOMRenderer extends index.Renderer {
render(children, root, ctx) {
if (!(root instanceof Node)) {
throw new TypeError("root is not a node");
}
return super.render(children, root, ctx);
}
parse(text) {
if (typeof document.createRange === "function") {
return document.createRange().createContextualFragment(text);
}
else {
const fragment = document.createDocumentFragment();
const childNodes = new DOMParser().parseFromString(text, "text/html").body
.childNodes;
for (let i = 0; i < childNodes.length; i++) {
fragment.appendChild(childNodes[i]);
}
return fragment;
}
}
scope(tag, props, scope) {

@@ -22,3 +41,2 @@ switch (tag) {

}
// TODO: cache createElement calls and use cloneNode
create(tag, props, ns) {

@@ -31,21 +49,4 @@ if (typeof tag !== "string") {

}
if (ns !== undefined) {
return document.createElementNS(ns, tag);
}
return document.createElement(tag);
return ns ? document.createElementNS(ns, tag) : document.createElement(tag);
}
parse(text) {
if (typeof document.createRange === "function") {
return document.createRange().createContextualFragment(text);
}
else {
const fragment = document.createDocumentFragment();
const childNodes = new DOMParser().parseFromString(text, "text/html").body
.childNodes;
for (let i = 0; i < childNodes.length; i++) {
fragment.appendChild(childNodes[i]);
}
return fragment;
}
}
patch(tag, props, el, ns) {

@@ -89,6 +90,6 @@ for (let name in props) {

}
else if (value === false || value == null) {
else if (!value) {
el.removeAttribute("class");
}
else if (ns === undefined) {
else if (!ns) {
el["className"] = value;

@@ -113,3 +114,3 @@ }

}
else if (ns === undefined && !forceAttribute && name in el) {
else if (!forceAttribute && !ns && name in el) {
el[name] = value;

@@ -132,3 +133,3 @@ }

if (tag === index.Portal && !(parent instanceof Node)) {
throw new Error("Portal must have a root of type Node");
throw new TypeError("Portal root is not a node");
}

@@ -135,0 +136,0 @@ if (!("innerHTML" in props) &&

@@ -9,3 +9,3 @@ import { ElementValue, Renderer } from "./index";

}
declare class StringRenderer extends Renderer<Node | string, void, string> {
declare class StringRenderer extends Renderer<Node | string, undefined, unknown, string> {
create(): Node;

@@ -12,0 +12,0 @@ escape(text: string): string;

@@ -83,3 +83,2 @@ 'use strict';

}
// NOTE: using void for the root type allows render to be called with only one argument
class StringRenderer extends index.Renderer {

@@ -86,0 +85,0 @@ create() {

@@ -1,56 +0,440 @@

type Tag = Component<any> | string | symbol;
/**
* Represents all valid values which can be used for the tag of an element.
*
* @remarks
* Elements whose tags are strings or symbols are called “host” or “intrinsic” elements, and their behavior is determined by the renderer, while elements whose tags are components are called “component” elements, and their behavior is determined by the execution of the component.
*/
type Tag = string | symbol | Component;
/**
* Maps the tag of an element to its expected props.
*
* @typeparam TTag - The element’s tag.
*/
type TagProps<TTag extends Tag> = TTag extends string ? JSX.IntrinsicElements[TTag] : TTag extends Component<infer TProps> ? TProps : unknown;
/***
* SPECIAL TAGS
* Crank provides a couple tags which have special meaning for the renderer.
***/
/**
* A special element tag for grouping multiple children within a parent.
*
* @remarks
* All iterables which appear in the element tree are implicitly wrapped in fragments. The Fragment tag is just the empty string, and you can use the empty string in createElement calls/transpiler options to avoid having to reference the Fragment export directly.
*/
declare const Fragment = "";
type Fragment = typeof Fragment;
/**
* A special element tag for creating a new element subtree with a different root, passed via the root prop.
*
* @remarks
* This element tag is useful for creating element trees with multiple roots. Renderer.prototype.render will implicitly wrap the children which have been passed in in an implicit Portal element.
*/
declare const Portal: any;
type Portal = typeof Portal;
/**
* A special element tag which copies whatever child appeared previously in the element’s position.
*
* @remarks
* Copy elements are useful when you want to prevent a subtree from updating as a performance optimization.
*/
declare const Copy: any;
type Copy = typeof Copy;
declare const Portal: any;
type Portal = typeof Portal;
/**
* A special element tag for injecting raw nodes into an element tree via its value prop.
*
* @remarks
* If the value prop is a string, Renderer.prototype.parse will be called on the string and the element’s rendered value will be the result.
*/
declare const Raw: any;
type Raw = typeof Raw;
/**
* Describes all valid singular values of an element tree.
*
* @remarks
* Arbitrary objects can also be safely rendered but they will be converted to a string using the toString method. We exclude them from this type to catch potential type errors.
*/
type Child = Element | string | number | boolean | null | undefined;
interface ChildIterable extends Iterable<Child | ChildIterable> {
}
/**
* Describes all valid values of an element tree, including arbitrarily nested iterables of such values.
*/
type Children = Child | ChildIterable;
type Component<TProps = any> = (this: Context<TProps>, props: TProps) => Iterator<Children, Children | void, any> | AsyncIterator<Children, Children | void, any> | PromiseLike<Children> | Children;
/**
* Represents all functions which can be used as a component.
*
* @typeparam TProps - The expected props for the component.
*
* @remarks
* The return type of iterator objects returned from components has to be void because typescript will infer most generators as having a void return type.
*/
type Component<TProps = any> = (this: Context<TProps>, props: TProps) => Children | PromiseLike<Children> | Iterator<Children, Children | void, any> | AsyncIterator<Children, Children | void, any>;
/**
* All nodes in the element tree are narrowed from the union in Child to NarrowedChild. This greatly simplifies element diffing.
*/
type NarrowedChild = Element | string | undefined;
type Key = unknown;
declare const ElementSymbol: unique symbol;
/**
* Elements are the basic building blocks of Crank applications. They are JavaScript objects which are interpreted by special classes called renderers to produce and manage stateful nodes.
*
* @typeparam TTag - the type of the tag of the element.
*
* @example
* // specific element types
* let div: Element<"div">;
* let portal: Element<Portal>;
* let myEl: Element<MyComponent>;
*
* // general element types
* let host: Element<string | symbol>;
* let component: Element<Component>;
*
* @remarks
* Typically, you use the createElement function to create elements and not this class directly.
*/
declare class Element<TTag extends Tag = Tag> {
/**
* The tag of the element. Can be a function, string or symbol depending on the kind of element.
*/
tag: TTag;
/**
* An object containing the “properties” of an element. These correspond to the attributes of the element when using JSX syntax.
*
* @remarks
* The props of an object are passed to most renderer host methods, and as the first argument to components.
*/
props: TagProps<TTag>;
/**
* A value which uniquely identifies an element from its siblings so that it can be added/updated/moved/removed by the identity of the key rather than its position within the parent.
*
* @remarks
* Passed to the element as the prop "crank-key".
*/
key: Key;
/**
* A callback which is called with the element’s value when the value is committed.
*
* @remarks
* Passed to the element as the prop "crank-ref".
*/
ref: Function | undefined;
/**
* @internal
* A unique symbol to identify elements as elements across versions and realms, and to protect against basic injection attacks.
* https://overreacted.io/why-do-react-elements-have-typeof-property/
*/
$$typeof: typeof ElementSymbol;
/**
* @internal
* flags - A bitmask. See ELEMENT FLAGS.
*/
_f: number;
/**
* @internal
* context - The Context object associated with this element.
*
* @remarks
* Created and assigned by the Renderer for component elements when it mounts the element tree.
*/
_ctx: Context<TagProps<TTag>> | undefined;
/**
* @internal
* children - The rendered children of the element.
*/
_ch: Array<NarrowedChild> | NarrowedChild;
/**
* @internal
* node - The node associated with the element.
*
* @remarks
* Set by Renderer.prototype.create when the component is mounted. This property will only be set for host elements.
*/
_n: any;
/**
* @internal
* fallback - The value of the element while it has never committed.
*
* @remarks
* If an element takes place of a previously rendered value but renders asynchronously, this property is set to the previously rendered value until the element commits. This allows asynchronously updating element trees to show something while pending.
*/
_fb: any;
/**
* @internal
* inflightPromise - The current pending async run of the element.
*
* @remarks
* This value is used to make sure element copies do not fulfill immediately, to set the fallback of the next element when the previous element commits, and as the yield value of async generator components with async children. It is unset when the element is committed.
*/
_inf: Promise<any> | undefined;
_fb: any;
/**
* @internal
* onNewValues - the resolve function of a promise which represents the next child result. See the chase function for more info.
*/
_onv: Function | undefined;
tag: TTag;
props: TagProps<TTag>;
key: Key;
ref: Function | undefined;
constructor(tag: TTag, props: TagProps<TTag>, key: Key, ref: Function | undefined);
}
declare function isElement(value: any): value is Element;
declare function createElement<TTag extends Tag>(tag: TTag, props?: TagProps<TTag> | null, ...children: Array<unknown>): Element<TTag>;
/**
* Creates an element with the specified tag, props and children.
*
* @remarks
* This function is usually used as a transpilation target for JSX transpilers, but it can also be called directly. It additionally extracts the crank-key and crank-ref props so they aren’t accessible to the renderer methods or components, and assigns the children prop according to the remaining arguments passed to the function.
*/
declare function createElement<TTag extends Tag>(tag: TTag, props?: TagProps<TTag> | null | undefined, ...children: Array<unknown>): Element<TTag>;
/**
* Clones a given element.
*
* @remarks
* Mainly used internally to make sure we don’t accidentally reuse elements in an element tree, because elements are directly mutated by the renderer.
*/
declare function cloneElement<TTag extends Tag>(el: Element<TTag>): Element<TTag>;
type ElementValue<T> = Array<T | string> | T | string | undefined;
type Scope = unknown;
declare class Renderer<TNode, TRoot = TNode, TResult = ElementValue<TNode>> {
/*** ELEMENT VALUE UTILITIES ***/
/**
* A helper type which repesents all the possible rendered values of an element.
*
* @typeparam TNode - The node type for the element assigned by the renderer.
*
* @remarks
* When asking the question, what is the value of a specific element, the answer varies depending on the type of the element. For host or Raw elements, the answer is simply the nodes (DOM nodes in the case of the DOM renderer) created for the element. For fragments, the values are usually an array of nodes. For portals, the value is undefined, because a Portal element’s root and children are opaque to parents. For components, the value can be any of the above, because the value of a component is determined by its children. Rendered values can also be strings or arrays of nodes and strings, in the case of a component or fragment with strings for children. All of these possible values are reflected in this utility type.
*/
type ElementValue<TNode> = Array<TNode | string> | TNode | string | undefined;
/**
* An abstract class which is subclassed to render to different target environments. This class is responsible for kicking off the rendering process, caching previous trees by root, and creating/mutating/disposing the nodes of the target environment.
*
* @typeparam TNode - The type of the node for a specific rendering environment. It is the type of the return value of Renderer.prototype.create and Renderer.prototype.parse.
* @typeparam TRoot - The type of the root for a specific rendering environment. It is the type of the second parameter passed to Renderer.prototype.render, as well as the expected type of the root portal.
* @typeparam TResult - The type of the exposed values. It is the return value of Renderer.prototype.read, and revealed at various points of the renderer/element API.
*/
declare class Renderer<TNode, TScope, TRoot = TNode, TResult = ElementValue<TNode>> {
/**
* @internal
* A weakmap which stores element trees by root.
*/
_cache: WeakMap<object, Element<Portal>>;
constructor();
// TODO: allow parent contexts from a different renderer to be passed into here
render(children: Children, root: TRoot): Promise<TResult> | TResult;
/**
* Renders an element tree into a specific root.
*
* @param children - An element tree. You can render null with a previously used root to delete the previously rendered element tree from the cache.
* @param root - The node to be rendered into. The renderer will cache element trees per root.
* @param ctx - An optional context that will be the ancestor context of all elements in the tree. Useful for connecting renderers which call each other so that events/provisions properly propagate. The context for a given root must be the same or an error will be thrown.
*
* @returns The read result of rendering the children, or a possible promise of the read result if the element tree renders asynchronously.
*/
/**
* Renders an element tree into a specific root.
*
* @param children - An element tree. You can render null with a previously used root to delete the previously rendered element tree from the cache.
* @param root - The node to be rendered into. The renderer will cache element trees per root.
* @param ctx - An optional context that will be the ancestor context of all elements in the tree. Useful for connecting renderers which call each other so that events/provisions properly propagate. The context for a given root must be the same or an error will be thrown.
*
* @returns The read result of rendering the children, or a possible promise of the read result if the element tree renders asynchronously.
*/
render(children: Children, root?: TRoot | undefined, ctx?: Context | undefined): Promise<TResult> | TResult;
/**
* Called when an element’s value is exposed via render, schedule, refresh, refs, or generator yield expressions.
*
* @param value - The value of the element being read. Can be a node, a string, undefined, or an array of nodes and strings, depending on the element.
* @returns Varies according to the specific renderer subclass. By default, it exposes the element’s value.
*
* @remarks
* This is useful for renderers which don’t want to expose their internal nodes. For instance, the HTML renderer will convert all internal nodes to strings.
*
*/
/**
* Called when an element’s value is exposed via render, schedule, refresh, refs, or generator yield expressions.
*
* @param value - The value of the element being read. Can be a node, a string, undefined, or an array of nodes and strings, depending on the element.
* @returns Varies according to the specific renderer subclass. By default, it exposes the element’s value.
*
* @remarks
* This is useful for renderers which don’t want to expose their internal nodes. For instance, the HTML renderer will convert all internal nodes to strings.
*
*/
read(value: ElementValue<TNode>): TResult;
escape(text: string, _scope: Scope): string;
parse(text: string, _scope: Scope): TNode | string;
scope<TTag extends string | symbol>(_tag: TTag, _props: TagProps<TTag>, scope: Scope | undefined): Scope | undefined;
create<TTag extends string | symbol>(_tag: TTag, _props: TagProps<TTag>, _scope: Scope): TNode;
patch<TTag extends string | symbol>(_tag: TTag, _props: TagProps<TTag>, _node: TNode, _scope: Scope): unknown;
/**
* Called in a preorder traversal for each host element.
*
* @remarks
* Useful for passing data down the element tree. For instance, the DOM renderer uses this method to keep track of whether we’re in an SVG subtree.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param scope - The current scope.
*
* @returns The scope to be passed to create and patch for child host elements.
*
* @remarks
* This method sets the scope for child host elements, not the current host element.
*/
/**
* Called in a preorder traversal for each host element.
*
* @remarks
* Useful for passing data down the element tree. For instance, the DOM renderer uses this method to keep track of whether we’re in an SVG subtree.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param scope - The current scope.
*
* @returns The scope to be passed to create and patch for child host elements.
*
* @remarks
* This method sets the scope for child host elements, not the current host element.
*/
scope<TTag extends string | symbol>(_tag: TTag, _props: TagProps<TTag>, scope: TScope | undefined): TScope;
/**
* Called for each string in an element tree.
*
* @param text - The string child.
* @param scope - The current scope.
*
* @returns The escaped string.
*
* @remarks
* Rather than returning text nodes for whatever environment we’re rendering to, we defer that step for Renderer.prototype.arrange. We do this so that adjacent strings can be concatenated and the actual element tree can be rendered in a normalized form.
*/
/**
* Called for each string in an element tree.
*
* @param text - The string child.
* @param scope - The current scope.
*
* @returns The escaped string.
*
* @remarks
* Rather than returning text nodes for whatever environment we’re rendering to, we defer that step for Renderer.prototype.arrange. We do this so that adjacent strings can be concatenated and the actual element tree can be rendered in a normalized form.
*/
escape(text: string, _scope: TScope): string;
/**
* Called for each Raw element whose value prop is a string.
*
* @param text - The string child.
* @param scope - The current scope.
*
* @returns The parsed node or string.
*/
/**
* Called for each Raw element whose value prop is a string.
*
* @param text - The string child.
* @param scope - The current scope.
*
* @returns The parsed node or string.
*/
parse(text: string, _scope: TScope): TNode | string;
/**
* Called for each host element when it is committed for the first time.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param scope - The current scope.
*
* @returns A “node” which determines the value of the host element.
*/
/**
* Called for each host element when it is committed for the first time.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param scope - The current scope.
*
* @returns A “node” which determines the value of the host element.
*/
create<TTag extends string | symbol>(_tag: TTag, _props: TagProps<TTag>, _scope: TScope): TNode;
/**
* Called for each host element when it is committed.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param node - The node associated with the host element.
* @param scope - The current scope.
*
* @returns The return value is ignored.
*
* @remarks
* Used to mutate the node associated with an element when new props are passed.
*/
/**
* Called for each host element when it is committed.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param node - The node associated with the host element.
* @param scope - The current scope.
*
* @returns The return value is ignored.
*
* @remarks
* Used to mutate the node associated with an element when new props are passed.
*/
patch<TTag extends string | symbol>(_tag: TTag, _props: TagProps<TTag>, _node: TNode, _scope: TScope | undefined): unknown;
/**
* Called for each host element after its children have committed with the actual values of the children.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param node - The node associated with the host element.
* @param children - An array of nodes and strings from child elements.
*
* @returns The return value is ignored.
*
* @remarks
* This method is also called by child components contexts as the last step of a refresh.
*/
// TODO: pass hints into arrange about where the dirty children start and end
/**
* Called for each host element after its children have committed with the actual values of the children.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param node - The node associated with the host element.
* @param children - An array of nodes and strings from child elements.
*
* @returns The return value is ignored.
*
* @remarks
* This method is also called by child components contexts as the last step of a refresh.
*/
arrange<TTag extends string | symbol>(_tag: TTag, _props: TagProps<TTag>, _parent: TNode | TRoot, _children: Array<TNode | string>): unknown;
/**
* Called for each host element when it is unmounted.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param node - The node associated with the host element.
*
* @returns The return value is ignored.
*/
// TODO: remove(): a method which is called to remove a child from a parent to optimize arrange
/**
* Called for each host element when it is unmounted.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param node - The node associated with the host element.
*
* @returns The return value is ignored.
*/
dispose<TTag extends string | symbol>(_tag: TTag, _props: TagProps<TTag>, _node: TNode): unknown;
/**
* Called at the end of the rendering process for each root of the tree.
*
* @param root - The root prop passed to portals or the render method.
*
* @returns The return value is ignored.
*/
/**
* Called at the end of the rendering process for each root of the tree.
*
* @param root - The root prop passed to portals or the render method.
*
* @returns The return value is ignored.
*/
complete(_root: TRoot): unknown;
}
/**
* A map of event type strings to Event subclasses. Can be extended via TypeScript module augmentation to have strongly typed event listeners.
*/
interface EventMap {

@@ -69,32 +453,156 @@ [type: string]: Event;

}
/**
* An interface which can be extended to provide strongly typed provisions (see Context.prototype.get and Context.prototype.set)
*/
interface ProvisionMap {
}
/**
* A class which is instantiated and passed to every component as its this value. In addition to the element tree, contexts also form a tree. Components can use this context tree to communicate data upwards via events and downwards via provisions.
* @typeparam TProps - The expected shape of the props passed to the component. Used to strongly type the Context iterator methods.
* @typeparam TResult - The readable element value type. It is used in places such as the return value of refresh and the argument passed to schedule/cleanup callbacks.
*/
declare class Context<TProps = any, TResult = any> implements EventTarget {
/**
* @internal
* flags - A bitmask. See CONTEXT FLAGS above.
*/
_f: number;
_re: Renderer<unknown, unknown, TResult>;
/**
* @internal
* renderer - The renderer which created this context.
*/
_re: Renderer<unknown, unknown, unknown, TResult>;
/**
* @internal
* el - The associated component element.
*/
_el: Element<Component>;
/**
* @internal
* parent - The parent context.
*/
_pa: Context<unknown, TResult> | undefined;
/**
* @internal
* root - The root node set by an ancestor’s Portal prop.
*/
_rt: unknown;
/**
* @internal
* host - The nearest ancestor host element.
* @remarks
* When refresh is called, the host element will be arranged as the last step of the commit, to make sure the parent’s children properly reflects the components’s children.
*/
_ho: Element<string | symbol>;
_pa: Context<unknown, TResult> | undefined;
_sc: Scope;
_el: Element<Component>;
/**
* @internal
* scope - The value of the scope at the point of element’s creation.
*/
_sc: unknown;
/**
* @internal
* iterator - The iterator returned by the component function.
*/
_it: Iterator<Children, Children | void, unknown> | AsyncIterator<Children, Children | void, unknown> | undefined;
/**
* @internal
* onProps - A callback used in conjunction with the Available flag to implement the props async iterator. See the Symbol.asyncIterator method and the resume function.
*/
_op: ((props: any) => unknown) | undefined;
/**
* @internal
* inflightPending
*/
_ip: Promise<unknown> | undefined;
/**
* @internal
* inflightValue
*/
_iv: Promise<ElementValue<any>> | undefined;
/**
* @internal
* enqueuedPending
*/
_ep: Promise<unknown> | undefined;
_iv: Promise<ElementValue<any>> | undefined;
/**
* @internal
* enqueuedValue
*/
_ev: Promise<ElementValue<any>> | undefined;
/**
* @internal
* listeners - An array of event listeners added to the context via Context.prototype.addEventListener
*/
_ls: Array<EventListenerRecord> | undefined;
/**
* @internal
* provisions - A map of values which can be set via Context.prototype.set and read from child contexts via Context.prototype.get
*/
_ps: Map<unknown, unknown> | undefined;
/**
* @internal
* schedules - a set of callbacks registered via Context.prototype.schedule, which fire when the component has committed.
*/
_ss: Set<(value: TResult) => unknown> | undefined;
/**
* @internal
* cleanups - a set of callbacks registered via Context.prototype.cleanup, which fire when the component has unmounted.
*/
_cs: Set<(value: TResult) => unknown> | undefined;
constructor(renderer: Renderer<unknown, unknown, TResult>, root: unknown, host: Element<string | symbol>, parent: Context<unknown, TResult> | undefined, scope: Scope, el: Element<Component>);
/**
* @internal
*/
/**
* @internal
*/
constructor(renderer: Renderer<unknown, unknown, unknown, TResult>, root: unknown, host: Element<string | symbol>, parent: Context<unknown, TResult> | undefined, scope: unknown, el: Element<Component>);
get<TKey extends keyof ProvisionMap>(key: TKey): ProvisionMap[TKey];
set<TKey extends keyof ProvisionMap>(key: TKey, value: ProvisionMap[TKey]): void;
/**
* The current props of the associated element.
*
* @remarks
* Typically, you should read props either via the first parameter of the component or via the context iterator methods. This property is mainly for plugins or utilities which wrap contexts.
*/
get props(): TProps;
/**
* The current value of the associated element.
*
* @remarks
* Typically, you should read values via refs, generator yield expressions, or the refresh, schedule or cleanup methods. This property is mainly for plugins or utilities which wrap contexts.
*/
get value(): TResult;
[Symbol.iterator](): Generator<TProps>;
[Symbol.asyncIterator](): AsyncGenerator<TProps>;
/**
* Re-executes the component.
*
* @returns The rendered value of the component or a promise of the rendered value if the component or its children execute asynchronously.
*
* @remarks
* The refresh method works a little differently for async generator components, in that it will resume the Context async iterator rather than resuming execution. This is because async generator components are perpetually resumed independent of updates/refresh.
*/
/**
* Re-executes the component.
*
* @returns The rendered value of the component or a promise of the rendered value if the component or its children execute asynchronously.
*
* @remarks
* The refresh method works a little differently for async generator components, in that it will resume the Context async iterator rather than resuming execution. This is because async generator components are perpetually resumed independent of updates/refresh.
*/
refresh(): Promise<TResult> | TResult;
schedule(callback: (value: unknown) => unknown): void;
cleanup(callback: (value: unknown) => unknown): void;
/**
* Registers a callback which fires when the component commits. Will only fire once per callback and update.
*/
/**
* Registers a callback which fires when the component commits. Will only fire once per callback and update.
*/
schedule(callback: (value: TResult) => unknown): void;
/**
* Registers a callback which fires when the component unmounts. Will only fire once per callback.
*/
/**
* Registers a callback which fires when the component unmounts. Will only fire once per callback.
*/
cleanup(callback: (value: TResult) => unknown): void;
addEventListener<T extends string>(type: T, listener: MappedEventListenerOrEventListenerObject<T> | null, options?: boolean | AddEventListenerOptions): void;

@@ -114,2 +622,2 @@ removeEventListener<T extends string>(type: T, listener: MappedEventListenerOrEventListenerObject<T> | null, options?: EventListenerOptions | boolean): void;

}
export { Tag, TagProps, Fragment, Copy, Portal, Raw, Child, Children, Component, Element, isElement, createElement, cloneElement, ElementValue, Renderer, EventMap, ProvisionMap, Context };
export { Tag, TagProps, Fragment, Portal, Copy, Raw, Child, Children, Component, Element, isElement, createElement, cloneElement, ElementValue, Renderer, EventMap, ProvisionMap, Context };

@@ -5,3 +5,9 @@ 'use strict';

// UTILITY FUNCTIONS
/*** UTILITIES ***/
function wrap(value) {
return !value ? [] : Array.isArray(value) ? value : [value];
}
function unwrap(arr) {
return arr.length > 1 ? arr : arr[0];
}
function isIterable(value) {

@@ -16,23 +22,38 @@ return value != null && typeof value[Symbol.iterator] === "function";

}
function arrayify(value) {
return !value ? [] : Array.isArray(value) ? value : [value];
}
function unwrap(arr) {
return arr.length > 1 ? arr : arr[0];
}
function isPromiseLike(value) {
return value != null && typeof value.then === "function";
}
function upgradePromiseLike(value) {
if (!(value instanceof Promise)) {
return Promise.resolve(value);
}
return value;
}
// SPECIAL TAGS
/***
* SPECIAL TAGS
* Crank provides a couple tags which have special meaning for the renderer.
***/
/**
* A special element tag for grouping multiple children within a parent.
*
* @remarks
* All iterables which appear in the element tree are implicitly wrapped in fragments. The Fragment tag is just the empty string, and you can use the empty string in createElement calls/transpiler options to avoid having to reference the Fragment export directly.
*/
const Fragment = "";
// TODO: We assert symbol tags to be any because typescript support for symbol tags in JSX does not exist yet.
// NOTE: We assert the following symbol tags to be any because typescript support for symbol tags in JSX does not exist yet.
// https://github.com/microsoft/TypeScript/issues/38367
/**
* A special element tag for creating a new element subtree with a different root, passed via the root prop.
*
* @remarks
* This element tag is useful for creating element trees with multiple roots. Renderer.prototype.render will implicitly wrap the children which have been passed in in an implicit Portal element.
*/
const Portal = Symbol.for("crank.Portal");
/**
* A special element tag which copies whatever child appeared previously in the element’s position.
*
* @remarks
* Copy elements are useful when you want to prevent a subtree from updating as a performance optimization.
*/
const Copy = Symbol.for("crank.Copy");
const Portal = Symbol.for("crank.Portal");
/**
* A special element tag for injecting raw nodes into an element tree via its value prop.
*
* @remarks
* If the value prop is a string, Renderer.prototype.parse will be called on the string and the element’s rendered value will be the result.
*/
const Raw = Symbol.for("crank.Raw");

@@ -50,7 +71,32 @@ function narrow(child) {

}
// https://overreacted.io/why-do-react-elements-have-typeof-property/
const ElementSymbol = Symbol.for("crank.Element");
// ELEMENT FLAGS
/*** ELEMENT FLAGS ***/
/**
* A flag which is set when the component has been mounted. Used mainly to detect whether an element is being reused so that it can be cloned.
*/
const Mounted = 1 << 0;
/**
* A flag which is set when the component has committed at least once.
*/
const Committed = 1 << 1;
// NOTE: To save on filesize, we mangle the internal properties of Crank classes by hand. These internal properties are prefixed with an underscore. Refer to their definitions to see their unabbreviated names.
// NOTE: to maximize compatibility between Crank versions, starting with 0.2.0, the $$typeof property and the non-internal properties will not be changed, and any change to these properties will be considered a breaking change. This is to ensure maximum compatibility between components which use different Crank versions.
/**
* Elements are the basic building blocks of Crank applications. They are JavaScript objects which are interpreted by special classes called renderers to produce and manage stateful nodes.
*
* @typeparam TTag - the type of the tag of the element.
*
* @example
* // specific element types
* let div: Element<"div">;
* let portal: Element<Portal>;
* let myEl: Element<MyComponent>;
*
* // general element types
* let host: Element<string | symbol>;
* let component: Element<Component>;
*
* @remarks
* Typically, you use the createElement function to create elements and not this class directly.
*/
class Element {

@@ -76,2 +122,3 @@ constructor(tag, props, key, ref) {

if (name === "crank-key") {
// NOTE: We have to make sure we don’t assign null to the key because we don’t check for null keys in the diffing functions.
if (props[name] != null) {

@@ -104,2 +151,8 @@ key = props[name];

}
/**
* Clones a given element.
*
* @remarks
* Mainly used internally to make sure we don’t accidentally reuse elements in an element tree, because elements are directly mutated by the renderer.
*/
function cloneElement(el) {

@@ -109,4 +162,10 @@ if (!isElement(el)) {

}
return new Element(el.tag, el.props, el.key, el.ref);
return new Element(el.tag, { ...el.props }, el.key, el.ref);
}
/**
* Takes an array of element values and normalizes the output as an array of nodes and strings.
*
* @remarks
* Normalize will flatten only one level of nested arrays, because it is designed to be called once at each level of the tree. It will also concatenate adjacent strings and remove all undefineds.
*/
function normalize(values) {

@@ -136,3 +195,3 @@ const result = [];

else {
if (buffer !== undefined) {
if (buffer) {
result.push(buffer);

@@ -151,24 +210,24 @@ buffer = undefined;

}
function getChildNodes(el) {
let nodes = [];
const children = arrayify(el._ch);
/**
* Walks an element’s children to find its child values.
* @returns A normalized array of nodes and strings.
*/
function getChildValues(el) {
const values = [];
const children = wrap(el._ch);
for (let i = 0; i < children.length; i++) {
const child = children[i];
if (!child) ;
else if (typeof child === "string") {
nodes.push(child);
if (typeof child === "string") {
values.push(child);
}
else if (typeof child._fb !== "undefined") {
nodes = nodes.concat(arrayify(child._fb));
else if (typeof child === "object") {
values.push(getValue(child));
}
else if (typeof child.tag === "function" || child.tag === Fragment) {
nodes = nodes.concat(getChildNodes(child));
}
else if (child.tag !== Portal) {
// Portals have a value but are opaque to their parents
nodes.push(child._n);
}
}
return nodes;
return normalize(values);
}
/**
* Finds the value of the element according to its type.
* @returns The value of the element.
*/
function getValue(el) {

@@ -184,4 +243,11 @@ if (typeof el._fb !== "undefined") {

}
return unwrap(getChildNodes(el));
return unwrap(getChildValues(el));
}
/**
* An abstract class which is subclassed to render to different target environments. This class is responsible for kicking off the rendering process, caching previous trees by root, and creating/mutating/disposing the nodes of the target environment.
*
* @typeparam TNode - The type of the node for a specific rendering environment. It is the type of the return value of Renderer.prototype.create and Renderer.prototype.parse.
* @typeparam TRoot - The type of the root for a specific rendering environment. It is the type of the second parameter passed to Renderer.prototype.render, as well as the expected type of the root portal.
* @typeparam TResult - The type of the exposed values. It is the return value of Renderer.prototype.read, and revealed at various points of the renderer/element API.
*/
class Renderer {

@@ -191,4 +257,12 @@ constructor() {

}
// TODO: allow parent contexts from a different renderer to be passed into here
render(children, root) {
/**
* Renders an element tree into a specific root.
*
* @param children - An element tree. You can render null with a previously used root to delete the previously rendered element tree from the cache.
* @param root - The node to be rendered into. The renderer will cache element trees per root.
* @param ctx - An optional context that will be the ancestor context of all elements in the tree. Useful for connecting renderers which call each other so that events/provisions properly propagate. The context for a given root must be the same or an error will be thrown.
*
* @returns The read result of rendering the children, or a possible promise of the read result if the element tree renders asynchronously.
*/
render(children, root, ctx) {
let portal;

@@ -200,2 +274,3 @@ if (typeof root === "object" && root !== null) {

portal = createElement(Portal, { children, root });
portal._ctx = ctx;
if (typeof root === "object" && root !== null && children != null) {

@@ -206,2 +281,5 @@ this._cache.set(root, portal);

else {
if (portal._ctx !== ctx) {
throw new Error("render must be called with the same context per root");
}
portal.props = { children, root };

@@ -212,6 +290,8 @@ if (typeof root === "object" && root !== null && children == null) {

}
const value = update(this, root, portal, undefined, undefined, portal);
const scope = undefined;
const value = update(this, root, portal, ctx, scope, portal);
// NOTE: we return the read child values of the portal because portals themselves have no readable value.
if (isPromiseLike(value)) {
return value.then(() => {
const result = this.read(unwrap(getChildNodes(portal)));
const result = this.read(unwrap(getChildValues(portal)));
if (root == null) {

@@ -223,3 +303,3 @@ unmount(this, portal, undefined, portal);

}
const result = this.read(getChildNodes(portal));
const result = this.read(unwrap(getChildValues(portal)));
if (root == null) {

@@ -230,17 +310,83 @@ unmount(this, portal, undefined, portal);

}
/**
* Called when an element’s value is exposed via render, schedule, refresh, refs, or generator yield expressions.
*
* @param value - The value of the element being read. Can be a node, a string, undefined, or an array of nodes and strings, depending on the element.
* @returns Varies according to the specific renderer subclass. By default, it exposes the element’s value.
*
* @remarks
* This is useful for renderers which don’t want to expose their internal nodes. For instance, the HTML renderer will convert all internal nodes to strings.
*
*/
read(value) {
return value;
}
/**
* Called in a preorder traversal for each host element.
*
* @remarks
* Useful for passing data down the element tree. For instance, the DOM renderer uses this method to keep track of whether we’re in an SVG subtree.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param scope - The current scope.
*
* @returns The scope to be passed to create and patch for child host elements.
*
* @remarks
* This method sets the scope for child host elements, not the current host element.
*/
scope(_tag, _props, scope) {
return scope;
}
/**
* Called for each string in an element tree.
*
* @param text - The string child.
* @param scope - The current scope.
*
* @returns The escaped string.
*
* @remarks
* Rather than returning text nodes for whatever environment we’re rendering to, we defer that step for Renderer.prototype.arrange. We do this so that adjacent strings can be concatenated and the actual element tree can be rendered in a normalized form.
*/
escape(text, _scope) {
return text;
}
/**
* Called for each Raw element whose value prop is a string.
*
* @param text - The string child.
* @param scope - The current scope.
*
* @returns The parsed node or string.
*/
parse(text, _scope) {
return text;
}
scope(_tag, _props, scope) {
return scope;
}
/**
* Called for each host element when it is committed for the first time.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param scope - The current scope.
*
* @returns A “node” which determines the value of the host element.
*/
create(_tag, _props, _scope) {
throw new Error("Not implemented");
}
/**
* Called for each host element when it is committed.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param node - The node associated with the host element.
* @param scope - The current scope.
*
* @returns The return value is ignored.
*
* @remarks
* Used to mutate the node associated with an element when new props are passed.
*/
patch(_tag, _props, _node, _scope) {

@@ -250,2 +396,15 @@ return;

// TODO: pass hints into arrange about where the dirty children start and end
/**
* Called for each host element after its children have committed with the actual values of the children.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param node - The node associated with the host element.
* @param children - An array of nodes and strings from child elements.
*
* @returns The return value is ignored.
*
* @remarks
* This method is also called by child components contexts as the last step of a refresh.
*/
arrange(_tag, _props, _parent, _children) {

@@ -255,5 +414,21 @@ return;

// TODO: remove(): a method which is called to remove a child from a parent to optimize arrange
/**
* Called for each host element when it is unmounted.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param node - The node associated with the host element.
*
* @returns The return value is ignored.
*/
dispose(_tag, _props, _node) {
return;
}
/**
* Called at the end of the rendering process for each root of the tree.
*
* @param root - The root prop passed to portals or the render method.
*
* @returns The return value is ignored.
*/
complete(_root) {

@@ -263,3 +438,4 @@ return;

}
// PRIVATE RENDERER FUNCTIONS
/*** PRIVATE RENDERER FUNCTIONS ***/
// NOTE: to aid in the mangling of this module, we use functions rather than methods for internal logic.
function mount(renderer, root, host, ctx, scope, el) {

@@ -275,7 +451,3 @@ el._f |= Mounted;

else if (el.tag !== Fragment) {
if (el.tag !== Portal) {
// TODO: maybe we can defer create calls to when the element is committing
el._n = renderer.create(el.tag, el.props, scope);
}
else {
if (el.tag === Portal) {
root = el.props.root;

@@ -286,2 +458,3 @@ }

}
// NOTE: The primary benefit of having a separate codepath for mounting is that it’s slightly faster because we don’t have to align and diff children against old children. But for singular child values, updateChild is sufficient.
if (isNonStringIterable(el.props.children)) {

@@ -305,3 +478,4 @@ return mountChildren(renderer, root, host, ctx, scope, el, el.props.children);

}
[child, value] = compare(renderer, root, host, ctx, scope, undefined, child);
[child, value] = diff(renderer, root, host, ctx, scope, undefined, // oldChild
child);
newChildren[i] = child;

@@ -321,3 +495,3 @@ values.push(value);

}
return race(renderer, host, ctx, scope, parent, values1);
return chase(renderer, host, ctx, scope, parent, values1);
}

@@ -360,3 +534,3 @@ function update(renderer, root, host, ctx, scope, el) {

let value;
[newChild, value] = compare(renderer, root, host, ctx, scope, oldChild, newChild);
[newChild, value] = diff(renderer, root, host, ctx, scope, oldChild, newChild);
if (typeof oldChild === "object" && oldChild !== newChild) {

@@ -366,7 +540,5 @@ unmount(renderer, host, ctx, oldChild);

parent._ch = newChild;
// TODO: allow single values to be passed to race
const values = isPromiseLike(value)
? value.then((value) => [value])
: [value];
return race(renderer, host, ctx, scope, parent, values);
// TODO: allow single values to be passed to chase
const values = isPromiseLike(value) ? value.then(wrap) : wrap(value);
return chase(renderer, host, ctx, scope, parent, values);
}

@@ -388,3 +560,3 @@ function mapChildrenByKey(children) {

const values = [];
const oldChildren = arrayify(parent._ch);
const oldChildren = wrap(parent._ch);
const newChildren = Array.from(children);

@@ -444,3 +616,3 @@ const graveyard = [];

let value;
[newChild, value] = compare(renderer, root, host, ctx, scope, oldChild, newChild);
[newChild, value] = diff(renderer, root, host, ctx, scope, oldChild, newChild);
values.push(value);

@@ -463,3 +635,3 @@ newChildren[j] = newChild;

}
// TODO: async removal of keyed nodes
// TODO: async unmounting
if (childrenByKey !== undefined && childrenByKey.size > 0) {

@@ -476,5 +648,5 @@ graveyard.push(...childrenByKey.values());

}
return race(renderer, host, ctx, scope, parent, values1);
return chase(renderer, host, ctx, scope, parent, values1);
}
function compare(renderer, root, host, ctx, scope, oldChild, newChild) {
function diff(renderer, root, host, ctx, scope, oldChild, newChild) {
let value;

@@ -484,2 +656,3 @@ if (typeof oldChild === "object" &&

oldChild.tag === newChild.tag) {
// TODO: implement Raw element parse caching
if (oldChild.tag === Portal) {

@@ -490,3 +663,2 @@ if (oldChild.props.root !== newChild.props.root) {

}
// TODO: implement Raw element parse caching
if (oldChild !== newChild) {

@@ -524,3 +696,2 @@ oldChild.props = newChild.props;

if (typeof oldChild._inf === "object") {
// TODO: figure out if this branch is actually necessary
oldChild._inf

@@ -544,3 +715,9 @@ .then((value) => {

}
function race(renderer, host, ctx, scope, el, values) {
/**
* A function to race current child values with future child values.
*
* @remarks
* When an element’s children update asynchronously, we race the resulting promise with the next update of the element’s children. By induction, this ensures that when any update to an element settles, all past updates to that same element will have settled as well. This prevents deadlocks and unnecessary awaiting when an element’s children have been cleared, for instance.
*/
function chase(renderer, host, ctx, scope, el, values) {
if (isPromiseLike(values)) {

@@ -564,8 +741,4 @@ let onNewValues;

}
function commit(renderer, scope, el, nodes) {
el._f |= Committed;
if (typeof el._fb !== "undefined") {
el._fb = undefined;
}
let value = unwrap(nodes);
function commit(renderer, scope, el, values) {
let value = unwrap(values);
if (typeof el.tag === "function") {

@@ -577,3 +750,3 @@ if (typeof el._ctx === "object") {

else if (el.tag === Portal) {
renderer.arrange(Portal, el.props, el.props.root, nodes);
renderer.arrange(Portal, el.props, el.props.root, values);
renderer.complete(el.props.root);

@@ -592,12 +765,19 @@ value = undefined;

else if (el.tag !== Fragment) {
if (!(el._f & Committed)) {
el._n = renderer.create(el.tag, el.props, scope);
}
renderer.patch(el.tag, el.props, el._n, scope);
renderer.arrange(el.tag, el.props, el._n, nodes);
renderer.arrange(el.tag, el.props, el._n, values);
value = el._n;
}
if (typeof el.ref === "function") {
el._f |= Committed;
if (el.ref) {
el.ref(renderer.read(value));
}
if (typeof el._inf === "object") {
if (el._inf) {
el._inf = undefined;
}
if (el._fb) {
el._fb = undefined;
}
return value;

@@ -630,3 +810,3 @@ }

}
const children = arrayify(el._ch);
const children = wrap(el._ch);
for (let i = 0; i < children.length; i++) {

@@ -639,3 +819,4 @@ const child = children[i];

}
// EVENT UTILITY FUNCTIONS
/*** EVENT UTILITIES ***/
// EVENT PHASE CONSTANTS (https://developer.mozilla.org/en-US/docs/Web/API/Event/eventPhase)
const NONE = 0;

@@ -665,2 +846,8 @@ const CAPTURING_PHASE = 1;

}
/**
* A function to reconstruct an array of every listener given a context and a host element.
*
* @remarks
* This function exploits the fact that contexts retain their nearest ancestor host element. We can determine all the contexts which are directly listening to an element by traversing up the context tree and checking that the host element passed in matches the context’s host property.
*/
function getListeners(ctx, host) {

@@ -678,6 +865,6 @@ let listeners;

if (typeof ctx._ls !== "undefined" && ctx._ls.length > 0) {
for (const node of getChildNodes(ctx._el)) {
if (isEventTarget(node)) {
for (const value of getChildValues(ctx._el)) {
if (isEventTarget(value)) {
for (const record of ctx._ls) {
node.removeEventListener(record.type, record.callback, record.options);
value.removeEventListener(record.type, record.callback, record.options);
}

@@ -690,12 +877,43 @@ }

// CONTEXT FLAGS
// TODO: write an explanation for each of these flags
const Independent = 1 << 0;
/**
* A flag which is set when the component is being updated by the parent and cleared when the component has committed. Used to determine whether the nearest host ancestor needs to be rearranged.
*/
const Updating = 1 << 0;
/**
* A flag which is set when the component is called or stepped through. It is used to ensure that a component which synchronously triggers a second update in the course of rendering does not cause an infinite loop or a generator error.
*/
const Stepping = 1 << 1;
/**
* A flag used to make sure multiple values are not pulled from context prop iterators without a yield.
*/
const Iterating = 1 << 2;
/**
* A flag used by async generator components in conjunction with the onProps functions (_op) to mark whether new props can be pulled via the context iterator methods.
*/
const Available = 1 << 3;
/**
* A flag which is set when generator components return. Set whenever an iterator returns an iteration with the done property set to true. Finished components will stick to their last rendered value and ignore further updates.
*/
const Finished = 1 << 4;
/**
* A flag which is set when the component is unmounted. Unmounted components are no longer in the element tree, and cannot run or refresh.
*/
const Unmounted = 1 << 5;
/**
* A flag which indicates that the component is a sync generator component.
*/
const SyncGen = 1 << 6;
/**
* A flag which indicates that the component is an async generator component.
*/
const AsyncGen = 1 << 7;
/**
* A class which is instantiated and passed to every component as its this value. In addition to the element tree, contexts also form a tree. Components can use this context tree to communicate data upwards via events and downwards via provisions.
* @typeparam TProps - The expected shape of the props passed to the component. Used to strongly type the Context iterator methods.
* @typeparam TResult - The readable element value type. It is used in places such as the return value of refresh and the argument passed to schedule/cleanup callbacks.
*/
class Context {
/**
* @internal
*/
constructor(renderer, root, host, parent, scope, el) {

@@ -723,7 +941,19 @@ this._f = 0;

}
/**
* The current props of the associated element.
*
* @remarks
* Typically, you should read props either via the first parameter of the component or via the context iterator methods. This property is mainly for plugins or utilities which wrap contexts.
*/
get props() {
return this._el.props;
}
/**
* The current value of the associated element.
*
* @remarks
* Typically, you should read values via refs, generator yield expressions, or the refresh, schedule or cleanup methods. This property is mainly for plugins or utilities which wrap contexts.
*/
get value() {
return this._re.read(unwrap(getChildNodes(this._el)));
return this._re.read(getValue(this._el));
}

@@ -765,2 +995,10 @@ *[Symbol.iterator]() {

}
/**
* Re-executes the component.
*
* @returns The rendered value of the component or a promise of the rendered value if the component or its children execute asynchronously.
*
* @remarks
* The refresh method works a little differently for async generator components, in that it will resume the Context async iterator rather than resuming execution. This is because async generator components are perpetually resumed independent of updates/refresh.
*/
refresh() {

@@ -771,6 +1009,9 @@ if (this._f & (Stepping | Unmounted)) {

}
this._f |= Independent;
this._f &= ~Updating;
resume(this);
return this._re.read(run(this));
}
/**
* Registers a callback which fires when the component commits. Will only fire once per callback and update.
*/
schedule(callback) {

@@ -782,2 +1023,5 @@ if (typeof this._ss === "undefined") {

}
/**
* Registers a callback which fires when the component unmounts. Will only fire once per callback.
*/
cleanup(callback) {

@@ -823,5 +1067,5 @@ if (typeof this._cs === "undefined") {

this._ls.push(record);
for (const node of getChildNodes(this._el)) {
if (isEventTarget(node)) {
node.addEventListener(record.type, record.callback, record.options);
for (const value of getChildValues(this._el)) {
if (isEventTarget(value)) {
value.addEventListener(record.type, record.callback, record.options);
}

@@ -843,5 +1087,5 @@ }

this._ls.splice(i, 1);
for (const node of getChildNodes(this._el)) {
if (isEventTarget(node)) {
node.removeEventListener(record.type, record.callback, record.options);
for (const value of getChildValues(this._el)) {
if (isEventTarget(value)) {
value.removeEventListener(record.type, record.callback, record.options);
}

@@ -944,3 +1188,6 @@ }

}
// PRIVATE CONTEXT FUNCTIONS
/*** PRIVATE CONTEXT FUNCTIONS ***/
/**
* Called to make props available to the Context async iterator for async generator components.
*/
function resume(ctx) {

@@ -955,2 +1202,6 @@ if (typeof ctx._op === "function") {

}
// NOTE: The functions run, step and advance work together to implement the async queueing behavior of components. The run function calls the step function, which returns two results in a tuple. The first result, called “pending,” is a possible promise which settles when the component can accept new updates, and represents the duration during which the component is blocked from accepting new updates. The second result, called “value,” is the actual result of the update. The run function caches pending/value from the step function on the context, according to whether the component is currently blocked. The “inflight” pending/value properties are the currently executing update, and the “enqueued” pending/value properties are promises which represent the next step. Lastly, the run function calls the advance function in a Promise.prototype.finally callback to allow new steps to be enqueued.
/**
* Enqueues and executes the component associated with the context.
*/
function run(ctx) {

@@ -963,3 +1214,3 @@ if (typeof ctx._ip === "undefined") {

.catch((err) => {
if (ctx._f & Independent) {
if (!(ctx._f & Updating)) {
return propagateError(ctx._pa, err);

@@ -977,3 +1228,3 @@ }

catch (err) {
if (ctx._f & Independent) {
if (!(ctx._f & Updating)) {
return propagateError(ctx._pa, err);

@@ -999,3 +1250,3 @@ }

return pending.catch((err) => {
if (ctx._f & Independent) {
if (!(ctx._f & Updating)) {
return propagateError(ctx._pa, err);

@@ -1007,3 +1258,3 @@ }

catch (err) {
if (ctx._f & Independent) {
if (!(ctx._f & Updating)) {
return propagateError(ctx._pa, err);

@@ -1018,2 +1269,17 @@ }

}
/**
* The step function is responsible for executing the component and handling all the different component types.
*
* @returns A tuple [pending, value]
* pending - A possible promise which represents the duration during which the component is blocked from updating.
* value - The actual rendered value of the children.
*
* @remarks
* Each component type will block/unblock according to the type of the component.
* Sync function components never block and will transparently pass updates to children.
* Async function components and async generator components block while executing itself, but will not block for async children.
* Sync generator components block while any children are pending.
*
* The reason sync generator components block while their children are pending is that they are expected to only resume when they’ve actually rendered. Additionally, they have no mechanism for awaiting async children.
*/
function step(ctx) {

@@ -1025,23 +1291,26 @@ const el = ctx._el;

let initial = false;
ctx._f |= Stepping;
if (typeof ctx._it === "undefined") {
initial = true;
clearEventListeners(ctx);
const result = el.tag.call(ctx, el.props);
if (isIteratorLike(result)) {
ctx._it = result;
try {
ctx._f |= Stepping;
if (typeof ctx._it === "undefined") {
initial = true;
clearEventListeners(ctx);
const result = el.tag.call(ctx, el.props);
if (isIteratorLike(result)) {
ctx._it = result;
}
else if (isPromiseLike(result)) {
const result1 = result instanceof Promise ? result : Promise.resolve(result);
const pending = result1;
const value = result1.then((result) => updateCtxChildren(ctx, result));
return [pending, value];
}
else {
// sync function component
return [undefined, updateCtxChildren(ctx, result)];
}
}
else if (isPromiseLike(result)) {
const result1 = upgradePromiseLike(result);
const pending = result1;
const value1 = result1.then((result) => updateCtxChildren(ctx, result));
ctx._f &= ~Stepping;
return [pending, value1];
}
else {
const value = updateCtxChildren(ctx, result);
ctx._f &= ~Stepping;
return [undefined, value];
}
}
finally {
ctx._f &= ~Stepping;
}
let oldValue;

@@ -1057,6 +1326,10 @@ if (typeof ctx._el._inf === "object") {

}
// TODO: clean up/deduplicate logic here
// TODO: generator components which throw errors should be fragile, if rerendered they should be unmounted and remounted
const iteration = ctx._it.next(oldValue);
ctx._f &= ~Stepping;
let iteration;
try {
ctx._f |= Stepping;
iteration = ctx._it.next(oldValue);
}
finally {
ctx._f &= ~Stepping;
}
if (isPromiseLike(iteration)) {

@@ -1074,16 +1347,5 @@ // async generator component

try {
let value = updateCtxChildren(ctx, iteration.value);
const value = updateCtxChildren(ctx, iteration.value);
if (isPromiseLike(value)) {
if (!(ctx._f & Finished) && typeof ctx._it.throw === "function") {
value = value.catch((err) => {
resume(ctx);
const iteration = ctx._it.throw(err);
return iteration.then((iteration) => {
if (iteration.done) {
ctx._f |= Finished;
}
return updateCtxChildren(ctx, iteration.value);
});
});
}
return value.catch((err) => handleChildError(ctx, err));
}

@@ -1093,12 +1355,3 @@ return value;

catch (err) {
if (ctx._f & Finished || typeof ctx._it.throw !== "function") {
throw err;
}
const iteration = ctx._it.throw(err);
return iteration.then((iteration) => {
if (iteration.done) {
ctx._f |= Finished;
}
return updateCtxChildren(ctx, iteration.value);
});
return handleChildError(ctx, err);
}

@@ -1108,51 +1361,35 @@ });

}
else {
// sync generator component
if (initial) {
ctx._f |= SyncGen;
// sync generator component
if (initial) {
ctx._f |= SyncGen;
}
ctx._f &= ~Iterating;
if (iteration.done) {
ctx._f |= Finished;
}
let value;
try {
value = updateCtxChildren(ctx, iteration.value);
if (isPromiseLike(value)) {
value = value.catch((err) => handleChildError(ctx, err));
}
ctx._f &= ~Iterating;
if (iteration.done) {
ctx._f |= Finished;
}
let value;
try {
value = updateCtxChildren(ctx, iteration.value);
if (isPromiseLike(value)) {
if (!(ctx._f & Finished) && typeof ctx._it.throw === "function") {
value = value.catch((err) => {
ctx._f |= Stepping;
const iteration = ctx._it.throw(err);
ctx._f &= ~Stepping;
if (iteration.done) {
ctx._f |= Finished;
}
return updateCtxChildren(ctx, iteration.value);
});
}
const pending = value.catch(() => { });
return [pending, value];
}
}
catch (err) {
if (ctx._f & Finished || typeof ctx._it.throw !== "function") {
throw err;
}
ctx._f |= Stepping;
const iteration = ctx._it.throw(err);
ctx._f &= ~Stepping;
if (iteration.done) {
ctx._f |= Finished;
}
const value = updateCtxChildren(ctx, iteration.value);
if (isPromiseLike(value)) {
const pending = value.catch(() => { });
return [pending, value];
}
return [undefined, value];
}
return [undefined, value];
}
catch (err) {
value = handleChildError(ctx, err);
}
if (isPromiseLike(value)) {
// Because
return [value.catch(() => { }), value];
}
return [undefined, value];
}
/**
* @remarks
* Called when the inflight pending promise settles.
*/
function advance(ctx) {
// _ip: inflightPending
// _iv: inflightValue
// _ep: enqueuedPending
// _ev: enqueuedValue
ctx._ip = ctx._ep;

@@ -1163,10 +1400,36 @@ ctx._iv = ctx._ev;

if (ctx._f & AsyncGen && !(ctx._f & Finished)) {
ctx._f |= Independent;
run(ctx);
}
}
// TODO: generator components which throw errors should be recoverable
function handleChildError(ctx, err) {
if (ctx._f & Finished || !ctx._it || typeof ctx._it.throw !== "function") {
throw err;
}
resume(ctx);
let iteration;
try {
ctx._f |= Stepping;
iteration = ctx._it.throw(err);
}
finally {
ctx._f &= ~Stepping;
}
if (isPromiseLike(iteration)) {
return iteration.then((iteration) => {
if (iteration.done) {
ctx._f |= Finished;
}
return updateCtxChildren(ctx, iteration.value);
});
}
if (iteration.done) {
ctx._f |= Finished;
}
return updateCtxChildren(ctx, iteration.value);
}
function propagateError(ctx, err) {
if (!ctx ||
ctx._f & Finished ||
typeof ctx._it !== "object" ||
!ctx._it ||
typeof ctx._it.throw !== "function") {

@@ -1177,8 +1440,15 @@ throw err;

resume(ctx);
const result = ctx._it.throw(err);
if (isPromiseLike(result)) {
return result
.catch((err) => propagateError(ctx._pa, err))
.then(() => undefined);
let iteration;
try {
ctx._f |= Stepping;
iteration = ctx._it.throw(err);
}
finally {
ctx._f &= ~Stepping;
}
if (isPromiseLike(iteration)) {
return iteration
.then(() => undefined)
.catch((err) => propagateError(ctx._pa, err));
}
}

@@ -1190,5 +1460,3 @@ catch (err) {

function updateCtx(ctx) {
if (ctx._f & AsyncGen) {
ctx._f &= ~Independent;
}
ctx._f |= Updating;
resume(ctx);

@@ -1205,3 +1473,7 @@ return run(ctx);

}
return updateChild(ctx._re, ctx._rt, ctx._ho, ctx, ctx._sc, ctx._el, child);
return updateChild(ctx._re, ctx._rt, // root
ctx._ho, // host
ctx, ctx._sc, // scope
ctx._el, // element
child);
}

@@ -1213,3 +1485,3 @@ function commitCtx(ctx, value) {

if (typeof ctx._ls !== "undefined" && ctx._ls.length > 0) {
for (const child of arrayify(value)) {
for (const child of wrap(value)) {
if (isEventTarget(child)) {

@@ -1222,3 +1494,3 @@ for (const record of ctx._ls) {

}
if (ctx._f & Independent) {
if (!(ctx._f & Updating)) {
const listeners = getListeners(ctx._pa, ctx._ho);

@@ -1228,3 +1500,3 @@ if (listeners !== undefined && listeners.length > 0) {

const record = listeners[i];
for (const v of arrayify(value)) {
for (const v of wrap(value)) {
if (isEventTarget(v)) {

@@ -1236,11 +1508,12 @@ v.addEventListener(record.type, record.callback, record.options);

}
// TODO: async generator components which yield multiple children synchronously will over-arrange the host. Maybe we can defer arrangement for this case.
// TODO: we don’t need to call arrange if none of the nodes have changed or moved
const host = ctx._ho;
ctx._re.arrange(host.tag, host.props, host.tag === Portal ? host.props.root : host._n, getChildNodes(host));
if (host._f & Committed) {
ctx._re.arrange(host.tag, host.props, host.tag === Portal ? host.props.root : host._n, getChildValues(host));
}
ctx._re.complete(ctx._rt);
ctx._f &= ~Independent;
}
if (typeof ctx._ss !== "undefined" && ctx._ss.size > 0) {
// We have to clear the set of callbacks before calling them, because a callback which refreshes the component would otherwise cause a stack overflow.
ctx._f &= ~Updating;
if (!!ctx._ss && ctx._ss.size > 0) {
// NOTE: We have to clear the set of callbacks before calling them, because a callback which refreshes the component would otherwise cause a stack overflow.
const callbacks = Array.from(ctx._ss);

@@ -1254,6 +1527,7 @@ ctx._ss.clear();

}
// TODO: async unmounting
function unmountCtx(ctx) {
ctx._f |= Unmounted;
clearEventListeners(ctx);
if (typeof ctx._cs === "object") {
if (ctx._cs) {
const value = ctx._re.read(getValue(ctx._el));

@@ -1268,5 +1542,14 @@ for (const cleanup of ctx._cs) {

resume(ctx);
if (typeof ctx._it === "object" && typeof ctx._it.return === "function") {
// TODO: handle async generator rejections
ctx._it.return();
if (!!ctx._it && typeof ctx._it.return === "function") {
let iteration;
try {
ctx._f |= Stepping;
iteration = ctx._it.return();
}
finally {
ctx._f &= ~Stepping;
}
if (isPromiseLike(iteration)) {
iteration.catch((err) => propagateError(ctx._pa, err));
}
}

@@ -1273,0 +1556,0 @@ }

@@ -1,2 +0,2 @@

import { Renderer, TagProps } from "./index";
import { Children, Context, ElementValue, Renderer, TagProps } from "./index";
declare module "index" {

@@ -6,7 +6,7 @@ interface EventMap extends GlobalEventHandlersEventMap {

}
declare class DOMRenderer extends Renderer<Node> {
declare class DOMRenderer extends Renderer<Node, string | undefined> {
render(children: Children, root: Node, ctx?: Context): Promise<ElementValue<Node>> | ElementValue<Node>;
parse(text: string): DocumentFragment;
scope(tag: string | symbol, props: Record<string, any>, scope: string | undefined): string | undefined;
// TODO: cache createElement calls and use cloneNode
create<TTag extends string | symbol>(tag: TTag, props: Record<string, any>, ns: string | undefined): Node;
parse(text: string): DocumentFragment;
patch<TTag extends string | symbol>(tag: TTag, props: TagProps<TTag>, el: Element, ns: string | undefined): void;

@@ -13,0 +13,0 @@ arrange<TTag extends string | symbol>(tag: TTag, props: Record<string, any>, parent: Node, children: Array<Node | string>): void;

import { Renderer, Portal } from './index.js';
const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
// TODO: override render to provide a better error message when a node is not passed in as the second argument
class DOMRenderer extends Renderer {
render(children, root, ctx) {
if (!(root instanceof Node)) {
throw new TypeError("root is not a node");
}
return super.render(children, root, ctx);
}
parse(text) {
if (typeof document.createRange === "function") {
return document.createRange().createContextualFragment(text);
}
else {
const fragment = document.createDocumentFragment();
const childNodes = new DOMParser().parseFromString(text, "text/html").body
.childNodes;
for (let i = 0; i < childNodes.length; i++) {
fragment.appendChild(childNodes[i]);
}
return fragment;
}
}
scope(tag, props, scope) {

@@ -17,3 +36,2 @@ switch (tag) {

}
// TODO: cache createElement calls and use cloneNode
create(tag, props, ns) {

@@ -26,21 +44,4 @@ if (typeof tag !== "string") {

}
if (ns !== undefined) {
return document.createElementNS(ns, tag);
}
return document.createElement(tag);
return ns ? document.createElementNS(ns, tag) : document.createElement(tag);
}
parse(text) {
if (typeof document.createRange === "function") {
return document.createRange().createContextualFragment(text);
}
else {
const fragment = document.createDocumentFragment();
const childNodes = new DOMParser().parseFromString(text, "text/html").body
.childNodes;
for (let i = 0; i < childNodes.length; i++) {
fragment.appendChild(childNodes[i]);
}
return fragment;
}
}
patch(tag, props, el, ns) {

@@ -84,6 +85,6 @@ for (let name in props) {

}
else if (value === false || value == null) {
else if (!value) {
el.removeAttribute("class");
}
else if (ns === undefined) {
else if (!ns) {
el["className"] = value;

@@ -108,3 +109,3 @@ }

}
else if (ns === undefined && !forceAttribute && name in el) {
else if (!forceAttribute && !ns && name in el) {
el[name] = value;

@@ -127,3 +128,3 @@ }

if (tag === Portal && !(parent instanceof Node)) {
throw new Error("Portal must have a root of type Node");
throw new TypeError("Portal root is not a node");
}

@@ -130,0 +131,0 @@ if (!("innerHTML" in props) &&

@@ -9,3 +9,3 @@ import { ElementValue, Renderer } from "./index";

}
declare class StringRenderer extends Renderer<Node | string, void, string> {
declare class StringRenderer extends Renderer<Node | string, undefined, unknown, string> {
create(): Node;

@@ -12,0 +12,0 @@ escape(text: string): string;

@@ -79,3 +79,2 @@ import { Renderer, Portal } from './index.js';

}
// NOTE: using void for the root type allows render to be called with only one argument
class StringRenderer extends Renderer {

@@ -82,0 +81,0 @@ create() {

@@ -1,56 +0,440 @@

type Tag = Component<any> | string | symbol;
/**
* Represents all valid values which can be used for the tag of an element.
*
* @remarks
* Elements whose tags are strings or symbols are called “host” or “intrinsic” elements, and their behavior is determined by the renderer, while elements whose tags are components are called “component” elements, and their behavior is determined by the execution of the component.
*/
type Tag = string | symbol | Component;
/**
* Maps the tag of an element to its expected props.
*
* @typeparam TTag - The element’s tag.
*/
type TagProps<TTag extends Tag> = TTag extends string ? JSX.IntrinsicElements[TTag] : TTag extends Component<infer TProps> ? TProps : unknown;
/***
* SPECIAL TAGS
* Crank provides a couple tags which have special meaning for the renderer.
***/
/**
* A special element tag for grouping multiple children within a parent.
*
* @remarks
* All iterables which appear in the element tree are implicitly wrapped in fragments. The Fragment tag is just the empty string, and you can use the empty string in createElement calls/transpiler options to avoid having to reference the Fragment export directly.
*/
declare const Fragment = "";
type Fragment = typeof Fragment;
/**
* A special element tag for creating a new element subtree with a different root, passed via the root prop.
*
* @remarks
* This element tag is useful for creating element trees with multiple roots. Renderer.prototype.render will implicitly wrap the children which have been passed in in an implicit Portal element.
*/
declare const Portal: any;
type Portal = typeof Portal;
/**
* A special element tag which copies whatever child appeared previously in the element’s position.
*
* @remarks
* Copy elements are useful when you want to prevent a subtree from updating as a performance optimization.
*/
declare const Copy: any;
type Copy = typeof Copy;
declare const Portal: any;
type Portal = typeof Portal;
/**
* A special element tag for injecting raw nodes into an element tree via its value prop.
*
* @remarks
* If the value prop is a string, Renderer.prototype.parse will be called on the string and the element’s rendered value will be the result.
*/
declare const Raw: any;
type Raw = typeof Raw;
/**
* Describes all valid singular values of an element tree.
*
* @remarks
* Arbitrary objects can also be safely rendered but they will be converted to a string using the toString method. We exclude them from this type to catch potential type errors.
*/
type Child = Element | string | number | boolean | null | undefined;
interface ChildIterable extends Iterable<Child | ChildIterable> {
}
/**
* Describes all valid values of an element tree, including arbitrarily nested iterables of such values.
*/
type Children = Child | ChildIterable;
type Component<TProps = any> = (this: Context<TProps>, props: TProps) => Iterator<Children, Children | void, any> | AsyncIterator<Children, Children | void, any> | PromiseLike<Children> | Children;
/**
* Represents all functions which can be used as a component.
*
* @typeparam TProps - The expected props for the component.
*
* @remarks
* The return type of iterator objects returned from components has to be void because typescript will infer most generators as having a void return type.
*/
type Component<TProps = any> = (this: Context<TProps>, props: TProps) => Children | PromiseLike<Children> | Iterator<Children, Children | void, any> | AsyncIterator<Children, Children | void, any>;
/**
* All nodes in the element tree are narrowed from the union in Child to NarrowedChild. This greatly simplifies element diffing.
*/
type NarrowedChild = Element | string | undefined;
type Key = unknown;
declare const ElementSymbol: unique symbol;
/**
* Elements are the basic building blocks of Crank applications. They are JavaScript objects which are interpreted by special classes called renderers to produce and manage stateful nodes.
*
* @typeparam TTag - the type of the tag of the element.
*
* @example
* // specific element types
* let div: Element<"div">;
* let portal: Element<Portal>;
* let myEl: Element<MyComponent>;
*
* // general element types
* let host: Element<string | symbol>;
* let component: Element<Component>;
*
* @remarks
* Typically, you use the createElement function to create elements and not this class directly.
*/
declare class Element<TTag extends Tag = Tag> {
/**
* The tag of the element. Can be a function, string or symbol depending on the kind of element.
*/
tag: TTag;
/**
* An object containing the “properties” of an element. These correspond to the attributes of the element when using JSX syntax.
*
* @remarks
* The props of an object are passed to most renderer host methods, and as the first argument to components.
*/
props: TagProps<TTag>;
/**
* A value which uniquely identifies an element from its siblings so that it can be added/updated/moved/removed by the identity of the key rather than its position within the parent.
*
* @remarks
* Passed to the element as the prop "crank-key".
*/
key: Key;
/**
* A callback which is called with the element’s value when the value is committed.
*
* @remarks
* Passed to the element as the prop "crank-ref".
*/
ref: Function | undefined;
/**
* @internal
* A unique symbol to identify elements as elements across versions and realms, and to protect against basic injection attacks.
* https://overreacted.io/why-do-react-elements-have-typeof-property/
*/
$$typeof: typeof ElementSymbol;
/**
* @internal
* flags - A bitmask. See ELEMENT FLAGS.
*/
_f: number;
/**
* @internal
* context - The Context object associated with this element.
*
* @remarks
* Created and assigned by the Renderer for component elements when it mounts the element tree.
*/
_ctx: Context<TagProps<TTag>> | undefined;
/**
* @internal
* children - The rendered children of the element.
*/
_ch: Array<NarrowedChild> | NarrowedChild;
/**
* @internal
* node - The node associated with the element.
*
* @remarks
* Set by Renderer.prototype.create when the component is mounted. This property will only be set for host elements.
*/
_n: any;
/**
* @internal
* fallback - The value of the element while it has never committed.
*
* @remarks
* If an element takes place of a previously rendered value but renders asynchronously, this property is set to the previously rendered value until the element commits. This allows asynchronously updating element trees to show something while pending.
*/
_fb: any;
/**
* @internal
* inflightPromise - The current pending async run of the element.
*
* @remarks
* This value is used to make sure element copies do not fulfill immediately, to set the fallback of the next element when the previous element commits, and as the yield value of async generator components with async children. It is unset when the element is committed.
*/
_inf: Promise<any> | undefined;
_fb: any;
/**
* @internal
* onNewValues - the resolve function of a promise which represents the next child result. See the chase function for more info.
*/
_onv: Function | undefined;
tag: TTag;
props: TagProps<TTag>;
key: Key;
ref: Function | undefined;
constructor(tag: TTag, props: TagProps<TTag>, key: Key, ref: Function | undefined);
}
declare function isElement(value: any): value is Element;
declare function createElement<TTag extends Tag>(tag: TTag, props?: TagProps<TTag> | null, ...children: Array<unknown>): Element<TTag>;
/**
* Creates an element with the specified tag, props and children.
*
* @remarks
* This function is usually used as a transpilation target for JSX transpilers, but it can also be called directly. It additionally extracts the crank-key and crank-ref props so they aren’t accessible to the renderer methods or components, and assigns the children prop according to the remaining arguments passed to the function.
*/
declare function createElement<TTag extends Tag>(tag: TTag, props?: TagProps<TTag> | null | undefined, ...children: Array<unknown>): Element<TTag>;
/**
* Clones a given element.
*
* @remarks
* Mainly used internally to make sure we don’t accidentally reuse elements in an element tree, because elements are directly mutated by the renderer.
*/
declare function cloneElement<TTag extends Tag>(el: Element<TTag>): Element<TTag>;
type ElementValue<T> = Array<T | string> | T | string | undefined;
type Scope = unknown;
declare class Renderer<TNode, TRoot = TNode, TResult = ElementValue<TNode>> {
/*** ELEMENT VALUE UTILITIES ***/
/**
* A helper type which repesents all the possible rendered values of an element.
*
* @typeparam TNode - The node type for the element assigned by the renderer.
*
* @remarks
* When asking the question, what is the value of a specific element, the answer varies depending on the type of the element. For host or Raw elements, the answer is simply the nodes (DOM nodes in the case of the DOM renderer) created for the element. For fragments, the values are usually an array of nodes. For portals, the value is undefined, because a Portal element’s root and children are opaque to parents. For components, the value can be any of the above, because the value of a component is determined by its children. Rendered values can also be strings or arrays of nodes and strings, in the case of a component or fragment with strings for children. All of these possible values are reflected in this utility type.
*/
type ElementValue<TNode> = Array<TNode | string> | TNode | string | undefined;
/**
* An abstract class which is subclassed to render to different target environments. This class is responsible for kicking off the rendering process, caching previous trees by root, and creating/mutating/disposing the nodes of the target environment.
*
* @typeparam TNode - The type of the node for a specific rendering environment. It is the type of the return value of Renderer.prototype.create and Renderer.prototype.parse.
* @typeparam TRoot - The type of the root for a specific rendering environment. It is the type of the second parameter passed to Renderer.prototype.render, as well as the expected type of the root portal.
* @typeparam TResult - The type of the exposed values. It is the return value of Renderer.prototype.read, and revealed at various points of the renderer/element API.
*/
declare class Renderer<TNode, TScope, TRoot = TNode, TResult = ElementValue<TNode>> {
/**
* @internal
* A weakmap which stores element trees by root.
*/
_cache: WeakMap<object, Element<Portal>>;
constructor();
// TODO: allow parent contexts from a different renderer to be passed into here
render(children: Children, root: TRoot): Promise<TResult> | TResult;
/**
* Renders an element tree into a specific root.
*
* @param children - An element tree. You can render null with a previously used root to delete the previously rendered element tree from the cache.
* @param root - The node to be rendered into. The renderer will cache element trees per root.
* @param ctx - An optional context that will be the ancestor context of all elements in the tree. Useful for connecting renderers which call each other so that events/provisions properly propagate. The context for a given root must be the same or an error will be thrown.
*
* @returns The read result of rendering the children, or a possible promise of the read result if the element tree renders asynchronously.
*/
/**
* Renders an element tree into a specific root.
*
* @param children - An element tree. You can render null with a previously used root to delete the previously rendered element tree from the cache.
* @param root - The node to be rendered into. The renderer will cache element trees per root.
* @param ctx - An optional context that will be the ancestor context of all elements in the tree. Useful for connecting renderers which call each other so that events/provisions properly propagate. The context for a given root must be the same or an error will be thrown.
*
* @returns The read result of rendering the children, or a possible promise of the read result if the element tree renders asynchronously.
*/
render(children: Children, root?: TRoot | undefined, ctx?: Context | undefined): Promise<TResult> | TResult;
/**
* Called when an element’s value is exposed via render, schedule, refresh, refs, or generator yield expressions.
*
* @param value - The value of the element being read. Can be a node, a string, undefined, or an array of nodes and strings, depending on the element.
* @returns Varies according to the specific renderer subclass. By default, it exposes the element’s value.
*
* @remarks
* This is useful for renderers which don’t want to expose their internal nodes. For instance, the HTML renderer will convert all internal nodes to strings.
*
*/
/**
* Called when an element’s value is exposed via render, schedule, refresh, refs, or generator yield expressions.
*
* @param value - The value of the element being read. Can be a node, a string, undefined, or an array of nodes and strings, depending on the element.
* @returns Varies according to the specific renderer subclass. By default, it exposes the element’s value.
*
* @remarks
* This is useful for renderers which don’t want to expose their internal nodes. For instance, the HTML renderer will convert all internal nodes to strings.
*
*/
read(value: ElementValue<TNode>): TResult;
escape(text: string, _scope: Scope): string;
parse(text: string, _scope: Scope): TNode | string;
scope<TTag extends string | symbol>(_tag: TTag, _props: TagProps<TTag>, scope: Scope | undefined): Scope | undefined;
create<TTag extends string | symbol>(_tag: TTag, _props: TagProps<TTag>, _scope: Scope): TNode;
patch<TTag extends string | symbol>(_tag: TTag, _props: TagProps<TTag>, _node: TNode, _scope: Scope): unknown;
/**
* Called in a preorder traversal for each host element.
*
* @remarks
* Useful for passing data down the element tree. For instance, the DOM renderer uses this method to keep track of whether we’re in an SVG subtree.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param scope - The current scope.
*
* @returns The scope to be passed to create and patch for child host elements.
*
* @remarks
* This method sets the scope for child host elements, not the current host element.
*/
/**
* Called in a preorder traversal for each host element.
*
* @remarks
* Useful for passing data down the element tree. For instance, the DOM renderer uses this method to keep track of whether we’re in an SVG subtree.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param scope - The current scope.
*
* @returns The scope to be passed to create and patch for child host elements.
*
* @remarks
* This method sets the scope for child host elements, not the current host element.
*/
scope<TTag extends string | symbol>(_tag: TTag, _props: TagProps<TTag>, scope: TScope | undefined): TScope;
/**
* Called for each string in an element tree.
*
* @param text - The string child.
* @param scope - The current scope.
*
* @returns The escaped string.
*
* @remarks
* Rather than returning text nodes for whatever environment we’re rendering to, we defer that step for Renderer.prototype.arrange. We do this so that adjacent strings can be concatenated and the actual element tree can be rendered in a normalized form.
*/
/**
* Called for each string in an element tree.
*
* @param text - The string child.
* @param scope - The current scope.
*
* @returns The escaped string.
*
* @remarks
* Rather than returning text nodes for whatever environment we’re rendering to, we defer that step for Renderer.prototype.arrange. We do this so that adjacent strings can be concatenated and the actual element tree can be rendered in a normalized form.
*/
escape(text: string, _scope: TScope): string;
/**
* Called for each Raw element whose value prop is a string.
*
* @param text - The string child.
* @param scope - The current scope.
*
* @returns The parsed node or string.
*/
/**
* Called for each Raw element whose value prop is a string.
*
* @param text - The string child.
* @param scope - The current scope.
*
* @returns The parsed node or string.
*/
parse(text: string, _scope: TScope): TNode | string;
/**
* Called for each host element when it is committed for the first time.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param scope - The current scope.
*
* @returns A “node” which determines the value of the host element.
*/
/**
* Called for each host element when it is committed for the first time.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param scope - The current scope.
*
* @returns A “node” which determines the value of the host element.
*/
create<TTag extends string | symbol>(_tag: TTag, _props: TagProps<TTag>, _scope: TScope): TNode;
/**
* Called for each host element when it is committed.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param node - The node associated with the host element.
* @param scope - The current scope.
*
* @returns The return value is ignored.
*
* @remarks
* Used to mutate the node associated with an element when new props are passed.
*/
/**
* Called for each host element when it is committed.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param node - The node associated with the host element.
* @param scope - The current scope.
*
* @returns The return value is ignored.
*
* @remarks
* Used to mutate the node associated with an element when new props are passed.
*/
patch<TTag extends string | symbol>(_tag: TTag, _props: TagProps<TTag>, _node: TNode, _scope: TScope | undefined): unknown;
/**
* Called for each host element after its children have committed with the actual values of the children.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param node - The node associated with the host element.
* @param children - An array of nodes and strings from child elements.
*
* @returns The return value is ignored.
*
* @remarks
* This method is also called by child components contexts as the last step of a refresh.
*/
// TODO: pass hints into arrange about where the dirty children start and end
/**
* Called for each host element after its children have committed with the actual values of the children.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param node - The node associated with the host element.
* @param children - An array of nodes and strings from child elements.
*
* @returns The return value is ignored.
*
* @remarks
* This method is also called by child components contexts as the last step of a refresh.
*/
arrange<TTag extends string | symbol>(_tag: TTag, _props: TagProps<TTag>, _parent: TNode | TRoot, _children: Array<TNode | string>): unknown;
/**
* Called for each host element when it is unmounted.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param node - The node associated with the host element.
*
* @returns The return value is ignored.
*/
// TODO: remove(): a method which is called to remove a child from a parent to optimize arrange
/**
* Called for each host element when it is unmounted.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param node - The node associated with the host element.
*
* @returns The return value is ignored.
*/
dispose<TTag extends string | symbol>(_tag: TTag, _props: TagProps<TTag>, _node: TNode): unknown;
/**
* Called at the end of the rendering process for each root of the tree.
*
* @param root - The root prop passed to portals or the render method.
*
* @returns The return value is ignored.
*/
/**
* Called at the end of the rendering process for each root of the tree.
*
* @param root - The root prop passed to portals or the render method.
*
* @returns The return value is ignored.
*/
complete(_root: TRoot): unknown;
}
/**
* A map of event type strings to Event subclasses. Can be extended via TypeScript module augmentation to have strongly typed event listeners.
*/
interface EventMap {

@@ -69,32 +453,156 @@ [type: string]: Event;

}
/**
* An interface which can be extended to provide strongly typed provisions (see Context.prototype.get and Context.prototype.set)
*/
interface ProvisionMap {
}
/**
* A class which is instantiated and passed to every component as its this value. In addition to the element tree, contexts also form a tree. Components can use this context tree to communicate data upwards via events and downwards via provisions.
* @typeparam TProps - The expected shape of the props passed to the component. Used to strongly type the Context iterator methods.
* @typeparam TResult - The readable element value type. It is used in places such as the return value of refresh and the argument passed to schedule/cleanup callbacks.
*/
declare class Context<TProps = any, TResult = any> implements EventTarget {
/**
* @internal
* flags - A bitmask. See CONTEXT FLAGS above.
*/
_f: number;
_re: Renderer<unknown, unknown, TResult>;
/**
* @internal
* renderer - The renderer which created this context.
*/
_re: Renderer<unknown, unknown, unknown, TResult>;
/**
* @internal
* el - The associated component element.
*/
_el: Element<Component>;
/**
* @internal
* parent - The parent context.
*/
_pa: Context<unknown, TResult> | undefined;
/**
* @internal
* root - The root node set by an ancestor’s Portal prop.
*/
_rt: unknown;
/**
* @internal
* host - The nearest ancestor host element.
* @remarks
* When refresh is called, the host element will be arranged as the last step of the commit, to make sure the parent’s children properly reflects the components’s children.
*/
_ho: Element<string | symbol>;
_pa: Context<unknown, TResult> | undefined;
_sc: Scope;
_el: Element<Component>;
/**
* @internal
* scope - The value of the scope at the point of element’s creation.
*/
_sc: unknown;
/**
* @internal
* iterator - The iterator returned by the component function.
*/
_it: Iterator<Children, Children | void, unknown> | AsyncIterator<Children, Children | void, unknown> | undefined;
/**
* @internal
* onProps - A callback used in conjunction with the Available flag to implement the props async iterator. See the Symbol.asyncIterator method and the resume function.
*/
_op: ((props: any) => unknown) | undefined;
/**
* @internal
* inflightPending
*/
_ip: Promise<unknown> | undefined;
/**
* @internal
* inflightValue
*/
_iv: Promise<ElementValue<any>> | undefined;
/**
* @internal
* enqueuedPending
*/
_ep: Promise<unknown> | undefined;
_iv: Promise<ElementValue<any>> | undefined;
/**
* @internal
* enqueuedValue
*/
_ev: Promise<ElementValue<any>> | undefined;
/**
* @internal
* listeners - An array of event listeners added to the context via Context.prototype.addEventListener
*/
_ls: Array<EventListenerRecord> | undefined;
/**
* @internal
* provisions - A map of values which can be set via Context.prototype.set and read from child contexts via Context.prototype.get
*/
_ps: Map<unknown, unknown> | undefined;
/**
* @internal
* schedules - a set of callbacks registered via Context.prototype.schedule, which fire when the component has committed.
*/
_ss: Set<(value: TResult) => unknown> | undefined;
/**
* @internal
* cleanups - a set of callbacks registered via Context.prototype.cleanup, which fire when the component has unmounted.
*/
_cs: Set<(value: TResult) => unknown> | undefined;
constructor(renderer: Renderer<unknown, unknown, TResult>, root: unknown, host: Element<string | symbol>, parent: Context<unknown, TResult> | undefined, scope: Scope, el: Element<Component>);
/**
* @internal
*/
/**
* @internal
*/
constructor(renderer: Renderer<unknown, unknown, unknown, TResult>, root: unknown, host: Element<string | symbol>, parent: Context<unknown, TResult> | undefined, scope: unknown, el: Element<Component>);
get<TKey extends keyof ProvisionMap>(key: TKey): ProvisionMap[TKey];
set<TKey extends keyof ProvisionMap>(key: TKey, value: ProvisionMap[TKey]): void;
/**
* The current props of the associated element.
*
* @remarks
* Typically, you should read props either via the first parameter of the component or via the context iterator methods. This property is mainly for plugins or utilities which wrap contexts.
*/
get props(): TProps;
/**
* The current value of the associated element.
*
* @remarks
* Typically, you should read values via refs, generator yield expressions, or the refresh, schedule or cleanup methods. This property is mainly for plugins or utilities which wrap contexts.
*/
get value(): TResult;
[Symbol.iterator](): Generator<TProps>;
[Symbol.asyncIterator](): AsyncGenerator<TProps>;
/**
* Re-executes the component.
*
* @returns The rendered value of the component or a promise of the rendered value if the component or its children execute asynchronously.
*
* @remarks
* The refresh method works a little differently for async generator components, in that it will resume the Context async iterator rather than resuming execution. This is because async generator components are perpetually resumed independent of updates/refresh.
*/
/**
* Re-executes the component.
*
* @returns The rendered value of the component or a promise of the rendered value if the component or its children execute asynchronously.
*
* @remarks
* The refresh method works a little differently for async generator components, in that it will resume the Context async iterator rather than resuming execution. This is because async generator components are perpetually resumed independent of updates/refresh.
*/
refresh(): Promise<TResult> | TResult;
schedule(callback: (value: unknown) => unknown): void;
cleanup(callback: (value: unknown) => unknown): void;
/**
* Registers a callback which fires when the component commits. Will only fire once per callback and update.
*/
/**
* Registers a callback which fires when the component commits. Will only fire once per callback and update.
*/
schedule(callback: (value: TResult) => unknown): void;
/**
* Registers a callback which fires when the component unmounts. Will only fire once per callback.
*/
/**
* Registers a callback which fires when the component unmounts. Will only fire once per callback.
*/
cleanup(callback: (value: TResult) => unknown): void;
addEventListener<T extends string>(type: T, listener: MappedEventListenerOrEventListenerObject<T> | null, options?: boolean | AddEventListenerOptions): void;

@@ -114,2 +622,2 @@ removeEventListener<T extends string>(type: T, listener: MappedEventListenerOrEventListenerObject<T> | null, options?: EventListenerOptions | boolean): void;

}
export { Tag, TagProps, Fragment, Copy, Portal, Raw, Child, Children, Component, Element, isElement, createElement, cloneElement, ElementValue, Renderer, EventMap, ProvisionMap, Context };
export { Tag, TagProps, Fragment, Portal, Copy, Raw, Child, Children, Component, Element, isElement, createElement, cloneElement, ElementValue, Renderer, EventMap, ProvisionMap, Context };

@@ -1,2 +0,8 @@

// UTILITY FUNCTIONS
/*** UTILITIES ***/
function wrap(value) {
return !value ? [] : Array.isArray(value) ? value : [value];
}
function unwrap(arr) {
return arr.length > 1 ? arr : arr[0];
}
function isIterable(value) {

@@ -11,23 +17,38 @@ return value != null && typeof value[Symbol.iterator] === "function";

}
function arrayify(value) {
return !value ? [] : Array.isArray(value) ? value : [value];
}
function unwrap(arr) {
return arr.length > 1 ? arr : arr[0];
}
function isPromiseLike(value) {
return value != null && typeof value.then === "function";
}
function upgradePromiseLike(value) {
if (!(value instanceof Promise)) {
return Promise.resolve(value);
}
return value;
}
// SPECIAL TAGS
/***
* SPECIAL TAGS
* Crank provides a couple tags which have special meaning for the renderer.
***/
/**
* A special element tag for grouping multiple children within a parent.
*
* @remarks
* All iterables which appear in the element tree are implicitly wrapped in fragments. The Fragment tag is just the empty string, and you can use the empty string in createElement calls/transpiler options to avoid having to reference the Fragment export directly.
*/
const Fragment = "";
// TODO: We assert symbol tags to be any because typescript support for symbol tags in JSX does not exist yet.
// NOTE: We assert the following symbol tags to be any because typescript support for symbol tags in JSX does not exist yet.
// https://github.com/microsoft/TypeScript/issues/38367
/**
* A special element tag for creating a new element subtree with a different root, passed via the root prop.
*
* @remarks
* This element tag is useful for creating element trees with multiple roots. Renderer.prototype.render will implicitly wrap the children which have been passed in in an implicit Portal element.
*/
const Portal = Symbol.for("crank.Portal");
/**
* A special element tag which copies whatever child appeared previously in the element’s position.
*
* @remarks
* Copy elements are useful when you want to prevent a subtree from updating as a performance optimization.
*/
const Copy = Symbol.for("crank.Copy");
const Portal = Symbol.for("crank.Portal");
/**
* A special element tag for injecting raw nodes into an element tree via its value prop.
*
* @remarks
* If the value prop is a string, Renderer.prototype.parse will be called on the string and the element’s rendered value will be the result.
*/
const Raw = Symbol.for("crank.Raw");

@@ -45,7 +66,32 @@ function narrow(child) {

}
// https://overreacted.io/why-do-react-elements-have-typeof-property/
const ElementSymbol = Symbol.for("crank.Element");
// ELEMENT FLAGS
/*** ELEMENT FLAGS ***/
/**
* A flag which is set when the component has been mounted. Used mainly to detect whether an element is being reused so that it can be cloned.
*/
const Mounted = 1 << 0;
/**
* A flag which is set when the component has committed at least once.
*/
const Committed = 1 << 1;
// NOTE: To save on filesize, we mangle the internal properties of Crank classes by hand. These internal properties are prefixed with an underscore. Refer to their definitions to see their unabbreviated names.
// NOTE: to maximize compatibility between Crank versions, starting with 0.2.0, the $$typeof property and the non-internal properties will not be changed, and any change to these properties will be considered a breaking change. This is to ensure maximum compatibility between components which use different Crank versions.
/**
* Elements are the basic building blocks of Crank applications. They are JavaScript objects which are interpreted by special classes called renderers to produce and manage stateful nodes.
*
* @typeparam TTag - the type of the tag of the element.
*
* @example
* // specific element types
* let div: Element<"div">;
* let portal: Element<Portal>;
* let myEl: Element<MyComponent>;
*
* // general element types
* let host: Element<string | symbol>;
* let component: Element<Component>;
*
* @remarks
* Typically, you use the createElement function to create elements and not this class directly.
*/
class Element {

@@ -71,2 +117,3 @@ constructor(tag, props, key, ref) {

if (name === "crank-key") {
// NOTE: We have to make sure we don’t assign null to the key because we don’t check for null keys in the diffing functions.
if (props[name] != null) {

@@ -99,2 +146,8 @@ key = props[name];

}
/**
* Clones a given element.
*
* @remarks
* Mainly used internally to make sure we don’t accidentally reuse elements in an element tree, because elements are directly mutated by the renderer.
*/
function cloneElement(el) {

@@ -104,4 +157,10 @@ if (!isElement(el)) {

}
return new Element(el.tag, el.props, el.key, el.ref);
return new Element(el.tag, { ...el.props }, el.key, el.ref);
}
/**
* Takes an array of element values and normalizes the output as an array of nodes and strings.
*
* @remarks
* Normalize will flatten only one level of nested arrays, because it is designed to be called once at each level of the tree. It will also concatenate adjacent strings and remove all undefineds.
*/
function normalize(values) {

@@ -131,3 +190,3 @@ const result = [];

else {
if (buffer !== undefined) {
if (buffer) {
result.push(buffer);

@@ -146,24 +205,24 @@ buffer = undefined;

}
function getChildNodes(el) {
let nodes = [];
const children = arrayify(el._ch);
/**
* Walks an element’s children to find its child values.
* @returns A normalized array of nodes and strings.
*/
function getChildValues(el) {
const values = [];
const children = wrap(el._ch);
for (let i = 0; i < children.length; i++) {
const child = children[i];
if (!child) ;
else if (typeof child === "string") {
nodes.push(child);
if (typeof child === "string") {
values.push(child);
}
else if (typeof child._fb !== "undefined") {
nodes = nodes.concat(arrayify(child._fb));
else if (typeof child === "object") {
values.push(getValue(child));
}
else if (typeof child.tag === "function" || child.tag === Fragment) {
nodes = nodes.concat(getChildNodes(child));
}
else if (child.tag !== Portal) {
// Portals have a value but are opaque to their parents
nodes.push(child._n);
}
}
return nodes;
return normalize(values);
}
/**
* Finds the value of the element according to its type.
* @returns The value of the element.
*/
function getValue(el) {

@@ -179,4 +238,11 @@ if (typeof el._fb !== "undefined") {

}
return unwrap(getChildNodes(el));
return unwrap(getChildValues(el));
}
/**
* An abstract class which is subclassed to render to different target environments. This class is responsible for kicking off the rendering process, caching previous trees by root, and creating/mutating/disposing the nodes of the target environment.
*
* @typeparam TNode - The type of the node for a specific rendering environment. It is the type of the return value of Renderer.prototype.create and Renderer.prototype.parse.
* @typeparam TRoot - The type of the root for a specific rendering environment. It is the type of the second parameter passed to Renderer.prototype.render, as well as the expected type of the root portal.
* @typeparam TResult - The type of the exposed values. It is the return value of Renderer.prototype.read, and revealed at various points of the renderer/element API.
*/
class Renderer {

@@ -186,4 +252,12 @@ constructor() {

}
// TODO: allow parent contexts from a different renderer to be passed into here
render(children, root) {
/**
* Renders an element tree into a specific root.
*
* @param children - An element tree. You can render null with a previously used root to delete the previously rendered element tree from the cache.
* @param root - The node to be rendered into. The renderer will cache element trees per root.
* @param ctx - An optional context that will be the ancestor context of all elements in the tree. Useful for connecting renderers which call each other so that events/provisions properly propagate. The context for a given root must be the same or an error will be thrown.
*
* @returns The read result of rendering the children, or a possible promise of the read result if the element tree renders asynchronously.
*/
render(children, root, ctx) {
let portal;

@@ -195,2 +269,3 @@ if (typeof root === "object" && root !== null) {

portal = createElement(Portal, { children, root });
portal._ctx = ctx;
if (typeof root === "object" && root !== null && children != null) {

@@ -201,2 +276,5 @@ this._cache.set(root, portal);

else {
if (portal._ctx !== ctx) {
throw new Error("render must be called with the same context per root");
}
portal.props = { children, root };

@@ -207,6 +285,8 @@ if (typeof root === "object" && root !== null && children == null) {

}
const value = update(this, root, portal, undefined, undefined, portal);
const scope = undefined;
const value = update(this, root, portal, ctx, scope, portal);
// NOTE: we return the read child values of the portal because portals themselves have no readable value.
if (isPromiseLike(value)) {
return value.then(() => {
const result = this.read(unwrap(getChildNodes(portal)));
const result = this.read(unwrap(getChildValues(portal)));
if (root == null) {

@@ -218,3 +298,3 @@ unmount(this, portal, undefined, portal);

}
const result = this.read(getChildNodes(portal));
const result = this.read(unwrap(getChildValues(portal)));
if (root == null) {

@@ -225,17 +305,83 @@ unmount(this, portal, undefined, portal);

}
/**
* Called when an element’s value is exposed via render, schedule, refresh, refs, or generator yield expressions.
*
* @param value - The value of the element being read. Can be a node, a string, undefined, or an array of nodes and strings, depending on the element.
* @returns Varies according to the specific renderer subclass. By default, it exposes the element’s value.
*
* @remarks
* This is useful for renderers which don’t want to expose their internal nodes. For instance, the HTML renderer will convert all internal nodes to strings.
*
*/
read(value) {
return value;
}
/**
* Called in a preorder traversal for each host element.
*
* @remarks
* Useful for passing data down the element tree. For instance, the DOM renderer uses this method to keep track of whether we’re in an SVG subtree.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param scope - The current scope.
*
* @returns The scope to be passed to create and patch for child host elements.
*
* @remarks
* This method sets the scope for child host elements, not the current host element.
*/
scope(_tag, _props, scope) {
return scope;
}
/**
* Called for each string in an element tree.
*
* @param text - The string child.
* @param scope - The current scope.
*
* @returns The escaped string.
*
* @remarks
* Rather than returning text nodes for whatever environment we’re rendering to, we defer that step for Renderer.prototype.arrange. We do this so that adjacent strings can be concatenated and the actual element tree can be rendered in a normalized form.
*/
escape(text, _scope) {
return text;
}
/**
* Called for each Raw element whose value prop is a string.
*
* @param text - The string child.
* @param scope - The current scope.
*
* @returns The parsed node or string.
*/
parse(text, _scope) {
return text;
}
scope(_tag, _props, scope) {
return scope;
}
/**
* Called for each host element when it is committed for the first time.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param scope - The current scope.
*
* @returns A “node” which determines the value of the host element.
*/
create(_tag, _props, _scope) {
throw new Error("Not implemented");
}
/**
* Called for each host element when it is committed.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param node - The node associated with the host element.
* @param scope - The current scope.
*
* @returns The return value is ignored.
*
* @remarks
* Used to mutate the node associated with an element when new props are passed.
*/
patch(_tag, _props, _node, _scope) {

@@ -245,2 +391,15 @@ return;

// TODO: pass hints into arrange about where the dirty children start and end
/**
* Called for each host element after its children have committed with the actual values of the children.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param node - The node associated with the host element.
* @param children - An array of nodes and strings from child elements.
*
* @returns The return value is ignored.
*
* @remarks
* This method is also called by child components contexts as the last step of a refresh.
*/
arrange(_tag, _props, _parent, _children) {

@@ -250,5 +409,21 @@ return;

// TODO: remove(): a method which is called to remove a child from a parent to optimize arrange
/**
* Called for each host element when it is unmounted.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param node - The node associated with the host element.
*
* @returns The return value is ignored.
*/
dispose(_tag, _props, _node) {
return;
}
/**
* Called at the end of the rendering process for each root of the tree.
*
* @param root - The root prop passed to portals or the render method.
*
* @returns The return value is ignored.
*/
complete(_root) {

@@ -258,3 +433,4 @@ return;

}
// PRIVATE RENDERER FUNCTIONS
/*** PRIVATE RENDERER FUNCTIONS ***/
// NOTE: to aid in the mangling of this module, we use functions rather than methods for internal logic.
function mount(renderer, root, host, ctx, scope, el) {

@@ -270,7 +446,3 @@ el._f |= Mounted;

else if (el.tag !== Fragment) {
if (el.tag !== Portal) {
// TODO: maybe we can defer create calls to when the element is committing
el._n = renderer.create(el.tag, el.props, scope);
}
else {
if (el.tag === Portal) {
root = el.props.root;

@@ -281,2 +453,3 @@ }

}
// NOTE: The primary benefit of having a separate codepath for mounting is that it’s slightly faster because we don’t have to align and diff children against old children. But for singular child values, updateChild is sufficient.
if (isNonStringIterable(el.props.children)) {

@@ -300,3 +473,4 @@ return mountChildren(renderer, root, host, ctx, scope, el, el.props.children);

}
[child, value] = compare(renderer, root, host, ctx, scope, undefined, child);
[child, value] = diff(renderer, root, host, ctx, scope, undefined, // oldChild
child);
newChildren[i] = child;

@@ -316,3 +490,3 @@ values.push(value);

}
return race(renderer, host, ctx, scope, parent, values1);
return chase(renderer, host, ctx, scope, parent, values1);
}

@@ -355,3 +529,3 @@ function update(renderer, root, host, ctx, scope, el) {

let value;
[newChild, value] = compare(renderer, root, host, ctx, scope, oldChild, newChild);
[newChild, value] = diff(renderer, root, host, ctx, scope, oldChild, newChild);
if (typeof oldChild === "object" && oldChild !== newChild) {

@@ -361,7 +535,5 @@ unmount(renderer, host, ctx, oldChild);

parent._ch = newChild;
// TODO: allow single values to be passed to race
const values = isPromiseLike(value)
? value.then((value) => [value])
: [value];
return race(renderer, host, ctx, scope, parent, values);
// TODO: allow single values to be passed to chase
const values = isPromiseLike(value) ? value.then(wrap) : wrap(value);
return chase(renderer, host, ctx, scope, parent, values);
}

@@ -383,3 +555,3 @@ function mapChildrenByKey(children) {

const values = [];
const oldChildren = arrayify(parent._ch);
const oldChildren = wrap(parent._ch);
const newChildren = Array.from(children);

@@ -439,3 +611,3 @@ const graveyard = [];

let value;
[newChild, value] = compare(renderer, root, host, ctx, scope, oldChild, newChild);
[newChild, value] = diff(renderer, root, host, ctx, scope, oldChild, newChild);
values.push(value);

@@ -458,3 +630,3 @@ newChildren[j] = newChild;

}
// TODO: async removal of keyed nodes
// TODO: async unmounting
if (childrenByKey !== undefined && childrenByKey.size > 0) {

@@ -471,5 +643,5 @@ graveyard.push(...childrenByKey.values());

}
return race(renderer, host, ctx, scope, parent, values1);
return chase(renderer, host, ctx, scope, parent, values1);
}
function compare(renderer, root, host, ctx, scope, oldChild, newChild) {
function diff(renderer, root, host, ctx, scope, oldChild, newChild) {
let value;

@@ -479,2 +651,3 @@ if (typeof oldChild === "object" &&

oldChild.tag === newChild.tag) {
// TODO: implement Raw element parse caching
if (oldChild.tag === Portal) {

@@ -485,3 +658,2 @@ if (oldChild.props.root !== newChild.props.root) {

}
// TODO: implement Raw element parse caching
if (oldChild !== newChild) {

@@ -519,3 +691,2 @@ oldChild.props = newChild.props;

if (typeof oldChild._inf === "object") {
// TODO: figure out if this branch is actually necessary
oldChild._inf

@@ -539,3 +710,9 @@ .then((value) => {

}
function race(renderer, host, ctx, scope, el, values) {
/**
* A function to race current child values with future child values.
*
* @remarks
* When an element’s children update asynchronously, we race the resulting promise with the next update of the element’s children. By induction, this ensures that when any update to an element settles, all past updates to that same element will have settled as well. This prevents deadlocks and unnecessary awaiting when an element’s children have been cleared, for instance.
*/
function chase(renderer, host, ctx, scope, el, values) {
if (isPromiseLike(values)) {

@@ -559,8 +736,4 @@ let onNewValues;

}
function commit(renderer, scope, el, nodes) {
el._f |= Committed;
if (typeof el._fb !== "undefined") {
el._fb = undefined;
}
let value = unwrap(nodes);
function commit(renderer, scope, el, values) {
let value = unwrap(values);
if (typeof el.tag === "function") {

@@ -572,3 +745,3 @@ if (typeof el._ctx === "object") {

else if (el.tag === Portal) {
renderer.arrange(Portal, el.props, el.props.root, nodes);
renderer.arrange(Portal, el.props, el.props.root, values);
renderer.complete(el.props.root);

@@ -587,12 +760,19 @@ value = undefined;

else if (el.tag !== Fragment) {
if (!(el._f & Committed)) {
el._n = renderer.create(el.tag, el.props, scope);
}
renderer.patch(el.tag, el.props, el._n, scope);
renderer.arrange(el.tag, el.props, el._n, nodes);
renderer.arrange(el.tag, el.props, el._n, values);
value = el._n;
}
if (typeof el.ref === "function") {
el._f |= Committed;
if (el.ref) {
el.ref(renderer.read(value));
}
if (typeof el._inf === "object") {
if (el._inf) {
el._inf = undefined;
}
if (el._fb) {
el._fb = undefined;
}
return value;

@@ -625,3 +805,3 @@ }

}
const children = arrayify(el._ch);
const children = wrap(el._ch);
for (let i = 0; i < children.length; i++) {

@@ -634,3 +814,4 @@ const child = children[i];

}
// EVENT UTILITY FUNCTIONS
/*** EVENT UTILITIES ***/
// EVENT PHASE CONSTANTS (https://developer.mozilla.org/en-US/docs/Web/API/Event/eventPhase)
const NONE = 0;

@@ -660,2 +841,8 @@ const CAPTURING_PHASE = 1;

}
/**
* A function to reconstruct an array of every listener given a context and a host element.
*
* @remarks
* This function exploits the fact that contexts retain their nearest ancestor host element. We can determine all the contexts which are directly listening to an element by traversing up the context tree and checking that the host element passed in matches the context’s host property.
*/
function getListeners(ctx, host) {

@@ -673,6 +860,6 @@ let listeners;

if (typeof ctx._ls !== "undefined" && ctx._ls.length > 0) {
for (const node of getChildNodes(ctx._el)) {
if (isEventTarget(node)) {
for (const value of getChildValues(ctx._el)) {
if (isEventTarget(value)) {
for (const record of ctx._ls) {
node.removeEventListener(record.type, record.callback, record.options);
value.removeEventListener(record.type, record.callback, record.options);
}

@@ -685,12 +872,43 @@ }

// CONTEXT FLAGS
// TODO: write an explanation for each of these flags
const Independent = 1 << 0;
/**
* A flag which is set when the component is being updated by the parent and cleared when the component has committed. Used to determine whether the nearest host ancestor needs to be rearranged.
*/
const Updating = 1 << 0;
/**
* A flag which is set when the component is called or stepped through. It is used to ensure that a component which synchronously triggers a second update in the course of rendering does not cause an infinite loop or a generator error.
*/
const Stepping = 1 << 1;
/**
* A flag used to make sure multiple values are not pulled from context prop iterators without a yield.
*/
const Iterating = 1 << 2;
/**
* A flag used by async generator components in conjunction with the onProps functions (_op) to mark whether new props can be pulled via the context iterator methods.
*/
const Available = 1 << 3;
/**
* A flag which is set when generator components return. Set whenever an iterator returns an iteration with the done property set to true. Finished components will stick to their last rendered value and ignore further updates.
*/
const Finished = 1 << 4;
/**
* A flag which is set when the component is unmounted. Unmounted components are no longer in the element tree, and cannot run or refresh.
*/
const Unmounted = 1 << 5;
/**
* A flag which indicates that the component is a sync generator component.
*/
const SyncGen = 1 << 6;
/**
* A flag which indicates that the component is an async generator component.
*/
const AsyncGen = 1 << 7;
/**
* A class which is instantiated and passed to every component as its this value. In addition to the element tree, contexts also form a tree. Components can use this context tree to communicate data upwards via events and downwards via provisions.
* @typeparam TProps - The expected shape of the props passed to the component. Used to strongly type the Context iterator methods.
* @typeparam TResult - The readable element value type. It is used in places such as the return value of refresh and the argument passed to schedule/cleanup callbacks.
*/
class Context {
/**
* @internal
*/
constructor(renderer, root, host, parent, scope, el) {

@@ -718,7 +936,19 @@ this._f = 0;

}
/**
* The current props of the associated element.
*
* @remarks
* Typically, you should read props either via the first parameter of the component or via the context iterator methods. This property is mainly for plugins or utilities which wrap contexts.
*/
get props() {
return this._el.props;
}
/**
* The current value of the associated element.
*
* @remarks
* Typically, you should read values via refs, generator yield expressions, or the refresh, schedule or cleanup methods. This property is mainly for plugins or utilities which wrap contexts.
*/
get value() {
return this._re.read(unwrap(getChildNodes(this._el)));
return this._re.read(getValue(this._el));
}

@@ -760,2 +990,10 @@ *[Symbol.iterator]() {

}
/**
* Re-executes the component.
*
* @returns The rendered value of the component or a promise of the rendered value if the component or its children execute asynchronously.
*
* @remarks
* The refresh method works a little differently for async generator components, in that it will resume the Context async iterator rather than resuming execution. This is because async generator components are perpetually resumed independent of updates/refresh.
*/
refresh() {

@@ -766,6 +1004,9 @@ if (this._f & (Stepping | Unmounted)) {

}
this._f |= Independent;
this._f &= ~Updating;
resume(this);
return this._re.read(run(this));
}
/**
* Registers a callback which fires when the component commits. Will only fire once per callback and update.
*/
schedule(callback) {

@@ -777,2 +1018,5 @@ if (typeof this._ss === "undefined") {

}
/**
* Registers a callback which fires when the component unmounts. Will only fire once per callback.
*/
cleanup(callback) {

@@ -818,5 +1062,5 @@ if (typeof this._cs === "undefined") {

this._ls.push(record);
for (const node of getChildNodes(this._el)) {
if (isEventTarget(node)) {
node.addEventListener(record.type, record.callback, record.options);
for (const value of getChildValues(this._el)) {
if (isEventTarget(value)) {
value.addEventListener(record.type, record.callback, record.options);
}

@@ -838,5 +1082,5 @@ }

this._ls.splice(i, 1);
for (const node of getChildNodes(this._el)) {
if (isEventTarget(node)) {
node.removeEventListener(record.type, record.callback, record.options);
for (const value of getChildValues(this._el)) {
if (isEventTarget(value)) {
value.removeEventListener(record.type, record.callback, record.options);
}

@@ -939,3 +1183,6 @@ }

}
// PRIVATE CONTEXT FUNCTIONS
/*** PRIVATE CONTEXT FUNCTIONS ***/
/**
* Called to make props available to the Context async iterator for async generator components.
*/
function resume(ctx) {

@@ -950,2 +1197,6 @@ if (typeof ctx._op === "function") {

}
// NOTE: The functions run, step and advance work together to implement the async queueing behavior of components. The run function calls the step function, which returns two results in a tuple. The first result, called “pending,” is a possible promise which settles when the component can accept new updates, and represents the duration during which the component is blocked from accepting new updates. The second result, called “value,” is the actual result of the update. The run function caches pending/value from the step function on the context, according to whether the component is currently blocked. The “inflight” pending/value properties are the currently executing update, and the “enqueued” pending/value properties are promises which represent the next step. Lastly, the run function calls the advance function in a Promise.prototype.finally callback to allow new steps to be enqueued.
/**
* Enqueues and executes the component associated with the context.
*/
function run(ctx) {

@@ -958,3 +1209,3 @@ if (typeof ctx._ip === "undefined") {

.catch((err) => {
if (ctx._f & Independent) {
if (!(ctx._f & Updating)) {
return propagateError(ctx._pa, err);

@@ -972,3 +1223,3 @@ }

catch (err) {
if (ctx._f & Independent) {
if (!(ctx._f & Updating)) {
return propagateError(ctx._pa, err);

@@ -994,3 +1245,3 @@ }

return pending.catch((err) => {
if (ctx._f & Independent) {
if (!(ctx._f & Updating)) {
return propagateError(ctx._pa, err);

@@ -1002,3 +1253,3 @@ }

catch (err) {
if (ctx._f & Independent) {
if (!(ctx._f & Updating)) {
return propagateError(ctx._pa, err);

@@ -1013,2 +1264,17 @@ }

}
/**
* The step function is responsible for executing the component and handling all the different component types.
*
* @returns A tuple [pending, value]
* pending - A possible promise which represents the duration during which the component is blocked from updating.
* value - The actual rendered value of the children.
*
* @remarks
* Each component type will block/unblock according to the type of the component.
* Sync function components never block and will transparently pass updates to children.
* Async function components and async generator components block while executing itself, but will not block for async children.
* Sync generator components block while any children are pending.
*
* The reason sync generator components block while their children are pending is that they are expected to only resume when they’ve actually rendered. Additionally, they have no mechanism for awaiting async children.
*/
function step(ctx) {

@@ -1020,23 +1286,26 @@ const el = ctx._el;

let initial = false;
ctx._f |= Stepping;
if (typeof ctx._it === "undefined") {
initial = true;
clearEventListeners(ctx);
const result = el.tag.call(ctx, el.props);
if (isIteratorLike(result)) {
ctx._it = result;
try {
ctx._f |= Stepping;
if (typeof ctx._it === "undefined") {
initial = true;
clearEventListeners(ctx);
const result = el.tag.call(ctx, el.props);
if (isIteratorLike(result)) {
ctx._it = result;
}
else if (isPromiseLike(result)) {
const result1 = result instanceof Promise ? result : Promise.resolve(result);
const pending = result1;
const value = result1.then((result) => updateCtxChildren(ctx, result));
return [pending, value];
}
else {
// sync function component
return [undefined, updateCtxChildren(ctx, result)];
}
}
else if (isPromiseLike(result)) {
const result1 = upgradePromiseLike(result);
const pending = result1;
const value1 = result1.then((result) => updateCtxChildren(ctx, result));
ctx._f &= ~Stepping;
return [pending, value1];
}
else {
const value = updateCtxChildren(ctx, result);
ctx._f &= ~Stepping;
return [undefined, value];
}
}
finally {
ctx._f &= ~Stepping;
}
let oldValue;

@@ -1052,6 +1321,10 @@ if (typeof ctx._el._inf === "object") {

}
// TODO: clean up/deduplicate logic here
// TODO: generator components which throw errors should be fragile, if rerendered they should be unmounted and remounted
const iteration = ctx._it.next(oldValue);
ctx._f &= ~Stepping;
let iteration;
try {
ctx._f |= Stepping;
iteration = ctx._it.next(oldValue);
}
finally {
ctx._f &= ~Stepping;
}
if (isPromiseLike(iteration)) {

@@ -1069,16 +1342,5 @@ // async generator component

try {
let value = updateCtxChildren(ctx, iteration.value);
const value = updateCtxChildren(ctx, iteration.value);
if (isPromiseLike(value)) {
if (!(ctx._f & Finished) && typeof ctx._it.throw === "function") {
value = value.catch((err) => {
resume(ctx);
const iteration = ctx._it.throw(err);
return iteration.then((iteration) => {
if (iteration.done) {
ctx._f |= Finished;
}
return updateCtxChildren(ctx, iteration.value);
});
});
}
return value.catch((err) => handleChildError(ctx, err));
}

@@ -1088,12 +1350,3 @@ return value;

catch (err) {
if (ctx._f & Finished || typeof ctx._it.throw !== "function") {
throw err;
}
const iteration = ctx._it.throw(err);
return iteration.then((iteration) => {
if (iteration.done) {
ctx._f |= Finished;
}
return updateCtxChildren(ctx, iteration.value);
});
return handleChildError(ctx, err);
}

@@ -1103,51 +1356,35 @@ });

}
else {
// sync generator component
if (initial) {
ctx._f |= SyncGen;
// sync generator component
if (initial) {
ctx._f |= SyncGen;
}
ctx._f &= ~Iterating;
if (iteration.done) {
ctx._f |= Finished;
}
let value;
try {
value = updateCtxChildren(ctx, iteration.value);
if (isPromiseLike(value)) {
value = value.catch((err) => handleChildError(ctx, err));
}
ctx._f &= ~Iterating;
if (iteration.done) {
ctx._f |= Finished;
}
let value;
try {
value = updateCtxChildren(ctx, iteration.value);
if (isPromiseLike(value)) {
if (!(ctx._f & Finished) && typeof ctx._it.throw === "function") {
value = value.catch((err) => {
ctx._f |= Stepping;
const iteration = ctx._it.throw(err);
ctx._f &= ~Stepping;
if (iteration.done) {
ctx._f |= Finished;
}
return updateCtxChildren(ctx, iteration.value);
});
}
const pending = value.catch(() => { });
return [pending, value];
}
}
catch (err) {
if (ctx._f & Finished || typeof ctx._it.throw !== "function") {
throw err;
}
ctx._f |= Stepping;
const iteration = ctx._it.throw(err);
ctx._f &= ~Stepping;
if (iteration.done) {
ctx._f |= Finished;
}
const value = updateCtxChildren(ctx, iteration.value);
if (isPromiseLike(value)) {
const pending = value.catch(() => { });
return [pending, value];
}
return [undefined, value];
}
return [undefined, value];
}
catch (err) {
value = handleChildError(ctx, err);
}
if (isPromiseLike(value)) {
// Because
return [value.catch(() => { }), value];
}
return [undefined, value];
}
/**
* @remarks
* Called when the inflight pending promise settles.
*/
function advance(ctx) {
// _ip: inflightPending
// _iv: inflightValue
// _ep: enqueuedPending
// _ev: enqueuedValue
ctx._ip = ctx._ep;

@@ -1158,10 +1395,36 @@ ctx._iv = ctx._ev;

if (ctx._f & AsyncGen && !(ctx._f & Finished)) {
ctx._f |= Independent;
run(ctx);
}
}
// TODO: generator components which throw errors should be recoverable
function handleChildError(ctx, err) {
if (ctx._f & Finished || !ctx._it || typeof ctx._it.throw !== "function") {
throw err;
}
resume(ctx);
let iteration;
try {
ctx._f |= Stepping;
iteration = ctx._it.throw(err);
}
finally {
ctx._f &= ~Stepping;
}
if (isPromiseLike(iteration)) {
return iteration.then((iteration) => {
if (iteration.done) {
ctx._f |= Finished;
}
return updateCtxChildren(ctx, iteration.value);
});
}
if (iteration.done) {
ctx._f |= Finished;
}
return updateCtxChildren(ctx, iteration.value);
}
function propagateError(ctx, err) {
if (!ctx ||
ctx._f & Finished ||
typeof ctx._it !== "object" ||
!ctx._it ||
typeof ctx._it.throw !== "function") {

@@ -1172,8 +1435,15 @@ throw err;

resume(ctx);
const result = ctx._it.throw(err);
if (isPromiseLike(result)) {
return result
.catch((err) => propagateError(ctx._pa, err))
.then(() => undefined);
let iteration;
try {
ctx._f |= Stepping;
iteration = ctx._it.throw(err);
}
finally {
ctx._f &= ~Stepping;
}
if (isPromiseLike(iteration)) {
return iteration
.then(() => undefined)
.catch((err) => propagateError(ctx._pa, err));
}
}

@@ -1185,5 +1455,3 @@ catch (err) {

function updateCtx(ctx) {
if (ctx._f & AsyncGen) {
ctx._f &= ~Independent;
}
ctx._f |= Updating;
resume(ctx);

@@ -1200,3 +1468,7 @@ return run(ctx);

}
return updateChild(ctx._re, ctx._rt, ctx._ho, ctx, ctx._sc, ctx._el, child);
return updateChild(ctx._re, ctx._rt, // root
ctx._ho, // host
ctx, ctx._sc, // scope
ctx._el, // element
child);
}

@@ -1208,3 +1480,3 @@ function commitCtx(ctx, value) {

if (typeof ctx._ls !== "undefined" && ctx._ls.length > 0) {
for (const child of arrayify(value)) {
for (const child of wrap(value)) {
if (isEventTarget(child)) {

@@ -1217,3 +1489,3 @@ for (const record of ctx._ls) {

}
if (ctx._f & Independent) {
if (!(ctx._f & Updating)) {
const listeners = getListeners(ctx._pa, ctx._ho);

@@ -1223,3 +1495,3 @@ if (listeners !== undefined && listeners.length > 0) {

const record = listeners[i];
for (const v of arrayify(value)) {
for (const v of wrap(value)) {
if (isEventTarget(v)) {

@@ -1231,11 +1503,12 @@ v.addEventListener(record.type, record.callback, record.options);

}
// TODO: async generator components which yield multiple children synchronously will over-arrange the host. Maybe we can defer arrangement for this case.
// TODO: we don’t need to call arrange if none of the nodes have changed or moved
const host = ctx._ho;
ctx._re.arrange(host.tag, host.props, host.tag === Portal ? host.props.root : host._n, getChildNodes(host));
if (host._f & Committed) {
ctx._re.arrange(host.tag, host.props, host.tag === Portal ? host.props.root : host._n, getChildValues(host));
}
ctx._re.complete(ctx._rt);
ctx._f &= ~Independent;
}
if (typeof ctx._ss !== "undefined" && ctx._ss.size > 0) {
// We have to clear the set of callbacks before calling them, because a callback which refreshes the component would otherwise cause a stack overflow.
ctx._f &= ~Updating;
if (!!ctx._ss && ctx._ss.size > 0) {
// NOTE: We have to clear the set of callbacks before calling them, because a callback which refreshes the component would otherwise cause a stack overflow.
const callbacks = Array.from(ctx._ss);

@@ -1249,6 +1522,7 @@ ctx._ss.clear();

}
// TODO: async unmounting
function unmountCtx(ctx) {
ctx._f |= Unmounted;
clearEventListeners(ctx);
if (typeof ctx._cs === "object") {
if (ctx._cs) {
const value = ctx._re.read(getValue(ctx._el));

@@ -1263,5 +1537,14 @@ for (const cleanup of ctx._cs) {

resume(ctx);
if (typeof ctx._it === "object" && typeof ctx._it.return === "function") {
// TODO: handle async generator rejections
ctx._it.return();
if (!!ctx._it && typeof ctx._it.return === "function") {
let iteration;
try {
ctx._f |= Stepping;
iteration = ctx._it.return();
}
finally {
ctx._f &= ~Stepping;
}
if (isPromiseLike(iteration)) {
iteration.catch((err) => propagateError(ctx._pa, err));
}
}

@@ -1268,0 +1551,0 @@ }

{
"name": "@bikeshaving/crank",
"version": "0.2.0-beta.3",
"version": "0.2.0-beta.4",
"description": "Write JSX-driven components with functions, promises and generators.",

@@ -5,0 +5,0 @@ "homepage": "https://crank.js.org",

@@ -1,72 +0,419 @@

type Tag = Component<any> | string | symbol;
type TagProps<TTag extends Tag> = TTag extends string ? JSX.IntrinsicElements[TTag] : TTag extends Component<infer TProps> ? TProps : unknown; // SPECIAL TAGS
// SPECIAL TAGS
/**
* Represents all valid values which can be used for the tag of an element.
*
* @remarks
* Elements whose tags are strings or symbols are called “host” or “intrinsic” elements, and their behavior is determined by the renderer, while elements whose tags are components are called “component” elements, and their behavior is determined by the execution of the component.
*/
type Tag = string | symbol | Component; /**
* Maps the tag of an element to its expected props.
*
* @typeparam TTag - The element’s tag.
*/
/**
* Maps the tag of an element to its expected props.
*
* @typeparam TTag - The element’s tag.
*/
type TagProps<TTag extends Tag> = TTag extends string ? JSX.IntrinsicElements[TTag] : TTag extends Component<infer TProps> ? TProps : unknown; /***
* SPECIAL TAGS
* Crank provides a couple tags which have special meaning for the renderer.
***/
/**
* A special element tag for grouping multiple children within a parent.
*
* @remarks
* All iterables which appear in the element tree are implicitly wrapped in fragments. The Fragment tag is just the empty string, and you can use the empty string in createElement calls/transpiler options to avoid having to reference the Fragment export directly.
*/
/***
* SPECIAL TAGS
* Crank provides a couple tags which have special meaning for the renderer.
***/
/**
* A special element tag for grouping multiple children within a parent.
*
* @remarks
* All iterables which appear in the element tree are implicitly wrapped in fragments. The Fragment tag is just the empty string, and you can use the empty string in createElement calls/transpiler options to avoid having to reference the Fragment export directly.
*/
declare const Fragment = "";
type Fragment = typeof Fragment; // TODO: We assert symbol tags to be any because typescript support for symbol tags in JSX does not exist yet.
type Fragment = typeof Fragment; // NOTE: We assert the following symbol tags to be any because typescript support for symbol tags in JSX does not exist yet.
// https://github.com/microsoft/TypeScript/issues/38367
// TODO: We assert symbol tags to be any because typescript support for symbol tags in JSX does not exist yet.
/**
* A special element tag for creating a new element subtree with a different root, passed via the root prop.
*
* @remarks
* This element tag is useful for creating element trees with multiple roots. Renderer.prototype.render will implicitly wrap the children which have been passed in in an implicit Portal element.
*/
// NOTE: We assert the following symbol tags to be any because typescript support for symbol tags in JSX does not exist yet.
// https://github.com/microsoft/TypeScript/issues/38367
/**
* A special element tag for creating a new element subtree with a different root, passed via the root prop.
*
* @remarks
* This element tag is useful for creating element trees with multiple roots. Renderer.prototype.render will implicitly wrap the children which have been passed in in an implicit Portal element.
*/
declare const Portal: any;
type Portal = typeof Portal; /**
* A special element tag which copies whatever child appeared previously in the element’s position.
*
* @remarks
* Copy elements are useful when you want to prevent a subtree from updating as a performance optimization.
*/
/**
* A special element tag which copies whatever child appeared previously in the element’s position.
*
* @remarks
* Copy elements are useful when you want to prevent a subtree from updating as a performance optimization.
*/
declare const Copy: any;
type Copy = typeof Copy;
declare const Portal: any;
type Portal = typeof Portal;
type Copy = typeof Copy; /**
* A special element tag for injecting raw nodes into an element tree via its value prop.
*
* @remarks
* If the value prop is a string, Renderer.prototype.parse will be called on the string and the element’s rendered value will be the result.
*/
/**
* A special element tag for injecting raw nodes into an element tree via its value prop.
*
* @remarks
* If the value prop is a string, Renderer.prototype.parse will be called on the string and the element’s rendered value will be the result.
*/
declare const Raw: any;
type Raw = typeof Raw;
type Child = Element | string | number | boolean | null | undefined;
type Raw = typeof Raw; /**
* Describes all valid singular values of an element tree.
*
* @remarks
* Arbitrary objects can also be safely rendered but they will be converted to a string using the toString method. We exclude them from this type to catch potential type errors.
*/
/**
* Describes all valid singular values of an element tree.
*
* @remarks
* Arbitrary objects can also be safely rendered but they will be converted to a string using the toString method. We exclude them from this type to catch potential type errors.
*/
type Child = Element | string | number | boolean | null | undefined; // NOTE: we use a recursive interface rather than making the Children type directly recursive because recursive type aliases were only added in TypeScript 3.7.
// NOTE: we use a recursive interface rather than making the Children type directly recursive because recursive type aliases were only added in TypeScript 3.7.
interface ChildIterable extends Iterable<Child | ChildIterable> {
}
type Children = Child | ChildIterable; // return type of iterators has to be void because typescript
// return type of iterators has to be void because typescript
type Component<TProps = any> = (this: Context<TProps>, props: TProps) => Iterator<Children, Children | void, any> | AsyncIterator<Children, Children | void, any> | PromiseLike<Children> | Children; // WHAT ARE WE DOING TO THE CHILDREN
// WHAT ARE WE DOING TO THE CHILDREN
} /**
* Describes all valid values of an element tree, including arbitrarily nested iterables of such values.
*/
/**
* Describes all valid values of an element tree, including arbitrarily nested iterables of such values.
*/
type Children = Child | ChildIterable; /**
* Represents all functions which can be used as a component.
*
* @typeparam TProps - The expected props for the component.
*
* @remarks
* The return type of iterator objects returned from components has to be void because typescript will infer most generators as having a void return type.
*/
/**
* Represents all functions which can be used as a component.
*
* @typeparam TProps - The expected props for the component.
*
* @remarks
* The return type of iterator objects returned from components has to be void because typescript will infer most generators as having a void return type.
*/
type Component<TProps = any> = (this: Context<TProps>, props: TProps) => Children | PromiseLike<Children> | Iterator<Children, Children | void, any> | AsyncIterator<Children, Children | void, any>; // WHAT ARE WE DOING TO THE CHILDREN???
/**
* All nodes in the element tree are narrowed from the union in Child to NarrowedChild. This greatly simplifies element diffing.
*/
// WHAT ARE WE DOING TO THE CHILDREN???
/**
* All nodes in the element tree are narrowed from the union in Child to NarrowedChild. This greatly simplifies element diffing.
*/
type NarrowedChild = Element | string | undefined;
type Key = unknown; // https://overreacted.io/why-do-react-elements-have-typeof-property/
declare const ElementSymbol: unique symbol; // ELEMENT FLAGS
// ELEMENT FLAGS
// ELEMENT FLAGS
type Key = unknown;
declare const ElementSymbol: unique symbol; /*** ELEMENT FLAGS ***/
/**
* A flag which is set when the component has been mounted. Used mainly to detect whether an element is being reused so that it can be cloned.
*/
/*** ELEMENT FLAGS ***/
/**
* A flag which is set when the component has been mounted. Used mainly to detect whether an element is being reused so that it can be cloned.
*/
/*** ELEMENT FLAGS ***/
/**
* A flag which is set when the component has been mounted. Used mainly to detect whether an element is being reused so that it can be cloned.
*/
// NOTE: To save on filesize, we mangle the internal properties of Crank classes by hand. These internal properties are prefixed with an underscore. Refer to their definitions to see their unabbreviated names.
// NOTE: to maximize compatibility between Crank versions, starting with 0.2.0, the $$typeof property and the non-internal properties will not be changed, and any change to these properties will be considered a breaking change. This is to ensure maximum compatibility between components which use different Crank versions.
/**
* Elements are the basic building blocks of Crank applications. They are JavaScript objects which are interpreted by special classes called renderers to produce and manage stateful nodes.
*
* @typeparam TTag - the type of the tag of the element.
*
* @example
* // specific element types
* let div: Element<"div">;
* let portal: Element<Portal>;
* let myEl: Element<MyComponent>;
*
* // general element types
* let host: Element<string | symbol>;
* let component: Element<Component>;
*
* @remarks
* Typically, you use the createElement function to create elements and not this class directly.
*/
declare class Element<TTag extends Tag = Tag> {
/**
* The tag of the element. Can be a function, string or symbol depending on the kind of element.
*/
tag: TTag;
/**
* An object containing the “properties” of an element. These correspond to the attributes of the element when using JSX syntax.
*
* @remarks
* The props of an object are passed to most renderer host methods, and as the first argument to components.
*/
props: TagProps<TTag>;
/**
* A value which uniquely identifies an element from its siblings so that it can be added/updated/moved/removed by the identity of the key rather than its position within the parent.
*
* @remarks
* Passed to the element as the prop "crank-key".
*/
key: Key;
/**
* A callback which is called with the element’s value when the value is committed.
*
* @remarks
* Passed to the element as the prop "crank-ref".
*/
ref: Function | undefined;
/**
* @internal
* A unique symbol to identify elements as elements across versions and realms, and to protect against basic injection attacks.
* https://overreacted.io/why-do-react-elements-have-typeof-property/
*/
$$typeof: typeof ElementSymbol;
// flags
/**
* @internal
* flags - A bitmask. See ELEMENT FLAGS.
*/
_f: number;
/**
* @internal
* context - The Context object associated with this element.
*
* @remarks
* Created and assigned by the Renderer for component elements when it mounts the element tree.
*/
_ctx: Context<TagProps<TTag>> | undefined;
// children
/**
* @internal
* children - The rendered children of the element.
*/
_ch: Array<NarrowedChild> | NarrowedChild;
// node
/**
* @internal
* node - The node associated with the element.
*
* @remarks
* Set by Renderer.prototype.create when the component is mounted. This property will only be set for host elements.
*/
_n: any;
// inflight promise
/**
* @internal
* fallback - The value of the element while it has never committed.
*
* @remarks
* If an element takes place of a previously rendered value but renders asynchronously, this property is set to the previously rendered value until the element commits. This allows asynchronously updating element trees to show something while pending.
*/
_fb: any;
/**
* @internal
* inflightPromise - The current pending async run of the element.
*
* @remarks
* This value is used to make sure element copies do not fulfill immediately, to set the fallback of the next element when the previous element commits, and as the yield value of async generator components with async children. It is unset when the element is committed.
*/
_inf: Promise<any> | undefined;
// fallback
_fb: any;
// onNewValues
/**
* @internal
* onNewValues - the resolve function of a promise which represents the next child result. See the chase function for more info.
*/
_onv: Function | undefined;
tag: TTag;
props: TagProps<TTag>;
key: Key;
ref: Function | undefined;
constructor(tag: TTag, props: TagProps<TTag>, key: Key, ref: Function | undefined);
}
declare function isElement(value: any): value is Element;
declare function createElement<TTag extends Tag>(tag: TTag, props?: TagProps<TTag> | null, ...children: Array<unknown>): Element<TTag>;
declare function cloneElement<TTag extends Tag>(el: Element<TTag>): Element<TTag>;
type ElementValue<T> = Array<T | string> | T | string | undefined;
type Scope = unknown;
declare class Renderer<TNode, TRoot = TNode, TResult = ElementValue<TNode>> {
declare function isElement(value: any): value is Element; /**
* Creates an element with the specified tag, props and children.
*
* @remarks
* This function is usually used as a transpilation target for JSX transpilers, but it can also be called directly. It additionally extracts the crank-key and crank-ref props so they aren’t accessible to the renderer methods or components, and assigns the children prop according to the remaining arguments passed to the function.
*/
/**
* Creates an element with the specified tag, props and children.
*
* @remarks
* This function is usually used as a transpilation target for JSX transpilers, but it can also be called directly. It additionally extracts the crank-key and crank-ref props so they aren’t accessible to the renderer methods or components, and assigns the children prop according to the remaining arguments passed to the function.
*/
declare function createElement<TTag extends Tag>(tag: TTag, props?: TagProps<TTag> | null | undefined, ...children: Array<unknown>): Element<TTag>;
/**
* Clones a given element.
*
* @remarks
* Mainly used internally to make sure we don’t accidentally reuse elements in an element tree, because elements are directly mutated by the renderer.
*/
declare function cloneElement<TTag extends Tag>(el: Element<TTag>): Element<TTag>; /*** ELEMENT VALUE UTILITIES ***/
/**
* A helper type which repesents all the possible rendered values of an element.
*
* @typeparam TNode - The node type for the element assigned by the renderer.
*
* @remarks
* When asking the question, what is the value of a specific element, the answer varies depending on the type of the element. For host or Raw elements, the answer is simply the nodes (DOM nodes in the case of the DOM renderer) created for the element. For fragments, the values are usually an array of nodes. For portals, the value is undefined, because a Portal element’s root and children are opaque to parents. For components, the value can be any of the above, because the value of a component is determined by its children. Rendered values can also be strings or arrays of nodes and strings, in the case of a component or fragment with strings for children. All of these possible values are reflected in this utility type.
*/
type ElementValue<TNode> = Array<TNode | string> | TNode | string | undefined; /**
* Takes an array of element values and normalizes the output as an array of nodes and strings.
*
* @remarks
* Normalize will flatten only one level of nested arrays, because it is designed to be called once at each level of the tree. It will also concatenate adjacent strings and remove all undefineds.
*/
/**
* Takes an array of element values and normalizes the output as an array of nodes and strings.
*
* @remarks
* Normalize will flatten only one level of nested arrays, because it is designed to be called once at each level of the tree. It will also concatenate adjacent strings and remove all undefineds.
*/
/**
* Takes an array of element values and normalizes the output as an array of nodes and strings.
*
* @remarks
* Normalize will flatten only one level of nested arrays, because it is designed to be called once at each level of the tree. It will also concatenate adjacent strings and remove all undefineds.
*/
declare class Renderer<TNode, TScope, TRoot = TNode, TResult = ElementValue<TNode>> {
/**
* @internal
* A weakmap which stores element trees by root.
*/
_cache: WeakMap<object, Element<Portal>>;
constructor();
// TODO: allow parent contexts from a different renderer to be passed into here
render(children: Children, root: TRoot): Promise<TResult> | TResult;
/**
* Renders an element tree into a specific root.
*
* @param children - An element tree. You can render null with a previously used root to delete the previously rendered element tree from the cache.
* @param root - The node to be rendered into. The renderer will cache element trees per root.
* @param ctx - An optional context that will be the ancestor context of all elements in the tree. Useful for connecting renderers which call each other so that events/provisions properly propagate. The context for a given root must be the same or an error will be thrown.
*
* @returns The read result of rendering the children, or a possible promise of the read result if the element tree renders asynchronously.
*/
render(children: Children, root?: TRoot | undefined, ctx?: Context | undefined): Promise<TResult> | TResult;
/**
* Called when an element’s value is exposed via render, schedule, refresh, refs, or generator yield expressions.
*
* @param value - The value of the element being read. Can be a node, a string, undefined, or an array of nodes and strings, depending on the element.
* @returns Varies according to the specific renderer subclass. By default, it exposes the element’s value.
*
* @remarks
* This is useful for renderers which don’t want to expose their internal nodes. For instance, the HTML renderer will convert all internal nodes to strings.
*
*/
read(value: ElementValue<TNode>): TResult;
escape(text: string, _scope: Scope): string;
parse(text: string, _scope: Scope): TNode | string;
scope<TTag extends string | symbol>(_tag: TTag, _props: TagProps<TTag>, scope: Scope | undefined): Scope | undefined;
create<TTag extends string | symbol>(_tag: TTag, _props: TagProps<TTag>, _scope: Scope): TNode;
patch<TTag extends string | symbol>(_tag: TTag, _props: TagProps<TTag>, _node: TNode, _scope: Scope): unknown;
/**
* Called in a preorder traversal for each host element.
*
* @remarks
* Useful for passing data down the element tree. For instance, the DOM renderer uses this method to keep track of whether we’re in an SVG subtree.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param scope - The current scope.
*
* @returns The scope to be passed to create and patch for child host elements.
*
* @remarks
* This method sets the scope for child host elements, not the current host element.
*/
scope<TTag extends string | symbol>(_tag: TTag, _props: TagProps<TTag>, scope: TScope | undefined): TScope;
/**
* Called for each string in an element tree.
*
* @param text - The string child.
* @param scope - The current scope.
*
* @returns The escaped string.
*
* @remarks
* Rather than returning text nodes for whatever environment we’re rendering to, we defer that step for Renderer.prototype.arrange. We do this so that adjacent strings can be concatenated and the actual element tree can be rendered in a normalized form.
*/
escape(text: string, _scope: TScope): string;
/**
* Called for each Raw element whose value prop is a string.
*
* @param text - The string child.
* @param scope - The current scope.
*
* @returns The parsed node or string.
*/
parse(text: string, _scope: TScope): TNode | string;
/**
* Called for each host element when it is committed for the first time.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param scope - The current scope.
*
* @returns A “node” which determines the value of the host element.
*/
create<TTag extends string | symbol>(_tag: TTag, _props: TagProps<TTag>, _scope: TScope): TNode;
/**
* Called for each host element when it is committed.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param node - The node associated with the host element.
* @param scope - The current scope.
*
* @returns The return value is ignored.
*
* @remarks
* Used to mutate the node associated with an element when new props are passed.
*/
patch<TTag extends string | symbol>(_tag: TTag, _props: TagProps<TTag>, _node: TNode, _scope: TScope | undefined): unknown;
// TODO: pass hints into arrange about where the dirty children start and end
/**
* Called for each host element after its children have committed with the actual values of the children.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param node - The node associated with the host element.
* @param children - An array of nodes and strings from child elements.
*
* @returns The return value is ignored.
*
* @remarks
* This method is also called by child components contexts as the last step of a refresh.
*/
arrange<TTag extends string | symbol>(_tag: TTag, _props: TagProps<TTag>, _parent: TNode | TRoot, _children: Array<TNode | string>): unknown;
// TODO: remove(): a method which is called to remove a child from a parent to optimize arrange
/**
* Called for each host element when it is unmounted.
*
* @param tag - The tag of the host element.
* @param props - The props of the host element.
* @param node - The node associated with the host element.
*
* @returns The return value is ignored.
*/
dispose<TTag extends string | symbol>(_tag: TTag, _props: TagProps<TTag>, _node: TNode): unknown;
/**
* Called at the end of the rendering process for each root of the tree.
*
* @param root - The root prop passed to portals or the render method.
*
* @returns The return value is ignored.
*/
complete(_root: TRoot): unknown;
} // PRIVATE RENDERER FUNCTIONS
// PRIVATE RENDERER FUNCTIONS
// PRIVATE RENDERER FUNCTIONS
} /*** PRIVATE RENDERER FUNCTIONS ***/
// NOTE: to aid in the mangling of this module, we use functions rather than methods for internal logic.
/*** PRIVATE RENDERER FUNCTIONS ***/
// NOTE: to aid in the mangling of this module, we use functions rather than methods for internal logic.
/*** PRIVATE RENDERER FUNCTIONS ***/
// NOTE: to aid in the mangling of this module, we use functions rather than methods for internal logic.
/**
* A map of event type strings to Event subclasses. Can be extended via TypeScript module augmentation to have strongly typed event listeners.
*/
interface EventMap {

@@ -87,61 +434,162 @@ [type: string]: Event;

} // CONTEXT FLAGS
// TODO: write an explanation for each of these flags
/**
* A flag which is set when the component is being updated by the parent and cleared when the component has committed. Used to determine whether the nearest host ancestor needs to be rearranged.
*/
// CONTEXT FLAGS
// TODO: write an explanation for each of these flags
/**
* A flag which is set when the component is being updated by the parent and cleared when the component has committed. Used to determine whether the nearest host ancestor needs to be rearranged.
*/
// CONTEXT FLAGS
// TODO: write an explanation for each of these flags
/**
* A flag which is set when the component is being updated by the parent and cleared when the component has committed. Used to determine whether the nearest host ancestor needs to be rearranged.
*/
declare class Context<TProps = any, TResult = any> implements EventTarget {
// flags
/**
* @internal
* flags - A bitmask. See CONTEXT FLAGS above.
*/
_f: number;
// renderer
_re: Renderer<unknown, unknown, TResult>;
// root
/**
* @internal
* renderer - The renderer which created this context.
*/
_re: Renderer<unknown, unknown, unknown, TResult>;
/**
* @internal
* el - The associated component element.
*/
_el: Element<Component>;
/**
* @internal
* parent - The parent context.
*/
_pa: Context<unknown, TResult> | undefined;
/**
* @internal
* root - The root node set by an ancestor’s Portal prop.
*/
_rt: unknown;
// host element
/**
* @internal
* host - The nearest ancestor host element.
* @remarks
* When refresh is called, the host element will be arranged as the last step of the commit, to make sure the parent’s children properly reflects the components’s children.
*/
_ho: Element<string | symbol>;
// parent context
_pa: Context<unknown, TResult> | undefined;
// scope
_sc: Scope;
// element
_el: Element<Component>;
// iterator
/**
* @internal
* scope - The value of the scope at the point of element’s creation.
*/
_sc: unknown;
/**
* @internal
* iterator - The iterator returned by the component function.
*/
_it: Iterator<Children, Children | void, unknown> | AsyncIterator<Children, Children | void, unknown> | undefined;
// onProps
/**
* @internal
* onProps - A callback used in conjunction with the Available flag to implement the props async iterator. See the Symbol.asyncIterator method and the resume function.
*/
_op: ((props: any) => unknown) | undefined;
// inflight pending
// See the run/step/advance functions for more notes on inflight/enqueued pending/value.
/**
* @internal
* inflightPending
*/
_ip: Promise<unknown> | undefined;
// enqueued pending
/**
* @internal
* inflightValue
*/
_iv: Promise<ElementValue<any>> | undefined;
/**
* @internal
* enqueuedPending
*/
_ep: Promise<unknown> | undefined;
// inflight value
_iv: Promise<ElementValue<any>> | undefined;
// enqueued value
/**
* @internal
* enqueuedValue
*/
_ev: Promise<ElementValue<any>> | undefined;
// listeners
/**
* @internal
* listeners - An array of event listeners added to the context via Context.prototype.addEventListener
*/
_ls: Array<EventListenerRecord> | undefined;
// provisions
/**
* @internal
* provisions - A map of values which can be set via Context.prototype.set and read from child contexts via Context.prototype.get
*/
_ps: Map<unknown, unknown> | undefined;
// schedule callbacks
/**
* @internal
* schedules - a set of callbacks registered via Context.prototype.schedule, which fire when the component has committed.
*/
_ss: Set<(value: TResult) => unknown> | undefined;
// cleanup callbacks
/**
* @internal
* cleanups - a set of callbacks registered via Context.prototype.cleanup, which fire when the component has unmounted.
*/
_cs: Set<(value: TResult) => unknown> | undefined;
constructor(renderer: Renderer<unknown, unknown, TResult>, root: unknown, host: Element<string | symbol>, parent: Context<unknown, TResult> | undefined, scope: Scope, el: Element<Component>);
/**
* @internal
*/
constructor(renderer: Renderer<unknown, unknown, unknown, TResult>, root: unknown, host: Element<string | symbol>, parent: Context<unknown, TResult> | undefined, scope: unknown, el: Element<Component>);
get<TKey extends keyof ProvisionMap>(key: TKey): ProvisionMap[TKey];
set<TKey extends keyof ProvisionMap>(key: TKey, value: ProvisionMap[TKey]): void;
/**
* The current props of the associated element.
*
* @remarks
* Typically, you should read props either via the first parameter of the component or via the context iterator methods. This property is mainly for plugins or utilities which wrap contexts.
*/
get props(): TProps;
/**
* The current value of the associated element.
*
* @remarks
* Typically, you should read values via refs, generator yield expressions, or the refresh, schedule or cleanup methods. This property is mainly for plugins or utilities which wrap contexts.
*/
get value(): TResult;
[Symbol.iterator](): Generator<TProps>;
[Symbol.asyncIterator](): AsyncGenerator<TProps>;
/**
* Re-executes the component.
*
* @returns The rendered value of the component or a promise of the rendered value if the component or its children execute asynchronously.
*
* @remarks
* The refresh method works a little differently for async generator components, in that it will resume the Context async iterator rather than resuming execution. This is because async generator components are perpetually resumed independent of updates/refresh.
*/
refresh(): Promise<TResult> | TResult;
schedule(callback: (value: unknown) => unknown): void;
cleanup(callback: (value: unknown) => unknown): void;
/**
* Registers a callback which fires when the component commits. Will only fire once per callback and update.
*/
schedule(callback: (value: TResult) => unknown): void;
/**
* Registers a callback which fires when the component unmounts. Will only fire once per callback.
*/
cleanup(callback: (value: TResult) => unknown): void;
addEventListener<T extends string>(type: T, listener: MappedEventListenerOrEventListenerObject<T> | null, options?: boolean | AddEventListenerOptions): void;
removeEventListener<T extends string>(type: T, listener: MappedEventListenerOrEventListenerObject<T> | null, options?: EventListenerOptions | boolean): void;
dispatchEvent(ev: Event): boolean;
} // PRIVATE CONTEXT FUNCTIONS
// PRIVATE CONTEXT FUNCTIONS
// PRIVATE CONTEXT FUNCTIONS
} /*** PRIVATE CONTEXT FUNCTIONS ***/
/**
* Called to make props available to the Context async iterator for async generator components.
*/
/*** PRIVATE CONTEXT FUNCTIONS ***/
/**
* Called to make props available to the Context async iterator for async generator components.
*/
/*** PRIVATE CONTEXT FUNCTIONS ***/
/**
* Called to make props available to the Context async iterator for async generator components.
*/
// TODO: uncomment and use in the Element interface below
// type CrankElement = Element;
declare global {
module JSX {
// TODO: JSX interface doesn’t work
// TODO: JSX Element type (the result of JSX expressions) don’t work because TypeScript demands that all Components return JSX elements for some reason.
// interface Element extends CrankElement {}
interface IntrinsicElements {

@@ -160,8 +608,7 @@ [tag: string]: any;

}
// TODO: override render to provide a better error message when a node is not passed in as the second argument
class DOMRenderer extends Renderer<Node> {
class DOMRenderer extends Renderer<Node, string | undefined> {
render(children: Children, root: Node, ctx?: Context): Promise<ElementValue<Node>> | ElementValue<Node>;
parse(text: string): DocumentFragment;
scope(tag: string | symbol, props: Record<string, any>, scope: string | undefined): string | undefined;
// TODO: cache createElement calls and use cloneNode
create<TTag extends string | symbol>(tag: TTag, props: Record<string, any>, ns: string | undefined): Node;
parse(text: string): DocumentFragment;
patch<TTag extends string | symbol>(tag: TTag, props: TagProps<TTag>, el: Element, ns: string | undefined): void;

@@ -180,4 +627,3 @@ arrange<TTag extends string | symbol>(tag: TTag, props: Record<string, any>, parent: Node, children: Array<Node | string>): void;

}
// NOTE: using void for the root type allows render to be called with only one argument
class StringRenderer extends Renderer<Node | string, void, string> {
class StringRenderer extends Renderer<Node | string, undefined, unknown, string> {
create(): Node;

@@ -190,2 +636,2 @@ escape(text: string): string;

}
export { Tag, TagProps, Fragment, Copy, Portal, Raw, Child, Children, Component, Element, isElement, createElement, cloneElement, ElementValue, Renderer, EventMap, ProvisionMap, Context, dom, html };
export { Tag, TagProps, Fragment, Portal, Copy, Raw, Child, Children, Component, Element, isElement, createElement, cloneElement, ElementValue, Renderer, EventMap, ProvisionMap, Context, dom, html };

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc