@b9g/crank 0.7.1 → 0.7.3
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 +540 -84
- package/_css.js.map +1 -1
- package/_utils.js.map +1 -1
- package/async.cjs +42 -38
- package/async.cjs.map +1 -1
- package/async.d.ts +10 -7
- package/async.js +42 -38
- package/async.js.map +1 -1
- package/crank.cjs +50 -8
- package/crank.cjs.map +1 -1
- package/crank.js +50 -8
- package/crank.js.map +1 -1
- package/dom.cjs +16 -1
- package/dom.cjs.map +1 -1
- package/dom.js +16 -1
- package/dom.js.map +1 -1
- package/package.json +2 -1
- package/umd.js +66 -9
- package/umd.js.map +1 -1
package/umd.js
CHANGED
|
@@ -510,6 +510,7 @@
|
|
|
510
510
|
const IsInForAwaitOfLoop = 1 << 14;
|
|
511
511
|
const NeedsToYield = 1 << 15;
|
|
512
512
|
const PropsAvailable = 1 << 16;
|
|
513
|
+
const IsSchedulingRefresh = 1 << 17;
|
|
513
514
|
function getFlag(ret, flag) {
|
|
514
515
|
return !!(ret.f & flag);
|
|
515
516
|
}
|
|
@@ -863,10 +864,10 @@
|
|
|
863
864
|
}
|
|
864
865
|
}
|
|
865
866
|
else if (ret) {
|
|
867
|
+
let candidateFound = false;
|
|
866
868
|
// we do not need to add the retainer to the graveyard if it is the
|
|
867
869
|
// fallback of another retainer
|
|
868
870
|
// search for the tag in fallback chain
|
|
869
|
-
let candidateFound = false;
|
|
870
871
|
for (let predecessor = ret, candidate = ret.fallback; candidate; predecessor = candidate, candidate = candidate.fallback) {
|
|
871
872
|
if (candidate.el.tag === child.tag) {
|
|
872
873
|
// If we find a retainer in the fallback chain with the same tag,
|
|
@@ -1701,6 +1702,9 @@
|
|
|
1701
1702
|
});
|
|
1702
1703
|
}
|
|
1703
1704
|
}
|
|
1705
|
+
if (getFlag(ctx.ret, IsScheduling)) {
|
|
1706
|
+
setFlag(ctx.ret, IsSchedulingRefresh);
|
|
1707
|
+
}
|
|
1704
1708
|
let diff;
|
|
1705
1709
|
const schedulePromises = [];
|
|
1706
1710
|
try {
|
|
@@ -2046,10 +2050,11 @@
|
|
|
2046
2050
|
if (getFlag(ctx.ret, IsInForOfLoop) &&
|
|
2047
2051
|
!getFlag(ctx.ret, NeedsToYield) &&
|
|
2048
2052
|
!getFlag(ctx.ret, IsUnmounted) &&
|
|
2049
|
-
!getFlag(ctx.ret,
|
|
2053
|
+
!getFlag(ctx.ret, IsSchedulingRefresh)) {
|
|
2050
2054
|
console.error(`Component <${getTagName(ctx.ret.el.tag)}> yielded/returned more than once in for...of loop`);
|
|
2051
2055
|
}
|
|
2052
2056
|
setFlag(ctx.ret, NeedsToYield, false);
|
|
2057
|
+
setFlag(ctx.ret, IsSchedulingRefresh, false);
|
|
2053
2058
|
if (iteration.done) {
|
|
2054
2059
|
setFlag(ctx.ret, IsSyncGen, false);
|
|
2055
2060
|
ctx.iterator = undefined;
|
|
@@ -2095,11 +2100,12 @@
|
|
|
2095
2100
|
if (getFlag(ctx.ret, IsInForOfLoop) &&
|
|
2096
2101
|
!getFlag(ctx.ret, NeedsToYield) &&
|
|
2097
2102
|
!getFlag(ctx.ret, IsUnmounted) &&
|
|
2098
|
-
!getFlag(ctx.ret,
|
|
2103
|
+
!getFlag(ctx.ret, IsSchedulingRefresh)) {
|
|
2099
2104
|
console.error(`Component <${getTagName(ctx.ret.el.tag)}> yielded/returned more than once in for...of loop`);
|
|
2100
2105
|
}
|
|
2101
2106
|
}
|
|
2102
2107
|
setFlag(ctx.ret, NeedsToYield, false);
|
|
2108
|
+
setFlag(ctx.ret, IsSchedulingRefresh, false);
|
|
2103
2109
|
if (iteration.done) {
|
|
2104
2110
|
setFlag(ctx.ret, IsAsyncGen, false);
|
|
2105
2111
|
ctx.iterator = undefined;
|
|
@@ -2331,18 +2337,17 @@
|
|
|
2331
2337
|
});
|
|
2332
2338
|
return getValue(ctx.ret);
|
|
2333
2339
|
}
|
|
2334
|
-
const wasScheduling = getFlag(ctx.ret, IsScheduling);
|
|
2335
2340
|
const values = commitChildren(ctx.adapter, ctx.host, ctx, ctx.scope, ctx.ret, ctx.index, schedulePromises, hydrationNodes);
|
|
2336
2341
|
if (getFlag(ctx.ret, IsUnmounted)) {
|
|
2337
2342
|
return;
|
|
2338
2343
|
}
|
|
2339
2344
|
addEventTargetDelegates(ctx.ctx, values);
|
|
2340
2345
|
// Execute schedule callbacks early to check for async deferral
|
|
2341
|
-
const
|
|
2346
|
+
const wasScheduling = getFlag(ctx.ret, IsScheduling);
|
|
2342
2347
|
let schedulePromises1;
|
|
2348
|
+
const callbacks = scheduleMap.get(ctx);
|
|
2343
2349
|
if (callbacks) {
|
|
2344
2350
|
scheduleMap.delete(ctx);
|
|
2345
|
-
// TODO: think about error handling for schedule callbacks
|
|
2346
2351
|
setFlag(ctx.ret, IsScheduling);
|
|
2347
2352
|
const result = ctx.adapter.read(unwrap(values));
|
|
2348
2353
|
for (const callback of callbacks) {
|
|
@@ -2353,7 +2358,7 @@
|
|
|
2353
2358
|
}
|
|
2354
2359
|
if (schedulePromises1 && !getFlag(ctx.ret, DidCommit)) {
|
|
2355
2360
|
const scheduleCallbacksP = Promise.all(schedulePromises1).then(() => {
|
|
2356
|
-
setFlag(ctx.ret, IsScheduling,
|
|
2361
|
+
setFlag(ctx.ret, IsScheduling, wasScheduling);
|
|
2357
2362
|
propagateComponent(ctx);
|
|
2358
2363
|
if (ctx.ret.fallback) {
|
|
2359
2364
|
unmount(ctx.adapter, ctx.host, ctx.parent, ctx.ret.fallback, false);
|
|
@@ -2393,6 +2398,37 @@
|
|
|
2393
2398
|
// if schedule callbacks call refresh() or async mounting is happening.
|
|
2394
2399
|
return getValue(ctx.ret, true);
|
|
2395
2400
|
}
|
|
2401
|
+
/**
|
|
2402
|
+
* Checks if a target retainer is active (contributing) in the host's retainer tree.
|
|
2403
|
+
* Performs a downward traversal from host to find if target is in the active path.
|
|
2404
|
+
*/
|
|
2405
|
+
function isRetainerActive(target, host) {
|
|
2406
|
+
const stack = [host];
|
|
2407
|
+
while (stack.length > 0) {
|
|
2408
|
+
const current = stack.pop();
|
|
2409
|
+
if (current === target) {
|
|
2410
|
+
return true;
|
|
2411
|
+
}
|
|
2412
|
+
// Add direct children to stack (skip if this is a host boundary)
|
|
2413
|
+
// Host boundaries are: DOM elements (string tags) or Portal, but NOT Fragment
|
|
2414
|
+
const isHostBoundary = current !== host &&
|
|
2415
|
+
((typeof current.el.tag === "string" && current.el.tag !== Fragment) ||
|
|
2416
|
+
current.el.tag === Portal);
|
|
2417
|
+
if (current.children && !isHostBoundary) {
|
|
2418
|
+
const children = wrap(current.children);
|
|
2419
|
+
for (const child of children) {
|
|
2420
|
+
if (child) {
|
|
2421
|
+
stack.push(child);
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
// Add fallback chains (only if current retainer is using fallback)
|
|
2426
|
+
if (current.fallback && !getFlag(current, DidDiff)) {
|
|
2427
|
+
stack.push(current.fallback);
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2430
|
+
return false;
|
|
2431
|
+
}
|
|
2396
2432
|
/**
|
|
2397
2433
|
* Propagates component changes up to ancestors when rendering starts from a
|
|
2398
2434
|
* component via refresh() or multiple for await...of renders. This handles
|
|
@@ -2403,14 +2439,20 @@
|
|
|
2403
2439
|
const values = getChildValues(ctx.ret, ctx.index);
|
|
2404
2440
|
addEventTargetDelegates(ctx.ctx, values, (ctx1) => ctx1[_ContextState].host === ctx.host);
|
|
2405
2441
|
const host = ctx.host;
|
|
2442
|
+
const initiator = ctx.ret;
|
|
2443
|
+
// Check if initiator is active in the host's tree
|
|
2444
|
+
if (!isRetainerActive(initiator, host)) {
|
|
2445
|
+
return;
|
|
2446
|
+
}
|
|
2406
2447
|
const props = stripSpecialProps(host.el.props);
|
|
2448
|
+
const hostChildren = getChildValues(host, 0);
|
|
2407
2449
|
ctx.adapter.arrange({
|
|
2408
2450
|
tag: host.el.tag,
|
|
2409
2451
|
tagName: getTagName(host.el.tag),
|
|
2410
2452
|
node: host.value,
|
|
2411
2453
|
props,
|
|
2412
2454
|
oldProps: props,
|
|
2413
|
-
children:
|
|
2455
|
+
children: hostChildren,
|
|
2414
2456
|
});
|
|
2415
2457
|
flush(ctx.adapter, ctx.root, ctx);
|
|
2416
2458
|
}
|
|
@@ -2981,7 +3023,22 @@
|
|
|
2981
3023
|
!(typeof value === "string" &&
|
|
2982
3024
|
typeof element[name] === "boolean") &&
|
|
2983
3025
|
isWritableProperty(element, name)) {
|
|
2984
|
-
|
|
3026
|
+
// For URL properties like src and href, the DOM property returns the
|
|
3027
|
+
// resolved absolute URL. We need to resolve the prop value the same way
|
|
3028
|
+
// to compare correctly.
|
|
3029
|
+
let domValue = element[name];
|
|
3030
|
+
let propValue = value;
|
|
3031
|
+
if ((name === "src" || name === "href") &&
|
|
3032
|
+
typeof value === "string" &&
|
|
3033
|
+
typeof domValue === "string") {
|
|
3034
|
+
try {
|
|
3035
|
+
propValue = new URL(value, element.baseURI).href;
|
|
3036
|
+
}
|
|
3037
|
+
catch {
|
|
3038
|
+
// Invalid URL, use original value for comparison
|
|
3039
|
+
}
|
|
3040
|
+
}
|
|
3041
|
+
if (propValue !== domValue || oldValue === undefined) {
|
|
2985
3042
|
if (isHydrating &&
|
|
2986
3043
|
typeof element[name] === "string" &&
|
|
2987
3044
|
element[name] !== value) {
|