[REL] v2.4.0

# v2.4.0

 - [IMP] owl: add basic support for sub roots
 - [IMP] make set of timeout-able hooks (and their timeouts) clearer by using a const map
 - [IMP] devtools: add support for file urls on chrome
This commit is contained in:
Romeo Fragomeli
2024-09-30 10:49:39 +02:00
parent eb2b32ab60
commit 20c6cacb4e
4 changed files with 74 additions and 37 deletions
+71 -34
View File
@@ -2598,42 +2598,47 @@ class ComponentNode {
} }
const TIMEOUT = Symbol("timeout"); const TIMEOUT = Symbol("timeout");
const HOOK_TIMEOUT = {
onWillStart: 3000,
onWillUpdateProps: 3000,
};
function wrapError(fn, hookName) { function wrapError(fn, hookName) {
const error = new OwlError(`The following error occurred in ${hookName}: `); const error = new OwlError();
const timeoutError = new OwlError(`${hookName}'s promise hasn't resolved after 3 seconds`); const timeoutError = new OwlError();
const node = getCurrent(); const node = getCurrent();
return (...args) => { return (...args) => {
const onError = (cause) => { const onError = (cause) => {
error.cause = cause; error.cause = cause;
if (cause instanceof Error) { error.message =
error.message += `"${cause.message}"`; cause instanceof Error
} ? `The following error occurred in ${hookName}: "${cause.message}"`
else { : `Something that is not an Error was thrown in ${hookName} (see this Error's "cause" property)`;
error.message = `Something that is not an Error was thrown in ${hookName} (see this Error's "cause" property)`;
}
throw error; throw error;
}; };
let result;
try { try {
const result = fn(...args); result = fn(...args);
if (result instanceof Promise) {
if (hookName === "onWillStart" || hookName === "onWillUpdateProps") {
const fiber = node.fiber;
Promise.race([
result.catch(() => { }),
new Promise((resolve) => setTimeout(() => resolve(TIMEOUT), 3000)),
]).then((res) => {
if (res === TIMEOUT && node.fiber === fiber && node.status <= 2) {
console.log(timeoutError);
}
});
}
return result.catch(onError);
}
return result;
} }
catch (cause) { catch (cause) {
onError(cause); onError(cause);
} }
if (!(result instanceof Promise)) {
return result;
}
const timeout = HOOK_TIMEOUT[hookName];
if (timeout) {
const fiber = node.fiber;
Promise.race([
result.catch(() => { }),
new Promise((resolve) => setTimeout(() => resolve(TIMEOUT), timeout)),
]).then((res) => {
if (res === TIMEOUT && node.fiber === fiber && node.status <= 2) {
timeoutError.message = `${hookName}'s promise hasn't resolved after ${timeout / 1000} seconds`;
console.log(timeoutError);
}
});
}
return result.catch(onError);
}; };
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -5552,7 +5557,7 @@ function compile(template, options = {}) {
} }
// do not modify manually. This file is generated by the release script. // do not modify manually. This file is generated by the release script.
const version = "2.3.1"; const version = "2.4.0";
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Scheduler // Scheduler
@@ -5647,6 +5652,7 @@ class App extends TemplateSet {
constructor(Root, config = {}) { constructor(Root, config = {}) {
super(config); super(config);
this.scheduler = new Scheduler(); this.scheduler = new Scheduler();
this.subRoots = new Set();
this.root = null; this.root = null;
this.name = config.name || ""; this.name = config.name || "";
this.Root = Root; this.Root = Root;
@@ -5665,14 +5671,42 @@ class App extends TemplateSet {
this.props = config.props || {}; this.props = config.props || {};
} }
mount(target, options) { mount(target, options) {
App.validateTarget(target); const root = this.createRoot(this.Root, { props: this.props });
if (this.dev) { this.root = root.node;
validateProps(this.Root, this.props, { __owl__: { app: this } }); this.subRoots.delete(root.node);
return root.mount(target, options);
}
createRoot(Root, config = {}) {
const props = config.props || {};
// hack to make sure the sub root get the sub env if necessary. for owl 3,
// would be nice to rethink the initialization process to make sure that
// we can create a ComponentNode and give it explicitely the env, instead
// of looking it up in the app
const env = this.env;
if (config.env) {
this.env = config.env;
} }
const node = this.makeNode(this.Root, this.props); const node = this.makeNode(Root, props);
const prom = this.mountNode(node, target, options); if (config.env) {
this.root = node; this.env = env;
return prom; }
this.subRoots.add(node);
return {
node,
mount: (target, options) => {
App.validateTarget(target);
if (this.dev) {
validateProps(Root, props, { __owl__: { app: this } });
}
const prom = this.mountNode(node, target, options);
return prom;
},
destroy: () => {
this.subRoots.delete(node);
node.destroy();
this.scheduler.processTasks();
},
};
} }
makeNode(Component, props) { makeNode(Component, props) {
return new ComponentNode(Component, props, this, null, null); return new ComponentNode(Component, props, this, null, null);
@@ -5704,6 +5738,9 @@ class App extends TemplateSet {
} }
destroy() { destroy() {
if (this.root) { if (this.root) {
for (let subroot of this.subRoots) {
subroot.destroy();
}
this.root.destroy(); this.root.destroy();
this.scheduler.processTasks(); this.scheduler.processTasks();
} }
@@ -5981,6 +6018,6 @@ TemplateSet.prototype._compileTemplate = function _compileTemplate(name, templat
export { App, Component, EventBus, OwlError, __info__, batched, blockDom, loadFile, markRaw, markup, mount, onError, onMounted, onPatched, onRendered, onWillDestroy, onWillPatch, onWillRender, onWillStart, onWillUnmount, onWillUpdateProps, reactive, status, toRaw, useChildSubEnv, useComponent, useEffect, useEnv, useExternalListener, useRef, useState, useSubEnv, validate, validateType, whenReady, xml }; export { App, Component, EventBus, OwlError, __info__, batched, blockDom, loadFile, markRaw, markup, mount, onError, onMounted, onPatched, onRendered, onWillDestroy, onWillPatch, onWillRender, onWillStart, onWillUnmount, onWillUpdateProps, reactive, status, toRaw, useChildSubEnv, useComponent, useEffect, useEnv, useExternalListener, useRef, useState, useSubEnv, validate, validateType, whenReady, xml };
__info__.date = '2024-08-14T14:25:23.038Z'; __info__.date = '2024-09-30T08:49:29.420Z';
__info__.hash = '9c2d957'; __info__.hash = 'eb2b32a';
__info__.url = 'https://github.com/odoo/owl'; __info__.url = 'https://github.com/odoo/owl';
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@odoo/owl", "name": "@odoo/owl",
"version": "2.3.1", "version": "2.4.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@odoo/owl", "name": "@odoo/owl",
"version": "2.3.1", "version": "2.4.0",
"description": "Odoo Web Library (OWL)", "description": "Odoo Web Library (OWL)",
"main": "dist/owl.cjs.js", "main": "dist/owl.cjs.js",
"module": "dist/owl.es.js", "module": "dist/owl.es.js",
+1 -1
View File
@@ -1,2 +1,2 @@
// do not modify manually. This file is generated by the release script. // do not modify manually. This file is generated by the release script.
export const version = "2.3.1"; export const version = "2.4.0";