[IMP] component: refactor rendering pipeline

This commit introduces a brand new rendering system based on a fiber
class and a scheduler.

closes #330
This commit is contained in:
Aaron Bohy
2019-10-25 15:45:56 +02:00
committed by Géry Debongnie
parent 2bed1cfbd1
commit 9c5cad15c1
24 changed files with 2296 additions and 1320 deletions
+102 -141
View File
@@ -1,6 +1,8 @@
import { Observer } from "../core/observer";
import { CompiledTemplate, QWeb } from "../qweb/index";
import { h, patch, VNode } from "../vdom/index";
import { Fiber } from "./fiber";
import { scheduler } from "./scheduler";
import "./directive";
import "./props_validation";
@@ -11,7 +13,6 @@ import "./props_validation";
* contains:
*
* - the Env interface (generic type for the environment)
* - the Fiber interface (owl metadata attached to a rendering)
* - the Internal interface (the owl specific metadata attached to a component)
* - the Component class
*/
@@ -34,27 +35,6 @@ export interface Env {
[key: string]: any;
}
/**
* Fibers are small abstractions designed to contain all the internal state
* associated to a "rendering work unit", relative to a specific component.
*
* A rendering will cause the creation of a fiber for each impacted components.
*/
export interface Fiber<Props> {
force: boolean;
rootFiber: Fiber<any> | null;
isCancelled: boolean;
scope: any;
vars: any;
patchQueue: Fiber<any>[];
component: Component<any, any>;
vnode: VNode | null;
props: Props;
promise: Promise<VNode> | null;
// handlers?: any;
// mountedHandlers?: any;
}
/**
* This is mostly an internal detail of implementation. The Meta interface is
* useful to typecheck and describe the internal keys used by Owl to manage the
@@ -65,6 +45,7 @@ interface Internal<T extends Env, Props> {
// relationships
readonly id: number;
vnode: VNode | null;
pvnode: VNode | null;
isMounted: boolean;
isDestroyed: boolean;
@@ -77,7 +58,7 @@ interface Internal<T extends Env, Props> {
// the component instance back whenever the template is rerendered.
cmap: { [key: number]: number };
currentFiber: Fiber<Props> | null;
currentFiber: Fiber | null;
boundHandlers: { [key: number]: any };
observer: Observer | null;
@@ -183,6 +164,7 @@ export class Component<T extends Env, Props extends {}> {
this.__owl__ = {
id: id,
vnode: null,
pvnode: null,
isMounted: false,
isDestroyed: false,
parent: p,
@@ -296,29 +278,44 @@ export class Component<T extends Env, Props extends {}> {
async mount(target: HTMLElement, renderBeforeRemount: boolean = false): Promise<void> {
const __owl__ = this.__owl__;
if (__owl__.isMounted) {
return;
return Promise.resolve();
}
const fiber = this.__createFiber(false, undefined, undefined, undefined);
const fiber = new Fiber(null, this, this.props, undefined, undefined, false);
if (!__owl__.vnode) {
fiber.promise = this.__prepareAndRender(fiber);
const vnode = await fiber.promise;
if (__owl__.isDestroyed) {
// component was destroyed before we get here...
return;
}
this.__patch(vnode);
} else if (renderBeforeRemount) {
fiber.patchQueue.push(fiber);
fiber.promise = this.__render(fiber);
await fiber.promise;
this.__applyPatchQueue(fiber);
}
this.__prepareAndRender(fiber);
return new Promise(resolve => {
scheduler.addFiber(fiber, () => {
if (!__owl__.isDestroyed) {
this.__patch(fiber.vnode);
target.appendChild(this.el!);
if (document.body.contains(target)) {
this.__callMounted();
}
}
resolve();
});
});
} else if (renderBeforeRemount) {
this.__render(fiber);
return new Promise(resolve => {
scheduler.addFiber(fiber, () => {
if (!__owl__.isDestroyed) {
this.__patch(fiber.vnode);
target.appendChild(this.el!);
if (document.body.contains(target)) {
this.__callMounted();
}
}
resolve();
});
});
} else {
target.appendChild(this.el!);
if (document.body.contains(target)) {
this.__callMounted();
}
}
}
/**
* The unmount method is the opposite of the mount method. It is useful
@@ -342,19 +339,22 @@ export class Component<T extends Env, Props extends {}> {
*/
async render(force: boolean = false): Promise<void> {
const __owl__ = this.__owl__;
if (!__owl__.isMounted) {
if (
(!__owl__.isMounted && !__owl__.currentFiber) ||
(__owl__.currentFiber && !__owl__.currentFiber.isRendered)
) {
return;
}
const fiber = this.__createFiber(force, undefined, undefined, undefined);
fiber.patchQueue.push(fiber);
fiber.promise = this.__render(fiber);
await fiber.promise;
if (__owl__.isMounted && fiber === __owl__.currentFiber) {
// we only update the vnode and the actual DOM if no other rendering
// occurred between now and when the render method was initially called.
this.__applyPatchQueue(fiber);
const fiber = new Fiber(null, this, this.props, undefined, undefined, force);
this.__render(fiber);
return new Promise(resolve => {
scheduler.addFiber(fiber.root, () => {
if (__owl__.isMounted && fiber === fiber.root) {
fiber.__applyPatchQueue();
}
resolve();
});
});
}
/**
@@ -407,27 +407,6 @@ export class Component<T extends Env, Props extends {}> {
// Private
//--------------------------------------------------------------------------
/**
* This method is a helper to create a fiber element.
*/
__createFiber(force, scope, vars, parent?: Fiber<any>): Fiber<Props> {
const fiber: Fiber<Props> = {
force,
scope,
vars,
rootFiber: null,
isCancelled: false,
component: this,
vnode: null,
patchQueue: parent ? parent.patchQueue : [],
props: this.props,
promise: null
};
fiber.rootFiber = parent ? parent.rootFiber : fiber;
this.__owl__.currentFiber = fiber;
return fiber;
}
/**
* Private helper to perform a full destroy, from the point of view of an Owl
* component. It does not remove the el (this is done only once on the top
@@ -478,7 +457,7 @@ export class Component<T extends Env, Props extends {}> {
__owl__.mountedCB();
}
} catch (e) {
errorHandler(e, this);
console.error(e); // TODO : add a test
}
}
@@ -504,25 +483,35 @@ export class Component<T extends Env, Props extends {}> {
*/
async __updateProps(
nextProps: Props,
parentFiber: Fiber<any>,
scope?: any,
vars?: any
parentFiber: Fiber,
scope: any,
vars: any,
previousSibling?: Fiber | null
): Promise<void> {
const shouldUpdate = parentFiber.force || this.shouldUpdate(nextProps);
if (shouldUpdate) {
const __owl__ = this.__owl__;
const fiber = new Fiber(parentFiber, this, this.props, scope, vars, parentFiber.force);
if (!parentFiber.child) {
parentFiber.child = fiber;
} else {
previousSibling!.sibling = fiber;
}
const defaultProps = (<any>this.constructor).defaultProps;
if (defaultProps) {
nextProps = this.__applyDefaultProps(nextProps, defaultProps);
}
await Promise.all([
this.willUpdateProps(nextProps),
this.__owl__.willUpdatePropsCB && this.__owl__.willUpdatePropsCB(nextProps)
__owl__.willUpdatePropsCB && __owl__.willUpdatePropsCB(nextProps)
]);
if (fiber.isCancelled) {
return;
}
this.props = nextProps;
const fiber = this.__createFiber(parentFiber.force, scope, vars, parentFiber);
fiber.patchQueue.push(fiber);
await this.__render(fiber);
this.__render(fiber);
}
}
@@ -534,6 +523,7 @@ export class Component<T extends Env, Props extends {}> {
const __owl__ = this.__owl__;
const target = __owl__.vnode || document.createElement(vnode.sel!);
__owl__.vnode = patch(target, vnode);
__owl__.currentFiber = null;
}
/**
@@ -541,10 +531,15 @@ export class Component<T extends Env, Props extends {}> {
* subcomponent is created. It gets its scope and vars, if any, from the
* parent template.
*/
__prepare(parentFiber: Fiber<any>, scope: any, vars: any): Promise<VNode> {
const fiber = this.__createFiber(parentFiber.force, scope, vars, parentFiber);
fiber.promise = this.__prepareAndRender(fiber);
return fiber.promise;
__prepare(parentFiber: Fiber, scope: any, vars: any, previousSibling?: Fiber | null) {
const fiber = new Fiber(parentFiber, this, this.props, scope, vars, parentFiber.force);
fiber.shouldPatch = false;
if (!parentFiber.child) {
parentFiber.child = fiber;
} else {
previousSibling!.sibling = fiber;
}
return this.__prepareAndRender(fiber);
}
__getTemplate(qweb: QWeb): string {
@@ -570,68 +565,66 @@ export class Component<T extends Env, Props extends {}> {
}
return p._template;
}
async __prepareAndRender(fiber: Fiber<Props>): Promise<VNode> {
async __prepareAndRender(fiber: Fiber) {
try {
await Promise.all([this.willStart(), this.__owl__.willStartCB && this.__owl__.willStartCB()]);
} catch (e) {
errorHandler(e, this);
return Promise.resolve(h("div"));
errorHandler(e, fiber);
fiber.vnode = h("div"); // -> we render this div at the end
return Promise.resolve();
}
const __owl__ = this.__owl__;
if (__owl__.isDestroyed) {
return Promise.resolve(h("div"));
if (this.__owl__.isDestroyed) {
return Promise.resolve();
}
if (!fiber.isCancelled) {
this.__render(fiber);
}
return this.__render(fiber);
}
__render(fiber: Fiber<Props>): Promise<VNode> {
__render(fiber: Fiber) {
const __owl__ = this.__owl__;
const promises: Promise<void>[] = [];
if (__owl__.observer) {
__owl__.observer.allowMutations = false;
}
let vnode;
try {
vnode = __owl__.render!(this, {
promises,
handlers: __owl__.boundHandlers,
fiber: fiber
});
} catch (e) {
vnode = __owl__.vnode || h("div");
errorHandler(e, this);
errorHandler(e, fiber);
}
fiber.vnode = vnode;
if (__owl__.observer) {
__owl__.observer.allowMutations = true;
}
// this part is critical for the patching process to be done correctly. The
// tricky part is that a child component can be rerendered on its own, which
// will update its own vnode representation without the knowledge of the
// parent component. With this, we make sure that the parent component will be
// able to patch itself properly after
vnode.key = __owl__.id;
// we applly here the class information described on the component by the
// we apply here the class information described on the component by the
// template (so, something like <MyComponent class="..."/>) to the actual
// root vnode
if (__owl__.classObj) {
vnode.data.class = Object.assign(vnode.data.class || {}, __owl__.classObj);
}
return Promise.all(promises).then(() => vnode);
fiber.root.counter--;
fiber.isRendered = true;
}
/**
* Only called by qweb t-component directive
*/
__mount(vnode: VNode, elm: HTMLElement): VNode {
__mount(fiber: Fiber, elm: HTMLElement): VNode {
if (fiber !== this.__owl__.currentFiber) {
fiber = this.__owl__.currentFiber!; // TODO: check if we can remove fiber arg
}
const vnode = fiber.vnode!;
const __owl__ = this.__owl__;
if (__owl__.classObj) {
(<any>vnode).data.class = Object.assign((<any>vnode).data.class || {}, __owl__.classObj);
}
__owl__.vnode = patch(elm, vnode);
__owl__.currentFiber = null;
if (__owl__.parent!.__owl__.isMounted && !__owl__.isMounted) {
this.__callMounted();
}
@@ -664,47 +657,15 @@ export class Component<T extends Env, Props extends {}> {
}
return <Props>props;
}
/**
* Apply the given patch queue from a fiber.
* 1) Call 'willPatch' on the component of each patch
* 2) Call '__patch' on the component of each patch
* 3) Call 'patched' on the component of each patch, in reverse order
*/
__applyPatchQueue(fiber: Fiber<Props>) {
const patchQueue = fiber.patchQueue;
let component: Component<any, any> = this;
try {
const patchLen = patchQueue.length;
for (let i = 0; i < patchLen; i++) {
component = patchQueue[i].component;
if (component.__owl__.willPatchCB) {
component.__owl__.willPatchCB();
}
component.willPatch();
}
for (let i = 0; i < patchLen; i++) {
const fiber = patchQueue[i];
component = fiber.component;
component.__patch(fiber.vnode);
}
for (let i = patchLen - 1; i >= 0; i--) {
component = patchQueue[i].component;
component.patched();
if (component.__owl__.patchedCB) {
component.__owl__.patchedCB();
}
}
} catch (e) {
errorHandler(e, component);
}
}
}
//------------------------------------------------------------------------------
// Error handling
//------------------------------------------------------------------------------
Fiber.prototype.handleError = function(error) {
errorHandler(error, this);
};
/**
* This is the global error handler for errors occurring in Owl main lifecycle
* methods. Caught errors are triggered on the QWeb instance, and are
@@ -713,8 +674,9 @@ export class Component<T extends Env, Props extends {}> {
* If there are no such component, we destroy everything. This is better than
* being in a corrupted state.
*/
function errorHandler(error: Error, component: Component<any, any>) {
export function errorHandler(error: Error, fiber: Fiber) {
let canCatch = false;
let component = fiber.component;
let qweb = component.env.qweb;
let root = component;
while (component && !(canCatch = component.catchError !== Component.prototype.catchError)) {
@@ -722,7 +684,6 @@ function errorHandler(error: Error, component: Component<any, any>) {
component = component.__owl__.parent!;
}
console.error(error);
// we trigger error on QWeb so it can be logged/handled
qweb.trigger("error", error);
if (canCatch) {
+29 -22
View File
@@ -232,7 +232,6 @@ QWeb.addDirective({
let propStr = Object.keys(props)
.map(k => k + ":" + props[k])
.join(",");
let dummyID = ctx.generateID();
let defID = ctx.generateID();
let componentID = ctx.generateID();
let keyID = key && ctx.generateID();
@@ -350,18 +349,13 @@ QWeb.addDirective({
ctx.addLine(
`let w${componentID} = ${templateId} in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[${templateId}]] : false;`
);
if (ctx.parentNode) {
ctx.addLine(`let _${dummyID}_index = c${ctx.parentNode}.length;`);
}
let shouldProxy = false;
let shouldProxy = !ctx.parentNode;
if (keepAlive) {
ctx.addLine(
`const fiber${componentID} = Object.assign(Object.create(extra.fiber), {patchQueue: []});`
);
}
if (ctx.parentNode) {
ctx.addLine(`c${ctx.parentNode}.push(null);`);
} else {
if (shouldProxy) {
let id = ctx.generateID();
ctx.rootContext.rootNode = id;
shouldProxy = true;
@@ -378,15 +372,9 @@ QWeb.addDirective({
ctx.addIf(
`w${componentID} && w${componentID}.__owl__.currentFiber && !w${componentID}.__owl__.vnode`
);
ctx.addIf(
`utils.shallowEqual(props${componentID}, w${componentID}.__owl__.currentFiber.props)`
);
ctx.addLine(`def${defID} = w${componentID}.__owl__.currentFiber.promise;`);
ctx.addElse();
ctx.addLine(`w${componentID}.destroy();`);
ctx.addLine(`w${componentID} = false;`);
ctx.closeIf();
ctx.closeIf();
ctx.addIf(`!w${componentID}`);
// new component
@@ -450,15 +438,26 @@ QWeb.addDirective({
} else {
scopeVars = "undefined, undefined";
}
ctx.addLine(`def${defID} = w${componentID}.__prepare(extra.fiber, ${scopeVars});`);
ctx.addLine(`def${defID} = w${componentID}.__prepare(extra.fiber, ${scopeVars}, sibling);`);
// hack: specify empty remove hook to prevent the node from being removed from the DOM
let registerCode = `c${ctx.parentNode}[_${dummyID}_index]=pvnode;`;
let registerCode = "";
if (shouldProxy) {
registerCode = `utils.defineProxy(vn${ctx.rootNode}, pvnode);`;
}
ctx.addLine(
`def${defID} = def${defID}.then(vnode=>{if (w${componentID}.__owl__.isDestroyed){return}${createHook}let pvnode=h(vnode.sel, {key: ${templateId}, hook: {insert(vn) {let nvn=w${componentID}.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;${refExpr}${transitionsInsertCode}},remove() {},destroy(vn) {${finalizeComponentCode}}}});${registerCode}w${componentID}.__owl__.pvnode = pvnode;});`
`let pvnode = h('dummy', {key: ${templateId}, hook: {insert(vn) { let nvn=w${componentID}.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;${refExpr}${transitionsInsertCode}},remove() {},destroy(vn) {${finalizeComponentCode}}}});`
);
ctx.addLine(`const fiber = w${componentID}.__owl__.currentFiber;`);
ctx.addLine(
`def${defID}.then(function () {if (w${componentID}.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; ${createHook}});`
);
if (registerCode) {
ctx.addLine(registerCode);
}
if (ctx.parentNode) {
ctx.addLine(`c${ctx.parentNode}.push(pvnode);`);
}
ctx.addLine(`w${componentID}.__owl__.pvnode = pvnode;`);
ctx.addElse();
// need to update component
@@ -475,24 +474,32 @@ QWeb.addDirective({
}
ctx.addLine(
`def${defID} = def${defID} || w${componentID}.__updateProps(props${componentID}, ${patchQueueCode}${scopeVars &&
", " + scopeVars});`
", " + scopeVars}, sibling);`
);
ctx.addLine(`let pvnode = w${componentID}.__owl__.pvnode;`);
let keepAliveCode = "";
if (keepAlive) {
keepAliveCode = `pvnode.data.hook.insert = vn => {vn.elm.parentNode.replaceChild(w${componentID}.el,vn.elm);vn.elm=w${componentID}.el;w${componentID}.__remount();};`;
ctx.addLine(keepAliveCode);
}
if (registerCode) {
ctx.addLine(registerCode);
}
if (ctx.parentNode) {
ctx.addLine(`c${ctx.parentNode}.push(pvnode);`);
}
if (tattStyle) {
ctx.addLine(
`def${defID} = def${defID}.then(()=>{if (w${componentID}.__owl__.isDestroyed) {return};${
tattStyle ? `w${componentID}.el.style=${tattStyle};` : ""
}let pvnode=w${componentID}.__owl__.pvnode;${keepAliveCode}${registerCode}});`
`def${defID} = def${defID}.then(()=>{if (w${componentID}.__owl__.isDestroyed) {return};w${componentID}.el.style=${tattStyle};});`
);
}
ctx.closeIf();
if (classObj) {
ctx.addLine(`w${componentID}.__owl__.classObj=${classObj};`);
}
ctx.addLine(`extra.promises.push(def${defID});`);
ctx.addLine(`sibling = w${componentID}.__owl__.currentFiber;`);
return true;
}
+163
View File
@@ -0,0 +1,163 @@
import { VNode } from "../vdom/index";
import { Component } from "./component";
/**
* Fibers are small abstractions designed to contain all the internal state
* associated to a "rendering work unit", relative to a specific component.
*
* A rendering will cause the creation of a fiber for each impacted components.
*/
export class Fiber {
force: boolean;
isCancelled: boolean = false;
shouldPatch: boolean = true;
isRendered: boolean = false;
scope: any;
vars: any;
props: any;
component: Component<any, any>;
vnode: VNode | null = null;
root: Fiber;
child: Fiber | null = null;
sibling: Fiber | null = null;
parent: Fiber | null = null;
counter: number = 0;
constructor(parent: Fiber | null, component: Component<any, any>, props, scope, vars, force) {
this.force = force;
this.scope = scope;
this.vars = vars;
this.props = props;
this.component = component;
this.root = parent ? parent.root : this;
this.parent = parent;
let oldFiber = component.__owl__.currentFiber;
if (oldFiber && !oldFiber.isCancelled) {
this.__remapFiber(oldFiber);
}
this.root.counter++;
component.__owl__.currentFiber = this;
}
__remapFiber(oldFiber: Fiber) {
oldFiber.cancel();
if (oldFiber === oldFiber.root) {
oldFiber.root.counter++;
}
if (oldFiber.parent && !this.parent) {
// re-map links
this.parent = oldFiber.parent;
this.root = this.parent.root;
this.sibling = oldFiber.sibling;
if (this.parent.child === oldFiber) {
this.parent.child = this;
} else {
let current = this.parent.child!;
while (true) {
if (current.sibling === oldFiber) {
current.sibling = this;
break;
}
current = current.sibling!;
}
}
}
}
/**
* This function has been taken from
* https://medium.com/react-in-depth/the-how-and-why-on-reacts-usage-of-linked-list-in-fiber-67f1014d0eb7
*/
__walk(doWork: (f: Fiber) => Fiber | null) {
let root = this;
let current: Fiber = this;
while (true) {
const child = doWork(current);
if (child) {
current = child;
continue;
}
if (current === root) {
return;
}
while (!current.sibling) {
if (!current.parent || current.parent === root) {
return;
}
current = current.parent;
}
current = current.sibling;
}
}
/**
* Apply the given patch queue from a fiber.
* 1) Call 'willPatch' on the component of each patch
* 2) Call '__patch' on the component of each patch
* 3) Call 'patched' on the component of each patch, in reverse order
*/
__applyPatchQueue() {
const patchQueue: Fiber[] = [];
const doWork: (Fiber) => Fiber | null = function(f) {
if (f.shouldPatch) {
patchQueue.push(f);
}
return f.child;
};
this.__walk(doWork);
let component: Component<any, any> = this.component;
this.shouldPatch = false;
const patchLen = patchQueue.length;
try {
for (let i = 0; i < patchLen; i++) {
component = patchQueue[i].component;
if (component.__owl__.willPatchCB) {
component.__owl__.willPatchCB();
}
component.willPatch();
}
} catch (e) {
console.error(e);
}
try {
for (let i = 0; i < patchLen; i++) {
const fiber = patchQueue[i];
component = fiber.component;
component.__patch(fiber.vnode);
}
} catch (e) {
this.handleError(e);
}
try {
for (let i = patchLen - 1; i >= 0; i--) {
component = patchQueue[i].component;
component.patched();
if (component.__owl__.patchedCB) {
component.__owl__.patchedCB();
}
}
} catch (e) {
console.error(e);
}
this.shouldPatch = true;
}
cancel() {
this.__walk(f => {
if (!f.isRendered) {
f.root.counter--;
}
f.isCancelled = true;
return f.child;
});
}
handleError(e: Error) {}
}
+50
View File
@@ -0,0 +1,50 @@
import { Fiber } from "./fiber";
// scheduler
interface Task {
fiber: Fiber;
callback: () => void;
}
export const scheduler = {
tasks: [] as Task[],
isRunning: false,
addFiber(fiber, callback) {
this.tasks.push({ fiber, callback });
if (this.isRunning) {
return;
}
this.scheduleTasks();
},
flush() {
let tasks = this.tasks;
this.tasks = [];
tasks = tasks.filter(task => {
if (task.fiber.isCancelled) {
return false;
}
if (task.fiber.counter === 0) {
task.callback();
return false;
}
return true;
});
this.tasks = tasks.concat(this.tasks);
},
processTasks() {
this.flush();
if (this.tasks.length > 0) {
this.scheduleTasks();
} else {
this.isRunning = false;
}
},
scheduleTasks() {
this.isRunning = true;
this.requestAnimationFrame(() => this.processTasks());
},
requestAnimationFrame: requestAnimationFrame.bind(window)
};
+5 -3
View File
@@ -1,8 +1,8 @@
import { Component } from "./component/component";
import { Observer } from "./core/observer";
import { scheduler } from "./component/scheduler";
import { EventBus } from "./core/event_bus";
import { Observer } from "./core/observer";
import { onWillUnmount } from "./hooks";
/**
* The `Context` object provides a way to share data between an arbitrary number
* of component. Usually, data is passed from a parent to its children component,
@@ -53,7 +53,9 @@ export class Context extends EventBus {
const sub = subs[i];
const shouldCallback = sub.owner ? sub.owner.__owl__.isMounted : true;
if (shouldCallback) {
await sub.callback.call(sub.owner, id);
const render = sub.callback.call(sub.owner, id);
scheduler.flush();
await render;
}
}
}
+4
View File
@@ -22,6 +22,7 @@ export class CompilationContext {
shouldDefineUtils: boolean = false;
shouldDefineRefs: boolean = false;
shouldDefineResult: boolean = true;
shouldDefineSibling: boolean = true;
shouldProtectContext: boolean = false;
shouldTrackScope: boolean = false;
loopNumber: number = 0;
@@ -63,6 +64,9 @@ export class CompilationContext {
if (this.shouldDefineResult) {
this.code.unshift(" let result;");
}
if (this.shouldDefineSibling) {
this.code.unshift(" let sibling = null;");
}
if (this.shouldDefineRefs) {
this.code.unshift(" context.__owl__.refs = context.__owl__.refs || {};");
}
+1 -3
View File
@@ -207,9 +207,7 @@ QWeb.addDirective({
`slot${slotKey}.call(this, context.__owl__.parent, Object.assign({}, extra, {parentNode: ${parentNode}, vars: extra.vars, parent: owner}));`
);
if (!ctx.parentNode) {
ctx.addLine(
`Promise.all(extra.promises).then(() => utils.defineProxy(result, ${parentNode}[0]))`
);
ctx.addLine(`utils.defineProxy(result, ${parentNode}[0]);`);
}
ctx.closeIf();
return true;
+1 -1
View File
@@ -645,7 +645,7 @@ export class QWeb extends EventBus {
if (name.startsWith("t-att-")) {
let attName = name.slice(6);
const v = ctx.getValue(value);
let formattedValue = typeof v === 'string' ? ctx.formatExpression(v) : v.id;
let formattedValue = typeof v === "string" ? ctx.formatExpression(v) : v.id;
if (attName === "class") {
ctx.rootContext.shouldDefineUtils = true;
+5 -3
View File
@@ -557,13 +557,15 @@ type ArrayOrElement<T> = T | T[];
type VNodeChildren = ArrayOrElement<VNodeChildElement>;
export function addNS(data: any, children: VNodes | undefined, sel: string | undefined): void {
if (sel === "dummy") {
// we do not need to add the namespace on dummy elements, they come from a
// subcomponent, which will handle the namespace itself
return;
}
data.ns = "http://www.w3.org/2000/svg";
if (sel !== "foreignObject" && children !== undefined) {
for (let i = 0, iLen = children.length; i < iLen; ++i) {
const child = children[i];
if (child === null) {
continue;
}
let childData = child.data;
if (childData !== undefined) {
addNS(childData, (child as VNode).children as VNodes, child.sel);
+54 -52
View File
@@ -7,40 +7,40 @@ exports[`animations t-transition combined with component 1`] = `
let QWeb = this.constructor;
let parent = context;
let owner = context;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
//COMPONENT
let def3;
let templateId4 = \`__5__\`;
let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false;
let _2_index = c1.length;
c1.push(null);
let props4 = {};
if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) {
if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) {
def3 = w4.__owl__.currentFiber.promise;
} else {
w4.destroy();
w4 = false;
let def2;
let templateId3 = \`__4__\`;
let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false;
let props3 = {};
if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) {
w3.destroy();
w3 = false;
}
}
if (!w4) {
let componentKey4 = \`Child\`;
let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['Child'];
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
w4 = new W4(parent, props4);
parent.__owl__.cmap[templateId4] = w4.__owl__.id;
def3 = w4.__prepare(extra.fiber, undefined, undefined);
def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;utils.transitionInsert(vn, 'chimay');},remove() {},destroy(vn) {let finalize = () => {
w4.destroy();
if (!w3) {
let componentKey3 = \`Child\`;
let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['Child'];
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')}
w3 = new W3(parent, props3);
parent.__owl__.cmap[templateId3] = w3.__owl__.id;
def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;utils.transitionInsert(vn, 'chimay');},remove() {},destroy(vn) {let finalize = () => {
w3.destroy();
};
utils.transitionRemove(vn, 'chimay', finalize);}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
utils.transitionRemove(vn, 'chimay', finalize);}}});
const fiber = w3.__owl__.currentFiber;
def2.then(function () {if (w3.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
c1.push(pvnode);
w3.__owl__.pvnode = pvnode;
} else {
def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined);
def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;});
def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling);
let pvnode = w3.__owl__.pvnode;
c1.push(pvnode);
}
extra.promises.push(def3);
sibling = w3.__owl__.currentFiber;
return vn1;
}"
`;
@@ -52,41 +52,41 @@ exports[`animations t-transition combined with t-component and t-if 1`] = `
let QWeb = this.constructor;
let parent = context;
let owner = context;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
if (context['state'].display) {
//COMPONENT
let def3;
let templateId4 = \`__5__\`;
let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false;
let _2_index = c1.length;
c1.push(null);
let props4 = {};
if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) {
if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) {
def3 = w4.__owl__.currentFiber.promise;
} else {
w4.destroy();
w4 = false;
let def2;
let templateId3 = \`__4__\`;
let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false;
let props3 = {};
if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) {
w3.destroy();
w3 = false;
}
}
if (!w4) {
let componentKey4 = \`Child\`;
let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['Child'];
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
w4 = new W4(parent, props4);
parent.__owl__.cmap[templateId4] = w4.__owl__.id;
def3 = w4.__prepare(extra.fiber, undefined, undefined);
def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;utils.transitionInsert(vn, 'chimay');},remove() {},destroy(vn) {let finalize = () => {
w4.destroy();
if (!w3) {
let componentKey3 = \`Child\`;
let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['Child'];
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')}
w3 = new W3(parent, props3);
parent.__owl__.cmap[templateId3] = w3.__owl__.id;
def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;utils.transitionInsert(vn, 'chimay');},remove() {},destroy(vn) {let finalize = () => {
w3.destroy();
};
utils.transitionRemove(vn, 'chimay', finalize);}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
utils.transitionRemove(vn, 'chimay', finalize);}}});
const fiber = w3.__owl__.currentFiber;
def2.then(function () {if (w3.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
c1.push(pvnode);
w3.__owl__.pvnode = pvnode;
} else {
def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined);
def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;});
def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling);
let pvnode = w3.__owl__.pvnode;
c1.push(pvnode);
}
extra.promises.push(def3);
sibling = w3.__owl__.currentFiber;
}
return vn1;
}"
@@ -96,6 +96,7 @@ exports[`animations t-transition with no delay/duration 1`] = `
"function anonymous(context,extra
) {
let utils = this.constructor.utils;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('span', p1, c1);
@@ -116,6 +117,7 @@ exports[`animations t-transition, on a simple node (insert) 1`] = `
"function anonymous(context,extra
) {
let utils = this.constructor.utils;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('span', p1, c1);
+2 -2
View File
@@ -251,11 +251,11 @@ describe("animations", () => {
widget.state.display = false;
patchNextFrame(cb => {
expect(fixture.innerHTML).toBe(
'<div><span class="chimay-leave chimay-leave-active" data-owl-key="__5__">blue</span></div>'
'<div><span class="chimay-leave chimay-leave-active" data-owl-key="__4__">blue</span></div>'
);
cb();
expect(fixture.innerHTML).toBe(
'<div><span class="chimay-leave-active chimay-leave-to" data-owl-key="__5__">blue</span></div>'
'<div><span class="chimay-leave-active chimay-leave-to" data-owl-key="__4__">blue</span></div>'
);
def.resolve();
});
File diff suppressed because it is too large Load Diff
@@ -1,9 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`default props default values are also set whenever component is updated 1`] = `"<div>1</div>"`;
exports[`default props default values are also set whenever component is updated 2`] = `"<div>4</div>"`;
exports[`props validation props are validated in dev mode (code snapshot) 1`] = `
"function anonymous(context,extra
) {
@@ -11,38 +7,38 @@ exports[`props validation props are validated in dev mode (code snapshot) 1`] =
let QWeb = this.constructor;
let parent = context;
let owner = context;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
//COMPONENT
let def3;
let templateId4 = \`__5__\`;
let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false;
let _2_index = c1.length;
c1.push(null);
let props4 = {message:1};
if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) {
if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) {
def3 = w4.__owl__.currentFiber.promise;
let def2;
let templateId3 = \`__4__\`;
let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false;
let props3 = {message:1};
if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) {
w3.destroy();
w3 = false;
}
if (!w3) {
let componentKey3 = \`Child\`;
let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['Child'];
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')}
w3 = new W3(parent, props3);
parent.__owl__.cmap[templateId3] = w3.__owl__.id;
def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
const fiber = w3.__owl__.currentFiber;
def2.then(function () {if (w3.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
c1.push(pvnode);
w3.__owl__.pvnode = pvnode;
} else {
w4.destroy();
w4 = false;
utils.validateProps(w3.constructor, props3)
def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling);
let pvnode = w3.__owl__.pvnode;
c1.push(pvnode);
}
}
if (!w4) {
let componentKey4 = \`Child\`;
let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['Child'];
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
w4 = new W4(parent, props4);
parent.__owl__.cmap[templateId4] = w4.__owl__.id;
def3 = w4.__prepare(extra.fiber, undefined, undefined);
def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
} else {
utils.validateProps(w4.constructor, props4)
def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined);
def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;});
}
extra.promises.push(def3);
sibling = w3.__owl__.currentFiber;
return vn1;
}"
`;
File diff suppressed because it is too large Load Diff
+14 -12
View File
@@ -1,5 +1,6 @@
import { Component, Env } from "../../src/component/component";
import { makeTestFixture, makeTestEnv } from "../helpers";
import { makeTestFixture, makeTestEnv, nextTick } from "../helpers";
import { useState } from "../../src/hooks";
import { QWeb } from "../../src/qweb";
import { xml } from "../../src/tags";
@@ -336,21 +337,22 @@ describe("default props", () => {
test("default values are also set whenever component is updated", async () => {
class TestWidget extends Widget {
static template = xml`<div><t t-esc="props.p"/></div>`;
static defaultProps = { p: 4 };
}
env.qweb.addTemplates(`
<templates>
<div t-name="TestWidget"><t t-esc="props.p"/></div>
</templates>`);
class Parent extends Widget {
static template = xml`<div><TestWidget p="state.p"/></div>`;
static components = { TestWidget };
state: any = useState({ p: 1 });
}
const w = new TestWidget(env, { p: 1 });
const w = new Parent(env);
await w.mount(fixture);
expect(fixture.innerHTML).toMatchSnapshot();
const fiber = w.__createFiber(false, undefined, undefined, undefined);
await w.__updateProps({}, fiber);
await w.render();
expect(w.props.p).toBe(4);
expect(fixture.innerHTML).toMatchSnapshot();
expect(fixture.innerHTML).toBe("<div><div>1</div></div>");
w.state.p = undefined;
await nextTick();
expect(fixture.innerHTML).toBe("<div><div>4</div></div>");
});
test("can set default required boolean values", async () => {
+49 -1
View File
@@ -1,4 +1,4 @@
import { makeTestEnv, makeTestFixture, nextTick } from "./helpers";
import { makeDeferred, makeTestEnv, makeTestFixture, nextTick } from "./helpers";
import { Component, Env } from "../src/component/component";
import { Context, useContext } from "../src/context";
import { xml } from "../src/tags";
@@ -173,4 +173,52 @@ describe("Context", () => {
// event
expect(testContext.subscriptions.update.length).toBe(0);
});
test.skip("concurrent renderings", async () => {
const testContext = new Context({ x: { n: 1 }, key: "x" });
const def = makeDeferred();
let stateC;
class ComponentC extends Component<any, any> {
static template = xml`<span><t t-esc="context[props.key].n"/><t t-esc="state.x"/></span>`;
context = useContext(testContext);
state = useState({ x: "a" });
constructor(parent, props) {
super(parent, props);
stateC = this.state;
}
}
class ComponentB extends Component<any, any> {
static components = { ComponentC };
static template = xml`<p><ComponentC key="props.key"/></p>`;
willUpdateProps() {
return def;
}
}
class ComponentA extends Component<any, any> {
static components = { ComponentB };
static template = xml`<div><ComponentB key="context.key"/></div>`;
context = useContext(testContext);
}
const component = new ComponentA(env);
await component.mount(fixture);
expect(fixture.innerHTML).toBe("<div><p><span>1a</span></p></div>");
testContext.state.key = "y";
testContext.state.y = 2;
delete testContext.state.x;
await nextTick();
expect(fixture.innerHTML).toBe("<div><p><span>1a</span></p></div>");
stateC.x = "b";
await nextTick();
expect(fixture.innerHTML).toBe("<div><p><span>1a</span></p></div>");
def.resolve();
await nextTick();
expect(fixture.innerHTML).toBe("<div><p><span>1a</span></p></div>");
});
});
+2 -2
View File
@@ -13,7 +13,7 @@ const HEADING_REGEXP = /\n(#+\s*)(.*)/g;
function getFiles(): string[] {
const DOCFILES = fs.readdirSync("doc").map(f => `doc/${f}`);
const MAINREADME = "README.md";
DOCFILES.push('roadmap.md', MAINREADME)
DOCFILES.push("roadmap.md", MAINREADME);
return DOCFILES;
}
@@ -54,7 +54,7 @@ interface FileData {
}
function isLinkValid(link: MarkDownLink, current: FileData, files: FileData[]): boolean {
if (link.link.startsWith('http')) {
if (link.link.startsWith("http")) {
// no check on external links
return true;
}
+28 -2
View File
@@ -5,6 +5,16 @@ import "../src/qweb/base_directives";
import "../src/qweb/extensions";
import "../src/component/directive";
// modifies scheduler to make it easier to test components
// let current;
// scheduler.requestAnimationFrame = function(callback: FrameRequestCallback) {
// if (current) {
// throw new Error("should not schedule 2 callbacks!");
// }
// current = callback;
// return 1;
// };
// Some static cleanup
let nextSlotId;
let slots;
@@ -12,6 +22,7 @@ let nextId;
let TEMPLATES;
beforeEach(() => {
// current = null;
nextSlotId = QWeb.nextSlotId;
slots = Object.assign({}, QWeb.slots);
nextId = QWeb.nextId;
@@ -19,6 +30,7 @@ beforeEach(() => {
});
afterEach(() => {
// current = null;
QWeb.nextSlotId = nextSlotId;
QWeb.slots = slots;
QWeb.nextId = nextId;
@@ -29,9 +41,23 @@ afterEach(() => {
export function nextMicroTick(): Promise<void> {
return Promise.resolve();
}
// export async function nextTick(): Promise<void> {
// await Promise.resolve();
// let max = 1000;
// while (current && max > 0) {
// const cb = current;
// console.warn('next');
// current = null;
// cb();
// max--;
// await Promise.resolve();
// }
// }
export function nextTick(): Promise<void> {
return new Promise(resolve => setTimeout(resolve));
export async function nextTick(): Promise<void> {
return new Promise(function (resolve) {
setTimeout(() => requestAnimationFrame(() => resolve()));
});
}
export function makeTestFixture() {
+1 -1
View File
@@ -178,7 +178,7 @@ describe("Asyncroot", () => {
children[1]!.click();
await nextTick();
expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>1/1</span><span>1/0</span>");
expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>1/1</span><span>0/0</span>");
// finalize first re-rendering (coming from the props update)
def.resolve();
+122
View File
@@ -3,6 +3,7 @@
exports[`attributes class and t-attf-class with ternary operation 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
var _1 = 'hello ' + (context['value']?'world':'');
let c2 = [], p2 = {key:2,attrs:{class: _1}};
@@ -14,6 +15,7 @@ exports[`attributes class and t-attf-class with ternary operation 1`] = `
exports[`attributes dynamic attribute falsy variable 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
var _1 = context['value'];
let c2 = [], p2 = {key:2,attrs:{foo: _1}};
@@ -25,6 +27,7 @@ exports[`attributes dynamic attribute falsy variable 1`] = `
exports[`attributes dynamic attribute with a dash 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
var _1 = context['id'];
let c2 = [], p2 = {key:2,attrs:{\\"data-action-id\\": _1}};
@@ -36,6 +39,7 @@ exports[`attributes dynamic attribute with a dash 1`] = `
exports[`attributes dynamic attributes 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
var _1 = 'bar';
let c2 = [], p2 = {key:2,attrs:{foo: _1}};
@@ -47,6 +51,7 @@ exports[`attributes dynamic attributes 1`] = `
exports[`attributes dynamic formatted attributes with a dash 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
var _1 = \`Some text \${context['id']}\`;
let c2 = [], p2 = {key:2,attrs:{\\"aria-label\\": _1}};
@@ -58,6 +63,7 @@ exports[`attributes dynamic formatted attributes with a dash 1`] = `
exports[`attributes fixed variable 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
var _1 = context['value'];
let c2 = [], p2 = {key:2,attrs:{foo: _1}};
@@ -69,6 +75,7 @@ exports[`attributes fixed variable 1`] = `
exports[`attributes format expression 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
var _1 = (context['value']+37);
let c2 = [], p2 = {key:2,attrs:{foo: _1}};
@@ -80,6 +87,7 @@ exports[`attributes format expression 1`] = `
exports[`attributes format expression, other format 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
var _1 = (context['value']+37);
let c2 = [], p2 = {key:2,attrs:{foo: _1}};
@@ -91,6 +99,7 @@ exports[`attributes format expression, other format 1`] = `
exports[`attributes format literal 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
var _1 = \`bar\`;
let c2 = [], p2 = {key:2,attrs:{foo: _1}};
@@ -102,6 +111,7 @@ exports[`attributes format literal 1`] = `
exports[`attributes format multiple 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
var _1 = \`a \${context['value1']} is \${context['value2']} of \${context['value3']} ]\`;
let c2 = [], p2 = {key:2,attrs:{foo: _1}};
@@ -113,6 +123,7 @@ exports[`attributes format multiple 1`] = `
exports[`attributes format value 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
var _1 = \`b\${context['value']}r\`;
let c2 = [], p2 = {key:2,attrs:{foo: _1}};
@@ -125,6 +136,7 @@ exports[`attributes from object variables set previously 1`] = `
"function anonymous(context,extra
) {
let utils = this.constructor.utils;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -141,6 +153,7 @@ exports[`attributes from variables set previously 1`] = `
"function anonymous(context,extra
) {
let utils = this.constructor.utils;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -156,6 +169,7 @@ exports[`attributes from variables set previously 1`] = `
exports[`attributes object 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
var _1 = context['value'];
let c2 = [], p2 = {key:2,attrs:{}};
@@ -174,6 +188,7 @@ exports[`attributes object 1`] = `
exports[`attributes static attributes 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
var _1 = 'a';
var _2 = 'b';
@@ -187,6 +202,7 @@ exports[`attributes static attributes 1`] = `
exports[`attributes static attributes on void elements 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
var _1 = '/test.jpg';
var _2 = 'Test';
@@ -199,6 +215,7 @@ exports[`attributes static attributes on void elements 1`] = `
exports[`attributes static attributes with dashes 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
var _1 = 'Close';
let c2 = [], p2 = {key:2,attrs:{\\"aria-label\\": _1}};
@@ -211,6 +228,7 @@ exports[`attributes t-att-class and class should combine together 1`] = `
"function anonymous(context,extra
) {
let utils = this.constructor.utils;
let sibling = null;
var h = this.h;
let _2 = {'hello':true};
Object.assign(_2, utils.toObj(context['value']))
@@ -224,6 +242,7 @@ exports[`attributes t-att-class with object 1`] = `
"function anonymous(context,extra
) {
let utils = this.constructor.utils;
let sibling = null;
var h = this.h;
let _2 = {'static':true};
Object.assign(_2, utils.toObj({a:context['b'],c:context['d'],e:context['f']}))
@@ -236,6 +255,7 @@ exports[`attributes t-att-class with object 1`] = `
exports[`attributes t-attf-class should combine with class 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
var _1 = 'hello ' + \`world\`;
let c2 = [], p2 = {key:2,attrs:{class: _1}};
@@ -247,6 +267,7 @@ exports[`attributes t-attf-class should combine with class 1`] = `
exports[`attributes tuple literal 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
var _1 = ['foo','bar'];
let c2 = [], p2 = {key:2,attrs:{}};
@@ -265,6 +286,7 @@ exports[`attributes tuple literal 1`] = `
exports[`attributes tuple variable 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
var _1 = context['value'];
let c2 = [], p2 = {key:2,attrs:{}};
@@ -283,6 +305,7 @@ exports[`attributes tuple variable 1`] = `
exports[`debugging t-debug 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
debugger;
let c1 = [], p1 = {key:1};
@@ -301,6 +324,7 @@ exports[`debugging t-debug 1`] = `
exports[`debugging t-log 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -313,6 +337,7 @@ exports[`debugging t-log 1`] = `
exports[`foreach does not pollute the rendering context 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
context = Object.create(context);
var h = this.h;
let c1 = [], p1 = {key:1};
@@ -343,6 +368,7 @@ exports[`foreach does not pollute the rendering context 1`] = `
exports[`foreach iterate on items (on a element node) 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
context = Object.create(context);
var h = this.h;
let c1 = [], p1 = {key:1};
@@ -377,6 +403,7 @@ exports[`foreach iterate on items (on a element node) 1`] = `
exports[`foreach iterate on items 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
context = Object.create(context);
var h = this.h;
let c1 = [], p1 = {key:1};
@@ -419,6 +446,7 @@ exports[`foreach iterate on items 1`] = `
exports[`foreach iterate, dict param 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
context = Object.create(context);
var h = this.h;
let c1 = [], p1 = {key:1};
@@ -461,6 +489,7 @@ exports[`foreach iterate, dict param 1`] = `
exports[`foreach iterate, position 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
context = Object.create(context);
var h = this.h;
let c1 = [], p1 = {key:1};
@@ -500,6 +529,7 @@ exports[`foreach iterate, position 1`] = `
exports[`foreach t-foreach in t-forach 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
context = Object.create(context);
var h = this.h;
let c1 = [], p1 = {key:1};
@@ -551,6 +581,7 @@ exports[`foreach t-foreach in t-forach 1`] = `
exports[`foreach warn if no key in some case 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
context = Object.create(context);
var h = this.h;
let c1 = [], p1 = {key:1};
@@ -584,6 +615,7 @@ exports[`foreach warn if no key in some case 1`] = `
exports[`loading templates can initialize qweb with a string 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -595,6 +627,7 @@ exports[`loading templates can initialize qweb with a string 1`] = `
exports[`loading templates can load a few templates from a xml string 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('ul', p1, c1);
@@ -614,6 +647,7 @@ exports[`misc global 1`] = `
"function anonymous(context,extra
) {
let utils = this.constructor.utils;
let sibling = null;
context = Object.create(context);
var h = this.h;
let c1 = [], p1 = {key:1};
@@ -693,6 +727,7 @@ exports[`properly support svg add proper namespace to g tags 1`] = `
"function anonymous(context,extra
) {
let utils = this.constructor.utils;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('g', p1, c1);
@@ -715,6 +750,7 @@ exports[`properly support svg add proper namespace to svg 1`] = `
"function anonymous(context,extra
) {
let utils = this.constructor.utils;
let sibling = null;
var h = this.h;
var _1 = '100px';
var _2 = '90px';
@@ -738,6 +774,7 @@ exports[`properly support svg add proper namespace to svg 1`] = `
exports[`special cases for some boolean html attributes/properties input type= checkbox, with t-att-checked 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
var _1 = 'checkbox';
var _2 = context['flag'];
@@ -750,6 +787,7 @@ exports[`special cases for some boolean html attributes/properties input type= c
exports[`special cases for some boolean html attributes/properties various boolean html attributes 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -790,6 +828,7 @@ exports[`special cases for some boolean html attributes/properties various boole
exports[`static templates div with a span child node 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -804,6 +843,7 @@ exports[`static templates div with a span child node 1`] = `
exports[`static templates div with a text node 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -815,6 +855,7 @@ exports[`static templates div with a text node 1`] = `
exports[`static templates empty div 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -825,6 +866,7 @@ exports[`static templates empty div 1`] = `
exports[`static templates properly handle comments 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -838,6 +880,7 @@ exports[`static templates properly handle comments 1`] = `
exports[`static templates simple dynamic value 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
let result;
var h = this.h;
var _1 = context['text'];
@@ -852,6 +895,7 @@ exports[`static templates simple dynamic value 1`] = `
exports[`static templates simple string 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
let result;
var h = this.h;
var vn1 = {text: \`hello vdom\`};
@@ -863,6 +907,7 @@ exports[`static templates simple string 1`] = `
exports[`static templates simple string, with some dynamic value 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
let result;
var h = this.h;
var vn1 = {text: \`hello \`};
@@ -878,6 +923,7 @@ exports[`static templates simple string, with some dynamic value 1`] = `
exports[`t-call (template calling basic caller 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
let result;
var h = this.h;
let c1 = [], p1 = {key:1};
@@ -891,6 +937,7 @@ exports[`t-call (template calling basic caller 1`] = `
exports[`t-call (template calling call with several sub nodes on same line 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -913,6 +960,7 @@ exports[`t-call (template calling call with several sub nodes on same line 1`] =
exports[`t-call (template calling inherit context 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -928,6 +976,7 @@ exports[`t-call (template calling recursive template, part 1 1`] = `
"function anonymous(context,extra
) {
let owner = context;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -946,6 +995,7 @@ exports[`t-call (template calling recursive template, part 1 2`] = `
"function anonymous(context,extra
) {
let owner = context;
let sibling = null;
var h = this.h;
let c1 = extra.parentNode;
Object.assign(context, extra.fiber.scope);
@@ -966,6 +1016,7 @@ exports[`t-call (template calling recursive template, part 2 1`] = `
"function anonymous(context,extra
) {
let owner = context;
let sibling = null;
context = Object.create(context);
const scope = Object.create(null);
var h = this.h;
@@ -1013,6 +1064,7 @@ exports[`t-call (template calling recursive template, part 2 2`] = `
"function anonymous(context,extra
) {
let owner = context;
let sibling = null;
context = Object.create(context);
const scope = Object.create(null);
var h = this.h;
@@ -1057,6 +1109,7 @@ exports[`t-call (template calling recursive template, part 3 1`] = `
"function anonymous(context,extra
) {
let owner = context;
let sibling = null;
context = Object.create(context);
const scope = Object.create(null);
var h = this.h;
@@ -1104,6 +1157,7 @@ exports[`t-call (template calling recursive template, part 3 2`] = `
"function anonymous(context,extra
) {
let owner = context;
let sibling = null;
context = Object.create(context);
const scope = Object.create(null);
var h = this.h;
@@ -1147,6 +1201,7 @@ exports[`t-call (template calling recursive template, part 3 2`] = `
exports[`t-call (template calling scoped parameters 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -1165,6 +1220,7 @@ exports[`t-call (template calling scoped parameters 1`] = `
exports[`t-call (template calling t-call with t-if 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -1181,6 +1237,7 @@ exports[`t-call (template calling t-call with t-if 1`] = `
exports[`t-call (template calling t-call, global templates 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -1195,6 +1252,7 @@ exports[`t-call (template calling t-call, global templates 1`] = `
exports[`t-call (template calling with unused body 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
let result;
var h = this.h;
let c2 = [], p2 = {key:2};
@@ -1208,6 +1266,7 @@ exports[`t-call (template calling with unused body 1`] = `
exports[`t-call (template calling with unused setbody 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
let result;
var h = this.h;
{
@@ -1224,6 +1283,7 @@ exports[`t-call (template calling with unused setbody 1`] = `
exports[`t-call (template calling with used body 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
let result;
var h = this.h;
let c2 = [], p2 = {key:2};
@@ -1237,6 +1297,7 @@ exports[`t-call (template calling with used body 1`] = `
exports[`t-call (template calling with used set body 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('span', p1, c1);
@@ -1253,6 +1314,7 @@ exports[`t-call (template calling with used set body 1`] = `
exports[`t-esc escaping on a node 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('span', p1, c1);
@@ -1267,6 +1329,7 @@ exports[`t-esc escaping on a node 1`] = `
exports[`t-esc escaping on a node with a body 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('span', p1, c1);
@@ -1283,6 +1346,7 @@ exports[`t-esc escaping on a node with a body 1`] = `
exports[`t-esc escaping on a node with a body, as a default 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('span', p1, c1);
@@ -1299,6 +1363,7 @@ exports[`t-esc escaping on a node with a body, as a default 1`] = `
exports[`t-esc literal 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('span', p1, c1);
@@ -1313,6 +1378,7 @@ exports[`t-esc literal 1`] = `
exports[`t-esc variable 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('span', p1, c1);
@@ -1327,6 +1393,7 @@ exports[`t-esc variable 1`] = `
exports[`t-if boolean value condition elif 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -1349,6 +1416,7 @@ exports[`t-if boolean value condition elif 1`] = `
exports[`t-if boolean value condition else 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -1373,6 +1441,7 @@ exports[`t-if boolean value condition else 1`] = `
exports[`t-if boolean value condition false else 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -1397,6 +1466,7 @@ exports[`t-if boolean value condition false else 1`] = `
exports[`t-if boolean value condition missing 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('span', p1, c1);
@@ -1410,6 +1480,7 @@ exports[`t-if boolean value condition missing 1`] = `
exports[`t-if boolean value false condition 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -1423,6 +1494,7 @@ exports[`t-if boolean value false condition 1`] = `
exports[`t-if boolean value true condition 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -1436,6 +1508,7 @@ exports[`t-if boolean value true condition 1`] = `
exports[`t-if can use some boolean operators in expressions 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -1470,6 +1543,7 @@ exports[`t-if can use some boolean operators in expressions 1`] = `
exports[`t-if t-esc with t-elif 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -1489,6 +1563,7 @@ exports[`t-if t-esc with t-elif 1`] = `
exports[`t-if t-esc with t-if 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -1505,6 +1580,7 @@ exports[`t-if t-esc with t-if 1`] = `
exports[`t-if t-set, then t-elif, part 3 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -1529,6 +1605,7 @@ exports[`t-if t-set, then t-elif, part 3 1`] = `
exports[`t-if t-set, then t-if 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -1545,6 +1622,7 @@ exports[`t-if t-set, then t-if 1`] = `
exports[`t-if t-set, then t-if, part 2 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -1563,6 +1641,7 @@ exports[`t-if t-set, then t-if, part 2 1`] = `
exports[`t-key can use t-key directive on a node 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
const nodeKey1 = context['beer'].id
let c1 = [], p1 = {key:nodeKey1};
@@ -1578,6 +1657,7 @@ exports[`t-key can use t-key directive on a node 1`] = `
exports[`t-key t-key directive in a list 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
context = Object.create(context);
var h = this.h;
let c1 = [], p1 = {key:1};
@@ -1615,6 +1695,7 @@ exports[`t-on can bind event handler 1`] = `
"function anonymous(context,extra
) {
let owner = context;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1,on:{}};
var vn1 = h('button', p1, c1);
@@ -1629,6 +1710,7 @@ exports[`t-on can bind handlers with arguments 1`] = `
"function anonymous(context,extra
) {
let owner = context;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1,on:{}};
var vn1 = h('button', p1, c1);
@@ -1643,6 +1725,7 @@ exports[`t-on can bind handlers with empty object (with non empty inner string)
"function anonymous(context,extra
) {
let owner = context;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1,on:{}};
var vn1 = h('button', p1, c1);
@@ -1657,6 +1740,7 @@ exports[`t-on can bind handlers with empty object 1`] = `
"function anonymous(context,extra
) {
let owner = context;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1,on:{}};
var vn1 = h('button', p1, c1);
@@ -1671,6 +1755,7 @@ exports[`t-on can bind handlers with loop variable as argument 1`] = `
"function anonymous(context,extra
) {
let owner = context;
let sibling = null;
context = Object.create(context);
var h = this.h;
let c1 = [], p1 = {key:1};
@@ -1708,6 +1793,7 @@ exports[`t-on can bind handlers with object arguments 1`] = `
"function anonymous(context,extra
) {
let owner = context;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1,on:{}};
var vn1 = h('button', p1, c1);
@@ -1722,6 +1808,7 @@ exports[`t-on can bind two event handlers 1`] = `
"function anonymous(context,extra
) {
let owner = context;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1,on:{}};
var vn1 = h('button', p1, c1);
@@ -1738,6 +1825,7 @@ exports[`t-on handler is bound to proper owner 1`] = `
"function anonymous(context,extra
) {
let owner = context;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1,on:{}};
var vn1 = h('button', p1, c1);
@@ -1752,6 +1840,7 @@ exports[`t-on t-on combined with t-esc 1`] = `
"function anonymous(context,extra
) {
let owner = context;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -1773,6 +1862,7 @@ exports[`t-on t-on combined with t-raw 1`] = `
) {
let utils = this.constructor.utils;
let owner = context;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -1793,6 +1883,7 @@ exports[`t-on t-on with empty handler (only modifiers) 1`] = `
"function anonymous(context,extra
) {
let owner = context;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -1809,6 +1900,7 @@ exports[`t-on t-on with inline statement (function call) 1`] = `
"function anonymous(context,extra
) {
let owner = context;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1,on:{}};
var vn1 = h('button', p1, c1);
@@ -1823,6 +1915,7 @@ exports[`t-on t-on with inline statement 1`] = `
"function anonymous(context,extra
) {
let owner = context;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1,on:{}};
var vn1 = h('button', p1, c1);
@@ -1837,6 +1930,7 @@ exports[`t-on t-on with prevent and self modifiers (order matters) 1`] = `
"function anonymous(context,extra
) {
let owner = context;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -1857,6 +1951,7 @@ exports[`t-on t-on with prevent and/or stop modifiers 1`] = `
"function anonymous(context,extra
) {
let owner = context;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -1886,6 +1981,7 @@ exports[`t-on t-on with prevent modifier in t-foreach 1`] = `
"function anonymous(context,extra
) {
let owner = context;
let sibling = null;
context = Object.create(context);
var h = this.h;
let c1 = [], p1 = {key:1};
@@ -1925,6 +2021,7 @@ exports[`t-on t-on with self and prevent modifiers (order matters) 1`] = `
"function anonymous(context,extra
) {
let owner = context;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -1945,6 +2042,7 @@ exports[`t-on t-on with self modifier 1`] = `
"function anonymous(context,extra
) {
let owner = context;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -1974,6 +2072,7 @@ exports[`t-raw literal 1`] = `
"function anonymous(context,extra
) {
let utils = this.constructor.utils;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('span', p1, c1);
@@ -1989,6 +2088,7 @@ exports[`t-raw not escaping 1`] = `
"function anonymous(context,extra
) {
let utils = this.constructor.utils;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -2004,6 +2104,7 @@ exports[`t-raw t-raw and another sibling node 1`] = `
"function anonymous(context,extra
) {
let utils = this.constructor.utils;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('span', p1, c1);
@@ -2023,6 +2124,7 @@ exports[`t-raw variable 1`] = `
"function anonymous(context,extra
) {
let utils = this.constructor.utils;
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('span', p1, c1);
@@ -2038,6 +2140,7 @@ exports[`t-ref can get a dynamic ref on a node 1`] = `
"function anonymous(context,extra
) {
context.__owl__.refs = context.__owl__.refs || {};
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -2061,6 +2164,7 @@ exports[`t-ref can get a ref on a node 1`] = `
"function anonymous(context,extra
) {
context.__owl__.refs = context.__owl__.refs || {};
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -2084,6 +2188,7 @@ exports[`t-ref refs in a loop 1`] = `
"function anonymous(context,extra
) {
context.__owl__.refs = context.__owl__.refs || {};
let sibling = null;
context = Object.create(context);
var h = this.h;
let c1 = [], p1 = {key:1};
@@ -2127,6 +2232,7 @@ exports[`t-ref refs in a loop 1`] = `
exports[`t-set evaluate value expression 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -2141,6 +2247,7 @@ exports[`t-set evaluate value expression 1`] = `
exports[`t-set evaluate value expression, part 2 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -2155,6 +2262,7 @@ exports[`t-set evaluate value expression, part 2 1`] = `
exports[`t-set set from attribute literal 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -2169,6 +2277,7 @@ exports[`t-set set from attribute literal 1`] = `
exports[`t-set set from attribute lookup 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -2183,6 +2292,7 @@ exports[`t-set set from attribute lookup 1`] = `
exports[`t-set set from body literal 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
let result;
var h = this.h;
var vn1 = {text: \`ok\`};
@@ -2194,6 +2304,7 @@ exports[`t-set set from body literal 1`] = `
exports[`t-set set from body lookup 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -2208,6 +2319,7 @@ exports[`t-set set from body lookup 1`] = `
exports[`t-set set from empty body 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -2218,6 +2330,7 @@ exports[`t-set set from empty body 1`] = `
exports[`t-set t-set and t-if 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -2232,6 +2345,7 @@ exports[`t-set t-set and t-if 1`] = `
exports[`t-set t-set evaluates an expression only once 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -2249,6 +2363,7 @@ exports[`t-set t-set evaluates an expression only once 1`] = `
exports[`t-set t-set should reuse variable if possible 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
context = Object.create(context);
var h = this.h;
let c1 = [], p1 = {key:1};
@@ -2288,6 +2403,7 @@ exports[`t-set t-set should reuse variable if possible 1`] = `
exports[`t-set value priority 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -2302,6 +2418,7 @@ exports[`t-set value priority 1`] = `
exports[`whitespace handling consecutives whitespaces are condensed into a single space 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -2313,6 +2430,7 @@ exports[`whitespace handling consecutives whitespaces are condensed into a singl
exports[`whitespace handling nothing is done in pre tags 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('pre', p1, c1);
@@ -2324,6 +2442,7 @@ exports[`whitespace handling nothing is done in pre tags 1`] = `
exports[`whitespace handling nothing is done in pre tags 2`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('pre', p1, c1);
@@ -2337,6 +2456,7 @@ exports[`whitespace handling nothing is done in pre tags 2`] = `
exports[`whitespace handling nothing is done in pre tags 3`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('pre', p1, c1);
@@ -2350,6 +2470,7 @@ exports[`whitespace handling nothing is done in pre tags 3`] = `
exports[`whitespace handling white space only text nodes are condensed into a single space 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -2361,6 +2482,7 @@ exports[`whitespace handling white space only text nodes are condensed into a si
exports[`whitespace handling whitespace only text nodes with newlines are removed 1`] = `
"function anonymous(context,extra
) {
let sibling = null;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
@@ -5,6 +5,7 @@ exports[`Link component can render simple cases 1`] = `
) {
let utils = this.constructor.utils;
let owner = context;
let sibling = null;
var h = this.h;
let _1 = utils.toObj({'router-link-active':context['isActive']});
var _2 = context['href'];
@@ -7,38 +7,40 @@ exports[`RouteComponent can render simple cases 1`] = `
let QWeb = this.constructor;
let parent = context;
let owner = context;
let sibling = null;
let result;
var h = this.h;
if (context['routeComponent']) {
//COMPONENT
let key4 = 'key' + context['env'].router.currentRouteName;
let def2;
let templateId3 = \`__5__\` + key4;
let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false;
let vn6 = {};
result = vn6;
let props3 = Object.assign({}, context['env'].router.currentParams);
if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) {
if (utils.shallowEqual(props3, w3.__owl__.currentFiber.props)) {
def2 = w3.__owl__.currentFiber.promise;
let key3 = 'key' + context['env'].router.currentRouteName;
let def1;
let templateId2 = \`__4__\` + key3;
let w2 = templateId2 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId2]] : false;
let vn5 = {};
result = vn5;
let props2 = Object.assign({}, context['env'].router.currentParams);
if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
w2.destroy();
w2 = false;
}
if (!w2) {
let componentKey2 = \`routeComponent\`;
let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| context['routeComponent'];
if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
w2 = new W2(parent, props2);
parent.__owl__.cmap[templateId2] = w2.__owl__.id;
def1 = w2.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId2, hook: {insert(vn) { let nvn=w2.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w2.destroy();}}});
const fiber = w2.__owl__.currentFiber;
def1.then(function () {if (w2.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
utils.defineProxy(vn5, pvnode);
w2.__owl__.pvnode = pvnode;
} else {
w3.destroy();
w3 = false;
def1 = def1 || w2.__updateProps(props2, extra.fiber, undefined, undefined, sibling);
let pvnode = w2.__owl__.pvnode;
utils.defineProxy(vn5, pvnode);
}
}
if (!w3) {
let componentKey3 = \`routeComponent\`;
let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['routeComponent'];
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')}
w3 = new W3(parent, props3);
parent.__owl__.cmap[templateId3] = w3.__owl__.id;
def2 = w3.__prepare(extra.fiber, undefined, undefined);
def2 = def2.then(vnode=>{if (w3.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId3, hook: {insert(vn) {let nvn=w3.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});utils.defineProxy(vn6, pvnode);w3.__owl__.pvnode = pvnode;});
} else {
def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined);
def2 = def2.then(()=>{if (w3.__owl__.isDestroyed) {return};let pvnode=w3.__owl__.pvnode;utils.defineProxy(vn6, pvnode);});
}
extra.promises.push(def2);
sibling = w2.__owl__.currentFiber;
}
return result;
}"
+2 -2
View File
@@ -63,8 +63,8 @@ describe("connecting a component to store", () => {
<span t-esc="a.value"/>
<span t-esc="b.value"/>
</div>`;
a = useStore(state => ({value: state.a}));
b = useStore(state => ({value: state.b}));
a = useStore(state => ({ value: state.a }));
b = useStore(state => ({ value: state.b }));
}
App.prototype.__render = jest.fn(App.prototype.__render);
+5 -3
View File
@@ -1273,9 +1273,10 @@ const ASYNC_COMPONENTS = `// This example will not work if your browser does not
// In this example, we have 2 sub components, one of them being async (slow).
// However, we don't want renderings of the other sub component to be delayed
// because of the slow component. We use the 't-asyncroot' directive for this
// because of the slow component. We use the AsyncRoot component for this
// purpose. Try removing it to see the difference.
const { Component, useState } = owl;
const { AsyncRoot } = owl.misc;
class SlowComponent extends Component {
willUpdateProps() {
@@ -1311,9 +1312,10 @@ const ASYNC_COMPONENTS_XML = `<templates>
<div t-name="App" class="app">
<button t-on-click="increment">Increment</button>
<SlowComponent value="state.value"/>
<NotificationList t-asyncroot="1" notifications="state.notifs"/>
<AsyncRoot>
<NotificationList notifications="state.notifs"/>
</AsyncRoot>
</div>
<div t-name="SlowComponent" class="value" >
Current value: <t t-esc="props.value"/>
</div>