@bikeshaving/crank
Advanced tools
Comparing version 0.1.0-beta.0 to 0.1.0-beta.1
@@ -74,6 +74,7 @@ import { EventTarget as EventTargetShim } from "event-target-shim"; | ||
protected previousSibling?: Link; | ||
protected insertBefore(link: Link, newLink: Link): void; | ||
protected insertAfter(link: Link, newLink: Link): void; | ||
protected insertBefore(newLink: Link, refLink: Link): void; | ||
protected insertAfter(newLink: Link, refLink: Link): void; | ||
protected appendChild(link: Link): void; | ||
protected removeChild(link: Link): void; | ||
protected replaceChild(newLink: Link, refLink: Link): void; | ||
} | ||
@@ -88,4 +89,4 @@ declare class Host<T> extends Link { | ||
private done; | ||
private unmounted; | ||
private independent; | ||
private cachedChildNodes?; | ||
private iterator?; | ||
@@ -95,2 +96,6 @@ private intrinsic?; | ||
private hostsByKey?; | ||
private pending?; | ||
private enqueued?; | ||
private replacedBy?; | ||
private clock; | ||
protected firstChild?: Host<T>; | ||
@@ -101,10 +106,10 @@ protected lastChild?: Host<T>; | ||
constructor(parent: Host<T> | undefined, renderer: Renderer<T>); | ||
get tag(): Tag | undefined; | ||
get key(): unknown; | ||
props?: Props; | ||
private cachedChildNodes?; | ||
get childNodes(): (T | string)[]; | ||
get childNodeOrNodes(): (T | string)[] | T | string | undefined; | ||
update(guest: Guest): MaybePromise<undefined>; | ||
private nextRunId; | ||
private maxRunId; | ||
private step; | ||
private pending?; | ||
private enqueued?; | ||
run(): MaybePromise<undefined>; | ||
@@ -111,0 +116,0 @@ refresh(): MaybePromise<undefined>; |
@@ -352,3 +352,3 @@ 'use strict'; | ||
} | ||
// TODO: explain | ||
// TODO: explain what this function does | ||
function chase(fn) { | ||
@@ -373,5 +373,5 @@ var next = function () { }; | ||
} | ||
Link.prototype.insertBefore = function (link, newLink) { | ||
newLink.nextSibling = link; | ||
if (link.previousSibling === undefined) { | ||
Link.prototype.insertBefore = function (newLink, refLink) { | ||
newLink.nextSibling = refLink; | ||
if (refLink.previousSibling === undefined) { | ||
newLink.previousSibling = undefined; | ||
@@ -381,10 +381,10 @@ this.firstChild = newLink; | ||
else { | ||
newLink.previousSibling = link.previousSibling; | ||
link.previousSibling.nextSibling = newLink; | ||
newLink.previousSibling = refLink.previousSibling; | ||
refLink.previousSibling.nextSibling = newLink; | ||
} | ||
link.previousSibling = newLink; | ||
refLink.previousSibling = newLink; | ||
}; | ||
Link.prototype.insertAfter = function (link, newLink) { | ||
newLink.previousSibling = link; | ||
if (link.nextSibling === undefined) { | ||
Link.prototype.insertAfter = function (newLink, refLink) { | ||
newLink.previousSibling = refLink; | ||
if (refLink.nextSibling === undefined) { | ||
newLink.nextSibling = undefined; | ||
@@ -394,6 +394,6 @@ this.lastChild = newLink; | ||
else { | ||
newLink.nextSibling = link.nextSibling; | ||
link.nextSibling.previousSibling = newLink; | ||
newLink.nextSibling = refLink.nextSibling; | ||
refLink.nextSibling.previousSibling = newLink; | ||
} | ||
link.nextSibling = newLink; | ||
refLink.nextSibling = newLink; | ||
}; | ||
@@ -408,3 +408,3 @@ Link.prototype.appendChild = function (link) { | ||
else { | ||
this.insertAfter(this.lastChild, link); | ||
this.insertAfter(link, this.lastChild); | ||
} | ||
@@ -426,5 +426,8 @@ }; | ||
}; | ||
Link.prototype.replaceChild = function (newLink, refLink) { | ||
this.insertBefore(newLink, refLink); | ||
this.removeChild(refLink); | ||
}; | ||
return Link; | ||
}()); | ||
// TODO: don’t re-use hosts per tag and key | ||
var Host = /** @class */ (function (_super) { | ||
@@ -438,19 +441,8 @@ _tslib.__extends(Host, _super); | ||
_this.renderer = renderer; | ||
_this.node = undefined; | ||
_this.updating = false; | ||
_this.done = false; | ||
_this.unmounted = false; | ||
_this.independent = false; | ||
// TODO: maybe rename these properties to “value” and “cachedChildValues” | ||
_this.cachedChildNodes = undefined; | ||
_this.iterator = undefined; | ||
_this.intrinsic = undefined; | ||
_this.committer = undefined; | ||
_this.hostsByKey = undefined; | ||
_this.firstChild = undefined; | ||
_this.lastChild = undefined; | ||
_this.nextSibling = undefined; | ||
_this.previousSibling = undefined; | ||
// TODO: we won’t need these properties/checks if we stop reusing host nodes | ||
_this.nextRunId = 0; | ||
_this.maxRunId = -1; | ||
_this.clock = 0; | ||
// TODO: clean up this monster | ||
_this.updateChildren = chase(function updateChildren(children) { | ||
@@ -466,57 +458,102 @@ var e_8, _a, e_9, _b; | ||
} | ||
try { | ||
for (var children_1 = _tslib.__values(children), children_1_1 = children_1.next(); !children_1_1.done; children_1_1 = children_1.next()) { | ||
var child = children_1_1.value; | ||
if (isNonStringIterable(child)) { | ||
child = createElement(Fragment, null, child); | ||
var _loop_1 = function (child) { | ||
if (isNonStringIterable(child)) { | ||
child = createElement(Fragment, null, child); | ||
} | ||
var guest = toGuest(child); | ||
var tag = void 0; | ||
var key = void 0; | ||
var isNewHost = false; | ||
if (isElement(guest)) { | ||
tag = guest.tag; | ||
key = guest.key; | ||
if (hostsByKey !== undefined && hostsByKey.has(key)) { | ||
// TODO: warn about a duplicate key | ||
key = undefined; | ||
} | ||
else if (isKeyedElement(child) && | ||
hostsByKey && | ||
hostsByKey.has(child.key)) { | ||
// TODO: warn or throw | ||
child = _tslib.__assign(_tslib.__assign({}, child), { key: undefined }); | ||
} | ||
if (key != null) { | ||
var newHost = this_1.hostsByKey && this_1.hostsByKey.get(key); | ||
if (newHost === undefined) { | ||
newHost = new Host(this_1, this_1.renderer); | ||
isNewHost = true; | ||
} | ||
if (isKeyedElement(child)) { | ||
var keyedHost = this.hostsByKey && this.hostsByKey.get(child.key); | ||
if (keyedHost === undefined) { | ||
keyedHost = new Host(this, this.renderer); | ||
else { | ||
this_1.hostsByKey.delete(key); | ||
if (host !== newHost) { | ||
this_1.removeChild(newHost); | ||
} | ||
} | ||
if (host === undefined) { | ||
this_1.appendChild(newHost); | ||
} | ||
else if (host !== newHost) { | ||
if (isKeyedElement(host.guest)) { | ||
this_1.insertAfter(newHost, host); | ||
} | ||
else { | ||
this.hostsByKey.delete(child.key); | ||
if (host !== keyedHost) { | ||
this.removeChild(keyedHost); | ||
} | ||
this_1.insertBefore(newHost, host); | ||
} | ||
if (host === undefined) { | ||
this.appendChild(keyedHost); | ||
} | ||
host = newHost; | ||
} | ||
else if (host === undefined) { | ||
host = new Host(this_1, this_1.renderer); | ||
this_1.appendChild(host); | ||
isNewHost = true; | ||
} | ||
else if (host.key != null) { | ||
var newHost = new Host(this_1, this_1.renderer); | ||
this_1.insertAfter(newHost, host); | ||
host = newHost; | ||
isNewHost = true; | ||
} | ||
if (tag !== Copy) { | ||
if (isNewHost || (!host.unmounted && host.tag === tag)) { | ||
var updateP = host.update(guest); | ||
if (updateP !== undefined) { | ||
promises.push(updateP); | ||
} | ||
else if (host !== keyedHost) { | ||
if (isKeyedElement(host.guest)) { | ||
this.insertAfter(host, keyedHost); | ||
} | ||
else { | ||
this.insertBefore(host, keyedHost); | ||
} | ||
} | ||
else { | ||
var clock = host.clock++; | ||
var newHost_1 = new Host(this_1, this_1.renderer); | ||
newHost_1.clock = clock; | ||
var updateP = newHost_1.update(guest); | ||
host.unmount(); | ||
if (updateP === undefined) { | ||
this_1.replaceChild(newHost_1, host); | ||
host.replacedBy = newHost_1; | ||
} | ||
host = keyedHost; | ||
if (hostsByKey === undefined) { | ||
hostsByKey = new Map(); | ||
else { | ||
promises.push(updateP); | ||
var host1_1 = host; | ||
updateP.then(function () { | ||
if (host1_1.replacedBy === undefined) { | ||
_this.replaceChild(newHost_1, host1_1); | ||
host1_1.replacedBy = newHost_1; | ||
} | ||
else if (host1_1.replacedBy.replacedBy === undefined && | ||
host1_1.replacedBy.clock < newHost_1.clock) { | ||
_this.replaceChild(newHost_1, host1_1.replacedBy); | ||
host1_1.replacedBy = newHost_1; | ||
} | ||
}); | ||
} | ||
hostsByKey.set(child.key, keyedHost); | ||
} | ||
else if (host === undefined) { | ||
host = new Host(this, this.renderer); | ||
this.appendChild(host); | ||
} | ||
if (key !== undefined) { | ||
if (hostsByKey === undefined) { | ||
hostsByKey = new Map(); | ||
} | ||
else if (isKeyedElement(host.guest)) { | ||
var unkeyedHost = new Host(this, this.renderer); | ||
this.insertAfter(host, unkeyedHost); | ||
host = unkeyedHost; | ||
} | ||
var updateP = host.update(toGuest(child)); | ||
if (updateP !== undefined) { | ||
promises.push(updateP); | ||
} | ||
host = host.nextSibling; | ||
hostsByKey.set(key, host); | ||
} | ||
host = host.nextSibling; | ||
}; | ||
var this_1 = this; | ||
try { | ||
for (var children_1 = _tslib.__values(children), children_1_1 = children_1.next(); !children_1_1.done; children_1_1 = children_1.next()) { | ||
var child = children_1_1.value; | ||
_loop_1(child); | ||
} | ||
} | ||
@@ -532,4 +569,4 @@ catch (e_8_1) { e_8 = { error: e_8_1 }; } | ||
while (host !== undefined) { | ||
if (isKeyedElement(host.guest) && this.hostsByKey) { | ||
this.hostsByKey.delete(host.guest.key); | ||
if (this.hostsByKey !== undefined && host.key !== undefined) { | ||
this.hostsByKey.delete(host.key); | ||
} | ||
@@ -545,3 +582,3 @@ void host.unmount(); | ||
// TODO: implement async unmount for keyed hosts | ||
void host_1.unmount(); | ||
host_1.unmount(); | ||
this.removeChild(host_1); | ||
@@ -568,2 +605,17 @@ } | ||
} | ||
Object.defineProperty(Host.prototype, "tag", { | ||
get: function () { | ||
return isElement(this.guest) ? this.guest.tag : undefined; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(Host.prototype, "key", { | ||
get: function () { | ||
//key?: unknown; | ||
return isElement(this.guest) ? this.guest.key : undefined; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(Host.prototype, "childNodes", { | ||
@@ -618,17 +670,8 @@ get: function () { | ||
Host.prototype.update = function (guest) { | ||
if (isElement(guest) && guest.tag === Copy) { | ||
return; | ||
} | ||
this.updating = true; | ||
if (!isElement(this.guest) || | ||
!isElement(guest) || | ||
this.guest.tag !== guest.tag || | ||
// TODO: never reuse a host when keys differ | ||
this.guest.key !== guest.key) { | ||
void this.unmount(); | ||
this.guest = guest; | ||
if (this.guest === undefined) { | ||
if (isElement(guest)) { | ||
this.ctx = new Context(this, this.parent && this.parent.ctx); | ||
// TODO: allow people to provide custom intrinsics for fragments to | ||
// allow for stuff like an innerHTML property | ||
// TODO: allow custom intrinsics for fragments to allow for stuff like | ||
// an innerHTML property | ||
if (typeof guest.tag !== "function" && guest.tag !== Fragment) { | ||
@@ -639,8 +682,3 @@ this.intrinsic = this.renderer.intrinsicFor(guest.tag); | ||
} | ||
else if (typeof guest === "string") { | ||
this.guest = this.renderer.text(guest); | ||
} | ||
else { | ||
this.guest = guest; | ||
} | ||
this.guest = guest; | ||
return this.refresh(); | ||
@@ -669,3 +707,2 @@ }; | ||
} | ||
// TODO: call next with a promise for async generator components with async children | ||
var iteration = this.iterator.next(next); | ||
@@ -679,3 +716,3 @@ if (isPromiseLike(iteration)) { | ||
var next = new Pledge(updateP).then(function () { return _this.childNodeOrNodes; }); | ||
if (iteration.done) { | ||
if (iteration.done || _this.done) { | ||
_this.done = true; | ||
@@ -692,3 +729,5 @@ } | ||
} | ||
return new Pledge(iteration.value).then(function (child) { return _this.updateChildren(child); }); | ||
return new Pledge(iteration.value).then(function (child) { | ||
return _this.updateChildren(child); | ||
}); | ||
}; | ||
@@ -706,8 +745,4 @@ Host.prototype.run = function () { | ||
}); | ||
var runId_1 = this.nextRunId++; | ||
return Promise.resolve(step).then(function (child) { | ||
_this.maxRunId = Math.max(runId_1, _this.maxRunId); | ||
if (runId_1 === _this.maxRunId) { | ||
return _this.updateChildren(child); | ||
} | ||
return _this.updateChildren(child); | ||
}); | ||
@@ -736,8 +771,4 @@ } | ||
if (this.iterator === undefined) { | ||
var runId_2 = this.nextRunId++; | ||
this.enqueued = this.enqueued.then(function (child) { | ||
_this.maxRunId = Math.max(runId_2, _this.maxRunId); | ||
if (runId_2 === _this.maxRunId) { | ||
return _this.updateChildren(child); | ||
} | ||
return _this.updateChildren(child); | ||
}); | ||
@@ -753,3 +784,6 @@ } | ||
Host.prototype.refresh = function () { | ||
if (isElement(this.guest)) { | ||
if (this.unmounted) { | ||
return; | ||
} | ||
else if (isElement(this.guest)) { | ||
if (this.ctx !== undefined) { | ||
@@ -762,8 +796,9 @@ this.ctx.dispatchEvent(new CustomEvent("crank.refresh", { detail: { props: this.guest.props } })); | ||
else { | ||
this.maxRunId = ++this.nextRunId; | ||
return this.updateChildren(this.guest.props.children); | ||
} | ||
} | ||
else if (typeof this.guest === "string") { | ||
this.node = this.renderer.text(this.guest); | ||
} | ||
else { | ||
this.maxRunId = ++this.nextRunId; | ||
this.node = this.guest; | ||
@@ -809,14 +844,5 @@ } | ||
Host.prototype.unmount = function () { | ||
this.independent = false; | ||
this.node = undefined; | ||
this.guest = undefined; | ||
this.intrinsic = undefined; | ||
this.pending = undefined; | ||
this.enqueued = undefined; | ||
this.hostsByKey = undefined; | ||
this.cachedChildNodes = undefined; | ||
if (this.ctx !== undefined) { | ||
this.ctx.dispatchEvent(new Event("crank.unmount")); | ||
this.ctx.removeAllEventListeners(); | ||
this.ctx = undefined; | ||
} | ||
@@ -832,6 +858,6 @@ // TODO: await the return if the host is keyed and commit the parent | ||
} | ||
this.unmounted = true; | ||
this.committer = undefined; | ||
this.iterator = undefined; | ||
this.updating = false; | ||
this.done = false; | ||
this.unmountChildren(); | ||
@@ -916,3 +942,3 @@ }; | ||
}; | ||
// TODO: throw an error if refresh is called on an unmounted component | ||
// TODO: warn if refresh is called on an unmounted component | ||
Context.prototype.refresh = function () { | ||
@@ -1002,3 +1028,3 @@ return this.host.refresh(); | ||
if (this.env[Text] !== undefined) { | ||
// TODO: remove non-null assertion when typescript gets its shit together | ||
// TODO: remove non-null assertion when typescript gets its shit together with symbols | ||
return this.env[Text](text); | ||
@@ -1005,0 +1031,0 @@ } |
@@ -1,4 +0,2 @@ | ||
import { Child, Context, Environment, Props, Renderer } from "./crank"; | ||
export declare function updateDOMProps(el: HTMLElement, props: Props, newProps: Props): void; | ||
export declare function updateDOMChildren(el: HTMLElement, children?: (Node | string)[]): void; | ||
import { Child, Context, Environment, Renderer } from "./crank"; | ||
export declare const env: Environment<HTMLElement>; | ||
@@ -5,0 +3,0 @@ export declare class DOMRenderer extends Renderer<HTMLElement> { |
@@ -11,3 +11,3 @@ 'use strict'; | ||
var _a; | ||
function updateDOMProps(el, props, newProps) { | ||
function updateProps(el, props, newProps) { | ||
for (var name_1 in Object.assign({}, props, newProps)) { | ||
@@ -58,3 +58,3 @@ // TODO: throw an error if event props are found | ||
// https://stackoverflow.com/questions/59418120/what-is-the-most-efficient-way-to-update-the-childnodes-of-a-dom-node-with-an-ar | ||
function updateDOMChildren(el, children) { | ||
function updateChildren(el, children) { | ||
var e_1, _a, e_2, _b; | ||
@@ -142,6 +142,6 @@ if (children === void 0) { children = []; } | ||
if (node !== newNode) { | ||
updateDOMChildren(node); | ||
updateChildren(node); | ||
node = newNode; | ||
} | ||
updateDOMChildren(node, this.childNodes); | ||
updateChildren(node, this.childNodes); | ||
return [4 /*yield*/, node]; | ||
@@ -167,3 +167,3 @@ case 3: | ||
case 9: | ||
updateDOMChildren(node); | ||
updateChildren(node); | ||
return [7 /*endfinally*/]; | ||
@@ -176,3 +176,3 @@ case 10: return [2 /*return*/]; | ||
return function defaultDOM() { | ||
var node, props, _a, _b, props1, e_4_1; | ||
var node, props, prevChildNodes, _a, _b, props1, e_4_1; | ||
var e_4, _c; | ||
@@ -184,2 +184,3 @@ return _tslib.__generator(this, function (_d) { | ||
props = {}; | ||
prevChildNodes = []; | ||
_d.label = 1; | ||
@@ -193,5 +194,7 @@ case 1: | ||
props1 = _b.value; | ||
updateDOMProps(node, props, props1); | ||
if (!("innerHTML" in props1)) { | ||
updateDOMChildren(node, this.childNodes); | ||
updateProps(node, props, props1); | ||
if (!("innerHTML" in props1) && | ||
(this.childNodes.length > 0 || prevChildNodes.length > 0)) { | ||
updateChildren(node, this.childNodes); | ||
prevChildNodes = this.childNodes; | ||
} | ||
@@ -241,4 +244,2 @@ return [4 /*yield*/, node]; | ||
exports.renderer = renderer; | ||
exports.updateDOMChildren = updateDOMChildren; | ||
exports.updateDOMProps = updateDOMProps; | ||
//# sourceMappingURL=dom.js.map |
@@ -74,6 +74,7 @@ import { EventTarget as EventTargetShim } from "event-target-shim"; | ||
protected previousSibling?: Link; | ||
protected insertBefore(link: Link, newLink: Link): void; | ||
protected insertAfter(link: Link, newLink: Link): void; | ||
protected insertBefore(newLink: Link, refLink: Link): void; | ||
protected insertAfter(newLink: Link, refLink: Link): void; | ||
protected appendChild(link: Link): void; | ||
protected removeChild(link: Link): void; | ||
protected replaceChild(newLink: Link, refLink: Link): void; | ||
} | ||
@@ -88,4 +89,4 @@ declare class Host<T> extends Link { | ||
private done; | ||
private unmounted; | ||
private independent; | ||
private cachedChildNodes?; | ||
private iterator?; | ||
@@ -95,2 +96,6 @@ private intrinsic?; | ||
private hostsByKey?; | ||
private pending?; | ||
private enqueued?; | ||
private replacedBy?; | ||
private clock; | ||
protected firstChild?: Host<T>; | ||
@@ -101,10 +106,10 @@ protected lastChild?: Host<T>; | ||
constructor(parent: Host<T> | undefined, renderer: Renderer<T>); | ||
get tag(): Tag | undefined; | ||
get key(): unknown; | ||
props?: Props; | ||
private cachedChildNodes?; | ||
get childNodes(): (T | string)[]; | ||
get childNodeOrNodes(): (T | string)[] | T | string | undefined; | ||
update(guest: Guest): MaybePromise<undefined>; | ||
private nextRunId; | ||
private maxRunId; | ||
private step; | ||
private pending?; | ||
private enqueued?; | ||
run(): MaybePromise<undefined>; | ||
@@ -111,0 +116,0 @@ refresh(): MaybePromise<undefined>; |
270
lib/crank.js
@@ -348,3 +348,3 @@ import { _ as __extends, a as __values, b as __spread, c as __generator, d as __awaiter, e as __read, f as __assign } from './_tslib-21ac4f0e.js'; | ||
} | ||
// TODO: explain | ||
// TODO: explain what this function does | ||
function chase(fn) { | ||
@@ -369,5 +369,5 @@ var next = function () { }; | ||
} | ||
Link.prototype.insertBefore = function (link, newLink) { | ||
newLink.nextSibling = link; | ||
if (link.previousSibling === undefined) { | ||
Link.prototype.insertBefore = function (newLink, refLink) { | ||
newLink.nextSibling = refLink; | ||
if (refLink.previousSibling === undefined) { | ||
newLink.previousSibling = undefined; | ||
@@ -377,10 +377,10 @@ this.firstChild = newLink; | ||
else { | ||
newLink.previousSibling = link.previousSibling; | ||
link.previousSibling.nextSibling = newLink; | ||
newLink.previousSibling = refLink.previousSibling; | ||
refLink.previousSibling.nextSibling = newLink; | ||
} | ||
link.previousSibling = newLink; | ||
refLink.previousSibling = newLink; | ||
}; | ||
Link.prototype.insertAfter = function (link, newLink) { | ||
newLink.previousSibling = link; | ||
if (link.nextSibling === undefined) { | ||
Link.prototype.insertAfter = function (newLink, refLink) { | ||
newLink.previousSibling = refLink; | ||
if (refLink.nextSibling === undefined) { | ||
newLink.nextSibling = undefined; | ||
@@ -390,6 +390,6 @@ this.lastChild = newLink; | ||
else { | ||
newLink.nextSibling = link.nextSibling; | ||
link.nextSibling.previousSibling = newLink; | ||
newLink.nextSibling = refLink.nextSibling; | ||
refLink.nextSibling.previousSibling = newLink; | ||
} | ||
link.nextSibling = newLink; | ||
refLink.nextSibling = newLink; | ||
}; | ||
@@ -404,3 +404,3 @@ Link.prototype.appendChild = function (link) { | ||
else { | ||
this.insertAfter(this.lastChild, link); | ||
this.insertAfter(link, this.lastChild); | ||
} | ||
@@ -422,5 +422,8 @@ }; | ||
}; | ||
Link.prototype.replaceChild = function (newLink, refLink) { | ||
this.insertBefore(newLink, refLink); | ||
this.removeChild(refLink); | ||
}; | ||
return Link; | ||
}()); | ||
// TODO: don’t re-use hosts per tag and key | ||
var Host = /** @class */ (function (_super) { | ||
@@ -434,19 +437,8 @@ __extends(Host, _super); | ||
_this.renderer = renderer; | ||
_this.node = undefined; | ||
_this.updating = false; | ||
_this.done = false; | ||
_this.unmounted = false; | ||
_this.independent = false; | ||
// TODO: maybe rename these properties to “value” and “cachedChildValues” | ||
_this.cachedChildNodes = undefined; | ||
_this.iterator = undefined; | ||
_this.intrinsic = undefined; | ||
_this.committer = undefined; | ||
_this.hostsByKey = undefined; | ||
_this.firstChild = undefined; | ||
_this.lastChild = undefined; | ||
_this.nextSibling = undefined; | ||
_this.previousSibling = undefined; | ||
// TODO: we won’t need these properties/checks if we stop reusing host nodes | ||
_this.nextRunId = 0; | ||
_this.maxRunId = -1; | ||
_this.clock = 0; | ||
// TODO: clean up this monster | ||
_this.updateChildren = chase(function updateChildren(children) { | ||
@@ -462,57 +454,102 @@ var e_8, _a, e_9, _b; | ||
} | ||
try { | ||
for (var children_1 = __values(children), children_1_1 = children_1.next(); !children_1_1.done; children_1_1 = children_1.next()) { | ||
var child = children_1_1.value; | ||
if (isNonStringIterable(child)) { | ||
child = createElement(Fragment, null, child); | ||
var _loop_1 = function (child) { | ||
if (isNonStringIterable(child)) { | ||
child = createElement(Fragment, null, child); | ||
} | ||
var guest = toGuest(child); | ||
var tag = void 0; | ||
var key = void 0; | ||
var isNewHost = false; | ||
if (isElement(guest)) { | ||
tag = guest.tag; | ||
key = guest.key; | ||
if (hostsByKey !== undefined && hostsByKey.has(key)) { | ||
// TODO: warn about a duplicate key | ||
key = undefined; | ||
} | ||
else if (isKeyedElement(child) && | ||
hostsByKey && | ||
hostsByKey.has(child.key)) { | ||
// TODO: warn or throw | ||
child = __assign(__assign({}, child), { key: undefined }); | ||
} | ||
if (key != null) { | ||
var newHost = this_1.hostsByKey && this_1.hostsByKey.get(key); | ||
if (newHost === undefined) { | ||
newHost = new Host(this_1, this_1.renderer); | ||
isNewHost = true; | ||
} | ||
if (isKeyedElement(child)) { | ||
var keyedHost = this.hostsByKey && this.hostsByKey.get(child.key); | ||
if (keyedHost === undefined) { | ||
keyedHost = new Host(this, this.renderer); | ||
else { | ||
this_1.hostsByKey.delete(key); | ||
if (host !== newHost) { | ||
this_1.removeChild(newHost); | ||
} | ||
} | ||
if (host === undefined) { | ||
this_1.appendChild(newHost); | ||
} | ||
else if (host !== newHost) { | ||
if (isKeyedElement(host.guest)) { | ||
this_1.insertAfter(newHost, host); | ||
} | ||
else { | ||
this.hostsByKey.delete(child.key); | ||
if (host !== keyedHost) { | ||
this.removeChild(keyedHost); | ||
} | ||
this_1.insertBefore(newHost, host); | ||
} | ||
if (host === undefined) { | ||
this.appendChild(keyedHost); | ||
} | ||
host = newHost; | ||
} | ||
else if (host === undefined) { | ||
host = new Host(this_1, this_1.renderer); | ||
this_1.appendChild(host); | ||
isNewHost = true; | ||
} | ||
else if (host.key != null) { | ||
var newHost = new Host(this_1, this_1.renderer); | ||
this_1.insertAfter(newHost, host); | ||
host = newHost; | ||
isNewHost = true; | ||
} | ||
if (tag !== Copy) { | ||
if (isNewHost || (!host.unmounted && host.tag === tag)) { | ||
var updateP = host.update(guest); | ||
if (updateP !== undefined) { | ||
promises.push(updateP); | ||
} | ||
else if (host !== keyedHost) { | ||
if (isKeyedElement(host.guest)) { | ||
this.insertAfter(host, keyedHost); | ||
} | ||
else { | ||
this.insertBefore(host, keyedHost); | ||
} | ||
} | ||
else { | ||
var clock = host.clock++; | ||
var newHost_1 = new Host(this_1, this_1.renderer); | ||
newHost_1.clock = clock; | ||
var updateP = newHost_1.update(guest); | ||
host.unmount(); | ||
if (updateP === undefined) { | ||
this_1.replaceChild(newHost_1, host); | ||
host.replacedBy = newHost_1; | ||
} | ||
host = keyedHost; | ||
if (hostsByKey === undefined) { | ||
hostsByKey = new Map(); | ||
else { | ||
promises.push(updateP); | ||
var host1_1 = host; | ||
updateP.then(function () { | ||
if (host1_1.replacedBy === undefined) { | ||
_this.replaceChild(newHost_1, host1_1); | ||
host1_1.replacedBy = newHost_1; | ||
} | ||
else if (host1_1.replacedBy.replacedBy === undefined && | ||
host1_1.replacedBy.clock < newHost_1.clock) { | ||
_this.replaceChild(newHost_1, host1_1.replacedBy); | ||
host1_1.replacedBy = newHost_1; | ||
} | ||
}); | ||
} | ||
hostsByKey.set(child.key, keyedHost); | ||
} | ||
else if (host === undefined) { | ||
host = new Host(this, this.renderer); | ||
this.appendChild(host); | ||
} | ||
if (key !== undefined) { | ||
if (hostsByKey === undefined) { | ||
hostsByKey = new Map(); | ||
} | ||
else if (isKeyedElement(host.guest)) { | ||
var unkeyedHost = new Host(this, this.renderer); | ||
this.insertAfter(host, unkeyedHost); | ||
host = unkeyedHost; | ||
} | ||
var updateP = host.update(toGuest(child)); | ||
if (updateP !== undefined) { | ||
promises.push(updateP); | ||
} | ||
host = host.nextSibling; | ||
hostsByKey.set(key, host); | ||
} | ||
host = host.nextSibling; | ||
}; | ||
var this_1 = this; | ||
try { | ||
for (var children_1 = __values(children), children_1_1 = children_1.next(); !children_1_1.done; children_1_1 = children_1.next()) { | ||
var child = children_1_1.value; | ||
_loop_1(child); | ||
} | ||
} | ||
@@ -528,4 +565,4 @@ catch (e_8_1) { e_8 = { error: e_8_1 }; } | ||
while (host !== undefined) { | ||
if (isKeyedElement(host.guest) && this.hostsByKey) { | ||
this.hostsByKey.delete(host.guest.key); | ||
if (this.hostsByKey !== undefined && host.key !== undefined) { | ||
this.hostsByKey.delete(host.key); | ||
} | ||
@@ -541,3 +578,3 @@ void host.unmount(); | ||
// TODO: implement async unmount for keyed hosts | ||
void host_1.unmount(); | ||
host_1.unmount(); | ||
this.removeChild(host_1); | ||
@@ -564,2 +601,17 @@ } | ||
} | ||
Object.defineProperty(Host.prototype, "tag", { | ||
get: function () { | ||
return isElement(this.guest) ? this.guest.tag : undefined; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(Host.prototype, "key", { | ||
get: function () { | ||
//key?: unknown; | ||
return isElement(this.guest) ? this.guest.key : undefined; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(Host.prototype, "childNodes", { | ||
@@ -614,17 +666,8 @@ get: function () { | ||
Host.prototype.update = function (guest) { | ||
if (isElement(guest) && guest.tag === Copy) { | ||
return; | ||
} | ||
this.updating = true; | ||
if (!isElement(this.guest) || | ||
!isElement(guest) || | ||
this.guest.tag !== guest.tag || | ||
// TODO: never reuse a host when keys differ | ||
this.guest.key !== guest.key) { | ||
void this.unmount(); | ||
this.guest = guest; | ||
if (this.guest === undefined) { | ||
if (isElement(guest)) { | ||
this.ctx = new Context(this, this.parent && this.parent.ctx); | ||
// TODO: allow people to provide custom intrinsics for fragments to | ||
// allow for stuff like an innerHTML property | ||
// TODO: allow custom intrinsics for fragments to allow for stuff like | ||
// an innerHTML property | ||
if (typeof guest.tag !== "function" && guest.tag !== Fragment) { | ||
@@ -635,8 +678,3 @@ this.intrinsic = this.renderer.intrinsicFor(guest.tag); | ||
} | ||
else if (typeof guest === "string") { | ||
this.guest = this.renderer.text(guest); | ||
} | ||
else { | ||
this.guest = guest; | ||
} | ||
this.guest = guest; | ||
return this.refresh(); | ||
@@ -665,3 +703,2 @@ }; | ||
} | ||
// TODO: call next with a promise for async generator components with async children | ||
var iteration = this.iterator.next(next); | ||
@@ -675,3 +712,3 @@ if (isPromiseLike(iteration)) { | ||
var next = new Pledge(updateP).then(function () { return _this.childNodeOrNodes; }); | ||
if (iteration.done) { | ||
if (iteration.done || _this.done) { | ||
_this.done = true; | ||
@@ -688,3 +725,5 @@ } | ||
} | ||
return new Pledge(iteration.value).then(function (child) { return _this.updateChildren(child); }); | ||
return new Pledge(iteration.value).then(function (child) { | ||
return _this.updateChildren(child); | ||
}); | ||
}; | ||
@@ -702,8 +741,4 @@ Host.prototype.run = function () { | ||
}); | ||
var runId_1 = this.nextRunId++; | ||
return Promise.resolve(step).then(function (child) { | ||
_this.maxRunId = Math.max(runId_1, _this.maxRunId); | ||
if (runId_1 === _this.maxRunId) { | ||
return _this.updateChildren(child); | ||
} | ||
return _this.updateChildren(child); | ||
}); | ||
@@ -732,8 +767,4 @@ } | ||
if (this.iterator === undefined) { | ||
var runId_2 = this.nextRunId++; | ||
this.enqueued = this.enqueued.then(function (child) { | ||
_this.maxRunId = Math.max(runId_2, _this.maxRunId); | ||
if (runId_2 === _this.maxRunId) { | ||
return _this.updateChildren(child); | ||
} | ||
return _this.updateChildren(child); | ||
}); | ||
@@ -749,3 +780,6 @@ } | ||
Host.prototype.refresh = function () { | ||
if (isElement(this.guest)) { | ||
if (this.unmounted) { | ||
return; | ||
} | ||
else if (isElement(this.guest)) { | ||
if (this.ctx !== undefined) { | ||
@@ -758,8 +792,9 @@ this.ctx.dispatchEvent(new CustomEvent("crank.refresh", { detail: { props: this.guest.props } })); | ||
else { | ||
this.maxRunId = ++this.nextRunId; | ||
return this.updateChildren(this.guest.props.children); | ||
} | ||
} | ||
else if (typeof this.guest === "string") { | ||
this.node = this.renderer.text(this.guest); | ||
} | ||
else { | ||
this.maxRunId = ++this.nextRunId; | ||
this.node = this.guest; | ||
@@ -805,14 +840,5 @@ } | ||
Host.prototype.unmount = function () { | ||
this.independent = false; | ||
this.node = undefined; | ||
this.guest = undefined; | ||
this.intrinsic = undefined; | ||
this.pending = undefined; | ||
this.enqueued = undefined; | ||
this.hostsByKey = undefined; | ||
this.cachedChildNodes = undefined; | ||
if (this.ctx !== undefined) { | ||
this.ctx.dispatchEvent(new Event("crank.unmount")); | ||
this.ctx.removeAllEventListeners(); | ||
this.ctx = undefined; | ||
} | ||
@@ -828,6 +854,6 @@ // TODO: await the return if the host is keyed and commit the parent | ||
} | ||
this.unmounted = true; | ||
this.committer = undefined; | ||
this.iterator = undefined; | ||
this.updating = false; | ||
this.done = false; | ||
this.unmountChildren(); | ||
@@ -912,3 +938,3 @@ }; | ||
}; | ||
// TODO: throw an error if refresh is called on an unmounted component | ||
// TODO: warn if refresh is called on an unmounted component | ||
Context.prototype.refresh = function () { | ||
@@ -998,3 +1024,3 @@ return this.host.refresh(); | ||
if (this.env[Text] !== undefined) { | ||
// TODO: remove non-null assertion when typescript gets its shit together | ||
// TODO: remove non-null assertion when typescript gets its shit together with symbols | ||
return this.env[Text](text); | ||
@@ -1001,0 +1027,0 @@ } |
@@ -1,4 +0,2 @@ | ||
import { Child, Context, Environment, Props, Renderer } from "./crank"; | ||
export declare function updateDOMProps(el: HTMLElement, props: Props, newProps: Props): void; | ||
export declare function updateDOMChildren(el: HTMLElement, children?: (Node | string)[]): void; | ||
import { Child, Context, Environment, Renderer } from "./crank"; | ||
export declare const env: Environment<HTMLElement>; | ||
@@ -5,0 +3,0 @@ export declare class DOMRenderer extends Renderer<HTMLElement> { |
@@ -1,2 +0,2 @@ | ||
import { a as __values, c as __generator, _ as __extends } from './_tslib-21ac4f0e.js'; | ||
import { c as __generator, _ as __extends, a as __values } from './_tslib-21ac4f0e.js'; | ||
import 'event-target-shim'; | ||
@@ -7,3 +7,3 @@ import '@repeaterjs/repeater'; | ||
var _a; | ||
function updateDOMProps(el, props, newProps) { | ||
function updateProps(el, props, newProps) { | ||
for (var name_1 in Object.assign({}, props, newProps)) { | ||
@@ -54,3 +54,3 @@ // TODO: throw an error if event props are found | ||
// https://stackoverflow.com/questions/59418120/what-is-the-most-efficient-way-to-update-the-childnodes-of-a-dom-node-with-an-ar | ||
function updateDOMChildren(el, children) { | ||
function updateChildren(el, children) { | ||
var e_1, _a, e_2, _b; | ||
@@ -138,6 +138,6 @@ if (children === void 0) { children = []; } | ||
if (node !== newNode) { | ||
updateDOMChildren(node); | ||
updateChildren(node); | ||
node = newNode; | ||
} | ||
updateDOMChildren(node, this.childNodes); | ||
updateChildren(node, this.childNodes); | ||
return [4 /*yield*/, node]; | ||
@@ -163,3 +163,3 @@ case 3: | ||
case 9: | ||
updateDOMChildren(node); | ||
updateChildren(node); | ||
return [7 /*endfinally*/]; | ||
@@ -172,3 +172,3 @@ case 10: return [2 /*return*/]; | ||
return function defaultDOM() { | ||
var node, props, _a, _b, props1, e_4_1; | ||
var node, props, prevChildNodes, _a, _b, props1, e_4_1; | ||
var e_4, _c; | ||
@@ -180,2 +180,3 @@ return __generator(this, function (_d) { | ||
props = {}; | ||
prevChildNodes = []; | ||
_d.label = 1; | ||
@@ -189,5 +190,7 @@ case 1: | ||
props1 = _b.value; | ||
updateDOMProps(node, props, props1); | ||
if (!("innerHTML" in props1)) { | ||
updateDOMChildren(node, this.childNodes); | ||
updateProps(node, props, props1); | ||
if (!("innerHTML" in props1) && | ||
(this.childNodes.length > 0 || prevChildNodes.length > 0)) { | ||
updateChildren(node, this.childNodes); | ||
prevChildNodes = this.childNodes; | ||
} | ||
@@ -233,3 +236,3 @@ return [4 /*yield*/, node]; | ||
export { DOMRenderer, env, render, renderer, updateDOMChildren, updateDOMProps }; | ||
export { DOMRenderer, env, render, renderer }; | ||
//# sourceMappingURL=dom.js.map |
{ | ||
"name": "@bikeshaving/crank", | ||
"version": "0.1.0-beta.0", | ||
"version": "0.1.0-beta.1", | ||
"license": "MIT", | ||
@@ -11,3 +11,4 @@ "description": "JSX components with functions, promises and generators.", | ||
"string.js", | ||
"string.d.ts" | ||
"string.d.ts", | ||
"node_modules" | ||
], | ||
@@ -14,0 +15,0 @@ "main": "lib/cjs/crank.js", |
@@ -18,3 +18,3 @@ # Crank | ||
```ts | ||
function* Timer() { | ||
function *Timer() { | ||
let seconds = 0; | ||
@@ -52,21 +52,17 @@ const interval = setInterval(() => { | ||
async function* DogImage() { | ||
yield null; | ||
for await (const _ of this) { | ||
yield (<Loading message="Fetching a dog…" />); | ||
const res = await fetch("https://dog.ceo/api/breeds/image/random"); | ||
const data = await res.json(); | ||
if (Math.random() > 0.5) { | ||
await new Promise((resolve) => setTimeout(resolve, 2000)); | ||
} | ||
async function DogImage({throttle=false}) { | ||
if (throttle) { | ||
await new Promise((resolve) => setTimeout(resolve, 1000)); | ||
} | ||
yield ( | ||
<figure> | ||
<img src={data.message} height="400" /> | ||
<figcaption> | ||
<a href={data.message}>{data.message}</a> | ||
</figcaption> | ||
</figure> | ||
); | ||
} | ||
const res = await fetch("https://dog.ceo/api/breeds/image/random"); | ||
const data = await res.json(); | ||
return ( | ||
<figure> | ||
<img src={data.message} height="400" /> | ||
<figcaption> | ||
<a href={data.message}>{data.message}</a> | ||
</figcaption> | ||
</figure> | ||
); | ||
} | ||
@@ -73,0 +69,0 @@ |
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
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
129522
3067
85