@b9g/crank
Advanced tools
Comparing version 0.5.0-debug.0 to 0.5.0
134
crank.d.ts
/** | ||
* A type which represents all valid values for an element tag. | ||
*/ | ||
export declare type Tag = string | symbol | Component; | ||
export type Tag = string | symbol | Component; | ||
/** | ||
@@ -11,3 +11,3 @@ * A helper type to map the tag of an element to its expected props. | ||
*/ | ||
export declare type TagProps<TTag extends Tag> = TTag extends string ? JSX.IntrinsicElements[TTag] : TTag extends Component<infer TProps> ? TProps : Record<string, unknown>; | ||
export type TagProps<TTag extends Tag> = TTag extends string ? JSX.IntrinsicElements[TTag] : TTag extends Component<infer TProps> ? TProps : Record<string, unknown>; | ||
/*** | ||
@@ -29,3 +29,3 @@ * SPECIAL TAGS | ||
export declare const Fragment = ""; | ||
export declare type Fragment = typeof Fragment; | ||
export type Fragment = typeof Fragment; | ||
/** | ||
@@ -41,3 +41,3 @@ * A special tag for rendering into a new root node via a root prop. | ||
export declare const Portal: any; | ||
export declare type Portal = typeof Portal; | ||
export type Portal = typeof Portal; | ||
/** | ||
@@ -52,11 +52,10 @@ * A special tag which preserves whatever was previously rendered in the | ||
export declare const Copy: any; | ||
export declare type Copy = typeof Copy; | ||
export type Copy = typeof Copy; | ||
/** | ||
* A special tag for injecting raw nodes or strings via a value prop. | ||
* | ||
* If the value prop is a string, Renderer.prototype.parse() will be called on | ||
* the string and the result will be set as the element’s value. | ||
* Renderer.prototype.raw() is called with the value prop. | ||
*/ | ||
export declare const Raw: any; | ||
export declare type Raw = typeof Raw; | ||
export type Raw = typeof Raw; | ||
/** | ||
@@ -69,3 +68,3 @@ * Describes all valid values of an element tree, excluding iterables. | ||
*/ | ||
export declare type Child = Element | string | number | boolean | null | undefined; | ||
export type Child = Element | string | number | boolean | null | undefined; | ||
/** | ||
@@ -87,3 +86,3 @@ * An arbitrarily nested iterable of Child values. | ||
*/ | ||
export declare type Children = Child | ChildIterable; | ||
export type Children = Child | ChildIterable; | ||
/** | ||
@@ -94,3 +93,3 @@ * Represents all functions which can be used as a component. | ||
*/ | ||
export declare type Component<TProps extends Record<string, unknown> = any> = (this: Context<TProps>, props: TProps) => Children | PromiseLike<Children> | Iterator<Children, Children | void, any> | AsyncIterator<Children, Children | void, any>; | ||
export type Component<TProps extends Record<string, unknown> = any> = (this: Context<TProps>, props: TProps) => Children | PromiseLike<Children> | Iterator<Children, Children | void, any> | AsyncIterator<Children, Children | void, any>; | ||
/** | ||
@@ -100,3 +99,3 @@ * A type to keep track of keys. Any value can be a key, though null and | ||
*/ | ||
declare type Key = unknown; | ||
type Key = unknown; | ||
declare const ElementSymbol: unique symbol; | ||
@@ -202,3 +201,3 @@ export interface Element<TTag extends Tag = Tag> { | ||
*/ | ||
export declare type ElementValue<TNode> = Array<TNode | string> | TNode | string | undefined; | ||
export type ElementValue<TNode> = Array<TNode | string> | TNode | string | undefined; | ||
/** | ||
@@ -232,3 +231,3 @@ * @internal | ||
*/ | ||
cached: ElementValue<TNode>; | ||
cachedChildValues: ElementValue<TNode>; | ||
/** | ||
@@ -240,5 +239,5 @@ * The child which this retainer replaces. This property is used when an | ||
*/ | ||
fallback: RetainerChild<TNode>; | ||
inflight: Promise<ElementValue<TNode>> | undefined; | ||
onCommit: Function | undefined; | ||
fallbackValue: RetainerChild<TNode>; | ||
inflightValue: Promise<ElementValue<TNode>> | undefined; | ||
onNextValues: Function | undefined; | ||
constructor(el: Element); | ||
@@ -249,6 +248,11 @@ } | ||
*/ | ||
declare type RetainerChild<TNode> = Retainer<TNode> | string | undefined; | ||
type RetainerChild<TNode> = Retainer<TNode> | string | undefined; | ||
export interface HydrationData<TNode> { | ||
props: Record<string, unknown>; | ||
children: Array<TNode | string>; | ||
} | ||
export interface RendererImpl<TNode, TScope, TRoot extends TNode = TNode, TResult = ElementValue<TNode>> { | ||
scope<TTag extends string | symbol>(scope: TScope | undefined, tag: TTag, props: TagProps<TTag>): TScope | undefined; | ||
create<TTag extends string | symbol>(tag: TTag, props: TagProps<TTag>, scope: TScope | undefined): TNode; | ||
hydrate<TTag extends string | symbol>(tag: TTag, node: TNode | TRoot, props: TagProps<TTag>): HydrationData<TNode> | undefined; | ||
/** | ||
@@ -276,10 +280,10 @@ * Called when an element’s rendered value is exposed via render, schedule, | ||
* | ||
* @returns The escaped string. | ||
* @returns A string to be passed to arrange. | ||
* | ||
* 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. | ||
* Rather than returning Text nodes as we would in the DOM case, for example, | ||
* we delay 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 normalized form. | ||
*/ | ||
escape(text: string, scope: TScope | undefined): string; | ||
text(text: string, scope: TScope | undefined, hydration: HydrationData<TNode> | undefined): string; | ||
/** | ||
@@ -293,3 +297,3 @@ * Called for each Raw element whose value prop is a string. | ||
*/ | ||
parse(text: string, scope: TScope | undefined): ElementValue<TNode>; | ||
raw(value: string | TNode, scope: TScope | undefined, hydration: HydrationData<TNode> | undefined): ElementValue<TNode>; | ||
patch<TTag extends string | symbol, TName extends string>(tag: TTag, node: TNode, name: TName, value: TagProps<TTag>[TName], oldValue: TagProps<TTag>[TName] | undefined, scope: TScope): unknown; | ||
@@ -300,3 +304,3 @@ arrange<TTag extends string | symbol>(tag: TTag, node: TNode, props: TagProps<TTag>, children: Array<TNode | string>, oldProps: TagProps<TTag> | undefined, oldChildren: Array<TNode | string> | undefined): unknown; | ||
} | ||
declare const $RendererImpl: unique symbol; | ||
declare const _RendererImpl: unique symbol; | ||
/** | ||
@@ -318,3 +322,3 @@ * An abstract class which is subclassed to render to different target | ||
cache: WeakMap<object, Retainer<TNode>>; | ||
[$RendererImpl]: RendererImpl<TNode, TScope, TRoot, TResult>; | ||
[_RendererImpl]: RendererImpl<TNode, TScope, TRoot, TResult>; | ||
constructor(impl: Partial<RendererImpl<TNode, TScope, TRoot, TResult>>); | ||
@@ -328,3 +332,3 @@ /** | ||
* element trees per root. | ||
* @param ctx - An optional context that will be the ancestor context of all | ||
* @param bridge - An optional context that will be the ancestor context of all | ||
* elements in the tree. Useful for connecting different renderers so that | ||
@@ -338,2 +342,3 @@ * events/provisions properly propagate. The context for a given root must be | ||
render(children: Children, root?: TRoot | undefined, bridge?: Context | undefined): Promise<TResult> | TResult; | ||
hydrate(children: Children, root: TRoot, bridge?: Context | undefined): Promise<TResult> | TResult; | ||
} | ||
@@ -350,23 +355,17 @@ export interface Context extends Crank.Context { | ||
* @internal | ||
* The internal class which holds all context data. | ||
* The internal class which holds context data. | ||
*/ | ||
declare class ContextImpl<TNode = unknown, TScope = unknown, TRoot extends TNode = TNode, TResult = unknown> { | ||
/** | ||
* flags - A bitmask. See CONTEXT FLAGS above. | ||
*/ | ||
/** A bitmask. See CONTEXT FLAGS above. */ | ||
f: number; | ||
/** The actual context associated with this impl. */ | ||
owner: Context<unknown, TResult>; | ||
/** | ||
* ctx - The actual object passed as this to components. | ||
* The renderer which created this context. | ||
*/ | ||
ctx: Context<unknown, TResult>; | ||
/** | ||
* renderer - The renderer which created this context. | ||
*/ | ||
renderer: RendererImpl<TNode, TScope, TRoot, TResult>; | ||
/** | ||
* root - The root node as set by the nearest ancestor portal. | ||
*/ | ||
/** The root node as set by the nearest ancestor portal. */ | ||
root: TRoot | undefined; | ||
/** | ||
* host - The nearest host or portal retainer. | ||
* The nearest ancestor host or portal retainer. | ||
* | ||
@@ -378,44 +377,25 @@ * When refresh is called, the host element will be arranged as the last step | ||
host: Retainer<TNode>; | ||
/** | ||
* parent - The parent context. | ||
*/ | ||
/** The parent context impl. */ | ||
parent: ContextImpl<TNode, TScope, TRoot, TResult> | undefined; | ||
/** | ||
* scope - The value of the scope at the point of element’s creation. | ||
*/ | ||
/** The value of the scope at the point of element’s creation. */ | ||
scope: TScope | undefined; | ||
/** | ||
* retainer - The internal node associated with this context. | ||
*/ | ||
/** The internal node associated with this context. */ | ||
ret: Retainer<TNode>; | ||
/** | ||
* iterator - The iterator returned by the component function. | ||
* The iterator returned by the component function. | ||
* | ||
* Existence of this property implies that the component is a generator | ||
* component. It is deleted when a component is returned. | ||
*/ | ||
iterator: Iterator<Children, Children | void, unknown> | AsyncIterator<Children, Children | void, unknown> | undefined; | ||
/*** async properties ***/ | ||
/** | ||
* inflightBlock | ||
*/ | ||
inflightBlock: Promise<unknown> | undefined; | ||
/** | ||
* inflightValue | ||
*/ | ||
inflightValue: Promise<ElementValue<TNode>> | undefined; | ||
/** | ||
* enqueuedBlock | ||
*/ | ||
enqueuedBlock: Promise<unknown> | undefined; | ||
/** | ||
* enqueuedValue | ||
*/ | ||
enqueuedValue: Promise<ElementValue<TNode>> | undefined; | ||
/** | ||
* onavailable - A callback used in conjunction with the IsAvailable flag to | ||
* implement the props async iterator. See the Symbol.asyncIterator method | ||
* and the resumeCtxIterator function. | ||
*/ | ||
onAvailable: Function | undefined; | ||
onProps: ((props: Record<string, any>) => unknown) | undefined; | ||
onPropsRequested: Function | undefined; | ||
constructor(renderer: RendererImpl<TNode, TScope, TRoot, TResult>, root: TRoot | undefined, host: Retainer<TNode>, parent: ContextImpl<TNode, TScope, TRoot, TResult> | undefined, scope: TScope | undefined, ret: Retainer<TNode>); | ||
} | ||
declare const $ContextImpl: unique symbol; | ||
declare const _ContextImpl: unique symbol; | ||
type ComponentProps<T> = T extends (props: infer U) => any ? U : T; | ||
/** | ||
@@ -437,3 +417,3 @@ * A class which is instantiated and passed to every component as its this | ||
*/ | ||
[$ContextImpl]: ContextImpl<unknown, unknown, unknown, TResult>; | ||
[_ContextImpl]: ContextImpl<unknown, unknown, unknown, TResult>; | ||
constructor(impl: ContextImpl<unknown, unknown, unknown, TResult>); | ||
@@ -447,3 +427,3 @@ /** | ||
*/ | ||
get props(): TProps; | ||
get props(): ComponentProps<TProps>; | ||
/** | ||
@@ -457,4 +437,4 @@ * The current value of the associated element. | ||
get value(): TResult; | ||
[Symbol.iterator](): Generator<TProps>; | ||
[Symbol.asyncIterator](): AsyncGenerator<TProps>; | ||
[Symbol.iterator](): Generator<ComponentProps<TProps>>; | ||
[Symbol.asyncIterator](): AsyncGenerator<ComponentProps<TProps>>; | ||
/** | ||
@@ -503,4 +483,4 @@ * Re-executes a component. | ||
} | ||
declare type MappedEventListener<T extends string> = (ev: EventMap[T]) => unknown; | ||
declare type MappedEventListenerOrEventListenerObject<T extends string> = MappedEventListener<T> | { | ||
type MappedEventListener<T extends string> = (ev: EventMap[T]) => unknown; | ||
type MappedEventListenerOrEventListenerObject<T extends string> = MappedEventListener<T> | { | ||
handleEvent: MappedEventListener<T>; | ||
@@ -507,0 +487,0 @@ }; |
@@ -6,2 +6,3 @@ import { Children, Context, ElementValue, Renderer, RendererImpl } from "./crank.js"; | ||
render(children: Children, root: Node, ctx?: Context): Promise<ElementValue<Node>> | ElementValue<Node>; | ||
hydrate(children: Children, root: Node, ctx?: Context): Promise<ElementValue<Node>> | ElementValue<Node>; | ||
} | ||
@@ -8,0 +9,0 @@ export declare const renderer: DOMRenderer; |
128
dom.js
@@ -6,14 +6,3 @@ /// <reference types="dom.d.ts" /> | ||
const impl = { | ||
parse(text) { | ||
if (typeof document.createRange === "function") { | ||
const fragment = document.createRange().createContextualFragment(text); | ||
return Array.from(fragment.childNodes); | ||
} | ||
else { | ||
const childNodes = new DOMParser().parseFromString(text, "text/html").body | ||
.childNodes; | ||
return Array.from(childNodes); | ||
} | ||
}, | ||
scope(scope, tag) { | ||
scope(xmlns, tag) { | ||
// TODO: Should we handle xmlns??? | ||
@@ -23,10 +12,11 @@ switch (tag) { | ||
case "foreignObject": | ||
return undefined; | ||
xmlns = undefined; | ||
break; | ||
case "svg": | ||
return SVG_NAMESPACE; | ||
default: | ||
return scope; | ||
xmlns = SVG_NAMESPACE; | ||
break; | ||
} | ||
return xmlns; | ||
}, | ||
create(tag, _props, ns) { | ||
create(tag, _props, xmlns) { | ||
if (typeof tag !== "string") { | ||
@@ -36,6 +26,30 @@ throw new Error(`Unknown tag: ${tag.toString()}`); | ||
else if (tag.toLowerCase() === "svg") { | ||
ns = SVG_NAMESPACE; | ||
xmlns = SVG_NAMESPACE; | ||
} | ||
return ns ? document.createElementNS(ns, tag) : document.createElement(tag); | ||
return xmlns | ||
? document.createElementNS(xmlns, tag) | ||
: document.createElement(tag); | ||
}, | ||
hydrate(tag, node, props) { | ||
if (typeof tag !== "string" && tag !== Portal) { | ||
throw new Error(`Unknown tag: ${tag.toString()}`); | ||
} | ||
if (typeof tag === "string" && | ||
tag.toUpperCase() !== node.tagName) { | ||
console.error(`Expected <${tag}> while hydrating but found:`, node); | ||
return undefined; | ||
} | ||
const children = []; | ||
for (let i = 0; i < node.childNodes.length; i++) { | ||
const child = node.childNodes[i]; | ||
if (child.nodeType === Node.TEXT_NODE) { | ||
children.push(child.data); | ||
} | ||
else if (child.nodeType === Node.ELEMENT_NODE) { | ||
children.push(child); | ||
} | ||
} | ||
// TODO: extract props from nodes | ||
return { props, children }; | ||
}, | ||
patch(_tag, | ||
@@ -45,4 +59,4 @@ // TODO: Why does this assignment work? | ||
// TODO: Stricter typings? | ||
value, oldValue, scope) { | ||
const isSVG = scope === SVG_NAMESPACE; | ||
value, oldValue, xmlns) { | ||
const isSVG = xmlns === SVG_NAMESPACE; | ||
switch (name) { | ||
@@ -123,3 +137,5 @@ case "style": { | ||
(descriptor.writable === true || descriptor.set !== undefined)) { | ||
node[name] = value; | ||
if (node[name] !== value) { | ||
node[name] = value; | ||
} | ||
return; | ||
@@ -211,2 +227,56 @@ } | ||
}, | ||
text(text, _scope, hydrationData) { | ||
if (hydrationData != null) { | ||
let value = hydrationData.children.shift(); | ||
if (typeof value !== "string" || !value.startsWith(text)) { | ||
console.error(`Expected "${text}" while hydrating but found:`, value); | ||
} | ||
else if (text.length < value.length) { | ||
value = value.slice(text.length); | ||
hydrationData.children.unshift(value); | ||
} | ||
} | ||
return text; | ||
}, | ||
raw(value, xmlns, hydrationData) { | ||
let result; | ||
if (typeof value === "string") { | ||
const el = xmlns == null | ||
? document.createElement("div") | ||
: document.createElementNS(xmlns, "svg"); | ||
el.innerHTML = value; | ||
if (el.childNodes.length === 0) { | ||
result = undefined; | ||
} | ||
else if (el.childNodes.length === 1) { | ||
result = el.childNodes[0]; | ||
} | ||
else { | ||
result = Array.from(el.childNodes); | ||
} | ||
} | ||
else { | ||
result = value; | ||
} | ||
if (hydrationData != null) { | ||
// TODO: maybe we should warn on incorrect values | ||
if (Array.isArray(result)) { | ||
for (let i = 0; i < result.length; i++) { | ||
const node = result[i]; | ||
if (typeof node !== "string" && | ||
(node.nodeType === Node.ELEMENT_NODE || | ||
node.nodeType === Node.TEXT_NODE)) { | ||
hydrationData.children.shift(); | ||
} | ||
} | ||
} | ||
else if (result != null && typeof result !== "string") { | ||
if (result.nodeType === Node.ELEMENT_NODE || | ||
result.nodeType === Node.TEXT_NODE) { | ||
hydrationData.children.shift(); | ||
} | ||
} | ||
} | ||
return result; | ||
}, | ||
}; | ||
@@ -218,8 +288,16 @@ class DOMRenderer extends Renderer { | ||
render(children, root, ctx) { | ||
if (root == null || typeof root.nodeType !== "number") { | ||
throw new TypeError(`Render root is not a node. Received: ${JSON.stringify(root && root.toString())}`); | ||
} | ||
validateRoot(root); | ||
return super.render(children, root, ctx); | ||
} | ||
hydrate(children, root, ctx) { | ||
validateRoot(root); | ||
return super.hydrate(children, root, ctx); | ||
} | ||
} | ||
function validateRoot(root) { | ||
if (root === null || | ||
(typeof root === "object" && typeof root.nodeType !== "number")) { | ||
throw new TypeError(`Render root is not a node. Received: ${JSON.stringify(root && root.toString())}`); | ||
} | ||
} | ||
const renderer = new DOMRenderer(); | ||
@@ -226,0 +304,0 @@ |
@@ -1,2 +0,3 @@ | ||
import { Renderer, RendererImpl } from "./crank.js"; | ||
import { Renderer } from "./crank.js"; | ||
import type { RendererImpl } from "./crank.js"; | ||
interface Node { | ||
@@ -3,0 +4,0 @@ value: string; |
@@ -97,3 +97,3 @@ /// <reference types="html.d.ts" /> | ||
}, | ||
escape(text) { | ||
text(text) { | ||
return escape(text); | ||
@@ -100,0 +100,0 @@ }, |
{ | ||
"name": "@b9g/crank", | ||
"version": "0.5.0-debug.0", | ||
"version": "0.5.0", | ||
"description": "Write JSX-driven components with functions, promises and generators.", | ||
@@ -17,4 +17,4 @@ "homepage": "https://crank.js.org", | ||
".": { | ||
"import": "./mod.js", | ||
"require": "./mod.cjs" | ||
"import": "./crank.js", | ||
"require": "./crank.cjs" | ||
}, | ||
@@ -45,18 +45,26 @@ "./crank": { | ||
}, | ||
"./mod": { | ||
"import": "./mod.js", | ||
"require": "./mod.cjs" | ||
"./jsx-runtime": { | ||
"import": "./jsx-runtime.js", | ||
"require": "./jsx-runtime.cjs" | ||
}, | ||
"./mod.js": { | ||
"import": "./mod.js", | ||
"require": "./mod.cjs" | ||
"./jsx-runtime.js": { | ||
"import": "./jsx-runtime.js", | ||
"require": "./jsx-runtime.cjs" | ||
}, | ||
"./jsx-tag": { | ||
"import": "./jsx-tag.js", | ||
"require": "./jsx-tag.cjs" | ||
}, | ||
"./jsx-tag.js": { | ||
"import": "./jsx-tag.js", | ||
"require": "./jsx-tag.cjs" | ||
}, | ||
"./package.json": "./package.json", | ||
"./xm": { | ||
"import": "./xm.js", | ||
"require": "./xm.cjs" | ||
"./standalone": { | ||
"import": "./standalone.js", | ||
"require": "./standalone.cjs" | ||
}, | ||
"./xm.js": { | ||
"import": "./xm.js", | ||
"require": "./xm.cjs" | ||
"./standalone.js": { | ||
"import": "./standalone.js", | ||
"require": "./standalone.cjs" | ||
}, | ||
@@ -70,23 +78,23 @@ "./umd": { | ||
}, | ||
"main": "mod.cjs", | ||
"module": "mod.js", | ||
"types": "mod.d.ts", | ||
"main": "crank.cjs", | ||
"module": "crank.js", | ||
"types": "crank.d.ts", | ||
"devDependencies": { | ||
"@arkweid/lefthook": "^0.7.7", | ||
"@types/sinon": "^10.0.12", | ||
"@typescript-eslint/eslint-plugin": "^5.30.5", | ||
"@typescript-eslint/parser": "^5.30.5", | ||
"eslint": "^8.19.0", | ||
"eslint-config-prettier": "^8.5.0", | ||
"@types/sinon": "^10.0.13", | ||
"@typescript-eslint/eslint-plugin": "^5.48.2", | ||
"@typescript-eslint/parser": "^5.48.2", | ||
"eslint": "^8.32.0", | ||
"eslint-config-prettier": "^8.6.0", | ||
"eslint-plugin-prettier": "^4.2.1", | ||
"eslint-plugin-react": "^7.30.1", | ||
"magic-string": "^0.26.2", | ||
"playwright-test": "^8.1.1", | ||
"prettier": "^2.7.1", | ||
"rollup": "^2.76.0", | ||
"rollup-plugin-typescript2": "^0.32.1", | ||
"eslint-plugin-react": "^7.32.0", | ||
"magic-string": "^0.27.0", | ||
"playwright-test": "^8.1.2", | ||
"prettier": "^2.8.3", | ||
"rollup": "^3.10.0", | ||
"rollup-plugin-typescript2": "^0.34.1", | ||
"shx": "^0.3.4", | ||
"sinon": "^14.0.0", | ||
"ts-node": "^10.9.0", | ||
"typescript": "^4.7.4", | ||
"sinon": "^15.0.1", | ||
"ts-node": "^10.9.1", | ||
"typescript": "^4.9.4", | ||
"uvu": "^0.5.6" | ||
@@ -97,2 +105,2 @@ }, | ||
} | ||
} | ||
} |
# Crank.js | ||
### The most “Just JavaScript” web framework. | ||
### The Just JavaScript web framework. | ||
Crank.js is a framework where components are defined with *Plain Old JavaScript Functions*. But not just regular functions! Components can also be defined with async functions for working with promises, and generator functions for working with local state. | ||
Crank is a web framework where components can be defined with sync functions, async functions and generator functions. The documentation for Crank.js is available at [crank.js.org](https://crank.js.org). | ||
Its API aims to be minimal and transparent. By relying on standard JavaScript control flow operators and data structures, and by making the process of rendering explicit, Crank.js helps developers write durable, bug-free applications with the latest and greatest libraries and APIs. | ||
Rather than forcing developers to work with increasingly convoluted reactive solutions and bespoke templating languages, Crank.js uses the platform. It is a dedication to the web, to the promise of an accessible and inclusive medium for self-expression and commerce, built on the thesis that simpler code is how we’ll push the frontier. | ||
## Get Started | ||
The documentation for Crank.js is available at [crank.js.org](https://crank.js.org). Crank.js is published on NPM under the `@b9g` organization (short for “b*ikeshavin*g”). | ||
Crank.js is published on NPM under the `@b9g` organization (short for “b*ikeshavin*g”). | ||
```shell | ||
$ npm install @b9g/crank | ||
$ npm i @b9g/crank | ||
``` | ||
@@ -23,4 +19,2 @@ | ||
```jsx live | ||
/** @jsx createElement */ | ||
import {createElement} from "@b9g/crank"; | ||
import {renderer} from "@b9g/crank/dom"; | ||
@@ -40,4 +34,2 @@ | ||
```jsx live | ||
/** @jsx createElement */ | ||
import {createElement} from "@b9g/crank"; | ||
import {renderer} from "@b9g/crank/dom"; | ||
@@ -66,4 +58,2 @@ | ||
```jsx live | ||
/** @jsx createElement */ | ||
import {createElement} from "@b9g/crank"; | ||
import {renderer} from "@b9g/crank/dom"; | ||
@@ -87,4 +77,3 @@ | ||
```jsx live | ||
/** @jsx createElement */ | ||
import {createElement, Fragment} from "@b9g/crank"; | ||
import {Fragment} from "@b9g/crank"; | ||
import {renderer} from "@b9g/crank/dom"; | ||
@@ -127,3 +116,3 @@ | ||
while (true) { | ||
for ({} of this) { | ||
yield ( | ||
@@ -130,0 +119,0 @@ <Fragment> |
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
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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1204428
35
8603
126