From 4855977e7d43e93f1231a6f9ab2a5abd121acaf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Wed, 20 Jul 2022 10:02:51 +0200 Subject: [PATCH] [IMP] update owl to v2.0.0-beta-15 --- owl.js | 242 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 123 insertions(+), 119 deletions(-) diff --git a/owl.js b/owl.js index 8da9a7c0..dd8e47c1 100644 --- a/owl.js +++ b/owl.js @@ -80,6 +80,68 @@ return new VToggler(key, child); } + // Custom error class that wraps error that happen in the owl lifecycle + class OwlError extends Error { + } + // Maps fibers to thrown errors + const fibersInError = new WeakMap(); + const nodeErrorHandlers = new WeakMap(); + function _handleError(node, error) { + if (!node) { + return false; + } + const fiber = node.fiber; + if (fiber) { + fibersInError.set(fiber, error); + } + const errorHandlers = nodeErrorHandlers.get(node); + if (errorHandlers) { + let handled = false; + // execute in the opposite order + for (let i = errorHandlers.length - 1; i >= 0; i--) { + try { + errorHandlers[i](error); + handled = true; + break; + } + catch (e) { + error = e; + } + } + if (handled) { + return true; + } + } + return _handleError(node.parent, error); + } + function handleError(params) { + let { error } = params; + // Wrap error if it wasn't wrapped by wrapError (ie when not in dev mode) + if (!(error instanceof OwlError)) { + error = Object.assign(new OwlError("An error occured in the owl lifecycle"), { cause: error }); + } + const node = "node" in params ? params.node : params.fiber.node; + const fiber = "fiber" in params ? params.fiber : node.fiber; + // resets the fibers on components if possible. This is important so that + // new renderings can be properly included in the initial one, if any. + let current = fiber; + do { + current.node.fiber = current; + current = current.parent; + } while (current); + fibersInError.set(fiber.root, error); + const handled = _handleError(node, error); + if (!handled) { + console.warn(`[Owl] Unhandled error. Destroying the root component`); + try { + node.app.destroy(); + } + catch (e) { + console.error(e); + } + } + } + const { setAttribute: elemSetAttribute, removeAttribute } = Element.prototype; const tokenList = DOMTokenList.prototype; const tokenListAdd = tokenList.add; @@ -701,7 +763,7 @@ }; } } - throw new Error("boom"); + throw new OwlError("boom"); } function addRef(tree) { tree.isRef = true; @@ -1385,61 +1447,6 @@ vnode.remove(); } - // Maps fibers to thrown errors - const fibersInError = new WeakMap(); - const nodeErrorHandlers = new WeakMap(); - function _handleError(node, error) { - if (!node) { - return false; - } - const fiber = node.fiber; - if (fiber) { - fibersInError.set(fiber, error); - } - const errorHandlers = nodeErrorHandlers.get(node); - if (errorHandlers) { - let handled = false; - // execute in the opposite order - for (let i = errorHandlers.length - 1; i >= 0; i--) { - try { - errorHandlers[i](error); - handled = true; - break; - } - catch (e) { - error = e; - } - } - if (handled) { - return true; - } - } - return _handleError(node.parent, error); - } - function handleError(params) { - const error = params.error; - const node = "node" in params ? params.node : params.fiber.node; - const fiber = "fiber" in params ? params.fiber : node.fiber; - // resets the fibers on components if possible. This is important so that - // new renderings can be properly included in the initial one, if any. - let current = fiber; - do { - current.node.fiber = current; - current = current.parent; - } while (current); - fibersInError.set(fiber.root, error); - const handled = _handleError(node, error); - if (!handled) { - console.warn(`[Owl] Unhandled error. Destroying the root component`); - try { - node.app.destroy(); - } - catch (e) { - console.error(e); - } - } - } - function makeChildFiber(node, parent) { let current = node.fiber; if (current) { @@ -1478,7 +1485,7 @@ return fiber; } function throwOnRender() { - throw new Error("Attempted to render cancelled fiber"); + throw new OwlError("Attempted to render cancelled fiber"); } /** * @returns number of not-yet rendered fibers cancelled @@ -1855,7 +1862,7 @@ */ function reactive(target, callback = () => { }) { if (!canBeMadeReactive(target)) { - throw new Error(`Cannot make the given value reactive`); + throw new OwlError(`Cannot make the given value reactive`); } if (SKIP in target) { return target; @@ -2124,10 +2131,10 @@ } function validateTarget(target) { if (!(target instanceof HTMLElement)) { - throw new Error("Cannot mount component: the target is not a valid DOM element"); + throw new OwlError("Cannot mount component: the target is not a valid DOM element"); } if (!document.body.contains(target)) { - throw new Error("Cannot mount a component on a detached dom node"); + throw new OwlError("Cannot mount a component on a detached dom node"); } } class EventBus extends EventTarget { @@ -2148,7 +2155,7 @@ async function loadFile(url) { const result = await fetch(url); if (!result.ok) { - throw new Error("Error while fetching xml templates"); + throw new OwlError("Error while fetching xml templates"); } return await result.text(); } @@ -2170,7 +2177,7 @@ let currentNode = null; function getCurrent() { if (!currentNode) { - throw new Error("No active component (a hook function should only be called in 'setup')"); + throw new OwlError("No active component (a hook function should only be called in 'setup')"); } return currentNode; } @@ -2232,7 +2239,6 @@ this.parent = parent; this.props = props; this.parentKey = parentKey; - this.level = parent ? parent.level + 1 : 0; const defaultProps = C.defaultProps; props = Object.assign({}, props); if (defaultProps) { @@ -2465,17 +2471,24 @@ const TIMEOUT = Symbol("timeout"); function wrapError(fn, hookName) { - const error = new Error(`The following error occurred in ${hookName}: `); - const timeoutError = new Error(`${hookName}'s promise hasn't resolved after 3 seconds`); + const error = new OwlError(`The following error occurred in ${hookName}: `); + const timeoutError = new OwlError(`${hookName}'s promise hasn't resolved after 3 seconds`); const node = getCurrent(); return (...args) => { + const onError = (cause) => { + if (cause instanceof Error) { + error.cause = cause; + error.message += `"${cause.message}"`; + } + throw error; + }; try { const result = fn(...args); if (result instanceof Promise) { if (hookName === "onWillStart" || hookName === "onWillUpdateProps") { const fiber = node.fiber; Promise.race([ - result, + result.catch(() => { }), new Promise((resolve) => setTimeout(() => resolve(TIMEOUT), 3000)), ]).then((res) => { if (res === TIMEOUT && node.fiber === fiber) { @@ -2483,21 +2496,12 @@ } }); } - return result.catch((cause) => { - error.cause = cause; - if (cause instanceof Error) { - error.message += `"${cause.message}"`; - } - throw error; - }); + return result.catch(onError); } return result; } catch (cause) { - if (cause instanceof Error) { - error.message += `"${cause.message}"`; - } - throw error; + onError(cause); } }; } @@ -2601,7 +2605,7 @@ } this.target = el && el.querySelector(this.selector); if (!this.target) { - throw new Error("invalid portal target"); + throw new OwlError("invalid portal target"); } } this.realBDom.mount(this.target, null); @@ -2695,7 +2699,7 @@ function validate(obj, spec) { let errors = validateSchema(obj, spec); if (errors.length) { - throw new Error("Invalid object: " + errors.join(", ")); + throw new OwlError("Invalid object: " + errors.join(", ")); } } /** @@ -2848,7 +2852,7 @@ keys = Object.values(collection); } else { - throw new Error("Invalid loop expression"); + throw new OwlError("Invalid loop expression"); } const n = values.length; return [keys, values, n, new Array(n)]; @@ -2954,7 +2958,7 @@ if (el) { count++; if (count > 1) { - throw new Error("Cannot have 2 elements with same ref name at the same time"); + throw new OwlError("Cannot have 2 elements with same ref name at the same time"); } } if (count === 0 || el) { @@ -2991,13 +2995,13 @@ : name in schema && !("*" in schema) && !isOptional(schema[name]); for (let p in defaultProps) { if (isMandatory(p)) { - throw new Error(`A default value cannot be defined for a mandatory prop (name: '${p}', component: ${ComponentClass.name})`); + throw new OwlError(`A default value cannot be defined for a mandatory prop (name: '${p}', component: ${ComponentClass.name})`); } } } const errors = validateSchema(props, schema); if (errors.length) { - throw new Error(`Invalid props for component '${ComponentClass.name}': ` + errors.join(", ")); + throw new OwlError(`Invalid props for component '${ComponentClass.name}': ` + errors.join(", ")); } } const helpers = { @@ -3018,6 +3022,7 @@ bind, createCatcher, markRaw, + OwlError, }; const bdom = { text, createBlock, list, multi, html, toggler, comment }; @@ -3045,7 +3050,7 @@ } } } - throw new Error(msg); + throw new OwlError(msg); } return doc; } @@ -3076,7 +3081,7 @@ if (currentAsString === newAsString) { return; } - throw new Error(`Template ${name} already defined with different content`); + throw new OwlError(`Template ${name} already defined with different content`); } this.rawTemplates[name] = template; } @@ -3101,7 +3106,7 @@ extraInfo = ` (for component "${componentName}")`; } catch { } - throw new Error(`Missing template: "${name}"${extraInfo}`); + throw new OwlError(`Missing template: "${name}"${extraInfo}`); } const isFn = typeof rawTemplate === "function" && !(rawTemplate instanceof Element); const templateFn = isFn ? rawTemplate : this._compileTemplate(name, rawTemplate); @@ -3117,7 +3122,7 @@ return this.templates[name]; } _compileTemplate(name, template) { - throw new Error(`Unable to compile a template. Please use owl full build instead`); + throw new OwlError(`Unable to compile a template. Please use owl full build instead`); } callTemplate(owner, subTemplate, ctx, parent, key) { const template = this.getTemplate(subTemplate); @@ -3199,14 +3204,14 @@ i++; cur = expr[i]; if (!cur) { - throw new Error("Invalid expression"); + throw new OwlError("Invalid expression"); } s += cur; } i++; } if (expr[i] !== start) { - throw new Error("Invalid expression"); + throw new OwlError("Invalid expression"); } s += start; if (start === "`") { @@ -3313,7 +3318,7 @@ error = e; // Silence all errors and throw a generic error below } if (current.length || error) { - throw new Error(`Tokenizer error: could not tokenize \`${expr}\``); + throw new OwlError(`Tokenizer error: could not tokenize \`${expr}\``); } return result; } @@ -3864,7 +3869,7 @@ .slice(1) .map((m) => { if (!MODS.has(m)) { - throw new Error(`Unknown event modifier: '${m}'`); + throw new OwlError(`Unknown event modifier: '${m}'`); } return `"${m}"`; }); @@ -4185,7 +4190,8 @@ this.define(`key${this.target.loopLevel}`, ast.key ? compileExpr(ast.key) : loopVar); if (this.dev) { // Throw error on duplicate keys in dev mode - this.addLine(`if (keys${block.id}.has(key${this.target.loopLevel})) { throw new Error(\`Got duplicate key in t-foreach: \${key${this.target.loopLevel}}\`)}`); + this.helpers.add("OwlError"); + this.addLine(`if (keys${block.id}.has(key${this.target.loopLevel})) { throw new OwlError(\`Got duplicate key in t-foreach: \${key${this.target.loopLevel}}\`)}`); this.addLine(`keys${block.id}.add(key${this.target.loopLevel});`); } let id; @@ -4399,7 +4405,7 @@ value = `bind(ctx, ${value || undefined})`; } else { - throw new Error("Invalid prop suffix"); + throw new OwlError("Invalid prop suffix"); } } name = /^[a-z_]+$/i.test(name) ? name : `'${name}'`; @@ -4592,9 +4598,6 @@ } } - // ----------------------------------------------------------------------------- - // AST Type definition - // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // Parser // ----------------------------------------------------------------------------- @@ -4703,7 +4706,7 @@ return null; } if (tagName.startsWith("block-")) { - throw new Error(`Invalid tag name: '${tagName}'`); + throw new OwlError(`Invalid tag name: '${tagName}'`); } ctx = Object.assign({}, ctx); if (tagName === "pre") { @@ -4722,14 +4725,14 @@ const value = node.getAttribute(attr); if (attr.startsWith("t-on")) { if (attr === "t-on") { - throw new Error("Missing event name with t-on directive"); + throw new OwlError("Missing event name with t-on directive"); } on = on || {}; on[attr.slice(5)] = value; } else if (attr.startsWith("t-model")) { if (!["input", "select", "textarea"].includes(tagName)) { - throw new Error("The t-model directive only works with ,