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