@odoo/owl 2.0.0-beta-14 → 2.0.0-beta-17

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.cjs.js CHANGED
@@ -81,6 +81,68 @@ function toggler(key, child) {
81
81
  return new VToggler(key, child);
82
82
  }
83
83
 
84
+ // Custom error class that wraps error that happen in the owl lifecycle
85
+ class OwlError extends Error {
86
+ }
87
+ // Maps fibers to thrown errors
88
+ const fibersInError = new WeakMap();
89
+ const nodeErrorHandlers = new WeakMap();
90
+ function _handleError(node, error) {
91
+ if (!node) {
92
+ return false;
93
+ }
94
+ const fiber = node.fiber;
95
+ if (fiber) {
96
+ fibersInError.set(fiber, error);
97
+ }
98
+ const errorHandlers = nodeErrorHandlers.get(node);
99
+ if (errorHandlers) {
100
+ let handled = false;
101
+ // execute in the opposite order
102
+ for (let i = errorHandlers.length - 1; i >= 0; i--) {
103
+ try {
104
+ errorHandlers[i](error);
105
+ handled = true;
106
+ break;
107
+ }
108
+ catch (e) {
109
+ error = e;
110
+ }
111
+ }
112
+ if (handled) {
113
+ return true;
114
+ }
115
+ }
116
+ return _handleError(node.parent, error);
117
+ }
118
+ function handleError(params) {
119
+ let { error } = params;
120
+ // Wrap error if it wasn't wrapped by wrapError (ie when not in dev mode)
121
+ if (!(error instanceof OwlError)) {
122
+ error = Object.assign(new OwlError(`An error occured in the owl lifecycle (see this Error's "cause" property)`), { cause: error });
123
+ }
124
+ const node = "node" in params ? params.node : params.fiber.node;
125
+ const fiber = "fiber" in params ? params.fiber : node.fiber;
126
+ // resets the fibers on components if possible. This is important so that
127
+ // new renderings can be properly included in the initial one, if any.
128
+ let current = fiber;
129
+ do {
130
+ current.node.fiber = current;
131
+ current = current.parent;
132
+ } while (current);
133
+ fibersInError.set(fiber.root, error);
134
+ const handled = _handleError(node, error);
135
+ if (!handled) {
136
+ console.warn(`[Owl] Unhandled error. Destroying the root component`);
137
+ try {
138
+ node.app.destroy();
139
+ }
140
+ catch (e) {
141
+ console.error(e);
142
+ }
143
+ }
144
+ }
145
+
84
146
  const { setAttribute: elemSetAttribute, removeAttribute } = Element.prototype;
85
147
  const tokenList = DOMTokenList.prototype;
86
148
  const tokenListAdd = tokenList.add;
@@ -702,7 +764,7 @@ function buildTree(node, parent = null, domParentTree = null) {
702
764
  };
703
765
  }
704
766
  }
705
- throw new Error("boom");
767
+ throw new OwlError("boom");
706
768
  }
707
769
  function addRef(tree) {
708
770
  tree.isRef = true;
@@ -1386,61 +1448,6 @@ function remove(vnode, withBeforeRemove = false) {
1386
1448
  vnode.remove();
1387
1449
  }
1388
1450
 
1389
- // Maps fibers to thrown errors
1390
- const fibersInError = new WeakMap();
1391
- const nodeErrorHandlers = new WeakMap();
1392
- function _handleError(node, error) {
1393
- if (!node) {
1394
- return false;
1395
- }
1396
- const fiber = node.fiber;
1397
- if (fiber) {
1398
- fibersInError.set(fiber, error);
1399
- }
1400
- const errorHandlers = nodeErrorHandlers.get(node);
1401
- if (errorHandlers) {
1402
- let handled = false;
1403
- // execute in the opposite order
1404
- for (let i = errorHandlers.length - 1; i >= 0; i--) {
1405
- try {
1406
- errorHandlers[i](error);
1407
- handled = true;
1408
- break;
1409
- }
1410
- catch (e) {
1411
- error = e;
1412
- }
1413
- }
1414
- if (handled) {
1415
- return true;
1416
- }
1417
- }
1418
- return _handleError(node.parent, error);
1419
- }
1420
- function handleError(params) {
1421
- const error = params.error;
1422
- const node = "node" in params ? params.node : params.fiber.node;
1423
- const fiber = "fiber" in params ? params.fiber : node.fiber;
1424
- // resets the fibers on components if possible. This is important so that
1425
- // new renderings can be properly included in the initial one, if any.
1426
- let current = fiber;
1427
- do {
1428
- current.node.fiber = current;
1429
- current = current.parent;
1430
- } while (current);
1431
- fibersInError.set(fiber.root, error);
1432
- const handled = _handleError(node, error);
1433
- if (!handled) {
1434
- console.warn(`[Owl] Unhandled error. Destroying the root component`);
1435
- try {
1436
- node.app.destroy();
1437
- }
1438
- catch (e) {
1439
- console.error(e);
1440
- }
1441
- }
1442
- }
1443
-
1444
1451
  function makeChildFiber(node, parent) {
1445
1452
  let current = node.fiber;
1446
1453
  if (current) {
@@ -1479,7 +1486,7 @@ function makeRootFiber(node) {
1479
1486
  return fiber;
1480
1487
  }
1481
1488
  function throwOnRender() {
1482
- throw new Error("Attempted to render cancelled fiber");
1489
+ throw new OwlError("Attempted to render cancelled fiber");
1483
1490
  }
1484
1491
  /**
1485
1492
  * @returns number of not-yet rendered fibers cancelled
@@ -1856,7 +1863,7 @@ const reactiveCache = new WeakMap();
1856
1863
  */
1857
1864
  function reactive(target, callback = () => { }) {
1858
1865
  if (!canBeMadeReactive(target)) {
1859
- throw new Error(`Cannot make the given value reactive`);
1866
+ throw new OwlError(`Cannot make the given value reactive`);
1860
1867
  }
1861
1868
  if (SKIP in target) {
1862
1869
  return target;
@@ -2124,12 +2131,18 @@ function batched(callback) {
2124
2131
  };
2125
2132
  }
2126
2133
  function validateTarget(target) {
2127
- if (!(target instanceof HTMLElement)) {
2128
- throw new Error("Cannot mount component: the target is not a valid DOM element");
2129
- }
2130
- if (!document.body.contains(target)) {
2131
- throw new Error("Cannot mount a component on a detached dom node");
2134
+ // Get the document and HTMLElement corresponding to the target to allow mounting in iframes
2135
+ const document = target && target.ownerDocument;
2136
+ if (document) {
2137
+ const HTMLElement = document.defaultView.HTMLElement;
2138
+ if (target instanceof HTMLElement) {
2139
+ if (!document.body.contains(target)) {
2140
+ throw new OwlError("Cannot mount a component on a detached dom node");
2141
+ }
2142
+ return;
2143
+ }
2132
2144
  }
2145
+ throw new OwlError("Cannot mount component: the target is not a valid DOM element");
2133
2146
  }
2134
2147
  class EventBus extends EventTarget {
2135
2148
  trigger(name, payload) {
@@ -2149,7 +2162,7 @@ function whenReady(fn) {
2149
2162
  async function loadFile(url) {
2150
2163
  const result = await fetch(url);
2151
2164
  if (!result.ok) {
2152
- throw new Error("Error while fetching xml templates");
2165
+ throw new OwlError("Error while fetching xml templates");
2153
2166
  }
2154
2167
  return await result.text();
2155
2168
  }
@@ -2171,7 +2184,7 @@ function markup(value) {
2171
2184
  let currentNode = null;
2172
2185
  function getCurrent() {
2173
2186
  if (!currentNode) {
2174
- throw new Error("No active component (a hook function should only be called in 'setup')");
2187
+ throw new OwlError("No active component (a hook function should only be called in 'setup')");
2175
2188
  }
2176
2189
  return currentNode;
2177
2190
  }
@@ -2233,7 +2246,6 @@ class ComponentNode {
2233
2246
  this.parent = parent;
2234
2247
  this.props = props;
2235
2248
  this.parentKey = parentKey;
2236
- this.level = parent ? parent.level + 1 : 0;
2237
2249
  const defaultProps = C.defaultProps;
2238
2250
  props = Object.assign({}, props);
2239
2251
  if (defaultProps) {
@@ -2466,17 +2478,27 @@ class ComponentNode {
2466
2478
 
2467
2479
  const TIMEOUT = Symbol("timeout");
2468
2480
  function wrapError(fn, hookName) {
2469
- const error = new Error(`The following error occurred in ${hookName}: `);
2470
- const timeoutError = new Error(`${hookName}'s promise hasn't resolved after 3 seconds`);
2481
+ const error = new OwlError(`The following error occurred in ${hookName}: `);
2482
+ const timeoutError = new OwlError(`${hookName}'s promise hasn't resolved after 3 seconds`);
2471
2483
  const node = getCurrent();
2472
2484
  return (...args) => {
2485
+ const onError = (cause) => {
2486
+ error.cause = cause;
2487
+ if (cause instanceof Error) {
2488
+ error.message += `"${cause.message}"`;
2489
+ }
2490
+ else {
2491
+ error.message = `Something that is not an Error was thrown in ${hookName} (see this Error's "cause" property)`;
2492
+ }
2493
+ throw error;
2494
+ };
2473
2495
  try {
2474
2496
  const result = fn(...args);
2475
2497
  if (result instanceof Promise) {
2476
2498
  if (hookName === "onWillStart" || hookName === "onWillUpdateProps") {
2477
2499
  const fiber = node.fiber;
2478
2500
  Promise.race([
2479
- result,
2501
+ result.catch(() => { }),
2480
2502
  new Promise((resolve) => setTimeout(() => resolve(TIMEOUT), 3000)),
2481
2503
  ]).then((res) => {
2482
2504
  if (res === TIMEOUT && node.fiber === fiber) {
@@ -2484,21 +2506,12 @@ function wrapError(fn, hookName) {
2484
2506
  }
2485
2507
  });
2486
2508
  }
2487
- return result.catch((cause) => {
2488
- error.cause = cause;
2489
- if (cause instanceof Error) {
2490
- error.message += `"${cause.message}"`;
2491
- }
2492
- throw error;
2493
- });
2509
+ return result.catch(onError);
2494
2510
  }
2495
2511
  return result;
2496
2512
  }
2497
2513
  catch (cause) {
2498
- if (cause instanceof Error) {
2499
- error.message += `"${cause.message}"`;
2500
- }
2501
- throw error;
2514
+ onError(cause);
2502
2515
  }
2503
2516
  };
2504
2517
  }
@@ -2602,7 +2615,7 @@ class VPortal extends VText {
2602
2615
  }
2603
2616
  this.target = el && el.querySelector(this.selector);
2604
2617
  if (!this.target) {
2605
- throw new Error("invalid portal target");
2618
+ throw new OwlError("invalid portal target");
2606
2619
  }
2607
2620
  }
2608
2621
  this.realBDom.mount(this.target, null);
@@ -2696,7 +2709,7 @@ function toSchema(spec) {
2696
2709
  function validate(obj, spec) {
2697
2710
  let errors = validateSchema(obj, spec);
2698
2711
  if (errors.length) {
2699
- throw new Error("Invalid object: " + errors.join(", "));
2712
+ throw new OwlError("Invalid object: " + errors.join(", "));
2700
2713
  }
2701
2714
  }
2702
2715
  /**
@@ -2849,7 +2862,7 @@ function prepareList(collection) {
2849
2862
  keys = Object.values(collection);
2850
2863
  }
2851
2864
  else {
2852
- throw new Error("Invalid loop expression");
2865
+ throw new OwlError("Invalid loop expression");
2853
2866
  }
2854
2867
  const n = values.length;
2855
2868
  return [keys, values, n, new Array(n)];
@@ -2955,7 +2968,7 @@ function multiRefSetter(refs, name) {
2955
2968
  if (el) {
2956
2969
  count++;
2957
2970
  if (count > 1) {
2958
- throw new Error("Cannot have 2 elements with same ref name at the same time");
2971
+ throw new OwlError("Cannot have 2 elements with same ref name at the same time");
2959
2972
  }
2960
2973
  }
2961
2974
  if (count === 0 || el) {
@@ -2992,13 +3005,13 @@ function validateProps(name, props, parent) {
2992
3005
  : name in schema && !("*" in schema) && !isOptional(schema[name]);
2993
3006
  for (let p in defaultProps) {
2994
3007
  if (isMandatory(p)) {
2995
- throw new Error(`A default value cannot be defined for a mandatory prop (name: '${p}', component: ${ComponentClass.name})`);
3008
+ throw new OwlError(`A default value cannot be defined for a mandatory prop (name: '${p}', component: ${ComponentClass.name})`);
2996
3009
  }
2997
3010
  }
2998
3011
  }
2999
3012
  const errors = validateSchema(props, schema);
3000
3013
  if (errors.length) {
3001
- throw new Error(`Invalid props for component '${ComponentClass.name}': ` + errors.join(", "));
3014
+ throw new OwlError(`Invalid props for component '${ComponentClass.name}': ` + errors.join(", "));
3002
3015
  }
3003
3016
  }
3004
3017
  const helpers = {
@@ -3019,6 +3032,7 @@ const helpers = {
3019
3032
  bind,
3020
3033
  createCatcher,
3021
3034
  markRaw,
3035
+ OwlError,
3022
3036
  };
3023
3037
 
3024
3038
  const bdom = { text, createBlock, list, multi, html, toggler, comment };
@@ -3046,7 +3060,7 @@ function parseXML$1(xml) {
3046
3060
  }
3047
3061
  }
3048
3062
  }
3049
- throw new Error(msg);
3063
+ throw new OwlError(msg);
3050
3064
  }
3051
3065
  return doc;
3052
3066
  }
@@ -3077,7 +3091,7 @@ class TemplateSet {
3077
3091
  if (currentAsString === newAsString) {
3078
3092
  return;
3079
3093
  }
3080
- throw new Error(`Template ${name} already defined with different content`);
3094
+ throw new OwlError(`Template ${name} already defined with different content`);
3081
3095
  }
3082
3096
  this.rawTemplates[name] = template;
3083
3097
  }
@@ -3102,7 +3116,7 @@ class TemplateSet {
3102
3116
  extraInfo = ` (for component "${componentName}")`;
3103
3117
  }
3104
3118
  catch { }
3105
- throw new Error(`Missing template: "${name}"${extraInfo}`);
3119
+ throw new OwlError(`Missing template: "${name}"${extraInfo}`);
3106
3120
  }
3107
3121
  const isFn = typeof rawTemplate === "function" && !(rawTemplate instanceof Element);
3108
3122
  const templateFn = isFn ? rawTemplate : this._compileTemplate(name, rawTemplate);
@@ -3118,7 +3132,7 @@ class TemplateSet {
3118
3132
  return this.templates[name];
3119
3133
  }
3120
3134
  _compileTemplate(name, template) {
3121
- throw new Error(`Unable to compile a template. Please use owl full build instead`);
3135
+ throw new OwlError(`Unable to compile a template. Please use owl full build instead`);
3122
3136
  }
3123
3137
  callTemplate(owner, subTemplate, ctx, parent, key) {
3124
3138
  const template = this.getTemplate(subTemplate);
@@ -3200,14 +3214,14 @@ let tokenizeString = function (expr) {
3200
3214
  i++;
3201
3215
  cur = expr[i];
3202
3216
  if (!cur) {
3203
- throw new Error("Invalid expression");
3217
+ throw new OwlError("Invalid expression");
3204
3218
  }
3205
3219
  s += cur;
3206
3220
  }
3207
3221
  i++;
3208
3222
  }
3209
3223
  if (expr[i] !== start) {
3210
- throw new Error("Invalid expression");
3224
+ throw new OwlError("Invalid expression");
3211
3225
  }
3212
3226
  s += start;
3213
3227
  if (start === "`") {
@@ -3314,7 +3328,7 @@ function tokenize(expr) {
3314
3328
  error = e; // Silence all errors and throw a generic error below
3315
3329
  }
3316
3330
  if (current.length || error) {
3317
- throw new Error(`Tokenizer error: could not tokenize \`${expr}\``);
3331
+ throw new OwlError(`Tokenizer error: could not tokenize \`${expr}\``);
3318
3332
  }
3319
3333
  return result;
3320
3334
  }
@@ -3865,7 +3879,7 @@ class CodeGenerator {
3865
3879
  .slice(1)
3866
3880
  .map((m) => {
3867
3881
  if (!MODS.has(m)) {
3868
- throw new Error(`Unknown event modifier: '${m}'`);
3882
+ throw new OwlError(`Unknown event modifier: '${m}'`);
3869
3883
  }
3870
3884
  return `"${m}"`;
3871
3885
  });
@@ -3911,8 +3925,13 @@ class CodeGenerator {
3911
3925
  expr = compileExpr(ast.attrs[key]);
3912
3926
  if (attrName && isProp(ast.tag, attrName)) {
3913
3927
  // we force a new string or new boolean to bypass the equality check in blockdom when patching same value
3914
- const C = attrName === "value" ? "String" : "Boolean";
3915
- expr = `new ${C}(${expr})`;
3928
+ if (attrName === "value") {
3929
+ // When the expression is falsy, fall back to an empty string
3930
+ expr = `new String((${expr}) || "")`;
3931
+ }
3932
+ else {
3933
+ expr = `new Boolean(${expr})`;
3934
+ }
3916
3935
  }
3917
3936
  const idx = block.insertData(expr, "attr");
3918
3937
  if (key === "t-att") {
@@ -4186,7 +4205,8 @@ class CodeGenerator {
4186
4205
  this.define(`key${this.target.loopLevel}`, ast.key ? compileExpr(ast.key) : loopVar);
4187
4206
  if (this.dev) {
4188
4207
  // Throw error on duplicate keys in dev mode
4189
- this.addLine(`if (keys${block.id}.has(key${this.target.loopLevel})) { throw new Error(\`Got duplicate key in t-foreach: \${key${this.target.loopLevel}}\`)}`);
4208
+ this.helpers.add("OwlError");
4209
+ this.addLine(`if (keys${block.id}.has(key${this.target.loopLevel})) { throw new OwlError(\`Got duplicate key in t-foreach: \${key${this.target.loopLevel}}\`)}`);
4190
4210
  this.addLine(`keys${block.id}.add(key${this.target.loopLevel});`);
4191
4211
  }
4192
4212
  let id;
@@ -4400,7 +4420,7 @@ class CodeGenerator {
4400
4420
  value = `bind(ctx, ${value || undefined})`;
4401
4421
  }
4402
4422
  else {
4403
- throw new Error("Invalid prop suffix");
4423
+ throw new OwlError("Invalid prop suffix");
4404
4424
  }
4405
4425
  }
4406
4426
  name = /^[a-z_]+$/i.test(name) ? name : `'${name}'`;
@@ -4593,9 +4613,6 @@ class CodeGenerator {
4593
4613
  }
4594
4614
  }
4595
4615
 
4596
- // -----------------------------------------------------------------------------
4597
- // AST Type definition
4598
- // -----------------------------------------------------------------------------
4599
4616
  // -----------------------------------------------------------------------------
4600
4617
  // Parser
4601
4618
  // -----------------------------------------------------------------------------
@@ -4704,7 +4721,7 @@ function parseDOMNode(node, ctx) {
4704
4721
  return null;
4705
4722
  }
4706
4723
  if (tagName.startsWith("block-")) {
4707
- throw new Error(`Invalid tag name: '${tagName}'`);
4724
+ throw new OwlError(`Invalid tag name: '${tagName}'`);
4708
4725
  }
4709
4726
  ctx = Object.assign({}, ctx);
4710
4727
  if (tagName === "pre") {
@@ -4723,14 +4740,14 @@ function parseDOMNode(node, ctx) {
4723
4740
  const value = node.getAttribute(attr);
4724
4741
  if (attr.startsWith("t-on")) {
4725
4742
  if (attr === "t-on") {
4726
- throw new Error("Missing event name with t-on directive");
4743
+ throw new OwlError("Missing event name with t-on directive");
4727
4744
  }
4728
4745
  on = on || {};
4729
4746
  on[attr.slice(5)] = value;
4730
4747
  }
4731
4748
  else if (attr.startsWith("t-model")) {
4732
4749
  if (!["input", "select", "textarea"].includes(tagName)) {
4733
- throw new Error("The t-model directive only works with <input>, <textarea> and <select>");
4750
+ throw new OwlError("The t-model directive only works with <input>, <textarea> and <select>");
4734
4751
  }
4735
4752
  let baseExpr, expr;
4736
4753
  if (hasDotAtTheEnd.test(value)) {
@@ -4744,7 +4761,7 @@ function parseDOMNode(node, ctx) {
4744
4761
  expr = value.slice(index + 1, -1);
4745
4762
  }
4746
4763
  else {
4747
- throw new Error(`Invalid t-model expression: "${value}" (it should be assignable)`);
4764
+ throw new OwlError(`Invalid t-model expression: "${value}" (it should be assignable)`);
4748
4765
  }
4749
4766
  const typeAttr = node.getAttribute("type");
4750
4767
  const isInput = tagName === "input";
@@ -4774,11 +4791,11 @@ function parseDOMNode(node, ctx) {
4774
4791
  }
4775
4792
  }
4776
4793
  else if (attr.startsWith("block-")) {
4777
- throw new Error(`Invalid attribute: '${attr}'`);
4794
+ throw new OwlError(`Invalid attribute: '${attr}'`);
4778
4795
  }
4779
4796
  else if (attr !== "t-name") {
4780
4797
  if (attr.startsWith("t-") && !attr.startsWith("t-att")) {
4781
- throw new Error(`Unknown QWeb directive: '${attr}'`);
4798
+ throw new OwlError(`Unknown QWeb directive: '${attr}'`);
4782
4799
  }
4783
4800
  const tModel = ctx.tModelInfo;
4784
4801
  if (tModel && ["t-att-value", "t-attf-value"].includes(attr)) {
@@ -4829,7 +4846,7 @@ function parseTEscNode(node, ctx) {
4829
4846
  };
4830
4847
  }
4831
4848
  if (ast.type === 11 /* TComponent */) {
4832
- throw new Error("t-esc is not supported on Component nodes");
4849
+ throw new OwlError("t-esc is not supported on Component nodes");
4833
4850
  }
4834
4851
  return tesc;
4835
4852
  }
@@ -4877,7 +4894,7 @@ function parseTForEach(node, ctx) {
4877
4894
  node.removeAttribute("t-as");
4878
4895
  const key = node.getAttribute("t-key");
4879
4896
  if (!key) {
4880
- throw new Error(`"Directive t-foreach should always be used with a t-key!" (expression: t-foreach="${collection}" t-as="${elem}")`);
4897
+ throw new OwlError(`"Directive t-foreach should always be used with a t-key!" (expression: t-foreach="${collection}" t-as="${elem}")`);
4881
4898
  }
4882
4899
  node.removeAttribute("t-key");
4883
4900
  const memo = node.getAttribute("t-memo") || "";
@@ -5037,7 +5054,7 @@ function parseComponent(node, ctx) {
5037
5054
  const firstLetter = name[0];
5038
5055
  let isDynamic = node.hasAttribute("t-component");
5039
5056
  if (isDynamic && name !== "t") {
5040
- throw new Error(`Directive 't-component' can only be used on <t> nodes (used on a <${name}>)`);
5057
+ throw new OwlError(`Directive 't-component' can only be used on <t> nodes (used on a <${name}>)`);
5041
5058
  }
5042
5059
  if (!(firstLetter === firstLetter.toUpperCase() || isDynamic)) {
5043
5060
  return null;
@@ -5061,7 +5078,7 @@ function parseComponent(node, ctx) {
5061
5078
  }
5062
5079
  else {
5063
5080
  const message = directiveErrorMap.get(name.split("-").slice(0, 2).join("-"));
5064
- throw new Error(message || `unsupported directive on Component: ${name}`);
5081
+ throw new OwlError(message || `unsupported directive on Component: ${name}`);
5065
5082
  }
5066
5083
  }
5067
5084
  else {
@@ -5076,7 +5093,7 @@ function parseComponent(node, ctx) {
5076
5093
  const slotNodes = Array.from(clone.querySelectorAll("[t-set-slot]"));
5077
5094
  for (let slotNode of slotNodes) {
5078
5095
  if (slotNode.tagName !== "t") {
5079
- throw new Error(`Directive 't-set-slot' can only be used on <t> nodes (used on a <${slotNode.tagName}>)`);
5096
+ throw new OwlError(`Directive 't-set-slot' can only be used on <t> nodes (used on a <${slotNode.tagName}>)`);
5080
5097
  }
5081
5098
  const name = slotNode.getAttribute("t-set-slot");
5082
5099
  // check if this is defined in a sub component (in which case it should
@@ -5241,25 +5258,25 @@ function normalizeTIf(el) {
5241
5258
  let nattr = (name) => +!!node.getAttribute(name);
5242
5259
  if (prevElem && (pattr("t-if") || pattr("t-elif"))) {
5243
5260
  if (pattr("t-foreach")) {
5244
- throw new Error("t-if cannot stay at the same level as t-foreach when using t-elif or t-else");
5261
+ throw new OwlError("t-if cannot stay at the same level as t-foreach when using t-elif or t-else");
5245
5262
  }
5246
5263
  if (["t-if", "t-elif", "t-else"].map(nattr).reduce(function (a, b) {
5247
5264
  return a + b;
5248
5265
  }) > 1) {
5249
- throw new Error("Only one conditional branching directive is allowed per node");
5266
+ throw new OwlError("Only one conditional branching directive is allowed per node");
5250
5267
  }
5251
5268
  // All text (with only spaces) and comment nodes (nodeType 8) between
5252
5269
  // branch nodes are removed
5253
5270
  let textNode;
5254
5271
  while ((textNode = node.previousSibling) !== prevElem) {
5255
5272
  if (textNode.nodeValue.trim().length && textNode.nodeType !== 8) {
5256
- throw new Error("text is not allowed between branching directives");
5273
+ throw new OwlError("text is not allowed between branching directives");
5257
5274
  }
5258
5275
  textNode.remove();
5259
5276
  }
5260
5277
  }
5261
5278
  else {
5262
- throw new Error("t-elif and t-else directives must be preceded by a t-if or t-elif directive");
5279
+ throw new OwlError("t-elif and t-else directives must be preceded by a t-if or t-elif directive");
5263
5280
  }
5264
5281
  }
5265
5282
  }
@@ -5275,7 +5292,7 @@ function normalizeTEsc(el) {
5275
5292
  const elements = [...el.querySelectorAll("[t-esc]")].filter((el) => el.tagName[0] === el.tagName[0].toUpperCase() || el.hasAttribute("t-component"));
5276
5293
  for (const el of elements) {
5277
5294
  if (el.childNodes.length) {
5278
- throw new Error("Cannot have t-esc on a component that already has content");
5295
+ throw new OwlError("Cannot have t-esc on a component that already has content");
5279
5296
  }
5280
5297
  const value = el.getAttribute("t-esc");
5281
5298
  el.removeAttribute("t-esc");
@@ -5327,7 +5344,7 @@ function parseXML(xml) {
5327
5344
  }
5328
5345
  }
5329
5346
  }
5330
- throw new Error(msg);
5347
+ throw new OwlError(msg);
5331
5348
  }
5332
5349
  return doc;
5333
5350
  }
@@ -5381,7 +5398,7 @@ const mainEventHandler = (data, ev, currentTarget) => {
5381
5398
  if (Object.hasOwnProperty.call(data, 0)) {
5382
5399
  const handler = data[0];
5383
5400
  if (typeof handler !== "function") {
5384
- throw new Error(`Invalid handler (expected a function, received: '${handler}')`);
5401
+ throw new OwlError(`Invalid handler (expected a function, received: '${handler}')`);
5385
5402
  }
5386
5403
  let node = data[1] ? data[1].__owl__ : null;
5387
5404
  if (node ? node.status === 1 /* MOUNTED */ : true) {
@@ -5565,10 +5582,10 @@ class App extends TemplateSet {
5565
5582
  if (isStatic) {
5566
5583
  C = parent.constructor.components[name];
5567
5584
  if (!C) {
5568
- throw new Error(`Cannot find the definition of component "${name}"`);
5585
+ throw new OwlError(`Cannot find the definition of component "${name}"`);
5569
5586
  }
5570
5587
  else if (!(C.prototype instanceof Component)) {
5571
- throw new Error(`"${name}" is not a Component. It must inherit from the Component class`);
5588
+ throw new OwlError(`"${name}" is not a Component. It must inherit from the Component class`);
5572
5589
  }
5573
5590
  }
5574
5591
  node = new ComponentNode(C, props, this, ctx, key);
@@ -5727,6 +5744,7 @@ TemplateSet.prototype._compileTemplate = function _compileTemplate(name, templat
5727
5744
  exports.App = App;
5728
5745
  exports.Component = Component;
5729
5746
  exports.EventBus = EventBus;
5747
+ exports.OwlError = OwlError;
5730
5748
  exports.__info__ = __info__;
5731
5749
  exports.blockDom = blockDom;
5732
5750
  exports.loadFile = loadFile;
@@ -5759,7 +5777,7 @@ exports.whenReady = whenReady;
5759
5777
  exports.xml = xml;
5760
5778
 
5761
5779
 
5762
- __info__.version = '2.0.0-beta-14';
5763
- __info__.date = '2022-07-08T14:17:53.900Z';
5764
- __info__.hash = 'd111845';
5780
+ __info__.version = '2.0.0-beta-17';
5781
+ __info__.date = '2022-09-01T13:41:44.572Z';
5782
+ __info__.hash = '9cb74d6';
5765
5783
  __info__.url = 'https://github.com/odoo/owl';