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

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);
@@ -2006,13 +2001,21 @@ function makeRootFiber(node) {
2006
2001
  }
2007
2002
  return fiber;
2008
2003
  }
2004
+ function throwOnRender() {
2005
+ throw new Error("Attempted to render cancelled fiber");
2006
+ }
2009
2007
  /**
2010
2008
  * @returns number of not-yet rendered fibers cancelled
2011
2009
  */
2012
2010
  function cancelFibers(fibers) {
2013
2011
  let result = 0;
2014
2012
  for (let fiber of fibers) {
2015
- fiber.node.fiber = null;
2013
+ let node = fiber.node;
2014
+ fiber.render = throwOnRender;
2015
+ if (node.status === 0 /* NEW */) {
2016
+ node.destroy();
2017
+ }
2018
+ node.fiber = null;
2016
2019
  if (fiber.bdom) {
2017
2020
  // if fiber has been rendered, this means that the component props have
2018
2021
  // been updated. however, this fiber will not be patched to the dom, so
@@ -2020,7 +2023,7 @@ function cancelFibers(fibers) {
2020
2023
  // the same props, and skip the render completely. With the next line,
2021
2024
  // we kindly request the component code to force a render, so it works as
2022
2025
  // expected.
2023
- fiber.node.forceNextRender = true;
2026
+ node.forceNextRender = true;
2024
2027
  }
2025
2028
  else {
2026
2029
  result++;
@@ -2035,6 +2038,7 @@ class Fiber {
2035
2038
  this.children = [];
2036
2039
  this.appliedToDom = false;
2037
2040
  this.deep = false;
2041
+ this.childrenMap = {};
2038
2042
  this.node = node;
2039
2043
  this.parent = parent;
2040
2044
  if (parent) {
@@ -2051,21 +2055,17 @@ class Fiber {
2051
2055
  render() {
2052
2056
  // if some parent has a fiber => register in followup
2053
2057
  let prev = this.root.node;
2058
+ let scheduler = prev.app.scheduler;
2054
2059
  let current = prev.parent;
2055
2060
  while (current) {
2056
2061
  if (current.fiber) {
2057
2062
  let root = current.fiber.root;
2058
- if (root.counter) {
2059
- root.delayedRenders.push(this);
2060
- return;
2063
+ if (root.counter === 0 && prev.parentKey in current.fiber.childrenMap) {
2064
+ current = root.node;
2061
2065
  }
2062
2066
  else {
2063
- if (!root.reachedChildren.has(prev)) {
2064
- // is dead
2065
- this.node.app.scheduler.shouldClear = true;
2066
- return;
2067
- }
2068
- current = root.node;
2067
+ scheduler.delayedRenders.push(this);
2068
+ return;
2069
2069
  }
2070
2070
  }
2071
2071
  prev = current;
@@ -2079,12 +2079,13 @@ class Fiber {
2079
2079
  const root = this.root;
2080
2080
  if (root) {
2081
2081
  try {
2082
+ this.bdom = true;
2082
2083
  this.bdom = node.renderFn();
2083
- root.setCounter(root.counter - 1);
2084
2084
  }
2085
2085
  catch (e) {
2086
2086
  handleError({ node, error: e });
2087
2087
  }
2088
+ root.setCounter(root.counter - 1);
2088
2089
  }
2089
2090
  }
2090
2091
  }
@@ -2099,8 +2100,6 @@ class RootFiber extends Fiber {
2099
2100
  // A fiber is typically locked when it is completing and the patch has not, or is being applied.
2100
2101
  // i.e.: render triggered in onWillUnmount or in willPatch will be delayed
2101
2102
  this.locked = false;
2102
- this.delayedRenders = [];
2103
- this.reachedChildren = new WeakSet();
2104
2103
  }
2105
2104
  complete() {
2106
2105
  const node = this.node;
@@ -2153,14 +2152,6 @@ class RootFiber extends Fiber {
2153
2152
  setCounter(newValue) {
2154
2153
  this.counter = newValue;
2155
2154
  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
2155
  this.node.app.scheduler.flush();
2165
2156
  }
2166
2157
  }
@@ -2175,6 +2166,7 @@ class MountFiber extends RootFiber {
2175
2166
  let current = this;
2176
2167
  try {
2177
2168
  const node = this.node;
2169
+ node.children = this.childrenMap;
2178
2170
  node.app.constructor.validateTarget(this.target);
2179
2171
  if (node.bdom) {
2180
2172
  // this is a complicated situation: if we mount a fiber with an existing
@@ -2380,7 +2372,7 @@ function useState(state) {
2380
2372
  const node = getCurrent();
2381
2373
  let render = batchedRenderFunctions.get(node);
2382
2374
  if (!render) {
2383
- render = batched(node.render.bind(node));
2375
+ render = batched(node.render.bind(node, false));
2384
2376
  batchedRenderFunctions.set(node, render);
2385
2377
  // manual implementation of onWillDestroy to break cyclic dependency
2386
2378
  node.willDestroy.push(clearReactivesForCallback.bind(null, render));
@@ -2398,14 +2390,8 @@ function arePropsDifferent(props1, props2) {
2398
2390
  function component(name, props, key, ctx, parent) {
2399
2391
  let node = ctx.children[key];
2400
2392
  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
- }
2393
+ if (node && node.status === 2 /* DESTROYED */) {
2394
+ node = undefined;
2409
2395
  }
2410
2396
  if (isDynamic && node && node.component.constructor !== name) {
2411
2397
  node = undefined;
@@ -2436,15 +2422,15 @@ function component(name, props, key, ctx, parent) {
2436
2422
  throw new Error(`Cannot find the definition of component "${name}"`);
2437
2423
  }
2438
2424
  }
2439
- node = new ComponentNode(C, props, ctx.app, ctx);
2425
+ node = new ComponentNode(C, props, ctx.app, ctx, key);
2440
2426
  ctx.children[key] = node;
2441
2427
  node.initiateRender(new Fiber(node, parentFiber));
2442
2428
  }
2443
- parentFiber.root.reachedChildren.add(node);
2429
+ parentFiber.childrenMap[key] = node;
2444
2430
  return node;
2445
2431
  }
2446
2432
  class ComponentNode {
2447
- constructor(C, props, app, parent) {
2433
+ constructor(C, props, app, parent, parentKey) {
2448
2434
  this.fiber = null;
2449
2435
  this.bdom = null;
2450
2436
  this.status = 0 /* NEW */;
@@ -2460,7 +2446,8 @@ class ComponentNode {
2460
2446
  this.willDestroy = [];
2461
2447
  currentNode = this;
2462
2448
  this.app = app;
2463
- this.parent = parent || null;
2449
+ this.parent = parent;
2450
+ this.parentKey = parentKey;
2464
2451
  this.level = parent ? parent.level + 1 : 0;
2465
2452
  applyDefaultProps(props, C);
2466
2453
  const env = (parent && parent.childEnv) || app.env;
@@ -2493,9 +2480,9 @@ class ComponentNode {
2493
2480
  fiber.render();
2494
2481
  }
2495
2482
  }
2496
- async render(deep = false) {
2483
+ async render(deep) {
2497
2484
  let current = this.fiber;
2498
- if (current && current.root.locked) {
2485
+ if (current && (current.root.locked || current.bdom === true)) {
2499
2486
  await Promise.resolve();
2500
2487
  // situation may have changed after the microtask tick
2501
2488
  current = this.fiber;
@@ -2554,8 +2541,15 @@ class ComponentNode {
2554
2541
  for (let child of Object.values(this.children)) {
2555
2542
  child._destroy();
2556
2543
  }
2557
- for (let cb of this.willDestroy) {
2558
- 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
+ }
2559
2553
  }
2560
2554
  this.status = 2 /* DESTROYED */;
2561
2555
  }
@@ -2621,6 +2615,7 @@ class ComponentNode {
2621
2615
  bdom.mount(parent, anchor);
2622
2616
  this.status = 1 /* MOUNTED */;
2623
2617
  this.fiber.appliedToDom = true;
2618
+ this.children = this.fiber.childrenMap;
2624
2619
  this.fiber = null;
2625
2620
  }
2626
2621
  moveBefore(other, afterNode) {
@@ -2636,10 +2631,8 @@ class ComponentNode {
2636
2631
  }
2637
2632
  _patch() {
2638
2633
  const hasChildren = Object.keys(this.children).length > 0;
2634
+ this.children = this.fiber.childrenMap;
2639
2635
  this.bdom.patch(this.fiber.bdom, hasChildren);
2640
- if (hasChildren) {
2641
- this.cleanOutdatedChildren();
2642
- }
2643
2636
  this.fiber.appliedToDom = true;
2644
2637
  this.fiber = null;
2645
2638
  }
@@ -2649,19 +2642,6 @@ class ComponentNode {
2649
2642
  remove() {
2650
2643
  this.bdom.remove();
2651
2644
  }
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
2645
  // ---------------------------------------------------------------------------
2666
2646
  // Some debug helpers
2667
2647
  // ---------------------------------------------------------------------------
@@ -2681,7 +2661,7 @@ class Scheduler {
2681
2661
  constructor() {
2682
2662
  this.tasks = new Set();
2683
2663
  this.frame = 0;
2684
- this.shouldClear = false;
2664
+ this.delayedRenders = [];
2685
2665
  this.requestAnimationFrame = Scheduler.requestAnimationFrame;
2686
2666
  }
2687
2667
  addFiber(fiber) {
@@ -2692,16 +2672,22 @@ class Scheduler {
2692
2672
  * Other tasks are left unchanged.
2693
2673
  */
2694
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 */ && f.node.fiber === f) {
2680
+ f.render();
2681
+ }
2682
+ }
2683
+ }
2695
2684
  if (this.frame === 0) {
2696
2685
  this.frame = this.requestAnimationFrame(() => {
2697
2686
  this.frame = 0;
2698
2687
  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
- }
2688
+ for (let task of this.tasks) {
2689
+ if (task.node.status === 2 /* DESTROYED */) {
2690
+ this.tasks.delete(task);
2705
2691
  }
2706
2692
  }
2707
2693
  });
@@ -4015,8 +4001,11 @@ class CodeGenerator {
4015
4001
  let slotStr = [];
4016
4002
  for (let slotName in ast.slots) {
4017
4003
  const slotAst = ast.slots[slotName];
4018
- const name = this.compileInNewTarget("slot", slotAst.content, ctx, slotAst.on);
4019
- 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
+ }
4020
4009
  const scope = ast.slots[slotName].scope;
4021
4010
  if (scope) {
4022
4011
  params.push(`__scope: "${scope}"`);
@@ -4118,7 +4107,7 @@ class CodeGenerator {
4118
4107
  if (dynamic) {
4119
4108
  let name = this.generateId("slot");
4120
4109
  this.define(name, slotName);
4121
- blockString = `toggler(${name}, callSlot(ctx, node, key, ${name}), ${dynamic}, ${scope})`;
4110
+ blockString = `toggler(${name}, callSlot(ctx, node, key, ${name}, ${dynamic}, ${scope}))`;
4122
4111
  }
4123
4112
  else {
4124
4113
  blockString = `callSlot(ctx, node, key, ${slotName}, ${dynamic}, ${scope})`;
@@ -4659,28 +4648,26 @@ function parseComponent(node, ctx) {
4659
4648
  slotNode.removeAttribute("t-set-slot");
4660
4649
  slotNode.remove();
4661
4650
  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
- }
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;
4680
4667
  }
4681
- slots = slots || {};
4682
- slots[name] = { content: slotAst, on, attrs, scope };
4683
4668
  }
4669
+ slots = slots || {};
4670
+ slots[name] = { content: slotAst, on, attrs, scope };
4684
4671
  }
4685
4672
  // default slot
4686
4673
  const defaultContent = parseChildNodes(clone, ctx);
@@ -5026,7 +5013,7 @@ class Component {
5026
5013
  }
5027
5014
  setup() { }
5028
5015
  render(deep = false) {
5029
- this.__owl__.render(deep);
5016
+ this.__owl__.render(deep === true);
5030
5017
  }
5031
5018
  }
5032
5019
  Component.template = "";
@@ -5323,13 +5310,19 @@ class TemplateSet {
5323
5310
  }
5324
5311
  this.helpers = makeHelpers(this.getTemplate.bind(this));
5325
5312
  }
5326
- addTemplate(name, template, options = {}) {
5327
- if (name in this.rawTemplates && !options.allowDuplicate) {
5328
- throw new Error(`Template ${name} already defined`);
5313
+ addTemplate(name, template) {
5314
+ if (name in this.rawTemplates) {
5315
+ const rawTemplate = this.rawTemplates[name];
5316
+ const currentAsString = typeof rawTemplate === "string" ? rawTemplate : rawTemplate.outerHTML;
5317
+ const newAsString = typeof template === "string" ? template : template.outerHTML;
5318
+ if (currentAsString === newAsString) {
5319
+ return;
5320
+ }
5321
+ throw new Error(`Template ${name} already defined with different content`);
5329
5322
  }
5330
5323
  this.rawTemplates[name] = template;
5331
5324
  }
5332
- addTemplates(xml, options = {}) {
5325
+ addTemplates(xml) {
5333
5326
  if (!xml) {
5334
5327
  // empty string
5335
5328
  return;
@@ -5337,7 +5330,7 @@ class TemplateSet {
5337
5330
  xml = xml instanceof Document ? xml : parseXML(xml);
5338
5331
  for (const template of xml.querySelectorAll("[t-name]")) {
5339
5332
  const name = template.getAttribute("t-name");
5340
- this.addTemplate(name, template, options);
5333
+ this.addTemplate(name, template);
5341
5334
  }
5342
5335
  }
5343
5336
  getTemplate(name) {
@@ -5408,7 +5401,7 @@ class App extends TemplateSet {
5408
5401
  return prom;
5409
5402
  }
5410
5403
  makeNode(Component, props) {
5411
- return new ComponentNode(Component, props, this);
5404
+ return new ComponentNode(Component, props, this, null, null);
5412
5405
  }
5413
5406
  mountNode(node, target, options) {
5414
5407
  const promise = new Promise((resolve, reject) => {
@@ -5583,7 +5576,7 @@ const __info__ = {};
5583
5576
  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
5577
 
5585
5578
 
5586
- __info__.version = '2.0.0-beta-4';
5587
- __info__.date = '2022-03-29T13:50:04.545Z';
5588
- __info__.hash = '55dbc01';
5579
+ __info__.version = '2.0.0-beta-7';
5580
+ __info__.date = '2022-04-27T09:08:40.703Z';
5581
+ __info__.hash = '0cd66c8';
5589
5582
  __info__.url = 'https://github.com/odoo/owl';