Compare commits

..

3 Commits

Author SHA1 Message Date
Géry Debongnie 7ad08a6825 [TEST] experiment qith queuemicrotask 2023-01-11 14:52:35 +01:00
Samuel Degueldre 3d40533de1 [FIX] t-call-context: fix capture making component available in ctx
Previously, when using a component with a slot within a t-call with
t-call-context, the component would become available again inside the
slot despite the t-call-context. This was caused by the fact that the
capture helper function creates an object with the component as its
prototype which is incorrect. It should just use the previous context as
its prototype.
2023-01-09 09:28:10 +01:00
Samuel Degueldre 39329f80b2 [FIX] reactivity: don't subscribe to keys when making reactive
When attempting to create a reactive object, we first check if the
target can be made reactive, this is done with Object.toString, which
internally reads the Symbol.toStringTag on the underlying object. When
trying to make a reactive object from another, for example when
reobserving a reactive or when reading a reactive object from the
context of another, this would read the subscribe the original object to
the Symbol.toStringTag property.

This commit fixes that by calling Object.toString on the underlying
target object where applicable.
2022-12-08 14:22:39 +01:00
16 changed files with 177 additions and 329 deletions
+2 -2
View File
@@ -1140,7 +1140,7 @@ export class CodeGenerator {
if (this.target.loopLevel || !this.hasSafeContext) {
ctxStr = generateId("ctx");
this.helpers.add("capture");
this.define(ctxStr, `capture(ctx, this)`);
this.define(ctxStr, `capture(ctx)`);
}
let slotStr: string[] = [];
for (let slotName in ast.slots) {
@@ -1316,7 +1316,7 @@ export class CodeGenerator {
if (this.target.loopLevel || !this.hasSafeContext) {
ctxStr = generateId("ctx");
this.helpers.add("capture");
this.define(ctxStr, `capture(ctx, this)`);
this.define(ctxStr, `capture(ctx)`);
}
let id = generateId("comp");
this.staticDefs.push({
+1 -1
View File
@@ -25,7 +25,7 @@ export function makeRootFiber(node: ComponentNode): Fiber {
current.children = [];
current.childrenMap = {};
current.bdom = null;
if (fibersInError.has(root)) {
if (fibersInError.has(current)) {
fibersInError.delete(current);
fibersInError.delete(root);
current.appliedToDom = false;
+1 -1
View File
@@ -28,7 +28,7 @@ const COLLECTION_RAWTYPES = new Set(["Set", "Map", "WeakMap"]);
* @returns the raw type of the object
*/
function rawType(obj: any) {
return objectToString.call(obj).slice(8, -1);
return objectToString.call(toRaw(obj)).slice(8, -1);
}
/**
* Checks whether a given value can be made into a reactive object.
+3 -3
View File
@@ -46,8 +46,8 @@ function callSlot(
return slotBDom || text("");
}
function capture(ctx: any, component: any): any {
const result = ObjectCreate(component);
function capture(ctx: any): any {
const result = ObjectCreate(ctx);
for (let k in ctx) {
result[k] = ctx[k];
}
@@ -114,7 +114,7 @@ class LazyValue {
constructor(fn: any, ctx: any, component: any, node: any, key: any) {
this.fn = fn;
this.ctx = capture(ctx, component);
this.ctx = capture(ctx);
this.component = component;
this.node = node;
this.key = key;
+13 -14
View File
@@ -10,20 +10,19 @@ export type Callback = () => void;
*/
export function batched(callback: Callback): Callback {
let called = false;
return async () => {
// This await blocks all calls to the callback here, then releases them sequentially
// in the next microtick. This line decides the granularity of the batch.
await Promise.resolve();
if (!called) {
called = true;
// wait for all calls in this microtick to fall through before resetting "called"
// so that only the first call to the batched function calls the original callback.
// Schedule this before calling the callback so that calls to the batched function
// within the callback will proceed only after resetting called to false, and have
// a chance to execute the callback again
Promise.resolve().then(() => (called = false));
callback();
}
return () => {
queueMicrotask(() => {
if (!called) {
called = true;
// wait for all calls in this microtick to fall through before resetting "called"
// so that only the first call to the batched function calls the original callback.
// Schedule this before calling the callback so that calls to the batched function
// within the callback will proceed only after resetting called to false, and have
// a chance to execute the callback again
queueMicrotask(() => (called = false));
callback();
}
});
};
}
@@ -941,7 +941,7 @@ exports[`can catch errors catching error, rethrow, render parent -- a main comp
ctx[\`cp\`] = v_block1[i1];
const key1 = ctx['cp'].id;
const v1 = ctx['cp'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
c_block1[i1] = withKey(comp2({onError: ()=>this.cleanUp(v1.id),slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2__\${key1}\`, node, this, null), key1);
}
return list(c_block1);
@@ -1019,7 +1019,7 @@ exports[`can catch errors catching in child makes parent render 1`] = `
ctx[\`elem\`] = v_block1[i1];
const key1 = ctx['elem'][0];
const v1 = ctx['elem'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
c_block1[i1] = withKey(comp2({onError: (_error)=>this.onError(v1[0],_error),slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2__\${key1}\`, node, this, null), key1);
}
return list(c_block1);
@@ -1195,131 +1195,6 @@ exports[`can catch errors onError in class inheritance is not called if no rethr
}"
`;
exports[`can catch errors re-render parent when self is in error - 2 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const comp1 = app.createComponent(\`Classic\`, true, false, false, false);
const comp2 = app.createComponent(\`Classic\`, true, false, false, false);
return function template(ctx, node, key = \\"\\") {
const b2 = comp1({hasBoom: true,state: ctx['reactive']}, key + \`__1\`, node, this, null);
const b3 = comp2({hasBoom: false,state: ctx['reactive']}, key + \`__2\`, node, this, null);
return multi([b2, b3]);
}
}"
`;
exports[`can catch errors re-render parent when self is in error - 2 2`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const comp1 = app.createComponent(\`BoomWrapper\`, true, false, false, false);
let block3 = createBlock(\`<div><block-text-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
let b2,b3;
if (ctx['props'].hasBoom) {
b2 = comp1({state: ctx['props'].state}, key + \`__1\`, node, this, null);
} else {
let txt1 = ctx['props'].state.safeTree;
b3 = block3([txt1]);
}
return multi([b2, b3]);
}
}"
`;
exports[`can catch errors re-render parent when self is in error - 2 3`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const comp1 = app.createComponent(\`Boom\`, true, false, false, true);
return function template(ctx, node, key = \\"\\") {
let b2,b3;
if (ctx['state'].errorTree==='error') {
b2 = comp1({}, key + \`__1\`, node, this, null);
} else {
b3 = text(ctx['state'].errorTree);
}
return multi([b2, b3]);
}
}"
`;
exports[`can catch errors re-render parent when self is in error - 2 4`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let block1 = createBlock(\`<div/>\`);
return function template(ctx, node, key = \\"\\") {
return block1();
}
}"
`;
exports[`can catch errors re-render parent when self is in error 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const comp1 = app.createComponent(\`Classic\`, true, false, false, true);
return function template(ctx, node, key = \\"\\") {
return comp1({}, key + \`__1\`, node, this, null);
}
}"
`;
exports[`can catch errors re-render parent when self is in error 2`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { bind } = helpers;
const comp1 = app.createComponent(\`BoomWrapper\`, true, false, false, false);
let block3 = createBlock(\`<div/>\`);
return function template(ctx, node, key = \\"\\") {
let b2,b3;
if (!ctx['inError']) {
b2 = comp1({onError: bind(this, ctx['onErrorAsProps'])}, key + \`__1\`, node, this, null);
} else {
b3 = block3();
}
return multi([b2, b3]);
}
}"
`;
exports[`can catch errors re-render parent when self is in error 3`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const comp1 = app.createComponent(\`Boom\`, true, false, false, true);
return function template(ctx, node, key = \\"\\") {
return comp1({}, key + \`__1\`, node, this, null);
}
}"
`;
exports[`can catch errors re-render parent when self is in error 4`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let block1 = createBlock(\`<div/>\`);
return function template(ctx, node, key = \\"\\") {
return block1();
}
}"
`;
exports[`errors and promises a rendering error in a sub component will reject the mount promise 1`] = `
"function anonymous(app, bdom, helpers
) {
@@ -78,7 +78,7 @@ exports[`refs refs are properly bound in slots 1`] = `
return function template(ctx, node, key = \\"\\") {
let txt1 = ctx['state'].val;
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'footer': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([txt1], [b3]);
}
@@ -54,7 +54,7 @@ exports[`slots can define and call slots 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b4 = comp1({slots: markRaw({'header': {__render: slot1.bind(this), __ctx: ctx1}, 'footer': {__render: slot2.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b4]);
}
@@ -89,7 +89,7 @@ exports[`slots can define and call slots with bound params 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'abc': {__render: slot1.bind(this), __ctx: ctx1, getValue: bind(this, ctx['getValue'])}})}, key + \`__1\`, node, this, null);
}
}"
@@ -129,7 +129,7 @@ exports[`slots can define and call slots with params 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b4 = comp1({slots: markRaw({'header': {__render: slot1.bind(this), __ctx: ctx1, param: ctx['var']}, 'footer': {__render: slot2.bind(this), __ctx: ctx1, param: '5'}})}, key + \`__1\`, node, this, null);
return block1([], [b4]);
}
@@ -363,7 +363,7 @@ exports[`slots default content is not rendered if named slot is provided 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'header': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b3]);
}
@@ -442,7 +442,7 @@ exports[`slots default slot next to named slot, with default content 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'footer': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b3]);
}
@@ -619,7 +619,7 @@ exports[`slots dynamic slot in multiple locations 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp2({location: ctx['state'].location,slots: markRaw({'coffee': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
}
}"
@@ -685,7 +685,7 @@ exports[`slots dynamic t-slot call 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b6 = comp1({slots: markRaw({'slot1': {__render: slot1.bind(this), __ctx: ctx1}, 'slot2': {__render: slot2.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b6]);
}
@@ -732,7 +732,7 @@ exports[`slots dynamic t-slot call with default 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b6 = comp1({slots: markRaw({'slot1': {__render: slot1.bind(this), __ctx: ctx1}, 'slot2': {__render: slot2.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b6]);
}
@@ -865,7 +865,7 @@ exports[`slots mix of slots, t-call, t-call with body, and giving own props chil
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
}
}"
@@ -991,7 +991,7 @@ exports[`slots multiple roots are allowed in a named slot 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b5 = comp1({slots: markRaw({'content': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b5]);
}
@@ -1031,7 +1031,7 @@ exports[`slots multiple slots containing components 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp3({slots: markRaw({'s1': {__render: slot1.bind(this), __ctx: ctx1}, 's2': {__render: slot2.bind(this), __ctx: ctx1}})}, key + \`__3\`, node, this, null);
}
}"
@@ -1085,7 +1085,7 @@ exports[`slots named slot inside slot 1`] = `
}
function slot2(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx, this);
const ctx2 = capture(ctx);
return comp1({slots: markRaw({'brol': {__render: slot3.bind(this), __ctx: ctx2}})}, key + \`__1\`, node, this, null);
}
@@ -1095,7 +1095,7 @@ exports[`slots named slot inside slot 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b5 = comp2({slots: markRaw({'brol': {__render: slot1.bind(this), __ctx: ctx1}, 'default': {__render: slot2.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
return block1([], [b5]);
}
@@ -1136,7 +1136,7 @@ exports[`slots named slot inside slot, part 3 1`] = `
}
function slot2(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx, this);
const ctx2 = capture(ctx);
return comp1({slots: markRaw({'brol': {__render: slot3.bind(this), __ctx: ctx2}})}, key + \`__1\`, node, this, null);
}
@@ -1146,7 +1146,7 @@ exports[`slots named slot inside slot, part 3 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b5 = comp2({slots: markRaw({'brol': {__render: slot1.bind(this), __ctx: ctx1}, 'default': {__render: slot2.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
return block1([], [b5]);
}
@@ -1221,7 +1221,7 @@ exports[`slots named slots inside slot, again 1`] = `
}
function slot2(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx, this);
const ctx2 = capture(ctx);
return comp1({slots: markRaw({'brol2': {__render: slot3.bind(this), __ctx: ctx2}})}, key + \`__1\`, node, this, null);
}
@@ -1231,7 +1231,7 @@ exports[`slots named slots inside slot, again 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b5 = comp2({slots: markRaw({'brol1': {__render: slot1.bind(this), __ctx: ctx1}, 'default': {__render: slot2.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
return block1([], [b5]);
}
@@ -1569,7 +1569,7 @@ exports[`slots simple dynamic slot with slot scope 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'slotName': {__render: slot1.bind(this), __ctx: ctx1, __scope: \\"slotScope\\"}})}, key + \`__1\`, node, this, null);
}
}"
@@ -1599,7 +1599,7 @@ exports[`slots simple named and empty slot -- 2 1`] = `
const comp1 = app.createComponent(\`Child\`, true, true, false, true);
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'myEmptySlot': {myProp: 'myProp text'}})}, key + \`__1\`, node, this, null);
}
}"
@@ -1636,7 +1636,7 @@ exports[`slots simple named and empty slot 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'myEmptySlot': {}, 'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
}
}"
@@ -1676,7 +1676,7 @@ exports[`slots simple slot with slot scope 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'slotName': {__render: slot1.bind(this), __ctx: ctx1, __scope: \\"slotScope\\"}})}, key + \`__1\`, node, this, null);
}
}"
@@ -1712,7 +1712,7 @@ exports[`slots slot and (inline) t-call 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
return block1([], [b3]);
}
@@ -1762,7 +1762,7 @@ exports[`slots slot and t-call 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
return block1([], [b3]);
}
@@ -1997,7 +1997,7 @@ exports[`slots slot content is bound to caller (variation) 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
}
}"
@@ -2224,7 +2224,7 @@ exports[`slots slot preserves properly parented relationship, even through t-cal
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
return block1([], [b3]);
}
@@ -2285,7 +2285,7 @@ exports[`slots slot with slot scope and t-props 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'slotName': {__render: slot1.bind(this), __ctx: ctx1, __scope: \\"info\\"}})}, key + \`__1\`, node, this, null);
}
}"
@@ -2387,7 +2387,7 @@ exports[`slots slots are rendered with proper context 1`] = `
return function template(ctx, node, key = \\"\\") {
let txt1 = ctx['state'].val;
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'footer': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([txt1], [b3]);
}
@@ -2431,7 +2431,7 @@ exports[`slots slots are rendered with proper context, part 2 1`] = `
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`user\`] = v_block2[i1];
const key1 = ctx['user'].id;
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b7 = comp1({to: '/user/'+ctx['user'].id,slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1__\${key1}\`, node, this, null);
c_block2[i1] = withKey(block3([], [b7]), key1);
}
@@ -2480,7 +2480,7 @@ exports[`slots slots are rendered with proper context, part 3 1`] = `
ctx[\`user\`] = v_block2[i1];
const key1 = ctx['user'].id;
setContextValue(ctx, \\"userdescr\\", 'User '+ctx['user'].name);
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b5 = comp1({to: '/user/'+ctx['user'].id,slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1__\${key1}\`, node, this, null);
c_block2[i1] = withKey(block3([], [b5]), key1);
}
@@ -2523,7 +2523,7 @@ exports[`slots slots are rendered with proper context, part 4 1`] = `
ctx = Object.create(ctx);
ctx[isBoundary] = 1
setContextValue(ctx, \\"userdescr\\", 'User '+ctx['state'].user.name);
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({to: '/user/'+ctx['state'].user.id,slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b3]);
}
@@ -2565,7 +2565,7 @@ exports[`slots slots in slots, with vars 1`] = `
ctx = Object.create(ctx);
ctx[isBoundary] = 1
setContextValue(ctx, \\"test\\", ctx['state'].name);
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b3]);
}
@@ -2627,7 +2627,7 @@ exports[`slots slots in t-foreach and re-rendering 1`] = `
ctx[\`n\`] = v_block2[i1];
ctx[\`n_index\`] = i1;
const key1 = ctx['n_index'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
c_block2[i1] = withKey(comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1__\${key1}\`, node, this, null), key1);
}
const b2 = list(c_block2);
@@ -2682,7 +2682,7 @@ exports[`slots slots in t-foreach in t-foreach 1`] = `
for (let i2 = 0; i2 < l_block6; i2++) {
ctx[\`node2\`] = v_block6[i2];
const key2 = ctx['node2'].key;
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
c_block6[i2] = withKey(comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1__\${key1}__\${key2}\`, node, this, null), key2);
}
ctx = ctx.__proto__;
@@ -2734,7 +2734,7 @@ exports[`slots slots in t-foreach with t-set and re-rendering 1`] = `
ctx[\`n_index\`] = i1;
const key1 = ctx['n_index'];
setContextValue(ctx, \\"dummy\\", ctx['n_index']);
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
c_block2[i1] = withKey(comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1__\${key1}\`, node, this, null), key1);
}
const b2 = list(c_block2);
@@ -2774,7 +2774,7 @@ exports[`slots t-debug on a t-set-slot (defining a slot) 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'content': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b3]);
}
@@ -2813,7 +2813,7 @@ exports[`slots t-set t-value in a slot 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b3]);
}
@@ -2847,7 +2847,7 @@ exports[`slots t-set-slot=default has priority over rest of the content 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
}
}"
@@ -2905,7 +2905,7 @@ exports[`slots t-slot in recursive templates 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
}
}"
@@ -3083,7 +3083,7 @@ exports[`slots t-slot within dynamic t-call 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
return block1([], [b3]);
}
@@ -558,7 +558,7 @@ exports[`t-call t-call-context: ComponentNode is not looked up in the context 2`
const refs = this.__owl__.refs;
const ref1 = (el) => refs[\`myRef\`] = el;
const b2 = block2([ref1]);
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b7 = comp1({prop: bind(this, ctx['method']),slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return multi([b2, b7]);
}
@@ -576,3 +576,51 @@ exports[`t-call t-call-context: ComponentNode is not looked up in the context 3`
}
}"
`;
exports[`t-call t-call-context: slots don't make component available again when context is captured 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const callTemplate_1 = app.getTemplate(\`template\`);
return function template(ctx, node, key = \\"\\") {
let ctx1 = {};
return callTemplate_1.call(this, ctx1, node, key + \`__1\`);
}
}"
`;
exports[`t-call t-call-context: slots don't make component available again when context is captured 2`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { isBoundary, withDefault, setContextValue, capture, markRaw } = helpers;
const comp1 = app.createComponent(\`Child\`, true, true, false, true);
function slot1(ctx, node, key = \\"\\") {
return text(ctx['someValue']);
}
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
ctx[isBoundary] = 1
setContextValue(ctx, \\"dummy\\", 0);
const ctx1 = capture(ctx);
const props1 = {slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})};
helpers.validateProps(\`Child\`, props1, this);
return comp1(props1, key + \`__1\`, node, this, null);
}
}"
`;
exports[`t-call t-call-context: slots don't make component available again when context is captured 3`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { callSlot } = helpers;
return function template(ctx, node, key = \\"\\") {
return callSlot(ctx, node, key, 'default', false, {});
}
}"
`;
@@ -175,7 +175,7 @@ exports[`list of components order is correct when slots are not of same type 1`]
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'a': {__render: slot1.bind(this), __ctx: ctx1, active: !ctx['state'].active}, 'b': {__render: slot2.bind(this), __ctx: ctx1, active: true}, 'c': {__render: slot3.bind(this), __ctx: ctx1, active: ctx['state'].active}})}, key + \`__1\`, node, this, null);
}
}"
@@ -401,7 +401,7 @@ exports[`t-on t-on on t-set-slots 1`] = `
const b2 = text(\` [\`);
const b3 = text(ctx['state'].count);
const b4 = text(\`] \`);
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b8 = comp1({slots: markRaw({'myslot': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return multi([b2, b3, b4, b8]);
}
@@ -21,7 +21,7 @@ exports[`t-set slot setted value (with t-set) not accessible with t-esc 1`] = `
ctx[isBoundary] = 1
setContextValue(ctx, \\"iter\\", 'source');
let txt1 = ctx['iter'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b2 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
let txt2 = ctx['iter'];
return block1([txt1, txt2], [b2]);
@@ -70,7 +70,7 @@ exports[`t-set slots with a t-set with a component in body 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
}
}"
@@ -127,7 +127,7 @@ exports[`t-set slots with an t-set with a component in body 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
}
}"
@@ -178,7 +178,7 @@ exports[`t-set slots with an unused t-set with a component in body 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
}
}"
-126
View File
@@ -9,7 +9,6 @@ import {
onRendered,
onWillUnmount,
useState,
reactive,
xml,
} from "../../src/index";
import {
@@ -786,131 +785,6 @@ describe("can catch errors", () => {
expect(mockConsoleWarn).toBeCalledTimes(0);
});
test("re-render parent when self is in error", async () => {
class Boom extends Component {
static template = xml`<div />`;
setup() {
onWillStart(() => {
throw new Error("Boom Error");
});
}
}
const steps: string[] = [];
class BoomWrapper extends Component {
static template = xml`<Boom />`;
static components = { Boom };
setup() {
onError(() => {
steps.push("onError in child");
this.props.onError();
});
}
}
class Classic extends Component {
static template = xml`<BoomWrapper t-if="!inError" onError.bind="onErrorAsProps"/><div t-else="" />`;
static components = { BoomWrapper };
inError: Boolean = false;
setup() {
onMounted(() => {
steps.push("mounted");
});
}
onErrorAsProps() {
this.inError = true;
this.render(true);
}
}
class App extends Component {
static template = xml`<Classic />`;
static components = { Classic };
}
await mount(App, fixture);
expect(steps).toEqual(["onError in child", "mounted"]);
});
test("re-render parent when self is in error - 2", async () => {
class Boom extends Component {
static template = xml`<div />`;
setup() {
onWillStart(() => {
throw new Error("Boom Error");
});
}
}
const steps: string[] = [];
class BoomWrapper extends Component {
static template = xml`<Boom t-if="state.errorTree === 'error'" /><t t-else="" t-esc="state.errorTree" />`;
static components = { Boom };
state: any;
setup() {
this.state = useState(this.props.state);
onError(() => {
steps.push("onError");
this.state.onError();
});
onWillRender(() => {
steps.push(`BoomWrapper willRender`);
});
}
}
let classicId = 0;
class Classic extends Component {
static template = xml`
<BoomWrapper t-if="props.hasBoom" state="props.state"/>
<div t-else="" t-esc="props.state.safeTree" />
`;
static components = { BoomWrapper };
id: Number = 0;
state: any;
inError: Boolean = false;
setup() {
this.id = classicId++;
onMounted(() => {
steps.push(`mounted ${this.id}`);
});
onWillRender(() => {
steps.push(`Classic willRender ${this.id}`);
});
}
}
class App extends Component {
reactive: any;
setup() {
this.reactive = reactive({
errorTree: "error",
safeTree: "safe",
onError() {
this.safeTree = "safe2";
this.errorTree = "errorHandled";
},
});
}
static template = xml`<Classic hasBoom="true" state="reactive" /><Classic hasBoom="false" state="reactive"/>`;
static components = { Classic };
}
await mount(App, fixture);
expect(steps).toEqual([
"Classic willRender 0",
"Classic willRender 1",
"BoomWrapper willRender",
"onError",
"Classic willRender 1",
"BoomWrapper willRender",
"mounted 1",
"mounted 0",
]);
expect(fixture.innerHTML).toBe("errorHandled<div>safe2</div>");
});
test("can catch an error in the willStart call", async () => {
class ErrorComponent extends Component {
static template = xml`<div>Some text</div>`;
+26
View File
@@ -357,4 +357,30 @@ describe("t-call", () => {
expect(Object.keys(child.__owl__.refs)).toEqual([]);
expect(Object.keys(root.__owl__.refs)).toEqual(["myRef", "myRef2"]);
});
test("t-call-context: slots don't make component available again when context is captured", async () => {
class Child extends Component {
static template = xml`<t t-slot="default"/>`;
}
class Root extends Component {
static template = xml`<t t-call="template" t-call-context="{}"/>`;
static components = { Child };
someValue = "Hello";
}
await mount(Root, fixture, {
test: true,
templates: `
<templates>
<t t-name="template">
<t t-set="dummy" t-value="0"/>
<Child>
<t t-esc="someValue"/>
</Child>
</t>
</templates>`,
});
expect(fixture.innerHTML).toBe("");
});
});
+7 -7
View File
@@ -20,7 +20,7 @@ exports[`Portal Add and remove portals 1`] = `
for (let i1 = 0; i1 < l_block1; i1++) {
ctx[\`portalId\`] = v_block1[i1];
const key1 = ctx['portalId'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
c_block1[i1] = withKey(comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal), key1);
}
return list(c_block1);
@@ -49,7 +49,7 @@ exports[`Portal Add and remove portals on div 1`] = `
for (let i1 = 0; i1 < l_block1; i1++) {
ctx[\`portalId\`] = v_block1[i1];
const key1 = ctx['portalId'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
c_block1[i1] = withKey(comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal), key1);
}
return list(c_block1);
@@ -80,7 +80,7 @@ exports[`Portal Add and remove portals with t-foreach 1`] = `
ctx[\`portalId\`] = v_block1[i1];
const key1 = ctx['portalId'];
let txt1 = ctx['portalId'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b6 = comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal);
c_block1[i1] = withKey(block2([txt1], [b6]), key1);
}
@@ -112,7 +112,7 @@ exports[`Portal Add and remove portals with t-foreach and destroy 1`] = `
ctx[\`portalId\`] = v_block1[i1];
const key1 = ctx['portalId'];
let txt1 = ctx['portalId'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b6 = comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal);
c_block1[i1] = withKey(block2([txt1], [b6]), key1);
}
@@ -145,7 +145,7 @@ exports[`Portal Add and remove portals with t-foreach inside div 1`] = `
ctx[\`portalId\`] = v_block2[i1];
const key1 = ctx['portalId'];
let txt1 = ctx['portalId'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b7 = comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal);
c_block2[i1] = withKey(block3([txt1], [b7]), key1);
}
@@ -431,7 +431,7 @@ exports[`Portal conditional use of Portal with child and div 2`] = `
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`elem\`] = v_block2[i1];
const key1 = ctx['elem'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
c_block2[i1] = withKey(comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal), key1);
}
const b2 = list(c_block2);
@@ -481,7 +481,7 @@ exports[`Portal conditional use of Portal with child and div, variation 2`] = `
for (let i1 = 0; i1 < l_block3; i1++) {
ctx[\`elem\`] = v_block3[i1];
const key1 = ctx['elem'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
c_block3[i1] = withKey(comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal), key1);
}
const b3 = list(c_block3);
+27 -1
View File
@@ -9,7 +9,7 @@ import {
markRaw,
toRaw,
} from "../src";
import { reactive } from "../src/runtime/reactivity";
import { reactive, getSubscriptions } from "../src/runtime/reactivity";
import { batched } from "../src/runtime/utils";
import {
makeDeferred,
@@ -1020,6 +1020,32 @@ describe("Reactivity", () => {
expect(n3).toBe(2);
});
test("reactive inside other: reading the inner reactive from outer doesn't affect the inner's subscriptions", async () => {
const getObservedKeys = (obj: any) => getSubscriptions(obj).flatMap(({ keys }) => keys);
let n1 = 0;
let n2 = 0;
const innerCb = () => n1++;
const outerCb = () => n2++;
const inner = createReactive({ a: 1 }, innerCb);
const outer = createReactive({ b: inner }, outerCb);
expect(n1).toBe(0);
expect(n2).toBe(0);
expect(getObservedKeys(innerCb)).toEqual([]);
expect(getObservedKeys(outerCb)).toEqual([]);
outer.b.a;
expect(getObservedKeys(innerCb)).toEqual([]);
expect(getObservedKeys(outerCb)).toEqual(["b", "a"]);
expect(n1).toBe(0);
expect(n2).toBe(0);
outer.b.a = 2;
expect(getObservedKeys(innerCb)).toEqual([]);
expect(getObservedKeys(outerCb)).toEqual([]);
expect(n1).toBe(0);
expect(n2).toBe(1);
});
// test("notification is not done after unregistration", async () => {
// let n = 0;
// const observer = () => n++;