Init
This commit is contained in:
127
node_modules/preact/compat/src/suspense-list.js
generated
vendored
Normal file
127
node_modules/preact/compat/src/suspense-list.js
generated
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
import { Component, toChildArray } from 'preact';
|
||||
import { suspended } from './suspense.js';
|
||||
|
||||
// Indexes to linked list nodes (nodes are stored as arrays to save bytes).
|
||||
const SUSPENDED_COUNT = 0;
|
||||
const RESOLVED_COUNT = 1;
|
||||
const NEXT_NODE = 2;
|
||||
|
||||
// Having custom inheritance instead of a class here saves a lot of bytes.
|
||||
export function SuspenseList() {
|
||||
this._next = null;
|
||||
this._map = null;
|
||||
}
|
||||
|
||||
// Mark one of child's earlier suspensions as resolved.
|
||||
// Some pending callbacks may become callable due to this
|
||||
// (e.g. the last suspended descendant gets resolved when
|
||||
// revealOrder === 'together'). Process those callbacks as well.
|
||||
const resolve = (list, child, node) => {
|
||||
if (++node[RESOLVED_COUNT] === node[SUSPENDED_COUNT]) {
|
||||
// The number a child (or any of its descendants) has been suspended
|
||||
// matches the number of times it's been resolved. Therefore we
|
||||
// mark the child as completely resolved by deleting it from ._map.
|
||||
// This is used to figure out when *all* children have been completely
|
||||
// resolved when revealOrder is 'together'.
|
||||
list._map.delete(child);
|
||||
}
|
||||
|
||||
// If revealOrder is falsy then we can do an early exit, as the
|
||||
// callbacks won't get queued in the node anyway.
|
||||
// If revealOrder is 'together' then also do an early exit
|
||||
// if all suspended descendants have not yet been resolved.
|
||||
if (
|
||||
!list.props.revealOrder ||
|
||||
(list.props.revealOrder[0] === 't' && list._map.size)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Walk the currently suspended children in order, calling their
|
||||
// stored callbacks on the way. Stop if we encounter a child that
|
||||
// has not been completely resolved yet.
|
||||
node = list._next;
|
||||
while (node) {
|
||||
while (node.length > 3) {
|
||||
node.pop()();
|
||||
}
|
||||
if (node[RESOLVED_COUNT] < node[SUSPENDED_COUNT]) {
|
||||
break;
|
||||
}
|
||||
list._next = node = node[NEXT_NODE];
|
||||
}
|
||||
};
|
||||
|
||||
// Things we do here to save some bytes but are not proper JS inheritance:
|
||||
// - call `new Component()` as the prototype
|
||||
// - do not set `Suspense.prototype.constructor` to `Suspense`
|
||||
SuspenseList.prototype = new Component();
|
||||
|
||||
SuspenseList.prototype._suspended = function (child) {
|
||||
const list = this;
|
||||
const delegated = suspended(list._vnode);
|
||||
|
||||
let node = list._map.get(child);
|
||||
node[SUSPENDED_COUNT]++;
|
||||
|
||||
return unsuspend => {
|
||||
const wrappedUnsuspend = () => {
|
||||
if (!list.props.revealOrder) {
|
||||
// Special case the undefined (falsy) revealOrder, as there
|
||||
// is no need to coordinate a specific order or unsuspends.
|
||||
unsuspend();
|
||||
} else {
|
||||
node.push(unsuspend);
|
||||
resolve(list, child, node);
|
||||
}
|
||||
};
|
||||
if (delegated) {
|
||||
delegated(wrappedUnsuspend);
|
||||
} else {
|
||||
wrappedUnsuspend();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
SuspenseList.prototype.render = function (props) {
|
||||
this._next = null;
|
||||
this._map = new Map();
|
||||
|
||||
const children = toChildArray(props.children);
|
||||
if (props.revealOrder && props.revealOrder[0] === 'b') {
|
||||
// If order === 'backwards' (or, well, anything starting with a 'b')
|
||||
// then flip the child list around so that the last child will be
|
||||
// the first in the linked list.
|
||||
children.reverse();
|
||||
}
|
||||
// Build the linked list. Iterate through the children in reverse order
|
||||
// so that `_next` points to the first linked list node to be resolved.
|
||||
for (let i = children.length; i--; ) {
|
||||
// Create a new linked list node as an array of form:
|
||||
// [suspended_count, resolved_count, next_node]
|
||||
// where suspended_count and resolved_count are numeric counters for
|
||||
// keeping track how many times a node has been suspended and resolved.
|
||||
//
|
||||
// Note that suspended_count starts from 1 instead of 0, so we can block
|
||||
// processing callbacks until componentDidMount has been called. In a sense
|
||||
// node is suspended at least until componentDidMount gets called!
|
||||
//
|
||||
// Pending callbacks are added to the end of the node:
|
||||
// [suspended_count, resolved_count, next_node, callback_0, callback_1, ...]
|
||||
this._map.set(children[i], (this._next = [1, 0, this._next]));
|
||||
}
|
||||
return props.children;
|
||||
};
|
||||
|
||||
SuspenseList.prototype.componentDidUpdate =
|
||||
SuspenseList.prototype.componentDidMount = function () {
|
||||
// Iterate through all children after mounting for two reasons:
|
||||
// 1. As each node[SUSPENDED_COUNT] starts from 1, this iteration increases
|
||||
// each node[RELEASED_COUNT] by 1, therefore balancing the counters.
|
||||
// The nodes can now be completely consumed from the linked list.
|
||||
// 2. Handle nodes that might have gotten resolved between render and
|
||||
// componentDidMount.
|
||||
this._map.forEach((node, child) => {
|
||||
resolve(this, child, node);
|
||||
});
|
||||
};
|
Reference in New Issue
Block a user