@bikeshaving/crank
Advanced tools
Comparing version 0.3.1 to 0.3.2
# Changelog | ||
## [0.3.2] - 2020-08-06 | ||
### Changed | ||
- Duplicate keys and calling refresh on an unmounted or already executing component will now log errors to the console. | ||
- Changes to some of the error messages thrown by Crank. | ||
## [0.3.1] - 2020-07-30 | ||
@@ -3,0 +7,0 @@ ### Added |
@@ -6,2 +6,3 @@ 'use strict'; | ||
/*** UTILITIES ***/ | ||
const NOOP = () => { }; | ||
function wrap(value) { | ||
@@ -276,3 +277,3 @@ return value === undefined ? [] : Array.isArray(value) ? value : [value]; | ||
if (portal._ctx !== ctx) { | ||
throw new Error("render must be called with the same context per root"); | ||
throw new Error("Context mismatch"); | ||
} | ||
@@ -450,2 +451,3 @@ portal.props = { children, root }; | ||
let async = false; | ||
let seen; | ||
for (let i = 0; i < newChildren.length; i++) { | ||
@@ -460,2 +462,14 @@ let value; | ||
} | ||
if (typeof child === "object" && typeof child.key !== "undefined") { | ||
if (seen === undefined) { | ||
seen = new Set(); | ||
} | ||
else { | ||
if (seen.has(child.key)) { | ||
// eslint-disable-next-line no-console | ||
console.error("Duplicate key", child.key); | ||
} | ||
} | ||
seen.add(child.key); | ||
} | ||
[child, value] = diff(renderer, root, host, ctx, scope, undefined, // oldChild | ||
@@ -560,3 +574,4 @@ child); | ||
if (seen !== undefined && seen.has(newKey)) { | ||
// TODO: warn about a duplicate key | ||
// eslint-disable-next-line no-console | ||
console.error("Duplicate key", newKey); | ||
newKey = undefined; | ||
@@ -655,3 +670,3 @@ } | ||
if (isPromiseLike(value)) { | ||
value.then(newChild.ref).catch(() => { }); | ||
value.then(newChild.ref).catch(NOOP); | ||
} | ||
@@ -677,3 +692,3 @@ else { | ||
}) | ||
.catch(() => { }); | ||
.catch(NOOP); | ||
} | ||
@@ -849,5 +864,5 @@ } | ||
/** | ||
* A flag which is set when the component is called or stepped through. It is used to ensure that a component which synchronously triggers a second update in the course of rendering does not cause an infinite loop or a generator error. | ||
* A flag which is set when the component function is called or the component generator is resumed. This flags is used to ensure that a component which synchronously triggers a second update in the course of rendering does not cause an stack overflow or a generator error. | ||
*/ | ||
const Stepping = 1 << 1; | ||
const Executing = 1 << 1; | ||
/** | ||
@@ -872,7 +887,7 @@ * A flag used to make sure multiple values are not pulled from context prop iterators without a yield. | ||
*/ | ||
const SyncGen = 1 << 6; | ||
const IsSyncGen = 1 << 6; | ||
/** | ||
* A flag which indicates that the component is an async generator component. | ||
*/ | ||
const AsyncGen = 1 << 7; | ||
const IsAsyncGen = 1 << 7; | ||
/** | ||
@@ -931,6 +946,6 @@ * A class which is instantiated and passed to every component as its this value. Contexts form a tree just like elements and all components in the element tree are connected via contexts. Components can use this tree to communicate data upwards via events and downwards via provisions. | ||
if (this._f & Iterating) { | ||
throw new Error("You must yield for each iteration of this."); | ||
throw new Error("Context iterated twice without a yield"); | ||
} | ||
else if (this._f & AsyncGen) { | ||
throw new Error("Use for await...of in async generator components."); | ||
else if (this._f & IsAsyncGen) { | ||
throw new Error("Use for await…of in async generator components"); | ||
} | ||
@@ -945,6 +960,6 @@ this._f |= Iterating; | ||
if (this._f & Iterating) { | ||
throw new Error("You must yield for each iteration of this."); | ||
throw new Error("Context iterated twice without a yield"); | ||
} | ||
else if (this._f & SyncGen) { | ||
throw new Error("Use for...of in sync generator components."); | ||
else if (this._f & IsSyncGen) { | ||
throw new Error("Use for…of in sync generator components"); | ||
} | ||
@@ -973,6 +988,12 @@ this._f |= Iterating; | ||
refresh() { | ||
if (this._f & (Stepping | Unmounted)) { | ||
// TODO: log an error | ||
if (this._f & Unmounted) { | ||
// eslint-disable-next-line no-console | ||
console.error("Component is unmounted"); | ||
return this._re.read(undefined); | ||
} | ||
else if (this._f & Executing) { | ||
// eslint-disable-next-line no-console | ||
console.error("Component is already executing"); | ||
return this._re.read(undefined); | ||
} | ||
this._f &= ~Updating; | ||
@@ -1196,3 +1217,3 @@ resume(this); | ||
} | ||
else if (ctx._f & AsyncGen) { | ||
else if (ctx._f & IsAsyncGen) { | ||
return ctx._iv; | ||
@@ -1234,3 +1255,3 @@ } | ||
* block - A possible promise which represents the duration during which the component is blocked from updating. | ||
* value - The actual rendered value of the children. | ||
* value - A possible promise resolving to the rendered value of children. | ||
* | ||
@@ -1250,3 +1271,3 @@ * @remarks | ||
try { | ||
ctx._f |= Stepping; | ||
ctx._f |= Executing; | ||
if (typeof ctx._it === "undefined") { | ||
@@ -1273,3 +1294,3 @@ initial = true; | ||
finally { | ||
ctx._f &= ~Stepping; | ||
ctx._f &= ~Executing; | ||
} | ||
@@ -1288,3 +1309,3 @@ let oldValue; | ||
try { | ||
ctx._f |= Stepping; | ||
ctx._f |= Executing; | ||
iteration = ctx._it.next(oldValue); | ||
@@ -1297,3 +1318,3 @@ } | ||
finally { | ||
ctx._f &= ~Stepping; | ||
ctx._f &= ~Executing; | ||
} | ||
@@ -1303,3 +1324,3 @@ if (isPromiseLike(iteration)) { | ||
if (initial) { | ||
ctx._f |= AsyncGen; | ||
ctx._f |= IsAsyncGen; | ||
} | ||
@@ -1333,3 +1354,3 @@ const block = iteration; | ||
if (initial) { | ||
ctx._f |= SyncGen; | ||
ctx._f |= IsSyncGen; | ||
} | ||
@@ -1351,3 +1372,3 @@ ctx._f &= ~Iterating; | ||
if (isPromiseLike(value)) { | ||
return [value.catch(() => { }), value]; | ||
return [value.catch(NOOP), value]; | ||
} | ||
@@ -1369,3 +1390,3 @@ return [undefined, value]; | ||
ctx._ev = undefined; | ||
if (ctx._f & AsyncGen && !(ctx._f & Finished)) { | ||
if (ctx._f & IsAsyncGen && !(ctx._f & Finished)) { | ||
run(ctx); | ||
@@ -1384,3 +1405,3 @@ } | ||
try { | ||
ctx._f |= Stepping; | ||
ctx._f |= Executing; | ||
iteration = ctx._it.throw(err); | ||
@@ -1393,3 +1414,3 @@ } | ||
finally { | ||
ctx._f &= ~Stepping; | ||
ctx._f &= ~Executing; | ||
} | ||
@@ -1507,7 +1528,7 @@ if (isPromiseLike(iteration)) { | ||
try { | ||
ctx._f |= Stepping; | ||
ctx._f |= Executing; | ||
iteration = ctx._it.return(); | ||
} | ||
finally { | ||
ctx._f &= ~Stepping; | ||
ctx._f &= ~Executing; | ||
} | ||
@@ -1514,0 +1535,0 @@ if (isPromiseLike(iteration)) { |
@@ -11,3 +11,3 @@ 'use strict'; | ||
if (!(root instanceof Node)) { | ||
throw new TypeError("root is not a node"); | ||
throw new TypeError(`root (${root && root.toString()}) is not a node`); | ||
} | ||
@@ -14,0 +14,0 @@ return super.render(children, root, ctx); |
81
crank.js
/// <reference types="./crank.d.ts" /> | ||
/*** UTILITIES ***/ | ||
const NOOP = () => { }; | ||
function wrap(value) { | ||
@@ -272,3 +273,3 @@ return value === undefined ? [] : Array.isArray(value) ? value : [value]; | ||
if (portal._ctx !== ctx) { | ||
throw new Error("render must be called with the same context per root"); | ||
throw new Error("Context mismatch"); | ||
} | ||
@@ -446,2 +447,3 @@ portal.props = { children, root }; | ||
let async = false; | ||
let seen; | ||
for (let i = 0; i < newChildren.length; i++) { | ||
@@ -456,2 +458,14 @@ let value; | ||
} | ||
if (typeof child === "object" && typeof child.key !== "undefined") { | ||
if (seen === undefined) { | ||
seen = new Set(); | ||
} | ||
else { | ||
if (seen.has(child.key)) { | ||
// eslint-disable-next-line no-console | ||
console.error("Duplicate key", child.key); | ||
} | ||
} | ||
seen.add(child.key); | ||
} | ||
[child, value] = diff(renderer, root, host, ctx, scope, undefined, // oldChild | ||
@@ -556,3 +570,4 @@ child); | ||
if (seen !== undefined && seen.has(newKey)) { | ||
// TODO: warn about a duplicate key | ||
// eslint-disable-next-line no-console | ||
console.error("Duplicate key", newKey); | ||
newKey = undefined; | ||
@@ -651,3 +666,3 @@ } | ||
if (isPromiseLike(value)) { | ||
value.then(newChild.ref).catch(() => { }); | ||
value.then(newChild.ref).catch(NOOP); | ||
} | ||
@@ -673,3 +688,3 @@ else { | ||
}) | ||
.catch(() => { }); | ||
.catch(NOOP); | ||
} | ||
@@ -845,5 +860,5 @@ } | ||
/** | ||
* A flag which is set when the component is called or stepped through. It is used to ensure that a component which synchronously triggers a second update in the course of rendering does not cause an infinite loop or a generator error. | ||
* A flag which is set when the component function is called or the component generator is resumed. This flags is used to ensure that a component which synchronously triggers a second update in the course of rendering does not cause an stack overflow or a generator error. | ||
*/ | ||
const Stepping = 1 << 1; | ||
const Executing = 1 << 1; | ||
/** | ||
@@ -868,7 +883,7 @@ * A flag used to make sure multiple values are not pulled from context prop iterators without a yield. | ||
*/ | ||
const SyncGen = 1 << 6; | ||
const IsSyncGen = 1 << 6; | ||
/** | ||
* A flag which indicates that the component is an async generator component. | ||
*/ | ||
const AsyncGen = 1 << 7; | ||
const IsAsyncGen = 1 << 7; | ||
/** | ||
@@ -927,6 +942,6 @@ * A class which is instantiated and passed to every component as its this value. Contexts form a tree just like elements and all components in the element tree are connected via contexts. Components can use this tree to communicate data upwards via events and downwards via provisions. | ||
if (this._f & Iterating) { | ||
throw new Error("You must yield for each iteration of this."); | ||
throw new Error("Context iterated twice without a yield"); | ||
} | ||
else if (this._f & AsyncGen) { | ||
throw new Error("Use for await...of in async generator components."); | ||
else if (this._f & IsAsyncGen) { | ||
throw new Error("Use for await…of in async generator components"); | ||
} | ||
@@ -941,6 +956,6 @@ this._f |= Iterating; | ||
if (this._f & Iterating) { | ||
throw new Error("You must yield for each iteration of this."); | ||
throw new Error("Context iterated twice without a yield"); | ||
} | ||
else if (this._f & SyncGen) { | ||
throw new Error("Use for...of in sync generator components."); | ||
else if (this._f & IsSyncGen) { | ||
throw new Error("Use for…of in sync generator components"); | ||
} | ||
@@ -969,6 +984,12 @@ this._f |= Iterating; | ||
refresh() { | ||
if (this._f & (Stepping | Unmounted)) { | ||
// TODO: log an error | ||
if (this._f & Unmounted) { | ||
// eslint-disable-next-line no-console | ||
console.error("Component is unmounted"); | ||
return this._re.read(undefined); | ||
} | ||
else if (this._f & Executing) { | ||
// eslint-disable-next-line no-console | ||
console.error("Component is already executing"); | ||
return this._re.read(undefined); | ||
} | ||
this._f &= ~Updating; | ||
@@ -1192,3 +1213,3 @@ resume(this); | ||
} | ||
else if (ctx._f & AsyncGen) { | ||
else if (ctx._f & IsAsyncGen) { | ||
return ctx._iv; | ||
@@ -1230,3 +1251,3 @@ } | ||
* block - A possible promise which represents the duration during which the component is blocked from updating. | ||
* value - The actual rendered value of the children. | ||
* value - A possible promise resolving to the rendered value of children. | ||
* | ||
@@ -1246,3 +1267,3 @@ * @remarks | ||
try { | ||
ctx._f |= Stepping; | ||
ctx._f |= Executing; | ||
if (typeof ctx._it === "undefined") { | ||
@@ -1269,3 +1290,3 @@ initial = true; | ||
finally { | ||
ctx._f &= ~Stepping; | ||
ctx._f &= ~Executing; | ||
} | ||
@@ -1284,3 +1305,3 @@ let oldValue; | ||
try { | ||
ctx._f |= Stepping; | ||
ctx._f |= Executing; | ||
iteration = ctx._it.next(oldValue); | ||
@@ -1293,3 +1314,3 @@ } | ||
finally { | ||
ctx._f &= ~Stepping; | ||
ctx._f &= ~Executing; | ||
} | ||
@@ -1299,3 +1320,3 @@ if (isPromiseLike(iteration)) { | ||
if (initial) { | ||
ctx._f |= AsyncGen; | ||
ctx._f |= IsAsyncGen; | ||
} | ||
@@ -1329,3 +1350,3 @@ const block = iteration; | ||
if (initial) { | ||
ctx._f |= SyncGen; | ||
ctx._f |= IsSyncGen; | ||
} | ||
@@ -1347,3 +1368,3 @@ ctx._f &= ~Iterating; | ||
if (isPromiseLike(value)) { | ||
return [value.catch(() => { }), value]; | ||
return [value.catch(NOOP), value]; | ||
} | ||
@@ -1365,3 +1386,3 @@ return [undefined, value]; | ||
ctx._ev = undefined; | ||
if (ctx._f & AsyncGen && !(ctx._f & Finished)) { | ||
if (ctx._f & IsAsyncGen && !(ctx._f & Finished)) { | ||
run(ctx); | ||
@@ -1380,3 +1401,3 @@ } | ||
try { | ||
ctx._f |= Stepping; | ||
ctx._f |= Executing; | ||
iteration = ctx._it.throw(err); | ||
@@ -1389,3 +1410,3 @@ } | ||
finally { | ||
ctx._f &= ~Stepping; | ||
ctx._f &= ~Executing; | ||
} | ||
@@ -1503,7 +1524,7 @@ if (isPromiseLike(iteration)) { | ||
try { | ||
ctx._f |= Stepping; | ||
ctx._f |= Executing; | ||
iteration = ctx._it.return(); | ||
} | ||
finally { | ||
ctx._f &= ~Stepping; | ||
ctx._f &= ~Executing; | ||
} | ||
@@ -1510,0 +1531,0 @@ if (isPromiseLike(iteration)) { |
@@ -8,3 +8,3 @@ /// <reference types="./dom.d.ts" /> | ||
if (!(root instanceof Node)) { | ||
throw new TypeError("root is not a node"); | ||
throw new TypeError(`root (${root && root.toString()}) is not a node`); | ||
} | ||
@@ -11,0 +11,0 @@ return super.render(children, root, ctx); |
{ | ||
"name": "@bikeshaving/crank", | ||
"version": "0.3.1", | ||
"version": "0.3.2", | ||
"description": "Write JSX-driven components with functions, promises and generators.", | ||
@@ -104,3 +104,3 @@ "homepage": "https://crank.js.org", | ||
"build": "rollup -c rollup.config.js", | ||
"clean": "shx rm -rf cjs dist umd index.js dom.js html.js *.d.ts *.js.map", | ||
"clean": "shx rm -rf cjs dist umd index.js crank.js dom.js html.js *.d.ts *.js.map", | ||
"debug": "node --inspect-brk node_modules/.bin/jest --runInBand", | ||
@@ -107,0 +107,0 @@ "lint": "eslint --ext .js,.jsx,.ts,.tsx .", |
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
297325
7216