diff --git a/docs/owl.js b/docs/owl.js index 3e667837c..738aa71bd 100644 --- a/docs/owl.js +++ b/docs/owl.js @@ -2598,42 +2598,47 @@ class ComponentNode { } const TIMEOUT = Symbol("timeout"); +const HOOK_TIMEOUT = { + onWillStart: 3000, + onWillUpdateProps: 3000, +}; function wrapError(fn, hookName) { - const error = new OwlError(`The following error occurred in ${hookName}: `); - const timeoutError = new OwlError(`${hookName}'s promise hasn't resolved after 3 seconds`); + const error = new OwlError(); + const timeoutError = new OwlError(); const node = getCurrent(); return (...args) => { const onError = (cause) => { error.cause = cause; - if (cause instanceof Error) { - error.message += `"${cause.message}"`; - } - else { - error.message = `Something that is not an Error was thrown in ${hookName} (see this Error's "cause" property)`; - } + error.message = + cause instanceof Error + ? `The following error occurred in ${hookName}: "${cause.message}"` + : `Something that is not an Error was thrown in ${hookName} (see this Error's "cause" property)`; throw error; }; + let result; try { - const 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; + result = fn(...args); } catch (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. -const version = "2.3.1"; +const version = "2.4.0"; // ----------------------------------------------------------------------------- // Scheduler @@ -5647,6 +5652,7 @@ class App extends TemplateSet { constructor(Root, config = {}) { super(config); this.scheduler = new Scheduler(); + this.subRoots = new Set(); this.root = null; this.name = config.name || ""; this.Root = Root; @@ -5665,14 +5671,42 @@ class App extends TemplateSet { this.props = config.props || {}; } mount(target, options) { - App.validateTarget(target); - if (this.dev) { - validateProps(this.Root, this.props, { __owl__: { app: this } }); - } - const node = this.makeNode(this.Root, this.props); - const prom = this.mountNode(node, target, options); - this.root = node; - return prom; + const root = this.createRoot(this.Root, { props: this.props }); + this.root = root.node; + 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(Root, props); + if (config.env) { + this.env = env; + } + 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) { return new ComponentNode(Component, props, this, null, null); @@ -5704,6 +5738,9 @@ class App extends TemplateSet { } destroy() { if (this.root) { + for (let subroot of this.subRoots) { + subroot.destroy(); + } this.root.destroy(); 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 }; -__info__.date = '2024-08-14T14:25:23.038Z'; -__info__.hash = '9c2d957'; +__info__.date = '2024-09-30T08:49:29.420Z'; +__info__.hash = 'eb2b32a'; __info__.url = 'https://github.com/odoo/owl'; diff --git a/package-lock.json b/package-lock.json index d53033129..da89315f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@odoo/owl", - "version": "2.3.1", + "version": "2.4.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 686ccdcf7..3a4bc45f4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@odoo/owl", - "version": "2.3.1", + "version": "2.4.0", "description": "Odoo Web Library (OWL)", "main": "dist/owl.cjs.js", "module": "dist/owl.es.js", diff --git a/src/version.ts b/src/version.ts index c11a9375c..3b1ce5fe2 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1,2 +1,2 @@ // do not modify manually. This file is generated by the release script. -export const version = "2.3.1"; +export const version = "2.4.0";