@odoo/owl 2.0.0-beta-4 → 2.0.0-beta-5

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.iife.js CHANGED
@@ -1915,7 +1915,7 @@
1915
1915
  // Maps fibers to thrown errors
1916
1916
  const fibersInError = new WeakMap();
1917
1917
  const nodeErrorHandlers = new WeakMap();
1918
- function _handleError(node, error, isFirstRound = false) {
1918
+ function _handleError(node, error) {
1919
1919
  if (!node) {
1920
1920
  return false;
1921
1921
  }
@@ -1925,23 +1925,19 @@
1925
1925
  }
1926
1926
  const errorHandlers = nodeErrorHandlers.get(node);
1927
1927
  if (errorHandlers) {
1928
- let stopped = false;
1928
+ let handled = false;
1929
1929
  // execute in the opposite order
1930
1930
  for (let i = errorHandlers.length - 1; i >= 0; i--) {
1931
1931
  try {
1932
1932
  errorHandlers[i](error);
1933
- stopped = true;
1933
+ handled = true;
1934
1934
  break;
1935
1935
  }
1936
1936
  catch (e) {
1937
1937
  error = e;
1938
1938
  }
1939
1939
  }
1940
- if (stopped) {
1941
- if (isFirstRound && fiber && fiber.node.fiber) {
1942
- const root = fiber.root;
1943
- root.setCounter(root.counter - 1);
1944
- }
1940
+ if (handled) {
1945
1941
  return true;
1946
1942
  }
1947
1943
  }
@@ -1959,7 +1955,7 @@
1959
1955
  current = current.parent;
1960
1956
  } while (current);
1961
1957
  fibersInError.set(fiber.root, error);
1962
- const handled = _handleError(node, error, true);
1958
+ const handled = _handleError(node, error);
1963
1959
  if (!handled) {
1964
1960
  console.warn(`[Owl] Unhandled error. Destroying the root component`);
1965
1961
  try {
@@ -1976,10 +1972,6 @@
1976
1972
  if (current) {
1977
1973
  cancelFibers(current.children);
1978
1974
  current.root = null;
1979
- if (current instanceof RootFiber && current.delayedRenders.length) {
1980
- let root = parent.root;
1981
- root.delayedRenders = root.delayedRenders.concat(current.delayedRenders);
1982
- }
1983
1975
  }
1984
1976
  return new Fiber(node, parent);
1985
1977
  }
@@ -1987,12 +1979,15 @@
1987
1979
  let current = node.fiber;
1988
1980
  if (current) {
1989
1981
  let root = current.root;
1982
+ // lock root fiber because canceling children fibers may destroy components,
1983
+ // which means any arbitrary code can be run in onWillDestroy, which may
1984
+ // trigger new renderings
1985
+ root.locked = true;
1990
1986
  root.setCounter(root.counter + 1 - cancelFibers(current.children));
1987
+ root.locked = false;
1991
1988
  current.children = [];
1989
+ current.childrenMap = {};
1992
1990
  current.bdom = null;
1993
- if (current === root) {
1994
- root.reachedChildren = new WeakSet();
1995
- }
1996
1991
  if (fibersInError.has(current)) {
1997
1992
  fibersInError.delete(current);
1998
1993
  fibersInError.delete(root);
@@ -2015,7 +2010,11 @@
2015
2010
  function cancelFibers(fibers) {
2016
2011
  let result = 0;
2017
2012
  for (let fiber of fibers) {
2018
- fiber.node.fiber = null;
2013
+ let node = fiber.node;
2014
+ if (node.status === 0 /* NEW */) {
2015
+ node.destroy();
2016
+ }
2017
+ node.fiber = null;
2019
2018
  if (fiber.bdom) {
2020
2019
  // if fiber has been rendered, this means that the component props have
2021
2020
  // been updated. however, this fiber will not be patched to the dom, so
@@ -2023,7 +2022,7 @@
2023
2022
  // the same props, and skip the render completely. With the next line,
2024
2023
  // we kindly request the component code to force a render, so it works as
2025
2024
  // expected.
2026
- fiber.node.forceNextRender = true;
2025
+ node.forceNextRender = true;
2027
2026
  }
2028
2027
  else {
2029
2028
  result++;
@@ -2038,6 +2037,7 @@
2038
2037
  this.children = [];
2039
2038
  this.appliedToDom = false;
2040
2039
  this.deep = false;
2040
+ this.childrenMap = {};
2041
2041
  this.node = node;
2042
2042
  this.parent = parent;
2043
2043
  if (parent) {
@@ -2054,21 +2054,17 @@
2054
2054
  render() {
2055
2055
  // if some parent has a fiber => register in followup
2056
2056
  let prev = this.root.node;
2057
+ let scheduler = prev.app.scheduler;
2057
2058
  let current = prev.parent;
2058
2059
  while (current) {
2059
2060
  if (current.fiber) {
2060
2061
  let root = current.fiber.root;
2061
- if (root.counter) {
2062
- root.delayedRenders.push(this);
2063
- return;
2062
+ if (root.counter === 0 && prev.parentKey in current.fiber.childrenMap) {
2063
+ current = root.node;
2064
2064
  }
2065
2065
  else {
2066
- if (!root.reachedChildren.has(prev)) {
2067
- // is dead
2068
- this.node.app.scheduler.shouldClear = true;
2069
- return;
2070
- }
2071
- current = root.node;
2066
+ scheduler.delayedRenders.push(this);
2067
+ return;
2072
2068
  }
2073
2069
  }
2074
2070
  prev = current;
@@ -2082,12 +2078,13 @@
2082
2078
  const root = this.root;
2083
2079
  if (root) {
2084
2080
  try {
2081
+ this.bdom = true;
2085
2082
  this.bdom = node.renderFn();
2086
- root.setCounter(root.counter - 1);
2087
2083
  }
2088
2084
  catch (e) {
2089
2085
  handleError({ node, error: e });
2090
2086
  }
2087
+ root.setCounter(root.counter - 1);
2091
2088
  }
2092
2089
  }
2093
2090
  }
@@ -2102,8 +2099,6 @@
2102
2099
  // A fiber is typically locked when it is completing and the patch has not, or is being applied.
2103
2100
  // i.e.: render triggered in onWillUnmount or in willPatch will be delayed
2104
2101
  this.locked = false;
2105
- this.delayedRenders = [];
2106
- this.reachedChildren = new WeakSet();
2107
2102
  }
2108
2103
  complete() {
2109
2104
  const node = this.node;
@@ -2156,14 +2151,6 @@
2156
2151
  setCounter(newValue) {
2157
2152
  this.counter = newValue;
2158
2153
  if (newValue === 0) {
2159
- if (this.delayedRenders.length) {
2160
- for (let f of this.delayedRenders) {
2161
- if (f.root) {
2162
- f.render();
2163
- }
2164
- }
2165
- this.delayedRenders = [];
2166
- }
2167
2154
  this.node.app.scheduler.flush();
2168
2155
  }
2169
2156
  }
@@ -2178,6 +2165,7 @@
2178
2165
  let current = this;
2179
2166
  try {
2180
2167
  const node = this.node;
2168
+ node.children = this.childrenMap;
2181
2169
  node.app.constructor.validateTarget(this.target);
2182
2170
  if (node.bdom) {
2183
2171
  // this is a complicated situation: if we mount a fiber with an existing
@@ -2401,14 +2389,8 @@
2401
2389
  function component(name, props, key, ctx, parent) {
2402
2390
  let node = ctx.children[key];
2403
2391
  let isDynamic = typeof name !== "string";
2404
- if (node) {
2405
- if (node.status < 1 /* MOUNTED */) {
2406
- node.destroy();
2407
- node = undefined;
2408
- }
2409
- else if (node.status === 2 /* DESTROYED */) {
2410
- node = undefined;
2411
- }
2392
+ if (node && node.status === 2 /* DESTROYED */) {
2393
+ node = undefined;
2412
2394
  }
2413
2395
  if (isDynamic && node && node.component.constructor !== name) {
2414
2396
  node = undefined;
@@ -2439,15 +2421,15 @@
2439
2421
  throw new Error(`Cannot find the definition of component "${name}"`);
2440
2422
  }
2441
2423
  }
2442
- node = new ComponentNode(C, props, ctx.app, ctx);
2424
+ node = new ComponentNode(C, props, ctx.app, ctx, key);
2443
2425
  ctx.children[key] = node;
2444
2426
  node.initiateRender(new Fiber(node, parentFiber));
2445
2427
  }
2446
- parentFiber.root.reachedChildren.add(node);
2428
+ parentFiber.childrenMap[key] = node;
2447
2429
  return node;
2448
2430
  }
2449
2431
  class ComponentNode {
2450
- constructor(C, props, app, parent) {
2432
+ constructor(C, props, app, parent, parentKey) {
2451
2433
  this.fiber = null;
2452
2434
  this.bdom = null;
2453
2435
  this.status = 0 /* NEW */;
@@ -2463,7 +2445,8 @@
2463
2445
  this.willDestroy = [];
2464
2446
  currentNode = this;
2465
2447
  this.app = app;
2466
- this.parent = parent || null;
2448
+ this.parent = parent;
2449
+ this.parentKey = parentKey;
2467
2450
  this.level = parent ? parent.level + 1 : 0;
2468
2451
  applyDefaultProps(props, C);
2469
2452
  const env = (parent && parent.childEnv) || app.env;
@@ -2498,7 +2481,7 @@
2498
2481
  }
2499
2482
  async render(deep = false) {
2500
2483
  let current = this.fiber;
2501
- if (current && current.root.locked) {
2484
+ if (current && (current.root.locked || current.bdom === true)) {
2502
2485
  await Promise.resolve();
2503
2486
  // situation may have changed after the microtask tick
2504
2487
  current = this.fiber;
@@ -2557,8 +2540,15 @@
2557
2540
  for (let child of Object.values(this.children)) {
2558
2541
  child._destroy();
2559
2542
  }
2560
- for (let cb of this.willDestroy) {
2561
- cb.call(component);
2543
+ if (this.willDestroy.length) {
2544
+ try {
2545
+ for (let cb of this.willDestroy) {
2546
+ cb.call(component);
2547
+ }
2548
+ }
2549
+ catch (e) {
2550
+ handleError({ error: e, node: this });
2551
+ }
2562
2552
  }
2563
2553
  this.status = 2 /* DESTROYED */;
2564
2554
  }
@@ -2624,6 +2614,7 @@
2624
2614
  bdom.mount(parent, anchor);
2625
2615
  this.status = 1 /* MOUNTED */;
2626
2616
  this.fiber.appliedToDom = true;
2617
+ this.children = this.fiber.childrenMap;
2627
2618
  this.fiber = null;
2628
2619
  }
2629
2620
  moveBefore(other, afterNode) {
@@ -2639,10 +2630,8 @@
2639
2630
  }
2640
2631
  _patch() {
2641
2632
  const hasChildren = Object.keys(this.children).length > 0;
2633
+ this.children = this.fiber.childrenMap;
2642
2634
  this.bdom.patch(this.fiber.bdom, hasChildren);
2643
- if (hasChildren) {
2644
- this.cleanOutdatedChildren();
2645
- }
2646
2635
  this.fiber.appliedToDom = true;
2647
2636
  this.fiber = null;
2648
2637
  }
@@ -2652,19 +2641,6 @@
2652
2641
  remove() {
2653
2642
  this.bdom.remove();
2654
2643
  }
2655
- cleanOutdatedChildren() {
2656
- const children = this.children;
2657
- for (const key in children) {
2658
- const node = children[key];
2659
- const status = node.status;
2660
- if (status !== 1 /* MOUNTED */) {
2661
- delete children[key];
2662
- if (status !== 2 /* DESTROYED */) {
2663
- node.destroy();
2664
- }
2665
- }
2666
- }
2667
- }
2668
2644
  // ---------------------------------------------------------------------------
2669
2645
  // Some debug helpers
2670
2646
  // ---------------------------------------------------------------------------
@@ -2684,7 +2660,7 @@
2684
2660
  constructor() {
2685
2661
  this.tasks = new Set();
2686
2662
  this.frame = 0;
2687
- this.shouldClear = false;
2663
+ this.delayedRenders = [];
2688
2664
  this.requestAnimationFrame = Scheduler.requestAnimationFrame;
2689
2665
  }
2690
2666
  addFiber(fiber) {
@@ -2695,16 +2671,22 @@
2695
2671
  * Other tasks are left unchanged.
2696
2672
  */
2697
2673
  flush() {
2674
+ if (this.delayedRenders.length) {
2675
+ let renders = this.delayedRenders;
2676
+ this.delayedRenders = [];
2677
+ for (let f of renders) {
2678
+ if (f.root && f.node.status !== 2 /* DESTROYED */) {
2679
+ f.render();
2680
+ }
2681
+ }
2682
+ }
2698
2683
  if (this.frame === 0) {
2699
2684
  this.frame = this.requestAnimationFrame(() => {
2700
2685
  this.frame = 0;
2701
2686
  this.tasks.forEach((fiber) => this.processFiber(fiber));
2702
- if (this.shouldClear) {
2703
- this.shouldClear = false;
2704
- for (let task of this.tasks) {
2705
- if (task.node.status === 2 /* DESTROYED */) {
2706
- this.tasks.delete(task);
2707
- }
2687
+ for (let task of this.tasks) {
2688
+ if (task.node.status === 2 /* DESTROYED */) {
2689
+ this.tasks.delete(task);
2708
2690
  }
2709
2691
  }
2710
2692
  });
@@ -4018,8 +4000,11 @@
4018
4000
  let slotStr = [];
4019
4001
  for (let slotName in ast.slots) {
4020
4002
  const slotAst = ast.slots[slotName];
4021
- const name = this.compileInNewTarget("slot", slotAst.content, ctx, slotAst.on);
4022
- const params = [`__render: ${name}, __ctx: ${ctxStr}`];
4003
+ const params = [];
4004
+ if (slotAst.content) {
4005
+ const name = this.compileInNewTarget("slot", slotAst.content, ctx, slotAst.on);
4006
+ params.push(`__render: ${name}, __ctx: ${ctxStr}`);
4007
+ }
4023
4008
  const scope = ast.slots[slotName].scope;
4024
4009
  if (scope) {
4025
4010
  params.push(`__scope: "${scope}"`);
@@ -4121,7 +4106,7 @@
4121
4106
  if (dynamic) {
4122
4107
  let name = this.generateId("slot");
4123
4108
  this.define(name, slotName);
4124
- blockString = `toggler(${name}, callSlot(ctx, node, key, ${name}), ${dynamic}, ${scope})`;
4109
+ blockString = `toggler(${name}, callSlot(ctx, node, key, ${name}, ${dynamic}, ${scope}))`;
4125
4110
  }
4126
4111
  else {
4127
4112
  blockString = `callSlot(ctx, node, key, ${slotName}, ${dynamic}, ${scope})`;
@@ -4662,28 +4647,26 @@
4662
4647
  slotNode.removeAttribute("t-set-slot");
4663
4648
  slotNode.remove();
4664
4649
  const slotAst = parseNode(slotNode, ctx);
4665
- if (slotAst) {
4666
- let on = null;
4667
- let attrs = null;
4668
- let scope = null;
4669
- for (let attributeName of slotNode.getAttributeNames()) {
4670
- const value = slotNode.getAttribute(attributeName);
4671
- if (attributeName === "t-slot-scope") {
4672
- scope = value;
4673
- continue;
4674
- }
4675
- else if (attributeName.startsWith("t-on-")) {
4676
- on = on || {};
4677
- on[attributeName.slice(5)] = value;
4678
- }
4679
- else {
4680
- attrs = attrs || {};
4681
- attrs[attributeName] = value;
4682
- }
4650
+ let on = null;
4651
+ let attrs = null;
4652
+ let scope = null;
4653
+ for (let attributeName of slotNode.getAttributeNames()) {
4654
+ const value = slotNode.getAttribute(attributeName);
4655
+ if (attributeName === "t-slot-scope") {
4656
+ scope = value;
4657
+ continue;
4658
+ }
4659
+ else if (attributeName.startsWith("t-on-")) {
4660
+ on = on || {};
4661
+ on[attributeName.slice(5)] = value;
4662
+ }
4663
+ else {
4664
+ attrs = attrs || {};
4665
+ attrs[attributeName] = value;
4683
4666
  }
4684
- slots = slots || {};
4685
- slots[name] = { content: slotAst, on, attrs, scope };
4686
4667
  }
4668
+ slots = slots || {};
4669
+ slots[name] = { content: slotAst, on, attrs, scope };
4687
4670
  }
4688
4671
  // default slot
4689
4672
  const defaultContent = parseChildNodes(clone, ctx);
@@ -5411,7 +5394,7 @@ See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration
5411
5394
  return prom;
5412
5395
  }
5413
5396
  makeNode(Component, props) {
5414
- return new ComponentNode(Component, props, this);
5397
+ return new ComponentNode(Component, props, this, null, null);
5415
5398
  }
5416
5399
  mountNode(node, target, options) {
5417
5400
  const promise = new Promise((resolve, reject) => {
@@ -5619,9 +5602,9 @@ See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration
5619
5602
  Object.defineProperty(exports, '__esModule', { value: true });
5620
5603
 
5621
5604
 
5622
- __info__.version = '2.0.0-beta-4';
5623
- __info__.date = '2022-03-29T13:50:04.545Z';
5624
- __info__.hash = '55dbc01';
5605
+ __info__.version = '2.0.0-beta-5';
5606
+ __info__.date = '2022-04-07T13:36:37.300Z';
5607
+ __info__.hash = '1179e84';
5625
5608
  __info__.url = 'https://github.com/odoo/owl';
5626
5609
 
5627
5610