@b9g/crank 0.7.0 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +646 -138
- package/_css.cjs +80 -0
- package/_css.cjs.map +1 -0
- package/_css.d.ts +21 -0
- package/_css.js +76 -0
- package/_css.js.map +1 -0
- package/_utils.cjs +106 -0
- package/_utils.cjs.map +1 -0
- package/_utils.js +99 -0
- package/_utils.js.map +1 -0
- package/async.cjs +42 -39
- package/async.cjs.map +1 -1
- package/async.d.ts +10 -7
- package/async.js +42 -39
- package/async.js.map +1 -1
- package/crank.cjs +87 -141
- package/crank.cjs.map +1 -1
- package/crank.d.ts +255 -3
- package/crank.js +51 -105
- package/crank.js.map +1 -1
- package/dom.cjs +33 -19
- package/dom.cjs.map +1 -1
- package/dom.js +33 -19
- package/dom.js.map +1 -1
- package/html.cjs +5 -3
- package/html.cjs.map +1 -1
- package/html.js +5 -3
- package/html.js.map +1 -1
- package/jsx-runtime.cjs +0 -1
- package/jsx-runtime.cjs.map +1 -1
- package/jsx-runtime.js +0 -1
- package/jsx-runtime.js.map +1 -1
- package/jsx-tag.cjs +0 -1
- package/jsx-tag.cjs.map +1 -1
- package/jsx-tag.js +0 -1
- package/jsx-tag.js.map +1 -1
- package/package.json +3 -2
- package/standalone.cjs +0 -1
- package/standalone.cjs.map +1 -1
- package/standalone.js +0 -1
- package/standalone.js.map +1 -1
- package/umd.js +160 -28
- package/umd.js.map +1 -1
package/_css.cjs
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* CSS utility functions for style property transformation.
|
|
5
|
+
*
|
|
6
|
+
* This module handles camelCase to kebab-case conversion and automatic
|
|
7
|
+
* px unit conversion for numeric CSS values, making Crank more React-compatible.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Converts camelCase CSS property names to kebab-case.
|
|
11
|
+
* Handles vendor prefixes correctly (WebkitTransform -> -webkit-transform).
|
|
12
|
+
*/
|
|
13
|
+
function camelToKebabCase(str) {
|
|
14
|
+
// Handle vendor prefixes that start with capital letters (WebkitTransform -> -webkit-transform)
|
|
15
|
+
if (/^[A-Z]/.test(str)) {
|
|
16
|
+
return `-${str.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`).slice(1)}`;
|
|
17
|
+
}
|
|
18
|
+
// Handle normal camelCase (fontSize -> font-size)
|
|
19
|
+
return str.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* CSS properties that should remain unitless when given numeric values.
|
|
23
|
+
* Based on React's list of unitless properties.
|
|
24
|
+
*/
|
|
25
|
+
const UNITLESS_PROPERTIES = new Set([
|
|
26
|
+
"animation-iteration-count",
|
|
27
|
+
"aspect-ratio",
|
|
28
|
+
"border-image-outset",
|
|
29
|
+
"border-image-slice",
|
|
30
|
+
"border-image-width",
|
|
31
|
+
"box-flex",
|
|
32
|
+
"box-flex-group",
|
|
33
|
+
"box-ordinal-group",
|
|
34
|
+
"column-count",
|
|
35
|
+
"columns",
|
|
36
|
+
"flex",
|
|
37
|
+
"flex-grow",
|
|
38
|
+
"flex-positive",
|
|
39
|
+
"flex-shrink",
|
|
40
|
+
"flex-negative",
|
|
41
|
+
"flex-order",
|
|
42
|
+
"font-weight",
|
|
43
|
+
"grid-area",
|
|
44
|
+
"grid-column",
|
|
45
|
+
"grid-column-end",
|
|
46
|
+
"grid-column-span",
|
|
47
|
+
"grid-column-start",
|
|
48
|
+
"grid-row",
|
|
49
|
+
"grid-row-end",
|
|
50
|
+
"grid-row-span",
|
|
51
|
+
"grid-row-start",
|
|
52
|
+
"line-height",
|
|
53
|
+
"opacity",
|
|
54
|
+
"order",
|
|
55
|
+
"orphans",
|
|
56
|
+
"tab-size",
|
|
57
|
+
"widows",
|
|
58
|
+
"z-index",
|
|
59
|
+
"zoom",
|
|
60
|
+
]);
|
|
61
|
+
/**
|
|
62
|
+
* Formats CSS property values, automatically adding "px" to numeric values
|
|
63
|
+
* for properties that are not unitless.
|
|
64
|
+
*/
|
|
65
|
+
function formatStyleValue(name, value) {
|
|
66
|
+
if (typeof value === "number") {
|
|
67
|
+
// If the property should remain unitless, keep the number as-is
|
|
68
|
+
if (UNITLESS_PROPERTIES.has(name)) {
|
|
69
|
+
return String(value);
|
|
70
|
+
}
|
|
71
|
+
// Otherwise, append "px" for numeric values
|
|
72
|
+
return `${value}px`;
|
|
73
|
+
}
|
|
74
|
+
return String(value);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
exports.UNITLESS_PROPERTIES = UNITLESS_PROPERTIES;
|
|
78
|
+
exports.camelToKebabCase = camelToKebabCase;
|
|
79
|
+
exports.formatStyleValue = formatStyleValue;
|
|
80
|
+
//# sourceMappingURL=_css.cjs.map
|
package/_css.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_css.cjs","sources":["../src/_css.ts"],"sourcesContent":["/**\n * CSS utility functions for style property transformation.\n *\n * This module handles camelCase to kebab-case conversion and automatic\n * px unit conversion for numeric CSS values, making Crank more React-compatible.\n */\n\n/**\n * Converts camelCase CSS property names to kebab-case.\n * Handles vendor prefixes correctly (WebkitTransform -> -webkit-transform).\n */\nexport function camelToKebabCase(str: string): string {\n\t// Handle vendor prefixes that start with capital letters (WebkitTransform -> -webkit-transform)\n\tif (/^[A-Z]/.test(str)) {\n\t\treturn `-${str.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`).slice(1)}`;\n\t}\n\t// Handle normal camelCase (fontSize -> font-size)\n\treturn str.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);\n}\n\n/**\n * CSS properties that should remain unitless when given numeric values.\n * Based on React's list of unitless properties.\n */\nexport const UNITLESS_PROPERTIES = new Set([\n\t\"animation-iteration-count\",\n\t\"aspect-ratio\",\n\t\"border-image-outset\",\n\t\"border-image-slice\",\n\t\"border-image-width\",\n\t\"box-flex\",\n\t\"box-flex-group\",\n\t\"box-ordinal-group\",\n\t\"column-count\",\n\t\"columns\",\n\t\"flex\",\n\t\"flex-grow\",\n\t\"flex-positive\",\n\t\"flex-shrink\",\n\t\"flex-negative\",\n\t\"flex-order\",\n\t\"font-weight\",\n\t\"grid-area\",\n\t\"grid-column\",\n\t\"grid-column-end\",\n\t\"grid-column-span\",\n\t\"grid-column-start\",\n\t\"grid-row\",\n\t\"grid-row-end\",\n\t\"grid-row-span\",\n\t\"grid-row-start\",\n\t\"line-height\",\n\t\"opacity\",\n\t\"order\",\n\t\"orphans\",\n\t\"tab-size\",\n\t\"widows\",\n\t\"z-index\",\n\t\"zoom\",\n]);\n\n/**\n * Formats CSS property values, automatically adding \"px\" to numeric values\n * for properties that are not unitless.\n */\nexport function formatStyleValue(name: string, value: unknown): string {\n\tif (typeof value === \"number\") {\n\t\t// If the property should remain unitless, keep the number as-is\n\t\tif (UNITLESS_PROPERTIES.has(name)) {\n\t\t\treturn String(value);\n\t\t}\n\t\t// Otherwise, append \"px\" for numeric values\n\t\treturn `${value}px`;\n\t}\n\treturn String(value);\n}\n"],"names":[],"mappings":";;AAAA;;;;;AAKG;AAEH;;;AAGG;AACG,SAAU,gBAAgB,CAAC,GAAW,EAAA;;AAE3C,IAAA,IAAI,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;QACvB,OAAO,CAAA,CAAA,EAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,KAAK,IAAI,KAAK,CAAC,WAAW,EAAE,CAAE,CAAA,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA,CAAE;;;AAGlF,IAAA,OAAO,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,KAAK,IAAI,KAAK,CAAC,WAAW,EAAE,CAAA,CAAE,CAAC;AACnE;AAEA;;;AAGG;AACU,MAAA,mBAAmB,GAAG,IAAI,GAAG,CAAC;IAC1C,2BAA2B;IAC3B,cAAc;IACd,qBAAqB;IACrB,oBAAoB;IACpB,oBAAoB;IACpB,UAAU;IACV,gBAAgB;IAChB,mBAAmB;IACnB,cAAc;IACd,SAAS;IACT,MAAM;IACN,WAAW;IACX,eAAe;IACf,aAAa;IACb,eAAe;IACf,YAAY;IACZ,aAAa;IACb,WAAW;IACX,aAAa;IACb,iBAAiB;IACjB,kBAAkB;IAClB,mBAAmB;IACnB,UAAU;IACV,cAAc;IACd,eAAe;IACf,gBAAgB;IAChB,aAAa;IACb,SAAS;IACT,OAAO;IACP,SAAS;IACT,UAAU;IACV,QAAQ;IACR,SAAS;IACT,MAAM;AACN,CAAA;AAED;;;AAGG;AACa,SAAA,gBAAgB,CAAC,IAAY,EAAE,KAAc,EAAA;AAC5D,IAAA,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;;AAE9B,QAAA,IAAI,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;AAClC,YAAA,OAAO,MAAM,CAAC,KAAK,CAAC;;;QAGrB,OAAO,CAAA,EAAG,KAAK,CAAA,EAAA,CAAI;;AAEpB,IAAA,OAAO,MAAM,CAAC,KAAK,CAAC;AACrB;;;;;;"}
|
package/_css.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS utility functions for style property transformation.
|
|
3
|
+
*
|
|
4
|
+
* This module handles camelCase to kebab-case conversion and automatic
|
|
5
|
+
* px unit conversion for numeric CSS values, making Crank more React-compatible.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Converts camelCase CSS property names to kebab-case.
|
|
9
|
+
* Handles vendor prefixes correctly (WebkitTransform -> -webkit-transform).
|
|
10
|
+
*/
|
|
11
|
+
export declare function camelToKebabCase(str: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* CSS properties that should remain unitless when given numeric values.
|
|
14
|
+
* Based on React's list of unitless properties.
|
|
15
|
+
*/
|
|
16
|
+
export declare const UNITLESS_PROPERTIES: Set<string>;
|
|
17
|
+
/**
|
|
18
|
+
* Formats CSS property values, automatically adding "px" to numeric values
|
|
19
|
+
* for properties that are not unitless.
|
|
20
|
+
*/
|
|
21
|
+
export declare function formatStyleValue(name: string, value: unknown): string;
|
package/_css.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS utility functions for style property transformation.
|
|
3
|
+
*
|
|
4
|
+
* This module handles camelCase to kebab-case conversion and automatic
|
|
5
|
+
* px unit conversion for numeric CSS values, making Crank more React-compatible.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Converts camelCase CSS property names to kebab-case.
|
|
9
|
+
* Handles vendor prefixes correctly (WebkitTransform -> -webkit-transform).
|
|
10
|
+
*/
|
|
11
|
+
function camelToKebabCase(str) {
|
|
12
|
+
// Handle vendor prefixes that start with capital letters (WebkitTransform -> -webkit-transform)
|
|
13
|
+
if (/^[A-Z]/.test(str)) {
|
|
14
|
+
return `-${str.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`).slice(1)}`;
|
|
15
|
+
}
|
|
16
|
+
// Handle normal camelCase (fontSize -> font-size)
|
|
17
|
+
return str.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* CSS properties that should remain unitless when given numeric values.
|
|
21
|
+
* Based on React's list of unitless properties.
|
|
22
|
+
*/
|
|
23
|
+
const UNITLESS_PROPERTIES = new Set([
|
|
24
|
+
"animation-iteration-count",
|
|
25
|
+
"aspect-ratio",
|
|
26
|
+
"border-image-outset",
|
|
27
|
+
"border-image-slice",
|
|
28
|
+
"border-image-width",
|
|
29
|
+
"box-flex",
|
|
30
|
+
"box-flex-group",
|
|
31
|
+
"box-ordinal-group",
|
|
32
|
+
"column-count",
|
|
33
|
+
"columns",
|
|
34
|
+
"flex",
|
|
35
|
+
"flex-grow",
|
|
36
|
+
"flex-positive",
|
|
37
|
+
"flex-shrink",
|
|
38
|
+
"flex-negative",
|
|
39
|
+
"flex-order",
|
|
40
|
+
"font-weight",
|
|
41
|
+
"grid-area",
|
|
42
|
+
"grid-column",
|
|
43
|
+
"grid-column-end",
|
|
44
|
+
"grid-column-span",
|
|
45
|
+
"grid-column-start",
|
|
46
|
+
"grid-row",
|
|
47
|
+
"grid-row-end",
|
|
48
|
+
"grid-row-span",
|
|
49
|
+
"grid-row-start",
|
|
50
|
+
"line-height",
|
|
51
|
+
"opacity",
|
|
52
|
+
"order",
|
|
53
|
+
"orphans",
|
|
54
|
+
"tab-size",
|
|
55
|
+
"widows",
|
|
56
|
+
"z-index",
|
|
57
|
+
"zoom",
|
|
58
|
+
]);
|
|
59
|
+
/**
|
|
60
|
+
* Formats CSS property values, automatically adding "px" to numeric values
|
|
61
|
+
* for properties that are not unitless.
|
|
62
|
+
*/
|
|
63
|
+
function formatStyleValue(name, value) {
|
|
64
|
+
if (typeof value === "number") {
|
|
65
|
+
// If the property should remain unitless, keep the number as-is
|
|
66
|
+
if (UNITLESS_PROPERTIES.has(name)) {
|
|
67
|
+
return String(value);
|
|
68
|
+
}
|
|
69
|
+
// Otherwise, append "px" for numeric values
|
|
70
|
+
return `${value}px`;
|
|
71
|
+
}
|
|
72
|
+
return String(value);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export { UNITLESS_PROPERTIES, camelToKebabCase, formatStyleValue };
|
|
76
|
+
//# sourceMappingURL=_css.js.map
|
package/_css.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_css.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
package/_utils.cjs
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function wrap(value) {
|
|
4
|
+
return value === undefined ? [] : Array.isArray(value) ? value : [value];
|
|
5
|
+
}
|
|
6
|
+
function unwrap(arr) {
|
|
7
|
+
return arr.length === 0 ? undefined : arr.length === 1 ? arr[0] : arr;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Ensures a value is an array.
|
|
11
|
+
*
|
|
12
|
+
* This function does the same thing as wrap() above except it handles nulls
|
|
13
|
+
* and iterables, so it is appropriate for wrapping user-provided element
|
|
14
|
+
* children.
|
|
15
|
+
*/
|
|
16
|
+
function arrayify(value) {
|
|
17
|
+
return value == null
|
|
18
|
+
? []
|
|
19
|
+
: Array.isArray(value)
|
|
20
|
+
? value
|
|
21
|
+
: typeof value === "string" ||
|
|
22
|
+
typeof value[Symbol.iterator] !== "function"
|
|
23
|
+
? [value]
|
|
24
|
+
: [...value];
|
|
25
|
+
}
|
|
26
|
+
function isIteratorLike(value) {
|
|
27
|
+
return value != null && typeof value.next === "function";
|
|
28
|
+
}
|
|
29
|
+
function isPromiseLike(value) {
|
|
30
|
+
return value != null && typeof value.then === "function";
|
|
31
|
+
}
|
|
32
|
+
function createRaceRecord(contender) {
|
|
33
|
+
const deferreds = new Set();
|
|
34
|
+
const record = { deferreds, settled: false };
|
|
35
|
+
// This call to `then` happens once for the lifetime of the value.
|
|
36
|
+
Promise.resolve(contender).then((value) => {
|
|
37
|
+
for (const { resolve } of deferreds) {
|
|
38
|
+
resolve(value);
|
|
39
|
+
}
|
|
40
|
+
deferreds.clear();
|
|
41
|
+
record.settled = true;
|
|
42
|
+
}, (err) => {
|
|
43
|
+
for (const { reject } of deferreds) {
|
|
44
|
+
reject(err);
|
|
45
|
+
}
|
|
46
|
+
deferreds.clear();
|
|
47
|
+
record.settled = true;
|
|
48
|
+
});
|
|
49
|
+
return record;
|
|
50
|
+
}
|
|
51
|
+
// Promise.race is memory unsafe. This is alternative which is. See:
|
|
52
|
+
// https://github.com/nodejs/node/issues/17469#issuecomment-685235106
|
|
53
|
+
// Keys are the values passed to race.
|
|
54
|
+
// Values are a record of data containing a set of deferreds and whether the
|
|
55
|
+
// value has settled.
|
|
56
|
+
const wm = new WeakMap();
|
|
57
|
+
function safeRace(contenders) {
|
|
58
|
+
let deferred;
|
|
59
|
+
const result = new Promise((resolve, reject) => {
|
|
60
|
+
deferred = { resolve, reject };
|
|
61
|
+
for (const contender of contenders) {
|
|
62
|
+
if (!isPromiseLike(contender)) {
|
|
63
|
+
// If the contender is a not a then-able, attempting to use it as a key
|
|
64
|
+
// in the weakmap would throw an error. Luckily, it is safe to call
|
|
65
|
+
// `Promise.resolve(contender).then` on regular values multiple
|
|
66
|
+
// times because the promise fulfills immediately.
|
|
67
|
+
Promise.resolve(contender).then(resolve, reject);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
let record = wm.get(contender);
|
|
71
|
+
if (record === undefined) {
|
|
72
|
+
record = createRaceRecord(contender);
|
|
73
|
+
record.deferreds.add(deferred);
|
|
74
|
+
wm.set(contender, record);
|
|
75
|
+
}
|
|
76
|
+
else if (record.settled) {
|
|
77
|
+
// If the value has settled, it is safe to call
|
|
78
|
+
// `Promise.resolve(contender).then` on it.
|
|
79
|
+
Promise.resolve(contender).then(resolve, reject);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
record.deferreds.add(deferred);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
// The finally callback executes when any value settles, preventing any of
|
|
87
|
+
// the unresolved values from retaining a reference to the resolved value.
|
|
88
|
+
return result.finally(() => {
|
|
89
|
+
for (const contender of contenders) {
|
|
90
|
+
if (isPromiseLike(contender)) {
|
|
91
|
+
const record = wm.get(contender);
|
|
92
|
+
if (record) {
|
|
93
|
+
record.deferreds.delete(deferred);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
exports.arrayify = arrayify;
|
|
101
|
+
exports.isIteratorLike = isIteratorLike;
|
|
102
|
+
exports.isPromiseLike = isPromiseLike;
|
|
103
|
+
exports.safeRace = safeRace;
|
|
104
|
+
exports.unwrap = unwrap;
|
|
105
|
+
exports.wrap = wrap;
|
|
106
|
+
//# sourceMappingURL=_utils.cjs.map
|
package/_utils.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_utils.cjs","sources":["../src/_utils.ts"],"sourcesContent":["export function wrap<T>(value: Array<T> | T | undefined): Array<T> {\n\treturn value === undefined ? [] : Array.isArray(value) ? value : [value];\n}\n\nexport function unwrap<T>(arr: Array<T>): Array<T> | T | undefined {\n\treturn arr.length === 0 ? undefined : arr.length === 1 ? arr[0] : arr;\n}\n\nexport type NonStringIterable<T> = Iterable<T> & object;\n\n/**\n * Ensures a value is an array.\n *\n * This function does the same thing as wrap() above except it handles nulls\n * and iterables, so it is appropriate for wrapping user-provided element\n * children.\n */\nexport function arrayify<T>(\n\tvalue: NonStringIterable<T> | T | null | undefined,\n): Array<T> {\n\treturn value == null\n\t\t? []\n\t\t: Array.isArray(value)\n\t\t\t? value\n\t\t\t: typeof value === \"string\" ||\n\t\t\t\t typeof (value as any)[Symbol.iterator] !== \"function\"\n\t\t\t\t? [value as T]\n\t\t\t\t: [...(value as NonStringIterable<T>)];\n}\n\nexport function isIteratorLike(\n\tvalue: any,\n): value is Iterator<unknown> | AsyncIterator<unknown> {\n\treturn value != null && typeof value.next === \"function\";\n}\n\nexport function isPromiseLike(value: any): value is PromiseLike<unknown> {\n\treturn value != null && typeof value.then === \"function\";\n}\n\ntype Deferred<T = unknown> = {\n\tresolve: (value: T | PromiseLike<T>) => void;\n\treject: (reason?: unknown) => void;\n};\n\ntype RaceRecord = {\n\tdeferreds: Set<Deferred>;\n\tsettled: boolean;\n};\n\nfunction createRaceRecord(contender: PromiseLike<unknown>): RaceRecord {\n\tconst deferreds = new Set<Deferred>();\n\tconst record = {deferreds, settled: false};\n\n\t// This call to `then` happens once for the lifetime of the value.\n\tPromise.resolve(contender).then(\n\t\t(value) => {\n\t\t\tfor (const {resolve} of deferreds) {\n\t\t\t\tresolve(value);\n\t\t\t}\n\n\t\t\tdeferreds.clear();\n\t\t\trecord.settled = true;\n\t\t},\n\t\t(err) => {\n\t\t\tfor (const {reject} of deferreds) {\n\t\t\t\treject(err);\n\t\t\t}\n\n\t\t\tdeferreds.clear();\n\t\t\trecord.settled = true;\n\t\t},\n\t);\n\treturn record;\n}\n\n// Promise.race is memory unsafe. This is alternative which is. See:\n// https://github.com/nodejs/node/issues/17469#issuecomment-685235106\n// Keys are the values passed to race.\n// Values are a record of data containing a set of deferreds and whether the\n// value has settled.\nconst wm = new WeakMap<object, RaceRecord>();\nexport function safeRace<T>(\n\tcontenders: Iterable<T | PromiseLike<T>>,\n): Promise<Awaited<T>> {\n\tlet deferred: Deferred;\n\tconst result = new Promise((resolve, reject) => {\n\t\tdeferred = {resolve, reject};\n\t\tfor (const contender of contenders) {\n\t\t\tif (!isPromiseLike(contender)) {\n\t\t\t\t// If the contender is a not a then-able, attempting to use it as a key\n\t\t\t\t// in the weakmap would throw an error. Luckily, it is safe to call\n\t\t\t\t// `Promise.resolve(contender).then` on regular values multiple\n\t\t\t\t// times because the promise fulfills immediately.\n\t\t\t\tPromise.resolve(contender).then(resolve, reject);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet record = wm.get(contender);\n\t\t\tif (record === undefined) {\n\t\t\t\trecord = createRaceRecord(contender);\n\t\t\t\trecord.deferreds.add(deferred);\n\t\t\t\twm.set(contender, record);\n\t\t\t} else if (record.settled) {\n\t\t\t\t// If the value has settled, it is safe to call\n\t\t\t\t// `Promise.resolve(contender).then` on it.\n\t\t\t\tPromise.resolve(contender).then(resolve, reject);\n\t\t\t} else {\n\t\t\t\trecord.deferreds.add(deferred);\n\t\t\t}\n\t\t}\n\t});\n\n\t// The finally callback executes when any value settles, preventing any of\n\t// the unresolved values from retaining a reference to the resolved value.\n\treturn result.finally(() => {\n\t\tfor (const contender of contenders) {\n\t\t\tif (isPromiseLike(contender)) {\n\t\t\t\tconst record = wm.get(contender);\n\t\t\t\tif (record) {\n\t\t\t\t\trecord.deferreds.delete(deferred);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}) as Promise<Awaited<T>>;\n}\n"],"names":[],"mappings":"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"}
|
package/_utils.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
function wrap(value) {
|
|
2
|
+
return value === undefined ? [] : Array.isArray(value) ? value : [value];
|
|
3
|
+
}
|
|
4
|
+
function unwrap(arr) {
|
|
5
|
+
return arr.length === 0 ? undefined : arr.length === 1 ? arr[0] : arr;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Ensures a value is an array.
|
|
9
|
+
*
|
|
10
|
+
* This function does the same thing as wrap() above except it handles nulls
|
|
11
|
+
* and iterables, so it is appropriate for wrapping user-provided element
|
|
12
|
+
* children.
|
|
13
|
+
*/
|
|
14
|
+
function arrayify(value) {
|
|
15
|
+
return value == null
|
|
16
|
+
? []
|
|
17
|
+
: Array.isArray(value)
|
|
18
|
+
? value
|
|
19
|
+
: typeof value === "string" ||
|
|
20
|
+
typeof value[Symbol.iterator] !== "function"
|
|
21
|
+
? [value]
|
|
22
|
+
: [...value];
|
|
23
|
+
}
|
|
24
|
+
function isIteratorLike(value) {
|
|
25
|
+
return value != null && typeof value.next === "function";
|
|
26
|
+
}
|
|
27
|
+
function isPromiseLike(value) {
|
|
28
|
+
return value != null && typeof value.then === "function";
|
|
29
|
+
}
|
|
30
|
+
function createRaceRecord(contender) {
|
|
31
|
+
const deferreds = new Set();
|
|
32
|
+
const record = { deferreds, settled: false };
|
|
33
|
+
// This call to `then` happens once for the lifetime of the value.
|
|
34
|
+
Promise.resolve(contender).then((value) => {
|
|
35
|
+
for (const { resolve } of deferreds) {
|
|
36
|
+
resolve(value);
|
|
37
|
+
}
|
|
38
|
+
deferreds.clear();
|
|
39
|
+
record.settled = true;
|
|
40
|
+
}, (err) => {
|
|
41
|
+
for (const { reject } of deferreds) {
|
|
42
|
+
reject(err);
|
|
43
|
+
}
|
|
44
|
+
deferreds.clear();
|
|
45
|
+
record.settled = true;
|
|
46
|
+
});
|
|
47
|
+
return record;
|
|
48
|
+
}
|
|
49
|
+
// Promise.race is memory unsafe. This is alternative which is. See:
|
|
50
|
+
// https://github.com/nodejs/node/issues/17469#issuecomment-685235106
|
|
51
|
+
// Keys are the values passed to race.
|
|
52
|
+
// Values are a record of data containing a set of deferreds and whether the
|
|
53
|
+
// value has settled.
|
|
54
|
+
const wm = new WeakMap();
|
|
55
|
+
function safeRace(contenders) {
|
|
56
|
+
let deferred;
|
|
57
|
+
const result = new Promise((resolve, reject) => {
|
|
58
|
+
deferred = { resolve, reject };
|
|
59
|
+
for (const contender of contenders) {
|
|
60
|
+
if (!isPromiseLike(contender)) {
|
|
61
|
+
// If the contender is a not a then-able, attempting to use it as a key
|
|
62
|
+
// in the weakmap would throw an error. Luckily, it is safe to call
|
|
63
|
+
// `Promise.resolve(contender).then` on regular values multiple
|
|
64
|
+
// times because the promise fulfills immediately.
|
|
65
|
+
Promise.resolve(contender).then(resolve, reject);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
let record = wm.get(contender);
|
|
69
|
+
if (record === undefined) {
|
|
70
|
+
record = createRaceRecord(contender);
|
|
71
|
+
record.deferreds.add(deferred);
|
|
72
|
+
wm.set(contender, record);
|
|
73
|
+
}
|
|
74
|
+
else if (record.settled) {
|
|
75
|
+
// If the value has settled, it is safe to call
|
|
76
|
+
// `Promise.resolve(contender).then` on it.
|
|
77
|
+
Promise.resolve(contender).then(resolve, reject);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
record.deferreds.add(deferred);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
// The finally callback executes when any value settles, preventing any of
|
|
85
|
+
// the unresolved values from retaining a reference to the resolved value.
|
|
86
|
+
return result.finally(() => {
|
|
87
|
+
for (const contender of contenders) {
|
|
88
|
+
if (isPromiseLike(contender)) {
|
|
89
|
+
const record = wm.get(contender);
|
|
90
|
+
if (record) {
|
|
91
|
+
record.deferreds.delete(deferred);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export { arrayify, isIteratorLike, isPromiseLike, safeRace, unwrap, wrap };
|
|
99
|
+
//# sourceMappingURL=_utils.js.map
|
package/_utils.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_utils.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
package/async.cjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var crank = require('./crank.cjs');
|
|
4
|
-
require('./event-target.cjs');
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Creates a lazy-loaded component from an initializer function.
|
|
@@ -37,16 +36,12 @@ async function SuspenseEmpty() {
|
|
|
37
36
|
return null;
|
|
38
37
|
}
|
|
39
38
|
async function SuspenseFallback({ children, timeout, schedule, }) {
|
|
40
|
-
|
|
41
|
-
this.schedule(schedule);
|
|
42
|
-
}
|
|
39
|
+
this.schedule(schedule);
|
|
43
40
|
await new Promise((resolve) => setTimeout(resolve, timeout));
|
|
44
41
|
return children;
|
|
45
42
|
}
|
|
46
43
|
function SuspenseChildren({ children, schedule, }) {
|
|
47
|
-
|
|
48
|
-
this.schedule(schedule);
|
|
49
|
-
}
|
|
44
|
+
this.schedule(schedule);
|
|
50
45
|
return children;
|
|
51
46
|
}
|
|
52
47
|
/**
|
|
@@ -69,11 +64,7 @@ function SuspenseChildren({ children, schedule, }) {
|
|
|
69
64
|
*/
|
|
70
65
|
async function* Suspense({ children, fallback, timeout, }) {
|
|
71
66
|
const controller = this.consume(SuspenseListController);
|
|
72
|
-
if (controller) {
|
|
73
|
-
controller.register(this);
|
|
74
|
-
}
|
|
75
67
|
this.provide(SuspenseListController, undefined);
|
|
76
|
-
let initial = true;
|
|
77
68
|
for await ({ children, fallback, timeout } of this) {
|
|
78
69
|
if (timeout == null) {
|
|
79
70
|
if (controller) {
|
|
@@ -87,35 +78,32 @@ async function* Suspense({ children, fallback, timeout, }) {
|
|
|
87
78
|
yield crank.createElement(SuspenseFallback, {
|
|
88
79
|
timeout: timeout,
|
|
89
80
|
children: fallback,
|
|
81
|
+
schedule: () => { },
|
|
90
82
|
});
|
|
91
83
|
yield children;
|
|
92
84
|
continue;
|
|
93
85
|
}
|
|
86
|
+
const items = await controller.register(this);
|
|
94
87
|
if (controller.revealOrder !== "together") {
|
|
95
|
-
if (!controller.isHead(this)) {
|
|
88
|
+
if (!controller.isHead(this, items)) {
|
|
96
89
|
yield crank.createElement(SuspenseEmpty);
|
|
97
90
|
}
|
|
98
91
|
if (controller.tail !== "hidden") {
|
|
99
92
|
yield crank.createElement(SuspenseFallback, {
|
|
100
93
|
timeout: timeout,
|
|
101
|
-
schedule: initial
|
|
102
|
-
? () => controller.scheduleFallback(this)
|
|
103
|
-
: undefined,
|
|
104
94
|
children: fallback,
|
|
95
|
+
schedule: () => controller.scheduleFallback(this, items),
|
|
105
96
|
});
|
|
106
97
|
}
|
|
107
98
|
}
|
|
108
99
|
yield crank.createElement(SuspenseChildren, {
|
|
109
|
-
schedule: initial ? () => controller.scheduleChildren(this) : undefined,
|
|
110
100
|
children,
|
|
101
|
+
schedule: () => controller.scheduleChildren(this, items),
|
|
111
102
|
});
|
|
112
|
-
initial = false;
|
|
113
103
|
}
|
|
114
104
|
}
|
|
115
105
|
const SuspenseListController = Symbol.for("SuspenseListController");
|
|
116
106
|
/**
|
|
117
|
-
* Coordinates the reveal order of multiple <Suspense> children.
|
|
118
|
-
*
|
|
119
107
|
* Controls when child <Suspense> components show their content or fallbacks
|
|
120
108
|
* based on the specified reveal order. The <SuspenseList> resolves when
|
|
121
109
|
* coordination effort is complete (not necessarily when all content is
|
|
@@ -129,7 +117,7 @@ const SuspenseListController = Symbol.for("SuspenseListController");
|
|
|
129
117
|
* In Crank, the default behavior of async components is to render together,
|
|
130
118
|
* so "together" might not be necessary if you are not using <Suspense>
|
|
131
119
|
* fallbacks.
|
|
132
|
-
*
|
|
120
|
+
*e@param tail - How to handle fallbacks:
|
|
133
121
|
* - "collapsed" (default): Show only the fallback for the next unresolved
|
|
134
122
|
* Suspense component
|
|
135
123
|
* - "hidden": Hide all fallbacks
|
|
@@ -152,25 +140,30 @@ const SuspenseListController = Symbol.for("SuspenseListController");
|
|
|
152
140
|
* ```
|
|
153
141
|
*/
|
|
154
142
|
function* SuspenseList({ revealOrder = "forwards", tail = "collapsed", timeout, children, }) {
|
|
155
|
-
let
|
|
156
|
-
|
|
143
|
+
let finishRegistration;
|
|
144
|
+
let registering = null;
|
|
145
|
+
let items = [];
|
|
157
146
|
const controller = {
|
|
158
147
|
timeout,
|
|
159
148
|
revealOrder,
|
|
160
149
|
tail,
|
|
161
|
-
register(ctx) {
|
|
150
|
+
async register(ctx) {
|
|
162
151
|
if (registering) {
|
|
163
152
|
let childrenResolver;
|
|
164
153
|
const childrenPromise = new Promise((r) => (childrenResolver = r));
|
|
165
|
-
|
|
154
|
+
items.push({
|
|
166
155
|
ctx,
|
|
167
|
-
|
|
168
|
-
childrenPromise,
|
|
156
|
+
resolve: childrenResolver,
|
|
157
|
+
promise: childrenPromise,
|
|
169
158
|
});
|
|
170
|
-
|
|
159
|
+
// Wait for registration to complete
|
|
160
|
+
await registering;
|
|
161
|
+
return items;
|
|
171
162
|
}
|
|
163
|
+
console.error("Component registered outside SuspenseList registration window");
|
|
164
|
+
return [];
|
|
172
165
|
},
|
|
173
|
-
isHead(ctx) {
|
|
166
|
+
isHead(ctx, suspenseItems) {
|
|
174
167
|
const index = suspenseItems.findIndex((item) => item.ctx === ctx);
|
|
175
168
|
if (index === -1) {
|
|
176
169
|
return false;
|
|
@@ -183,34 +176,40 @@ function* SuspenseList({ revealOrder = "forwards", tail = "collapsed", timeout,
|
|
|
183
176
|
}
|
|
184
177
|
return false;
|
|
185
178
|
},
|
|
186
|
-
async scheduleFallback(ctx) {
|
|
179
|
+
async scheduleFallback(ctx, suspenseItems) {
|
|
187
180
|
const index = suspenseItems.findIndex((item) => item.ctx === ctx);
|
|
188
181
|
if (index === -1) {
|
|
189
182
|
return;
|
|
190
183
|
}
|
|
191
184
|
else if (revealOrder === "forwards") {
|
|
192
|
-
await Promise.all(suspenseItems.slice(0, index).map((item) => item.
|
|
185
|
+
await Promise.all(suspenseItems.slice(0, index).map((item) => item.promise));
|
|
193
186
|
}
|
|
194
187
|
else if (revealOrder === "backwards") {
|
|
195
|
-
await Promise.all(suspenseItems.slice(index + 1).map((item) => item.
|
|
188
|
+
await Promise.all(suspenseItems.slice(index + 1).map((item) => item.promise));
|
|
196
189
|
}
|
|
197
190
|
},
|
|
198
|
-
async scheduleChildren(ctx) {
|
|
191
|
+
async scheduleChildren(ctx, suspenseItems) {
|
|
199
192
|
const index = suspenseItems.findIndex((item) => item.ctx === ctx);
|
|
200
193
|
if (index === -1) {
|
|
201
194
|
return;
|
|
202
195
|
}
|
|
203
196
|
// This children content is ready
|
|
204
|
-
suspenseItems[index].
|
|
197
|
+
suspenseItems[index].resolve();
|
|
205
198
|
// Children coordination - determine when this content should show
|
|
206
199
|
if (revealOrder === "together") {
|
|
207
|
-
await Promise.all(suspenseItems.map((item) => item.
|
|
200
|
+
await Promise.all(suspenseItems.map((item) => item.promise));
|
|
208
201
|
}
|
|
209
202
|
else if (revealOrder === "forwards") {
|
|
210
|
-
|
|
203
|
+
const waitFor = suspenseItems
|
|
204
|
+
.slice(0, index + 1)
|
|
205
|
+
.map((item) => item.promise);
|
|
206
|
+
await Promise.all(waitFor);
|
|
211
207
|
}
|
|
212
208
|
else if (revealOrder === "backwards") {
|
|
213
|
-
|
|
209
|
+
const waitFor = suspenseItems
|
|
210
|
+
.slice(index + 1)
|
|
211
|
+
.map((item) => item.promise);
|
|
212
|
+
await Promise.all(waitFor);
|
|
214
213
|
}
|
|
215
214
|
},
|
|
216
215
|
};
|
|
@@ -221,12 +220,16 @@ function* SuspenseList({ revealOrder = "forwards", tail = "collapsed", timeout,
|
|
|
221
220
|
timeout,
|
|
222
221
|
children,
|
|
223
222
|
} of this) {
|
|
224
|
-
|
|
225
|
-
// TODO: Is there a fixed amount of microtasks that we can wait for?
|
|
226
|
-
setTimeout(() => (registering = false));
|
|
223
|
+
items = [];
|
|
227
224
|
controller.timeout = timeout;
|
|
228
225
|
controller.revealOrder = revealOrder;
|
|
229
226
|
controller.tail = tail;
|
|
227
|
+
registering = new Promise((r) => (finishRegistration = r));
|
|
228
|
+
// TODO: Is there a more precise timing for the registration window
|
|
229
|
+
setTimeout(() => {
|
|
230
|
+
finishRegistration();
|
|
231
|
+
registering = null;
|
|
232
|
+
});
|
|
230
233
|
yield children;
|
|
231
234
|
}
|
|
232
235
|
}
|