@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.es.js CHANGED
@@ -1912,7 +1912,7 @@ xml.nextId = 1;
1912
1912
  // Maps fibers to thrown errors
1913
1913
  const fibersInError = new WeakMap();
1914
1914
  const nodeErrorHandlers = new WeakMap();
1915
- function _handleError(node, error, isFirstRound = false) {
1915
+ function _handleError(node, error) {
1916
1916
  if (!node) {
1917
1917
  return false;
1918
1918
  }
@@ -1922,23 +1922,19 @@ function _handleError(node, error, isFirstRound = false) {
1922
1922
  }
1923
1923
  const errorHandlers = nodeErrorHandlers.get(node);
1924
1924
  if (errorHandlers) {
1925
- let stopped = false;
1925
+ let handled = false;
1926
1926
  // execute in the opposite order
1927
1927
  for (let i = errorHandlers.length - 1; i >= 0; i--) {
1928
1928
  try {
1929
1929
  errorHandlers[i](error);
1930
- stopped = true;
1930
+ handled = true;
1931
1931
  break;
1932
1932
  }
1933
1933
  catch (e) {
1934
1934
  error = e;
1935
1935
  }
1936
1936
  }
1937
- if (stopped) {
1938
- if (isFirstRound && fiber && fiber.node.fiber) {
1939
- const root = fiber.root;
1940
- root.setCounter(root.counter - 1);
1941
- }
1937
+ if (handled) {
1942
1938
  return true;
1943
1939
  }
1944
1940
  }
@@ -1956,7 +1952,7 @@ function handleError(params) {
1956
1952
  current = current.parent;
1957
1953
  } while (current);
1958
1954
  fibersInError.set(fiber.root, error);
1959
- const handled = _handleError(node, error, true);
1955
+ const handled = _handleError(node, error);
1960
1956
  if (!handled) {
1961
1957
  console.warn(`[Owl] Unhandled error. Destroying the root component`);
1962
1958
  try {
@@ -1973,10 +1969,6 @@ function makeChildFiber(node, parent) {
1973
1969
  if (current) {
1974
1970
  cancelFibers(current.children);
1975
1971
  current.root = null;
1976
- if (current instanceof RootFiber && current.delayedRenders.length) {
1977
- let root = parent.root;
1978
- root.delayedRenders = root.delayedRenders.concat(current.delayedRenders);
1979
- }
1980
1972
  }
1981
1973
  return new Fiber(node, parent);
1982
1974
  }
@@ -1984,12 +1976,15 @@ function makeRootFiber(node) {
1984
1976
  let current = node.fiber;
1985
1977
  if (current) {
1986
1978
  let root = current.root;
1979
+ // lock root fiber because canceling children fibers may destroy components,
1980
+ // which means any arbitrary code can be run in onWillDestroy, which may
1981
+ // trigger new renderings
1982
+ root.locked = true;
1987
1983
  root.setCounter(root.counter + 1 - cancelFibers(current.children));
1984
+ root.locked = false;
1988
1985
  current.children = [];
1986
+ current.childrenMap = {};
1989
1987
  current.bdom = null;
1990
- if (current === root) {
1991
- root.reachedChildren = new WeakSet();
1992
- }
1993
1988
  if (fibersInError.has(current)) {
1994
1989
  fibersInError.delete(current);
1995
1990
  fibersInError.delete(root);
@@ -2012,7 +2007,11 @@ function makeRootFiber(node) {
2012
2007
  function cancelFibers(fibers) {
2013
2008
  let result = 0;
2014
2009
  for (let fiber of fibers) {
2015
- fiber.node.fiber = null;
2010
+ let node = fiber.node;
2011
+ if (node.status === 0 /* NEW */) {
2012
+ node.destroy();
2013
+ }
2014
+ node.fiber = null;
2016
2015
  if (fiber.bdom) {
2017
2016
  // if fiber has been rendered, this means that the component props have
2018
2017
  // been updated. however, this fiber will not be patched to the dom, so
@@ -2020,7 +2019,7 @@ function cancelFibers(fibers) {
2020
2019
  // the same props, and skip the render completely. With the next line,
2021
2020
  // we kindly request the component code to force a render, so it works as
2022
2021
  // expected.
2023
- fiber.node.forceNextRender = true;
2022
+ node.forceNextRender = true;
2024
2023
  }
2025
2024
  else {
2026
2025
  result++;
@@ -2035,6 +2034,7 @@ class Fiber {
2035
2034
  this.children = [];
2036
2035
  this.appliedToDom = false;
2037
2036
  this.deep = false;
2037
+ this.childrenMap = {};
2038
2038
  this.node = node;
2039
2039
  this.parent = parent;
2040
2040
  if (parent) {
@@ -2051,21 +2051,17 @@ class Fiber {
2051
2051
  render() {
2052
2052
  // if some parent has a fiber => register in followup
2053
2053
  let prev = this.root.node;
2054
+ let scheduler = prev.app.scheduler;
2054
2055
  let current = prev.parent;
2055
2056
  while (current) {
2056
2057
  if (current.fiber) {
2057
2058
  let root = current.fiber.root;
2058
- if (root.counter) {
2059
- root.delayedRenders.push(this);
2060
- return;
2059
+ if (root.counter === 0 && prev.parentKey in current.fiber.childrenMap) {
2060
+ current = root.node;
2061
2061
  }
2062
2062
  else {
2063
- if (!root.reachedChildren.has(prev)) {
2064
- // is dead
2065
- this.node.app.scheduler.shouldClear = true;
2066
- return;
2067
- }
2068
- current = root.node;
2063
+ scheduler.delayedRenders.push(this);
2064
+ return;
2069
2065
  }
2070
2066
  }
2071
2067
  prev = current;
@@ -2079,12 +2075,13 @@ class Fiber {
2079
2075
  const root = this.root;
2080
2076
  if (root) {
2081
2077
  try {
2078
+ this.bdom = true;
2082
2079
  this.bdom = node.renderFn();
2083
- root.setCounter(root.counter - 1);
2084
2080
  }
2085
2081
  catch (e) {
2086
2082
  handleError({ node, error: e });
2087
2083
  }
2084
+ root.setCounter(root.counter - 1);
2088
2085
  }
2089
2086
  }
2090
2087
  }
@@ -2099,8 +2096,6 @@ class RootFiber extends Fiber {
2099
2096
  // A fiber is typically locked when it is completing and the patch has not, or is being applied.
2100
2097
  // i.e.: render triggered in onWillUnmount or in willPatch will be delayed
2101
2098
  this.locked = false;
2102
- this.delayedRenders = [];
2103
- this.reachedChildren = new WeakSet();
2104
2099
  }
2105
2100
  complete() {
2106
2101
  const node = this.node;
@@ -2153,14 +2148,6 @@ class RootFiber extends Fiber {
2153
2148
  setCounter(newValue) {
2154
2149
  this.counter = newValue;
2155
2150
  if (newValue === 0) {
2156
- if (this.delayedRenders.length) {
2157
- for (let f of this.delayedRenders) {
2158
- if (f.root) {
2159
- f.render();
2160
- }
2161
- }
2162
- this.delayedRenders = [];
2163
- }
2164
2151
  this.node.app.scheduler.flush();
2165
2152
  }
2166
2153
  }
@@ -2175,6 +2162,7 @@ class MountFiber extends RootFiber {
2175
2162
  let current = this;
2176
2163
  try {
2177
2164
  const node = this.node;
2165
+ node.children = this.childrenMap;
2178
2166
  node.app.constructor.validateTarget(this.target);
2179
2167
  if (node.bdom) {
2180
2168
  // this is a complicated situation: if we mount a fiber with an existing
@@ -2398,14 +2386,8 @@ function arePropsDifferent(props1, props2) {
2398
2386
  function component(name, props, key, ctx, parent) {
2399
2387
  let node = ctx.children[key];
2400
2388
  let isDynamic = typeof name !== "string";
2401
- if (node) {
2402
- if (node.status < 1 /* MOUNTED */) {
2403
- node.destroy();
2404
- node = undefined;
2405
- }
2406
- else if (node.status === 2 /* DESTROYED */) {
2407
- node = undefined;
2408
- }
2389
+ if (node && node.status === 2 /* DESTROYED */) {
2390
+ node = undefined;
2409
2391
  }
2410
2392
  if (isDynamic && node && node.component.constructor !== name) {
2411
2393
  node = undefined;
@@ -2436,15 +2418,15 @@ function component(name, props, key, ctx, parent) {
2436
2418
  throw new Error(`Cannot find the definition of component "${name}"`);
2437
2419
  }
2438
2420
  }
2439
- node = new ComponentNode(C, props, ctx.app, ctx);
2421
+ node = new ComponentNode(C, props, ctx.app, ctx, key);
2440
2422
  ctx.children[key] = node;
2441
2423
  node.initiateRender(new Fiber(node, parentFiber));
2442
2424
  }
2443
- parentFiber.root.reachedChildren.add(node);
2425
+ parentFiber.childrenMap[key] = node;
2444
2426
  return node;
2445
2427
  }
2446
2428
  class ComponentNode {
2447
- constructor(C, props, app, parent) {
2429
+ constructor(C, props, app, parent, parentKey) {
2448
2430
  this.fiber = null;
2449
2431
  this.bdom = null;
2450
2432
  this.status = 0 /* NEW */;
@@ -2460,7 +2442,8 @@ class ComponentNode {
2460
2442
  this.willDestroy = [];
2461
2443
  currentNode = this;
2462
2444
  this.app = app;
2463
- this.parent = parent || null;
2445
+ this.parent = parent;
2446
+ this.parentKey = parentKey;
2464
2447
  this.level = parent ? parent.level + 1 : 0;
2465
2448
  applyDefaultProps(props, C);
2466
2449
  const env = (parent && parent.childEnv) || app.env;
@@ -2495,7 +2478,7 @@ class ComponentNode {
2495
2478
  }
2496
2479
  async render(deep = false) {
2497
2480
  let current = this.fiber;
2498
- if (current && current.root.locked) {
2481
+ if (current && (current.root.locked || current.bdom === true)) {
2499
2482
  await Promise.resolve();
2500
2483
  // situation may have changed after the microtask tick
2501
2484
  current = this.fiber;
@@ -2554,8 +2537,15 @@ class ComponentNode {
2554
2537
  for (let child of Object.values(this.children)) {
2555
2538
  child._destroy();
2556
2539
  }
2557
- for (let cb of this.willDestroy) {
2558
- cb.call(component);
2540
+ if (this.willDestroy.length) {
2541
+ try {
2542
+ for (let cb of this.willDestroy) {
2543
+ cb.call(component);
2544
+ }
2545
+ }
2546
+ catch (e) {
2547
+ handleError({ error: e, node: this });
2548
+ }
2559
2549
  }
2560
2550
  this.status = 2 /* DESTROYED */;
2561
2551
  }
@@ -2621,6 +2611,7 @@ class ComponentNode {
2621
2611
  bdom.mount(parent, anchor);
2622
2612
  this.status = 1 /* MOUNTED */;
2623
2613
  this.fiber.appliedToDom = true;
2614
+ this.children = this.fiber.childrenMap;
2624
2615
  this.fiber = null;
2625
2616
  }
2626
2617
  moveBefore(other, afterNode) {
@@ -2636,10 +2627,8 @@ class ComponentNode {
2636
2627
  }
2637
2628
  _patch() {
2638
2629
  const hasChildren = Object.keys(this.children).length > 0;
2630
+ this.children = this.fiber.childrenMap;
2639
2631
  this.bdom.patch(this.fiber.bdom, hasChildren);
2640
- if (hasChildren) {
2641
- this.cleanOutdatedChildren();
2642
- }
2643
2632
  this.fiber.appliedToDom = true;
2644
2633
  this.fiber = null;
2645
2634
  }
@@ -2649,19 +2638,6 @@ class ComponentNode {
2649
2638
  remove() {
2650
2639
  this.bdom.remove();
2651
2640
  }
2652
- cleanOutdatedChildren() {
2653
- const children = this.children;
2654
- for (const key in children) {
2655
- const node = children[key];
2656
- const status = node.status;
2657
- if (status !== 1 /* MOUNTED */) {
2658
- delete children[key];
2659
- if (status !== 2 /* DESTROYED */) {
2660
- node.destroy();
2661
- }
2662
- }
2663
- }
2664
- }
2665
2641
  // ---------------------------------------------------------------------------
2666
2642
  // Some debug helpers
2667
2643
  // ---------------------------------------------------------------------------
@@ -2681,7 +2657,7 @@ class Scheduler {
2681
2657
  constructor() {
2682
2658
  this.tasks = new Set();
2683
2659
  this.frame = 0;
2684
- this.shouldClear = false;
2660
+ this.delayedRenders = [];
2685
2661
  this.requestAnimationFrame = Scheduler.requestAnimationFrame;
2686
2662
  }
2687
2663
  addFiber(fiber) {
@@ -2692,16 +2668,22 @@ class Scheduler {
2692
2668
  * Other tasks are left unchanged.
2693
2669
  */
2694
2670
  flush() {
2671
+ if (this.delayedRenders.length) {
2672
+ let renders = this.delayedRenders;
2673
+ this.delayedRenders = [];
2674
+ for (let f of renders) {
2675
+ if (f.root && f.node.status !== 2 /* DESTROYED */) {
2676
+ f.render();
2677
+ }
2678
+ }
2679
+ }
2695
2680
  if (this.frame === 0) {
2696
2681
  this.frame = this.requestAnimationFrame(() => {
2697
2682
  this.frame = 0;
2698
2683
  this.tasks.forEach((fiber) => this.processFiber(fiber));
2699
- if (this.shouldClear) {
2700
- this.shouldClear = false;
2701
- for (let task of this.tasks) {
2702
- if (task.node.status === 2 /* DESTROYED */) {
2703
- this.tasks.delete(task);
2704
- }
2684
+ for (let task of this.tasks) {
2685
+ if (task.node.status === 2 /* DESTROYED */) {
2686
+ this.tasks.delete(task);
2705
2687
  }
2706
2688
  }
2707
2689
  });
@@ -4015,8 +3997,11 @@ class CodeGenerator {
4015
3997
  let slotStr = [];
4016
3998
  for (let slotName in ast.slots) {
4017
3999
  const slotAst = ast.slots[slotName];
4018
- const name = this.compileInNewTarget("slot", slotAst.content, ctx, slotAst.on);
4019
- const params = [`__render: ${name}, __ctx: ${ctxStr}`];
4000
+ const params = [];
4001
+ if (slotAst.content) {
4002
+ const name = this.compileInNewTarget("slot", slotAst.content, ctx, slotAst.on);
4003
+ params.push(`__render: ${name}, __ctx: ${ctxStr}`);
4004
+ }
4020
4005
  const scope = ast.slots[slotName].scope;
4021
4006
  if (scope) {
4022
4007
  params.push(`__scope: "${scope}"`);
@@ -4118,7 +4103,7 @@ class CodeGenerator {
4118
4103
  if (dynamic) {
4119
4104
  let name = this.generateId("slot");
4120
4105
  this.define(name, slotName);
4121
- blockString = `toggler(${name}, callSlot(ctx, node, key, ${name}), ${dynamic}, ${scope})`;
4106
+ blockString = `toggler(${name}, callSlot(ctx, node, key, ${name}, ${dynamic}, ${scope}))`;
4122
4107
  }
4123
4108
  else {
4124
4109
  blockString = `callSlot(ctx, node, key, ${slotName}, ${dynamic}, ${scope})`;
@@ -4659,28 +4644,26 @@ function parseComponent(node, ctx) {
4659
4644
  slotNode.removeAttribute("t-set-slot");
4660
4645
  slotNode.remove();
4661
4646
  const slotAst = parseNode(slotNode, ctx);
4662
- if (slotAst) {
4663
- let on = null;
4664
- let attrs = null;
4665
- let scope = null;
4666
- for (let attributeName of slotNode.getAttributeNames()) {
4667
- const value = slotNode.getAttribute(attributeName);
4668
- if (attributeName === "t-slot-scope") {
4669
- scope = value;
4670
- continue;
4671
- }
4672
- else if (attributeName.startsWith("t-on-")) {
4673
- on = on || {};
4674
- on[attributeName.slice(5)] = value;
4675
- }
4676
- else {
4677
- attrs = attrs || {};
4678
- attrs[attributeName] = value;
4679
- }
4647
+ let on = null;
4648
+ let attrs = null;
4649
+ let scope = null;
4650
+ for (let attributeName of slotNode.getAttributeNames()) {
4651
+ const value = slotNode.getAttribute(attributeName);
4652
+ if (attributeName === "t-slot-scope") {
4653
+ scope = value;
4654
+ continue;
4655
+ }
4656
+ else if (attributeName.startsWith("t-on-")) {
4657
+ on = on || {};
4658
+ on[attributeName.slice(5)] = value;
4659
+ }
4660
+ else {
4661
+ attrs = attrs || {};
4662
+ attrs[attributeName] = value;
4680
4663
  }
4681
- slots = slots || {};
4682
- slots[name] = { content: slotAst, on, attrs, scope };
4683
4664
  }
4665
+ slots = slots || {};
4666
+ slots[name] = { content: slotAst, on, attrs, scope };
4684
4667
  }
4685
4668
  // default slot
4686
4669
  const defaultContent = parseChildNodes(clone, ctx);
@@ -5408,7 +5391,7 @@ class App extends TemplateSet {
5408
5391
  return prom;
5409
5392
  }
5410
5393
  makeNode(Component, props) {
5411
- return new ComponentNode(Component, props, this);
5394
+ return new ComponentNode(Component, props, this, null, null);
5412
5395
  }
5413
5396
  mountNode(node, target, options) {
5414
5397
  const promise = new Promise((resolve, reject) => {
@@ -5583,7 +5566,7 @@ const __info__ = {};
5583
5566
  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, whenReady, xml };
5584
5567
 
5585
5568
 
5586
- __info__.version = '2.0.0-beta-4';
5587
- __info__.date = '2022-03-29T13:50:04.545Z';
5588
- __info__.hash = '55dbc01';
5569
+ __info__.version = '2.0.0-beta-5';
5570
+ __info__.date = '2022-04-07T13:36:37.300Z';
5571
+ __info__.hash = '1179e84';
5589
5572
  __info__.url = 'https://github.com/odoo/owl';