@odoo/owl 2.0.0-beta-12 → 2.0.0-beta-16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/owl.es.js CHANGED
@@ -77,6 +77,68 @@ function toggler(key, child) {
77
77
  return new VToggler(key, child);
78
78
  }
79
79
 
80
+ // Custom error class that wraps error that happen in the owl lifecycle
81
+ class OwlError extends Error {
82
+ }
83
+ // Maps fibers to thrown errors
84
+ const fibersInError = new WeakMap();
85
+ const nodeErrorHandlers = new WeakMap();
86
+ function _handleError(node, error) {
87
+ if (!node) {
88
+ return false;
89
+ }
90
+ const fiber = node.fiber;
91
+ if (fiber) {
92
+ fibersInError.set(fiber, error);
93
+ }
94
+ const errorHandlers = nodeErrorHandlers.get(node);
95
+ if (errorHandlers) {
96
+ let handled = false;
97
+ // execute in the opposite order
98
+ for (let i = errorHandlers.length - 1; i >= 0; i--) {
99
+ try {
100
+ errorHandlers[i](error);
101
+ handled = true;
102
+ break;
103
+ }
104
+ catch (e) {
105
+ error = e;
106
+ }
107
+ }
108
+ if (handled) {
109
+ return true;
110
+ }
111
+ }
112
+ return _handleError(node.parent, error);
113
+ }
114
+ function handleError(params) {
115
+ let { error } = params;
116
+ // Wrap error if it wasn't wrapped by wrapError (ie when not in dev mode)
117
+ if (!(error instanceof OwlError)) {
118
+ error = Object.assign(new OwlError(`An error occured in the owl lifecycle (see this Error's "cause" property)`), { cause: error });
119
+ }
120
+ const node = "node" in params ? params.node : params.fiber.node;
121
+ const fiber = "fiber" in params ? params.fiber : node.fiber;
122
+ // resets the fibers on components if possible. This is important so that
123
+ // new renderings can be properly included in the initial one, if any.
124
+ let current = fiber;
125
+ do {
126
+ current.node.fiber = current;
127
+ current = current.parent;
128
+ } while (current);
129
+ fibersInError.set(fiber.root, error);
130
+ const handled = _handleError(node, error);
131
+ if (!handled) {
132
+ console.warn(`[Owl] Unhandled error. Destroying the root component`);
133
+ try {
134
+ node.app.destroy();
135
+ }
136
+ catch (e) {
137
+ console.error(e);
138
+ }
139
+ }
140
+ }
141
+
80
142
  const { setAttribute: elemSetAttribute, removeAttribute } = Element.prototype;
81
143
  const tokenList = DOMTokenList.prototype;
82
144
  const tokenListAdd = tokenList.add;
@@ -214,7 +276,7 @@ function updateClass(val, oldVal) {
214
276
  function makePropSetter(name) {
215
277
  return function setProp(value) {
216
278
  // support 0, fallback to empty string for other falsy values
217
- this[name] = value === 0 ? 0 : value || "";
279
+ this[name] = value === 0 ? 0 : value ? value.valueOf() : "";
218
280
  };
219
281
  }
220
282
  function isProp(tag, key) {
@@ -698,7 +760,7 @@ function buildTree(node, parent = null, domParentTree = null) {
698
760
  };
699
761
  }
700
762
  }
701
- throw new Error("boom");
763
+ throw new OwlError("boom");
702
764
  }
703
765
  function addRef(tree) {
704
766
  tree.isRef = true;
@@ -1382,61 +1444,6 @@ function remove(vnode, withBeforeRemove = false) {
1382
1444
  vnode.remove();
1383
1445
  }
1384
1446
 
1385
- // Maps fibers to thrown errors
1386
- const fibersInError = new WeakMap();
1387
- const nodeErrorHandlers = new WeakMap();
1388
- function _handleError(node, error) {
1389
- if (!node) {
1390
- return false;
1391
- }
1392
- const fiber = node.fiber;
1393
- if (fiber) {
1394
- fibersInError.set(fiber, error);
1395
- }
1396
- const errorHandlers = nodeErrorHandlers.get(node);
1397
- if (errorHandlers) {
1398
- let handled = false;
1399
- // execute in the opposite order
1400
- for (let i = errorHandlers.length - 1; i >= 0; i--) {
1401
- try {
1402
- errorHandlers[i](error);
1403
- handled = true;
1404
- break;
1405
- }
1406
- catch (e) {
1407
- error = e;
1408
- }
1409
- }
1410
- if (handled) {
1411
- return true;
1412
- }
1413
- }
1414
- return _handleError(node.parent, error);
1415
- }
1416
- function handleError(params) {
1417
- const error = params.error;
1418
- const node = "node" in params ? params.node : params.fiber.node;
1419
- const fiber = "fiber" in params ? params.fiber : node.fiber;
1420
- // resets the fibers on components if possible. This is important so that
1421
- // new renderings can be properly included in the initial one, if any.
1422
- let current = fiber;
1423
- do {
1424
- current.node.fiber = current;
1425
- current = current.parent;
1426
- } while (current);
1427
- fibersInError.set(fiber.root, error);
1428
- const handled = _handleError(node, error);
1429
- if (!handled) {
1430
- console.warn(`[Owl] Unhandled error. Destroying the root component`);
1431
- try {
1432
- node.app.destroy();
1433
- }
1434
- catch (e) {
1435
- console.error(e);
1436
- }
1437
- }
1438
- }
1439
-
1440
1447
  function makeChildFiber(node, parent) {
1441
1448
  let current = node.fiber;
1442
1449
  if (current) {
@@ -1475,7 +1482,7 @@ function makeRootFiber(node) {
1475
1482
  return fiber;
1476
1483
  }
1477
1484
  function throwOnRender() {
1478
- throw new Error("Attempted to render cancelled fiber");
1485
+ throw new OwlError("Attempted to render cancelled fiber");
1479
1486
  }
1480
1487
  /**
1481
1488
  * @returns number of not-yet rendered fibers cancelled
@@ -1852,7 +1859,7 @@ const reactiveCache = new WeakMap();
1852
1859
  */
1853
1860
  function reactive(target, callback = () => { }) {
1854
1861
  if (!canBeMadeReactive(target)) {
1855
- throw new Error(`Cannot make the given value reactive`);
1862
+ throw new OwlError(`Cannot make the given value reactive`);
1856
1863
  }
1857
1864
  if (SKIP in target) {
1858
1865
  return target;
@@ -2121,10 +2128,10 @@ function batched(callback) {
2121
2128
  }
2122
2129
  function validateTarget(target) {
2123
2130
  if (!(target instanceof HTMLElement)) {
2124
- throw new Error("Cannot mount component: the target is not a valid DOM element");
2131
+ throw new OwlError("Cannot mount component: the target is not a valid DOM element");
2125
2132
  }
2126
2133
  if (!document.body.contains(target)) {
2127
- throw new Error("Cannot mount a component on a detached dom node");
2134
+ throw new OwlError("Cannot mount a component on a detached dom node");
2128
2135
  }
2129
2136
  }
2130
2137
  class EventBus extends EventTarget {
@@ -2145,7 +2152,7 @@ function whenReady(fn) {
2145
2152
  async function loadFile(url) {
2146
2153
  const result = await fetch(url);
2147
2154
  if (!result.ok) {
2148
- throw new Error("Error while fetching xml templates");
2155
+ throw new OwlError("Error while fetching xml templates");
2149
2156
  }
2150
2157
  return await result.text();
2151
2158
  }
@@ -2167,7 +2174,7 @@ function markup(value) {
2167
2174
  let currentNode = null;
2168
2175
  function getCurrent() {
2169
2176
  if (!currentNode) {
2170
- throw new Error("No active component (a hook function should only be called in 'setup')");
2177
+ throw new OwlError("No active component (a hook function should only be called in 'setup')");
2171
2178
  }
2172
2179
  return currentNode;
2173
2180
  }
@@ -2229,7 +2236,6 @@ class ComponentNode {
2229
2236
  this.parent = parent;
2230
2237
  this.props = props;
2231
2238
  this.parentKey = parentKey;
2232
- this.level = parent ? parent.level + 1 : 0;
2233
2239
  const defaultProps = C.defaultProps;
2234
2240
  props = Object.assign({}, props);
2235
2241
  if (defaultProps) {
@@ -2462,17 +2468,27 @@ class ComponentNode {
2462
2468
 
2463
2469
  const TIMEOUT = Symbol("timeout");
2464
2470
  function wrapError(fn, hookName) {
2465
- const error = new Error(`The following error occurred in ${hookName}: `);
2466
- const timeoutError = new Error(`${hookName}'s promise hasn't resolved after 3 seconds`);
2471
+ const error = new OwlError(`The following error occurred in ${hookName}: `);
2472
+ const timeoutError = new OwlError(`${hookName}'s promise hasn't resolved after 3 seconds`);
2467
2473
  const node = getCurrent();
2468
2474
  return (...args) => {
2475
+ const onError = (cause) => {
2476
+ error.cause = cause;
2477
+ if (cause instanceof Error) {
2478
+ error.message += `"${cause.message}"`;
2479
+ }
2480
+ else {
2481
+ error.message = `Something that is not an Error was thrown in ${hookName} (see this Error's "cause" property)`;
2482
+ }
2483
+ throw error;
2484
+ };
2469
2485
  try {
2470
2486
  const result = fn(...args);
2471
2487
  if (result instanceof Promise) {
2472
2488
  if (hookName === "onWillStart" || hookName === "onWillUpdateProps") {
2473
2489
  const fiber = node.fiber;
2474
2490
  Promise.race([
2475
- result,
2491
+ result.catch(() => { }),
2476
2492
  new Promise((resolve) => setTimeout(() => resolve(TIMEOUT), 3000)),
2477
2493
  ]).then((res) => {
2478
2494
  if (res === TIMEOUT && node.fiber === fiber) {
@@ -2480,21 +2496,12 @@ function wrapError(fn, hookName) {
2480
2496
  }
2481
2497
  });
2482
2498
  }
2483
- return result.catch((cause) => {
2484
- error.cause = cause;
2485
- if (cause instanceof Error) {
2486
- error.message += `"${cause.message}"`;
2487
- }
2488
- throw error;
2489
- });
2499
+ return result.catch(onError);
2490
2500
  }
2491
2501
  return result;
2492
2502
  }
2493
2503
  catch (cause) {
2494
- if (cause instanceof Error) {
2495
- error.message += `"${cause.message}"`;
2496
- }
2497
- throw error;
2504
+ onError(cause);
2498
2505
  }
2499
2506
  };
2500
2507
  }
@@ -2598,7 +2605,7 @@ class VPortal extends VText {
2598
2605
  }
2599
2606
  this.target = el && el.querySelector(this.selector);
2600
2607
  if (!this.target) {
2601
- throw new Error("invalid portal target");
2608
+ throw new OwlError("invalid portal target");
2602
2609
  }
2603
2610
  }
2604
2611
  this.realBDom.mount(this.target, null);
@@ -2692,7 +2699,7 @@ function toSchema(spec) {
2692
2699
  function validate(obj, spec) {
2693
2700
  let errors = validateSchema(obj, spec);
2694
2701
  if (errors.length) {
2695
- throw new Error("Invalid object: " + errors.join(", "));
2702
+ throw new OwlError("Invalid object: " + errors.join(", "));
2696
2703
  }
2697
2704
  }
2698
2705
  /**
@@ -2845,7 +2852,7 @@ function prepareList(collection) {
2845
2852
  keys = Object.values(collection);
2846
2853
  }
2847
2854
  else {
2848
- throw new Error("Invalid loop expression");
2855
+ throw new OwlError("Invalid loop expression");
2849
2856
  }
2850
2857
  const n = values.length;
2851
2858
  return [keys, values, n, new Array(n)];
@@ -2951,7 +2958,7 @@ function multiRefSetter(refs, name) {
2951
2958
  if (el) {
2952
2959
  count++;
2953
2960
  if (count > 1) {
2954
- throw new Error("Cannot have 2 elements with same ref name at the same time");
2961
+ throw new OwlError("Cannot have 2 elements with same ref name at the same time");
2955
2962
  }
2956
2963
  }
2957
2964
  if (count === 0 || el) {
@@ -2988,13 +2995,13 @@ function validateProps(name, props, parent) {
2988
2995
  : name in schema && !("*" in schema) && !isOptional(schema[name]);
2989
2996
  for (let p in defaultProps) {
2990
2997
  if (isMandatory(p)) {
2991
- throw new Error(`A default value cannot be defined for a mandatory prop (name: '${p}', component: ${ComponentClass.name})`);
2998
+ throw new OwlError(`A default value cannot be defined for a mandatory prop (name: '${p}', component: ${ComponentClass.name})`);
2992
2999
  }
2993
3000
  }
2994
3001
  }
2995
3002
  const errors = validateSchema(props, schema);
2996
3003
  if (errors.length) {
2997
- throw new Error(`Invalid props for component '${ComponentClass.name}': ` + errors.join(", "));
3004
+ throw new OwlError(`Invalid props for component '${ComponentClass.name}': ` + errors.join(", "));
2998
3005
  }
2999
3006
  }
3000
3007
  const helpers = {
@@ -3015,6 +3022,7 @@ const helpers = {
3015
3022
  bind,
3016
3023
  createCatcher,
3017
3024
  markRaw,
3025
+ OwlError,
3018
3026
  };
3019
3027
 
3020
3028
  const bdom = { text, createBlock, list, multi, html, toggler, comment };
@@ -3042,7 +3050,7 @@ function parseXML$1(xml) {
3042
3050
  }
3043
3051
  }
3044
3052
  }
3045
- throw new Error(msg);
3053
+ throw new OwlError(msg);
3046
3054
  }
3047
3055
  return doc;
3048
3056
  }
@@ -3073,7 +3081,7 @@ class TemplateSet {
3073
3081
  if (currentAsString === newAsString) {
3074
3082
  return;
3075
3083
  }
3076
- throw new Error(`Template ${name} already defined with different content`);
3084
+ throw new OwlError(`Template ${name} already defined with different content`);
3077
3085
  }
3078
3086
  this.rawTemplates[name] = template;
3079
3087
  }
@@ -3098,7 +3106,7 @@ class TemplateSet {
3098
3106
  extraInfo = ` (for component "${componentName}")`;
3099
3107
  }
3100
3108
  catch { }
3101
- throw new Error(`Missing template: "${name}"${extraInfo}`);
3109
+ throw new OwlError(`Missing template: "${name}"${extraInfo}`);
3102
3110
  }
3103
3111
  const isFn = typeof rawTemplate === "function" && !(rawTemplate instanceof Element);
3104
3112
  const templateFn = isFn ? rawTemplate : this._compileTemplate(name, rawTemplate);
@@ -3114,7 +3122,7 @@ class TemplateSet {
3114
3122
  return this.templates[name];
3115
3123
  }
3116
3124
  _compileTemplate(name, template) {
3117
- throw new Error(`Unable to compile a template. Please use owl full build instead`);
3125
+ throw new OwlError(`Unable to compile a template. Please use owl full build instead`);
3118
3126
  }
3119
3127
  callTemplate(owner, subTemplate, ctx, parent, key) {
3120
3128
  const template = this.getTemplate(subTemplate);
@@ -3196,14 +3204,14 @@ let tokenizeString = function (expr) {
3196
3204
  i++;
3197
3205
  cur = expr[i];
3198
3206
  if (!cur) {
3199
- throw new Error("Invalid expression");
3207
+ throw new OwlError("Invalid expression");
3200
3208
  }
3201
3209
  s += cur;
3202
3210
  }
3203
3211
  i++;
3204
3212
  }
3205
3213
  if (expr[i] !== start) {
3206
- throw new Error("Invalid expression");
3214
+ throw new OwlError("Invalid expression");
3207
3215
  }
3208
3216
  s += start;
3209
3217
  if (start === "`") {
@@ -3310,7 +3318,7 @@ function tokenize(expr) {
3310
3318
  error = e; // Silence all errors and throw a generic error below
3311
3319
  }
3312
3320
  if (current.length || error) {
3313
- throw new Error(`Tokenizer error: could not tokenize \`${expr}\``);
3321
+ throw new OwlError(`Tokenizer error: could not tokenize \`${expr}\``);
3314
3322
  }
3315
3323
  return result;
3316
3324
  }
@@ -3861,7 +3869,7 @@ class CodeGenerator {
3861
3869
  .slice(1)
3862
3870
  .map((m) => {
3863
3871
  if (!MODS.has(m)) {
3864
- throw new Error(`Unknown event modifier: '${m}'`);
3872
+ throw new OwlError(`Unknown event modifier: '${m}'`);
3865
3873
  }
3866
3874
  return `"${m}"`;
3867
3875
  });
@@ -3903,13 +3911,18 @@ class CodeGenerator {
3903
3911
  attrs["block-attribute-" + idx] = attrName;
3904
3912
  }
3905
3913
  else if (key.startsWith("t-att")) {
3914
+ attrName = key === "t-att" ? null : key.slice(6);
3906
3915
  expr = compileExpr(ast.attrs[key]);
3916
+ if (attrName && isProp(ast.tag, attrName)) {
3917
+ // we force a new string or new boolean to bypass the equality check in blockdom when patching same value
3918
+ const C = attrName === "value" ? "String" : "Boolean";
3919
+ expr = `new ${C}(${expr})`;
3920
+ }
3907
3921
  const idx = block.insertData(expr, "attr");
3908
3922
  if (key === "t-att") {
3909
3923
  attrs[`block-attributes`] = String(idx);
3910
3924
  }
3911
3925
  else {
3912
- attrName = key.slice(6);
3913
3926
  attrs[`block-attribute-${idx}`] = attrName;
3914
3927
  }
3915
3928
  }
@@ -4177,7 +4190,8 @@ class CodeGenerator {
4177
4190
  this.define(`key${this.target.loopLevel}`, ast.key ? compileExpr(ast.key) : loopVar);
4178
4191
  if (this.dev) {
4179
4192
  // Throw error on duplicate keys in dev mode
4180
- this.addLine(`if (keys${block.id}.has(key${this.target.loopLevel})) { throw new Error(\`Got duplicate key in t-foreach: \${key${this.target.loopLevel}}\`)}`);
4193
+ this.helpers.add("OwlError");
4194
+ this.addLine(`if (keys${block.id}.has(key${this.target.loopLevel})) { throw new OwlError(\`Got duplicate key in t-foreach: \${key${this.target.loopLevel}}\`)}`);
4181
4195
  this.addLine(`keys${block.id}.add(key${this.target.loopLevel});`);
4182
4196
  }
4183
4197
  let id;
@@ -4391,7 +4405,7 @@ class CodeGenerator {
4391
4405
  value = `bind(ctx, ${value || undefined})`;
4392
4406
  }
4393
4407
  else {
4394
- throw new Error("Invalid prop suffix");
4408
+ throw new OwlError("Invalid prop suffix");
4395
4409
  }
4396
4410
  }
4397
4411
  name = /^[a-z_]+$/i.test(name) ? name : `'${name}'`;
@@ -4584,9 +4598,6 @@ class CodeGenerator {
4584
4598
  }
4585
4599
  }
4586
4600
 
4587
- // -----------------------------------------------------------------------------
4588
- // AST Type definition
4589
- // -----------------------------------------------------------------------------
4590
4601
  // -----------------------------------------------------------------------------
4591
4602
  // Parser
4592
4603
  // -----------------------------------------------------------------------------
@@ -4695,7 +4706,7 @@ function parseDOMNode(node, ctx) {
4695
4706
  return null;
4696
4707
  }
4697
4708
  if (tagName.startsWith("block-")) {
4698
- throw new Error(`Invalid tag name: '${tagName}'`);
4709
+ throw new OwlError(`Invalid tag name: '${tagName}'`);
4699
4710
  }
4700
4711
  ctx = Object.assign({}, ctx);
4701
4712
  if (tagName === "pre") {
@@ -4714,14 +4725,14 @@ function parseDOMNode(node, ctx) {
4714
4725
  const value = node.getAttribute(attr);
4715
4726
  if (attr.startsWith("t-on")) {
4716
4727
  if (attr === "t-on") {
4717
- throw new Error("Missing event name with t-on directive");
4728
+ throw new OwlError("Missing event name with t-on directive");
4718
4729
  }
4719
4730
  on = on || {};
4720
4731
  on[attr.slice(5)] = value;
4721
4732
  }
4722
4733
  else if (attr.startsWith("t-model")) {
4723
4734
  if (!["input", "select", "textarea"].includes(tagName)) {
4724
- throw new Error("The t-model directive only works with <input>, <textarea> and <select>");
4735
+ throw new OwlError("The t-model directive only works with <input>, <textarea> and <select>");
4725
4736
  }
4726
4737
  let baseExpr, expr;
4727
4738
  if (hasDotAtTheEnd.test(value)) {
@@ -4735,7 +4746,7 @@ function parseDOMNode(node, ctx) {
4735
4746
  expr = value.slice(index + 1, -1);
4736
4747
  }
4737
4748
  else {
4738
- throw new Error(`Invalid t-model expression: "${value}" (it should be assignable)`);
4749
+ throw new OwlError(`Invalid t-model expression: "${value}" (it should be assignable)`);
4739
4750
  }
4740
4751
  const typeAttr = node.getAttribute("type");
4741
4752
  const isInput = tagName === "input";
@@ -4765,11 +4776,11 @@ function parseDOMNode(node, ctx) {
4765
4776
  }
4766
4777
  }
4767
4778
  else if (attr.startsWith("block-")) {
4768
- throw new Error(`Invalid attribute: '${attr}'`);
4779
+ throw new OwlError(`Invalid attribute: '${attr}'`);
4769
4780
  }
4770
4781
  else if (attr !== "t-name") {
4771
4782
  if (attr.startsWith("t-") && !attr.startsWith("t-att")) {
4772
- throw new Error(`Unknown QWeb directive: '${attr}'`);
4783
+ throw new OwlError(`Unknown QWeb directive: '${attr}'`);
4773
4784
  }
4774
4785
  const tModel = ctx.tModelInfo;
4775
4786
  if (tModel && ["t-att-value", "t-attf-value"].includes(attr)) {
@@ -4820,7 +4831,7 @@ function parseTEscNode(node, ctx) {
4820
4831
  };
4821
4832
  }
4822
4833
  if (ast.type === 11 /* TComponent */) {
4823
- throw new Error("t-esc is not supported on Component nodes");
4834
+ throw new OwlError("t-esc is not supported on Component nodes");
4824
4835
  }
4825
4836
  return tesc;
4826
4837
  }
@@ -4868,7 +4879,7 @@ function parseTForEach(node, ctx) {
4868
4879
  node.removeAttribute("t-as");
4869
4880
  const key = node.getAttribute("t-key");
4870
4881
  if (!key) {
4871
- throw new Error(`"Directive t-foreach should always be used with a t-key!" (expression: t-foreach="${collection}" t-as="${elem}")`);
4882
+ throw new OwlError(`"Directive t-foreach should always be used with a t-key!" (expression: t-foreach="${collection}" t-as="${elem}")`);
4872
4883
  }
4873
4884
  node.removeAttribute("t-key");
4874
4885
  const memo = node.getAttribute("t-memo") || "";
@@ -5028,7 +5039,7 @@ function parseComponent(node, ctx) {
5028
5039
  const firstLetter = name[0];
5029
5040
  let isDynamic = node.hasAttribute("t-component");
5030
5041
  if (isDynamic && name !== "t") {
5031
- throw new Error(`Directive 't-component' can only be used on <t> nodes (used on a <${name}>)`);
5042
+ throw new OwlError(`Directive 't-component' can only be used on <t> nodes (used on a <${name}>)`);
5032
5043
  }
5033
5044
  if (!(firstLetter === firstLetter.toUpperCase() || isDynamic)) {
5034
5045
  return null;
@@ -5052,7 +5063,7 @@ function parseComponent(node, ctx) {
5052
5063
  }
5053
5064
  else {
5054
5065
  const message = directiveErrorMap.get(name.split("-").slice(0, 2).join("-"));
5055
- throw new Error(message || `unsupported directive on Component: ${name}`);
5066
+ throw new OwlError(message || `unsupported directive on Component: ${name}`);
5056
5067
  }
5057
5068
  }
5058
5069
  else {
@@ -5067,7 +5078,7 @@ function parseComponent(node, ctx) {
5067
5078
  const slotNodes = Array.from(clone.querySelectorAll("[t-set-slot]"));
5068
5079
  for (let slotNode of slotNodes) {
5069
5080
  if (slotNode.tagName !== "t") {
5070
- throw new Error(`Directive 't-set-slot' can only be used on <t> nodes (used on a <${slotNode.tagName}>)`);
5081
+ throw new OwlError(`Directive 't-set-slot' can only be used on <t> nodes (used on a <${slotNode.tagName}>)`);
5071
5082
  }
5072
5083
  const name = slotNode.getAttribute("t-set-slot");
5073
5084
  // check if this is defined in a sub component (in which case it should
@@ -5232,25 +5243,25 @@ function normalizeTIf(el) {
5232
5243
  let nattr = (name) => +!!node.getAttribute(name);
5233
5244
  if (prevElem && (pattr("t-if") || pattr("t-elif"))) {
5234
5245
  if (pattr("t-foreach")) {
5235
- throw new Error("t-if cannot stay at the same level as t-foreach when using t-elif or t-else");
5246
+ throw new OwlError("t-if cannot stay at the same level as t-foreach when using t-elif or t-else");
5236
5247
  }
5237
5248
  if (["t-if", "t-elif", "t-else"].map(nattr).reduce(function (a, b) {
5238
5249
  return a + b;
5239
5250
  }) > 1) {
5240
- throw new Error("Only one conditional branching directive is allowed per node");
5251
+ throw new OwlError("Only one conditional branching directive is allowed per node");
5241
5252
  }
5242
5253
  // All text (with only spaces) and comment nodes (nodeType 8) between
5243
5254
  // branch nodes are removed
5244
5255
  let textNode;
5245
5256
  while ((textNode = node.previousSibling) !== prevElem) {
5246
5257
  if (textNode.nodeValue.trim().length && textNode.nodeType !== 8) {
5247
- throw new Error("text is not allowed between branching directives");
5258
+ throw new OwlError("text is not allowed between branching directives");
5248
5259
  }
5249
5260
  textNode.remove();
5250
5261
  }
5251
5262
  }
5252
5263
  else {
5253
- throw new Error("t-elif and t-else directives must be preceded by a t-if or t-elif directive");
5264
+ throw new OwlError("t-elif and t-else directives must be preceded by a t-if or t-elif directive");
5254
5265
  }
5255
5266
  }
5256
5267
  }
@@ -5266,7 +5277,7 @@ function normalizeTEsc(el) {
5266
5277
  const elements = [...el.querySelectorAll("[t-esc]")].filter((el) => el.tagName[0] === el.tagName[0].toUpperCase() || el.hasAttribute("t-component"));
5267
5278
  for (const el of elements) {
5268
5279
  if (el.childNodes.length) {
5269
- throw new Error("Cannot have t-esc on a component that already has content");
5280
+ throw new OwlError("Cannot have t-esc on a component that already has content");
5270
5281
  }
5271
5282
  const value = el.getAttribute("t-esc");
5272
5283
  el.removeAttribute("t-esc");
@@ -5318,7 +5329,7 @@ function parseXML(xml) {
5318
5329
  }
5319
5330
  }
5320
5331
  }
5321
- throw new Error(msg);
5332
+ throw new OwlError(msg);
5322
5333
  }
5323
5334
  return doc;
5324
5335
  }
@@ -5372,7 +5383,7 @@ const mainEventHandler = (data, ev, currentTarget) => {
5372
5383
  if (Object.hasOwnProperty.call(data, 0)) {
5373
5384
  const handler = data[0];
5374
5385
  if (typeof handler !== "function") {
5375
- throw new Error(`Invalid handler (expected a function, received: '${handler}')`);
5386
+ throw new OwlError(`Invalid handler (expected a function, received: '${handler}')`);
5376
5387
  }
5377
5388
  let node = data[1] ? data[1].__owl__ : null;
5378
5389
  if (node ? node.status === 1 /* MOUNTED */ : true) {
@@ -5556,10 +5567,10 @@ class App extends TemplateSet {
5556
5567
  if (isStatic) {
5557
5568
  C = parent.constructor.components[name];
5558
5569
  if (!C) {
5559
- throw new Error(`Cannot find the definition of component "${name}"`);
5570
+ throw new OwlError(`Cannot find the definition of component "${name}"`);
5560
5571
  }
5561
5572
  else if (!(C.prototype instanceof Component)) {
5562
- throw new Error(`"${name}" is not a Component. It must inherit from the Component class`);
5573
+ throw new OwlError(`"${name}" is not a Component. It must inherit from the Component class`);
5563
5574
  }
5564
5575
  }
5565
5576
  node = new ComponentNode(C, props, this, ctx, key);
@@ -5715,10 +5726,10 @@ TemplateSet.prototype._compileTemplate = function _compileTemplate(name, templat
5715
5726
  });
5716
5727
  };
5717
5728
 
5718
- export { App, Component, EventBus, __info__, 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, whenReady, xml };
5729
+ export { App, Component, EventBus, OwlError, __info__, 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, whenReady, xml };
5719
5730
 
5720
5731
 
5721
- __info__.version = '2.0.0-beta-12';
5722
- __info__.date = '2022-06-29T09:13:12.680Z';
5723
- __info__.hash = '6c72e0a';
5732
+ __info__.version = '2.0.0-beta-16';
5733
+ __info__.date = '2022-07-22T07:43:54.584Z';
5734
+ __info__.hash = 'b90aa0e';
5724
5735
  __info__.url = 'https://github.com/odoo/owl';