@odoo/owl 2.8.1 → 3.0.0-alpha.10

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.
Files changed (73) hide show
  1. package/dist/compile_templates.mjs +92 -189
  2. package/dist/compiler.js +2378 -0
  3. package/dist/owl-devtools.zip +0 -0
  4. package/dist/owl.cjs.js +1371 -1261
  5. package/dist/owl.cjs.runtime.js +4094 -0
  6. package/dist/owl.es.js +1358 -1252
  7. package/dist/owl.es.runtime.js +4050 -0
  8. package/dist/owl.iife.js +1371 -1261
  9. package/dist/owl.iife.min.js +1 -1
  10. package/dist/owl.iife.runtime.js +4098 -0
  11. package/dist/owl.iife.runtime.min.js +1 -0
  12. package/dist/types/compiler/code_generator.d.ts +3 -5
  13. package/dist/types/compiler/index.d.ts +4 -4
  14. package/dist/types/compiler/inline_expressions.d.ts +1 -1
  15. package/dist/types/compiler/parser.d.ts +21 -28
  16. package/dist/types/owl.d.ts +299 -205
  17. package/dist/types/runtime/app.d.ts +29 -31
  18. package/dist/types/runtime/blockdom/block_compiler.d.ts +3 -3
  19. package/dist/types/runtime/blockdom/config.d.ts +1 -1
  20. package/dist/types/runtime/blockdom/event_catcher.d.ts +2 -2
  21. package/dist/types/runtime/blockdom/events.d.ts +1 -1
  22. package/dist/types/runtime/blockdom/index.d.ts +1 -1
  23. package/dist/types/runtime/cancellableContext.d.ts +15 -0
  24. package/dist/types/runtime/cancellablePromise.d.ts +15 -0
  25. package/dist/types/runtime/component.d.ts +5 -13
  26. package/dist/types/runtime/component_node.d.ts +15 -35
  27. package/dist/types/runtime/event_handling.d.ts +1 -1
  28. package/dist/types/runtime/executionContext.d.ts +0 -0
  29. package/dist/types/runtime/hooks.d.ts +7 -33
  30. package/dist/types/runtime/index.d.ts +15 -5
  31. package/dist/types/runtime/lifecycle_hooks.d.ts +1 -3
  32. package/dist/types/runtime/listOperation.d.ts +1 -0
  33. package/dist/types/runtime/plugins.d.ts +23 -0
  34. package/dist/types/runtime/portal.d.ts +4 -6
  35. package/dist/types/runtime/props.d.ts +65 -0
  36. package/dist/types/runtime/reactivity/computations.d.ts +31 -0
  37. package/dist/types/runtime/reactivity/computed.d.ts +7 -0
  38. package/dist/types/runtime/reactivity/derived.d.ts +7 -0
  39. package/dist/types/runtime/reactivity/effect.d.ts +2 -0
  40. package/dist/types/runtime/reactivity/proxy.d.ts +46 -0
  41. package/dist/types/runtime/reactivity/reactivity.d.ts +46 -0
  42. package/dist/types/runtime/reactivity/signal.d.ts +17 -0
  43. package/dist/types/runtime/reactivity/signals.d.ts +30 -0
  44. package/dist/types/runtime/registry.d.ts +19 -0
  45. package/dist/types/runtime/relationalModel/discussModel.d.ts +19 -0
  46. package/dist/types/runtime/relationalModel/discussModelTypes.d.ts +22 -0
  47. package/dist/types/runtime/relationalModel/field.d.ts +20 -0
  48. package/dist/types/runtime/relationalModel/model.d.ts +59 -0
  49. package/dist/types/runtime/relationalModel/modelData.d.ts +18 -0
  50. package/dist/types/runtime/relationalModel/modelRegistry.d.ts +3 -0
  51. package/dist/types/runtime/relationalModel/modelUtils.d.ts +4 -0
  52. package/dist/types/runtime/relationalModel/store.d.ts +16 -0
  53. package/dist/types/runtime/relationalModel/types.d.ts +83 -0
  54. package/dist/types/runtime/relationalModel/util.d.ts +1 -0
  55. package/dist/types/runtime/relationalModel/web/WebDataPoint.d.ts +25 -0
  56. package/dist/types/runtime/relationalModel/web/WebRecord.d.ts +131 -0
  57. package/dist/types/runtime/relationalModel/web/WebStaticList.d.ts +63 -0
  58. package/dist/types/runtime/relationalModel/web/webModel.d.ts +5 -0
  59. package/dist/types/runtime/relationalModel/web/webModelTypes.d.ts +139 -0
  60. package/dist/types/runtime/rendering/error_handling.d.ts +13 -0
  61. package/dist/types/runtime/rendering/fibers.d.ts +37 -0
  62. package/dist/types/runtime/rendering/scheduler.d.ts +21 -0
  63. package/dist/types/runtime/rendering/template_helpers.d.ts +50 -0
  64. package/dist/types/runtime/resource.d.ts +12 -0
  65. package/dist/types/runtime/signals.d.ts +19 -0
  66. package/dist/types/runtime/status.d.ts +2 -3
  67. package/dist/types/runtime/task.d.ts +12 -0
  68. package/dist/types/runtime/template_set.d.ts +3 -4
  69. package/dist/types/runtime/utils.d.ts +1 -2
  70. package/dist/types/runtime/validation.d.ts +6 -6
  71. package/dist/types/utils/registry.d.ts +15 -0
  72. package/dist/types/version.d.ts +1 -1
  73. package/package.json +9 -9
package/dist/owl.iife.js CHANGED
@@ -32,6 +32,7 @@
32
32
  // -----------------------------------------------------------------------------
33
33
  // Toggler node
34
34
  // -----------------------------------------------------------------------------
35
+ const txt = document.createTextNode("");
35
36
  class VToggler {
36
37
  constructor(key, child) {
37
38
  this.key = key;
@@ -57,11 +58,13 @@
57
58
  child1.patch(child2, withBeforeRemove);
58
59
  }
59
60
  else {
60
- child2.mount(this.parentEl, child1.firstNode());
61
+ const firstNode = child1.firstNode();
62
+ firstNode.parentElement.insertBefore(txt, firstNode);
61
63
  if (withBeforeRemove) {
62
64
  child1.beforeRemove();
63
65
  }
64
66
  child1.remove();
67
+ child2.mount(this.parentEl, txt);
65
68
  this.child = child2;
66
69
  this.key = other.key;
67
70
  }
@@ -334,13 +337,6 @@
334
337
  }
335
338
  }).then(fn || function () { });
336
339
  }
337
- async function loadFile(url) {
338
- const result = await fetch(url);
339
- if (!result.ok) {
340
- throw new OwlError("Error while fetching xml templates");
341
- }
342
- return await result.text();
343
- }
344
340
  /*
345
341
  * This class just transports the fact that a string is safe
346
342
  * to be injected as HTML. Overriding a JS primitive is quite painful though
@@ -901,7 +897,7 @@
901
897
  function buildContext(tree, ctx, fromIdx) {
902
898
  if (!ctx) {
903
899
  const children = new Array(tree.info.filter((v) => v.type === "child").length);
904
- ctx = { collectors: [], locations: [], children, cbRefs: [], refN: tree.refN, refList: [] };
900
+ ctx = { collectors: [], locations: [], children, cbRefs: [], refN: tree.refN };
905
901
  fromIdx = 0;
906
902
  }
907
903
  if (tree.refN) {
@@ -1008,14 +1004,16 @@
1008
1004
  });
1009
1005
  break;
1010
1006
  }
1011
- case "ref":
1012
- const index = ctx.cbRefs.push(info.idx) - 1;
1007
+ case "ref": {
1013
1008
  ctx.locations.push({
1014
1009
  idx: info.idx,
1015
1010
  refIdx: info.refIdx,
1016
- setData: makeRefSetter(index, ctx.refList),
1011
+ setData: NO_OP,
1017
1012
  updateData: NO_OP,
1018
1013
  });
1014
+ ctx.cbRefs.push(info.idx);
1015
+ break;
1016
+ }
1019
1017
  }
1020
1018
  }
1021
1019
  }
@@ -1024,27 +1022,6 @@
1024
1022
  // -----------------------------------------------------------------------------
1025
1023
  function buildBlock(template, ctx) {
1026
1024
  let B = createBlockClass(template, ctx);
1027
- if (ctx.cbRefs.length) {
1028
- const cbRefs = ctx.cbRefs;
1029
- const refList = ctx.refList;
1030
- let cbRefsNumber = cbRefs.length;
1031
- B = class extends B {
1032
- mount(parent, afterNode) {
1033
- refList.push(new Array(cbRefsNumber));
1034
- super.mount(parent, afterNode);
1035
- for (let cbRef of refList.pop()) {
1036
- cbRef();
1037
- }
1038
- }
1039
- remove() {
1040
- super.remove();
1041
- for (let cbRef of cbRefs) {
1042
- let fn = this.data[cbRef];
1043
- fn(null);
1044
- }
1045
- }
1046
- };
1047
- }
1048
1025
  if (ctx.children.length) {
1049
1026
  B = class extends B {
1050
1027
  constructor(data, children) {
@@ -1058,14 +1035,9 @@
1058
1035
  return (data) => new B(data);
1059
1036
  }
1060
1037
  function createBlockClass(template, ctx) {
1061
- const { refN, collectors, children } = ctx;
1038
+ const { refN, collectors, children, locations, cbRefs } = ctx;
1062
1039
  const colN = collectors.length;
1063
- ctx.locations.sort((a, b) => a.idx - b.idx);
1064
- const locations = ctx.locations.map((loc) => ({
1065
- refIdx: loc.refIdx,
1066
- setData: loc.setData,
1067
- updateData: loc.updateData,
1068
- }));
1040
+ locations.sort((a, b) => a.idx - b.idx);
1069
1041
  const locN = locations.length;
1070
1042
  const childN = children.length;
1071
1043
  const childrenLocs = children;
@@ -1141,6 +1113,15 @@
1141
1113
  }
1142
1114
  this.el = el;
1143
1115
  this.parentEl = parent;
1116
+ if (cbRefs.length) {
1117
+ const data = this.data;
1118
+ const refs = this.refs;
1119
+ for (let cbRef of cbRefs) {
1120
+ const { idx, refIdx } = locations[cbRef];
1121
+ const fn = data[idx];
1122
+ fn(refs[refIdx], null);
1123
+ }
1124
+ }
1144
1125
  };
1145
1126
  Block.prototype.patch = function patch(other, withBeforeRemove) {
1146
1127
  if (this === other) {
@@ -1189,16 +1170,23 @@
1189
1170
  }
1190
1171
  }
1191
1172
  };
1173
+ Block.prototype.remove = function remove() {
1174
+ if (cbRefs.length) {
1175
+ const data = this.data;
1176
+ const refs = this.refs;
1177
+ for (let cbRef of cbRefs) {
1178
+ const { idx, refIdx } = locations[cbRef];
1179
+ const fn = data[idx];
1180
+ fn(null, refs[refIdx]);
1181
+ }
1182
+ }
1183
+ elementRemove.call(this.el);
1184
+ };
1192
1185
  }
1193
1186
  return Block;
1194
1187
  }
1195
1188
  function setText(value) {
1196
1189
  characterDataSetData.call(this, toText(value));
1197
- }
1198
- function makeRefSetter(index, refs) {
1199
- return function setRef(fn) {
1200
- refs[refs.length - 1][index] = () => fn(this);
1201
- };
1202
1190
  }
1203
1191
 
1204
1192
  const getDescriptor = (o, p) => Object.getOwnPropertyDescriptor(o, p);
@@ -1595,9 +1583,160 @@
1595
1583
  vnode.remove();
1596
1584
  }
1597
1585
 
1586
+ var ComputationState;
1587
+ (function (ComputationState) {
1588
+ ComputationState[ComputationState["EXECUTED"] = 0] = "EXECUTED";
1589
+ ComputationState[ComputationState["STALE"] = 1] = "STALE";
1590
+ ComputationState[ComputationState["PENDING"] = 2] = "PENDING";
1591
+ })(ComputationState || (ComputationState = {}));
1592
+ let Effects;
1593
+ let CurrentComputation;
1594
+ // export function computed<T>(fn: () => T, opts?: Opts) {
1595
+ // // todo: handle cleanup
1596
+ // let computedComputation: Computation = {
1597
+ // state: ComputationState.STALE,
1598
+ // sources: new Set(),
1599
+ // isEager: true,
1600
+ // compute: () => {
1601
+ // return fn();
1602
+ // },
1603
+ // value: undefined,
1604
+ // name: opts?.name,
1605
+ // };
1606
+ // updateComputation(computedComputation);
1607
+ // }
1608
+ function onReadAtom(atom) {
1609
+ if (!CurrentComputation)
1610
+ return;
1611
+ CurrentComputation.sources.add(atom);
1612
+ atom.observers.add(CurrentComputation);
1613
+ }
1614
+ function onWriteAtom(atom) {
1615
+ collectEffects(() => {
1616
+ for (const ctx of atom.observers) {
1617
+ if (ctx.state === ComputationState.EXECUTED) {
1618
+ if (ctx.isDerived)
1619
+ markDownstream(ctx);
1620
+ else
1621
+ Effects.push(ctx);
1622
+ }
1623
+ ctx.state = ComputationState.STALE;
1624
+ }
1625
+ });
1626
+ batchProcessEffects();
1627
+ }
1628
+ function collectEffects(fn) {
1629
+ if (Effects)
1630
+ return fn();
1631
+ Effects = [];
1632
+ try {
1633
+ return fn();
1634
+ }
1635
+ finally {
1636
+ }
1637
+ }
1638
+ const batchProcessEffects = batched(processEffects);
1639
+ function processEffects() {
1640
+ if (!Effects)
1641
+ return;
1642
+ for (const computation of Effects) {
1643
+ updateComputation(computation);
1644
+ }
1645
+ Effects = undefined;
1646
+ }
1647
+ function untrack(fn) {
1648
+ return runWithComputation(undefined, fn);
1649
+ }
1650
+ function getCurrentComputation() {
1651
+ return CurrentComputation;
1652
+ }
1653
+ function setComputation(computation) {
1654
+ CurrentComputation = computation;
1655
+ }
1656
+ // todo: should probably use updateComputation instead.
1657
+ function runWithComputation(computation, fn) {
1658
+ const previousComputation = CurrentComputation;
1659
+ CurrentComputation = computation;
1660
+ let result;
1661
+ try {
1662
+ result = fn();
1663
+ }
1664
+ finally {
1665
+ CurrentComputation = previousComputation;
1666
+ }
1667
+ return result;
1668
+ }
1669
+ function updateComputation(computation) {
1670
+ var _a;
1671
+ const state = computation.state;
1672
+ if (computation.isDerived)
1673
+ onReadAtom(computation);
1674
+ if (state === ComputationState.EXECUTED)
1675
+ return;
1676
+ if (state === ComputationState.PENDING) {
1677
+ computeSources(computation);
1678
+ // If the state is still not stale after processing the sources, it means
1679
+ // none of the dependencies have changed.
1680
+ // todo: test it
1681
+ if (computation.state !== ComputationState.STALE) {
1682
+ computation.state = ComputationState.EXECUTED;
1683
+ return;
1684
+ }
1685
+ }
1686
+ // todo: test performance. We might want to avoid removing the atoms to
1687
+ // directly re-add them at compute. Especially as we are making them stale.
1688
+ removeSources(computation);
1689
+ const previousComputation = CurrentComputation;
1690
+ CurrentComputation = computation;
1691
+ computation.value = (_a = computation.compute) === null || _a === void 0 ? void 0 : _a.call(computation);
1692
+ computation.state = ComputationState.EXECUTED;
1693
+ CurrentComputation = previousComputation;
1694
+ }
1695
+ function removeSources(computation) {
1696
+ const sources = computation.sources;
1697
+ for (const source of sources) {
1698
+ const observers = source.observers;
1699
+ observers.delete(computation);
1700
+ // todo: if source has no effect observer anymore, remove its sources too
1701
+ // todo: test it
1702
+ }
1703
+ sources.clear();
1704
+ }
1705
+ function markDownstream(derived) {
1706
+ for (const observer of derived.observers) {
1707
+ // if the state has already been marked, skip it
1708
+ if (observer.state)
1709
+ continue;
1710
+ observer.state = ComputationState.PENDING;
1711
+ if (observer.isDerived)
1712
+ markDownstream(observer);
1713
+ else
1714
+ Effects.push(observer);
1715
+ }
1716
+ }
1717
+ function computeSources(derived) {
1718
+ for (const source of derived.sources) {
1719
+ if (!("compute" in source))
1720
+ continue;
1721
+ updateComputation(source);
1722
+ }
1723
+ }
1724
+
1598
1725
  // Maps fibers to thrown errors
1599
1726
  const fibersInError = new WeakMap();
1600
1727
  const nodeErrorHandlers = new WeakMap();
1728
+ function destroyApp(app, error) {
1729
+ try {
1730
+ app.destroy();
1731
+ }
1732
+ catch (e) {
1733
+ // mute all errors here because we are in a corrupted state anyway
1734
+ }
1735
+ const e = Object.assign(new OwlError(`[Owl] Unhandled error. Destroying the root component`), {
1736
+ cause: error,
1737
+ });
1738
+ return e;
1739
+ }
1601
1740
  function _handleError(node, error) {
1602
1741
  if (!node) {
1603
1742
  return false;
@@ -1610,9 +1749,10 @@
1610
1749
  if (errorHandlers) {
1611
1750
  let handled = false;
1612
1751
  // execute in the opposite order
1752
+ const finalize = () => destroyApp(node.app, error);
1613
1753
  for (let i = errorHandlers.length - 1; i >= 0; i--) {
1614
1754
  try {
1615
- errorHandlers[i](error);
1755
+ errorHandlers[i](error, finalize);
1616
1756
  handled = true;
1617
1757
  break;
1618
1758
  }
@@ -1628,10 +1768,6 @@
1628
1768
  }
1629
1769
  function handleError(params) {
1630
1770
  let { error } = params;
1631
- // Wrap error if it wasn't wrapped by wrapError (ie when not in dev mode)
1632
- if (!(error instanceof OwlError)) {
1633
- error = Object.assign(new OwlError(`An error occured in the owl lifecycle (see this Error's "cause" property)`), { cause: error });
1634
- }
1635
1771
  const node = "node" in params ? params.node : params.fiber.node;
1636
1772
  const fiber = "fiber" in params ? params.fiber : node.fiber;
1637
1773
  if (fiber) {
@@ -1646,14 +1782,7 @@
1646
1782
  }
1647
1783
  const handled = _handleError(node, error);
1648
1784
  if (!handled) {
1649
- console.warn(`[Owl] Unhandled error. Destroying the root component`);
1650
- try {
1651
- node.app.destroy();
1652
- }
1653
- catch (e) {
1654
- console.error(e);
1655
- }
1656
- throw error;
1785
+ throw destroyApp(node.app, error);
1657
1786
  }
1658
1787
  }
1659
1788
 
@@ -1712,7 +1841,7 @@
1712
1841
  for (let fiber of fibers) {
1713
1842
  let node = fiber.node;
1714
1843
  fiber.render = throwOnRender;
1715
- if (node.status === 0 /* NEW */) {
1844
+ if (node.status === 0 /* STATUS.NEW */) {
1716
1845
  node.cancel();
1717
1846
  }
1718
1847
  node.fiber = null;
@@ -1778,13 +1907,16 @@
1778
1907
  const node = this.node;
1779
1908
  const root = this.root;
1780
1909
  if (root) {
1781
- try {
1782
- this.bdom = true;
1783
- this.bdom = node.renderFn();
1784
- }
1785
- catch (e) {
1786
- node.app.handleError({ node, error: e });
1787
- }
1910
+ // todo: should use updateComputation somewhere else.
1911
+ runWithComputation(node.signalComputation, () => {
1912
+ try {
1913
+ this.bdom = true;
1914
+ this.bdom = node.renderFn();
1915
+ }
1916
+ catch (e) {
1917
+ node.app.handleError({ node, error: e });
1918
+ }
1919
+ });
1788
1920
  root.setCounter(root.counter - 1);
1789
1921
  }
1790
1922
  }
@@ -1897,7 +2029,7 @@
1897
2029
  // unregistering the fiber before mounted since it can do another render
1898
2030
  // and that the current rendering is obviously completed
1899
2031
  node.fiber = null;
1900
- node.status = 1 /* MOUNTED */;
2032
+ node.status = 1 /* STATUS.MOUNTED */;
1901
2033
  this.appliedToDom = true;
1902
2034
  let mountedFibers = this.mounted;
1903
2035
  while ((current = mountedFibers.pop())) {
@@ -1914,567 +2046,104 @@
1914
2046
  }
1915
2047
  }
1916
2048
 
1917
- // Special key to subscribe to, to be notified of key creation/deletion
1918
- const KEYCHANGES = Symbol("Key changes");
1919
- // Used to specify the absence of a callback, can be used as WeakMap key but
1920
- // should only be used as a sentinel value and never called.
1921
- const NO_CALLBACK = () => {
1922
- throw new Error("Called NO_CALLBACK. Owl is broken, please report this to the maintainers.");
1923
- };
1924
- const objectToString = Object.prototype.toString;
1925
- const objectHasOwnProperty = Object.prototype.hasOwnProperty;
1926
- // Use arrays because Array.includes is faster than Set.has for small arrays
1927
- const SUPPORTED_RAW_TYPES = ["Object", "Array", "Set", "Map", "WeakMap"];
1928
- const COLLECTION_RAW_TYPES = ["Set", "Map", "WeakMap"];
1929
- /**
1930
- * extract "RawType" from strings like "[object RawType]" => this lets us ignore
1931
- * many native objects such as Promise (whose toString is [object Promise])
1932
- * or Date ([object Date]), while also supporting collections without using
1933
- * instanceof in a loop
1934
- *
1935
- * @param obj the object to check
1936
- * @returns the raw type of the object
1937
- */
1938
- function rawType(obj) {
1939
- return objectToString.call(toRaw(obj)).slice(8, -1);
2049
+ let currentNode = null;
2050
+ function saveCurrent() {
2051
+ let n = currentNode;
2052
+ return () => {
2053
+ currentNode = n;
2054
+ };
1940
2055
  }
1941
- /**
1942
- * Checks whether a given value can be made into a reactive object.
1943
- *
1944
- * @param value the value to check
1945
- * @returns whether the value can be made reactive
1946
- */
1947
- function canBeMadeReactive(value) {
1948
- if (typeof value !== "object") {
1949
- return false;
2056
+ function getCurrent() {
2057
+ if (!currentNode) {
2058
+ throw new OwlError("No active component (a hook function should only be called in 'setup')");
1950
2059
  }
1951
- return SUPPORTED_RAW_TYPES.includes(rawType(value));
1952
- }
1953
- /**
1954
- * Creates a reactive from the given object/callback if possible and returns it,
1955
- * returns the original object otherwise.
1956
- *
1957
- * @param value the value make reactive
1958
- * @returns a reactive for the given object when possible, the original otherwise
1959
- */
1960
- function possiblyReactive(val, cb) {
1961
- return canBeMadeReactive(val) ? reactive(val, cb) : val;
1962
- }
1963
- const skipped = new WeakSet();
1964
- /**
1965
- * Mark an object or array so that it is ignored by the reactivity system
1966
- *
1967
- * @param value the value to mark
1968
- * @returns the object itself
1969
- */
1970
- function markRaw(value) {
1971
- skipped.add(value);
1972
- return value;
1973
- }
1974
- /**
1975
- * Given a reactive objet, return the raw (non reactive) underlying object
1976
- *
1977
- * @param value a reactive value
1978
- * @returns the underlying value
1979
- */
1980
- function toRaw(value) {
1981
- return targets.has(value) ? targets.get(value) : value;
2060
+ return currentNode;
1982
2061
  }
1983
- const targetToKeysToCallbacks = new WeakMap();
1984
- /**
1985
- * Observes a given key on a target with an callback. The callback will be
1986
- * called when the given key changes on the target.
1987
- *
1988
- * @param target the target whose key should be observed
1989
- * @param key the key to observe (or Symbol(KEYCHANGES) for key creation
1990
- * or deletion)
1991
- * @param callback the function to call when the key changes
1992
- */
1993
- function observeTargetKey(target, key, callback) {
1994
- if (callback === NO_CALLBACK) {
1995
- return;
1996
- }
1997
- if (!targetToKeysToCallbacks.get(target)) {
1998
- targetToKeysToCallbacks.set(target, new Map());
1999
- }
2000
- const keyToCallbacks = targetToKeysToCallbacks.get(target);
2001
- if (!keyToCallbacks.get(key)) {
2002
- keyToCallbacks.set(key, new Set());
2003
- }
2004
- keyToCallbacks.get(key).add(callback);
2005
- if (!callbacksToTargets.has(callback)) {
2006
- callbacksToTargets.set(callback, new Set());
2007
- }
2008
- callbacksToTargets.get(callback).add(target);
2062
+ function useComponent() {
2063
+ return currentNode.component;
2009
2064
  }
2010
- /**
2011
- * Notify Reactives that are observing a given target that a key has changed on
2012
- * the target.
2013
- *
2014
- * @param target target whose Reactives should be notified that the target was
2015
- * changed.
2016
- * @param key the key that changed (or Symbol `KEYCHANGES` if a key was created
2017
- * or deleted)
2018
- */
2019
- function notifyReactives(target, key) {
2020
- const keyToCallbacks = targetToKeysToCallbacks.get(target);
2021
- if (!keyToCallbacks) {
2022
- return;
2023
- }
2024
- const callbacks = keyToCallbacks.get(key);
2025
- if (!callbacks) {
2026
- return;
2027
- }
2028
- // Loop on copy because clearReactivesForCallback will modify the set in place
2029
- for (const callback of [...callbacks]) {
2030
- clearReactivesForCallback(callback);
2031
- callback();
2065
+ class ComponentNode {
2066
+ constructor(C, props, app, parent, parentKey) {
2067
+ this.fiber = null;
2068
+ this.bdom = null;
2069
+ this.status = 0 /* STATUS.NEW */;
2070
+ this.forceNextRender = false;
2071
+ this.children = Object.create(null);
2072
+ this.willStart = [];
2073
+ this.willUpdateProps = [];
2074
+ this.willUnmount = [];
2075
+ this.mounted = [];
2076
+ this.willPatch = [];
2077
+ this.patched = [];
2078
+ this.willDestroy = [];
2079
+ this.name = C.name;
2080
+ currentNode = this;
2081
+ this.app = app;
2082
+ this.parent = parent;
2083
+ this.parentKey = parentKey;
2084
+ this.pluginManager = parent ? parent.pluginManager : app.pluginManager;
2085
+ this.signalComputation = {
2086
+ value: undefined,
2087
+ compute: () => this.render(false),
2088
+ sources: new Set(),
2089
+ state: ComputationState.EXECUTED,
2090
+ };
2091
+ this.props = Object.assign({}, props);
2092
+ const previousComputation = getCurrentComputation();
2093
+ setComputation(this.signalComputation);
2094
+ this.component = new C(this);
2095
+ const ctx = { this: this.component, __owl__: this };
2096
+ this.renderFn = app.getTemplate(C.template).bind(this.component, ctx, this);
2097
+ this.component.setup();
2098
+ setComputation(previousComputation);
2099
+ currentNode = null;
2032
2100
  }
2033
- }
2034
- const callbacksToTargets = new WeakMap();
2035
- /**
2036
- * Clears all subscriptions of the Reactives associated with a given callback.
2037
- *
2038
- * @param callback the callback for which the reactives need to be cleared
2039
- */
2040
- function clearReactivesForCallback(callback) {
2041
- const targetsToClear = callbacksToTargets.get(callback);
2042
- if (!targetsToClear) {
2043
- return;
2101
+ mountComponent(target, options) {
2102
+ const fiber = new MountFiber(this, target, options);
2103
+ this.app.scheduler.addFiber(fiber);
2104
+ this.initiateRender(fiber);
2044
2105
  }
2045
- for (const target of targetsToClear) {
2046
- const observedKeys = targetToKeysToCallbacks.get(target);
2047
- if (!observedKeys) {
2048
- continue;
2106
+ async initiateRender(fiber) {
2107
+ this.fiber = fiber;
2108
+ if (this.mounted.length) {
2109
+ fiber.root.mounted.push(fiber);
2049
2110
  }
2050
- for (const [key, callbacks] of observedKeys.entries()) {
2051
- callbacks.delete(callback);
2052
- if (!callbacks.size) {
2053
- observedKeys.delete(key);
2054
- }
2111
+ const component = this.component;
2112
+ try {
2113
+ let promises;
2114
+ runWithComputation(undefined, () => {
2115
+ promises = this.willStart.map((f) => f.call(component));
2116
+ });
2117
+ await Promise.all(promises);
2118
+ }
2119
+ catch (e) {
2120
+ this.app.handleError({ node: this, error: e });
2121
+ return;
2122
+ }
2123
+ if (this.status === 0 /* STATUS.NEW */ && this.fiber === fiber) {
2124
+ fiber.render();
2055
2125
  }
2056
2126
  }
2057
- targetsToClear.clear();
2058
- }
2059
- function getSubscriptions(callback) {
2060
- const targets = callbacksToTargets.get(callback) || [];
2061
- return [...targets].map((target) => {
2062
- const keysToCallbacks = targetToKeysToCallbacks.get(target);
2063
- let keys = [];
2064
- if (keysToCallbacks) {
2065
- for (const [key, cbs] of keysToCallbacks) {
2066
- if (cbs.has(callback)) {
2067
- keys.push(key);
2127
+ async render(deep) {
2128
+ if (this.status >= 2 /* STATUS.CANCELLED */) {
2129
+ return;
2130
+ }
2131
+ let current = this.fiber;
2132
+ if (current && (current.root.locked || current.bdom === true)) {
2133
+ await Promise.resolve();
2134
+ // situation may have changed after the microtask tick
2135
+ current = this.fiber;
2136
+ }
2137
+ if (current) {
2138
+ if (!current.bdom && !fibersInError.has(current)) {
2139
+ if (deep) {
2140
+ // we want the render from this point on to be with deep=true
2141
+ current.deep = deep;
2068
2142
  }
2143
+ return;
2069
2144
  }
2070
- }
2071
- return { target, keys };
2072
- });
2073
- }
2074
- // Maps reactive objects to the underlying target
2075
- const targets = new WeakMap();
2076
- const reactiveCache = new WeakMap();
2077
- /**
2078
- * Creates a reactive proxy for an object. Reading data on the reactive object
2079
- * subscribes to changes to the data. Writing data on the object will cause the
2080
- * notify callback to be called if there are suscriptions to that data. Nested
2081
- * objects and arrays are automatically made reactive as well.
2082
- *
2083
- * Whenever you are notified of a change, all subscriptions are cleared, and if
2084
- * you would like to be notified of any further changes, you should go read
2085
- * the underlying data again. We assume that if you don't go read it again after
2086
- * being notified, it means that you are no longer interested in that data.
2087
- *
2088
- * Subscriptions:
2089
- * + Reading a property on an object will subscribe you to changes in the value
2090
- * of that property.
2091
- * + Accessing an object's keys (eg with Object.keys or with `for..in`) will
2092
- * subscribe you to the creation/deletion of keys. Checking the presence of a
2093
- * key on the object with 'in' has the same effect.
2094
- * - getOwnPropertyDescriptor does not currently subscribe you to the property.
2095
- * This is a choice that was made because changing a key's value will trigger
2096
- * this trap and we do not want to subscribe by writes. This also means that
2097
- * Object.hasOwnProperty doesn't subscribe as it goes through this trap.
2098
- *
2099
- * @param target the object for which to create a reactive proxy
2100
- * @param callback the function to call when an observed property of the
2101
- * reactive has changed
2102
- * @returns a proxy that tracks changes to it
2103
- */
2104
- function reactive(target, callback = NO_CALLBACK) {
2105
- if (!canBeMadeReactive(target)) {
2106
- throw new OwlError(`Cannot make the given value reactive`);
2107
- }
2108
- if (skipped.has(target)) {
2109
- return target;
2110
- }
2111
- if (targets.has(target)) {
2112
- // target is reactive, create a reactive on the underlying object instead
2113
- return reactive(targets.get(target), callback);
2114
- }
2115
- if (!reactiveCache.has(target)) {
2116
- reactiveCache.set(target, new WeakMap());
2117
- }
2118
- const reactivesForTarget = reactiveCache.get(target);
2119
- if (!reactivesForTarget.has(callback)) {
2120
- const targetRawType = rawType(target);
2121
- const handler = COLLECTION_RAW_TYPES.includes(targetRawType)
2122
- ? collectionsProxyHandler(target, callback, targetRawType)
2123
- : basicProxyHandler(callback);
2124
- const proxy = new Proxy(target, handler);
2125
- reactivesForTarget.set(callback, proxy);
2126
- targets.set(proxy, target);
2127
- }
2128
- return reactivesForTarget.get(callback);
2129
- }
2130
- /**
2131
- * Creates a basic proxy handler for regular objects and arrays.
2132
- *
2133
- * @param callback @see reactive
2134
- * @returns a proxy handler object
2135
- */
2136
- function basicProxyHandler(callback) {
2137
- return {
2138
- get(target, key, receiver) {
2139
- // non-writable non-configurable properties cannot be made reactive
2140
- const desc = Object.getOwnPropertyDescriptor(target, key);
2141
- if (desc && !desc.writable && !desc.configurable) {
2142
- return Reflect.get(target, key, receiver);
2143
- }
2144
- observeTargetKey(target, key, callback);
2145
- return possiblyReactive(Reflect.get(target, key, receiver), callback);
2146
- },
2147
- set(target, key, value, receiver) {
2148
- const hadKey = objectHasOwnProperty.call(target, key);
2149
- const originalValue = Reflect.get(target, key, receiver);
2150
- const ret = Reflect.set(target, key, toRaw(value), receiver);
2151
- if (!hadKey && objectHasOwnProperty.call(target, key)) {
2152
- notifyReactives(target, KEYCHANGES);
2153
- }
2154
- // While Array length may trigger the set trap, it's not actually set by this
2155
- // method but is updated behind the scenes, and the trap is not called with the
2156
- // new value. We disable the "same-value-optimization" for it because of that.
2157
- if (originalValue !== Reflect.get(target, key, receiver) ||
2158
- (key === "length" && Array.isArray(target))) {
2159
- notifyReactives(target, key);
2160
- }
2161
- return ret;
2162
- },
2163
- deleteProperty(target, key) {
2164
- const ret = Reflect.deleteProperty(target, key);
2165
- // TODO: only notify when something was actually deleted
2166
- notifyReactives(target, KEYCHANGES);
2167
- notifyReactives(target, key);
2168
- return ret;
2169
- },
2170
- ownKeys(target) {
2171
- observeTargetKey(target, KEYCHANGES, callback);
2172
- return Reflect.ownKeys(target);
2173
- },
2174
- has(target, key) {
2175
- // TODO: this observes all key changes instead of only the presence of the argument key
2176
- // observing the key itself would observe value changes instead of presence changes
2177
- // so we may need a finer grained system to distinguish observing value vs presence.
2178
- observeTargetKey(target, KEYCHANGES, callback);
2179
- return Reflect.has(target, key);
2180
- },
2181
- };
2182
- }
2183
- /**
2184
- * Creates a function that will observe the key that is passed to it when called
2185
- * and delegates to the underlying method.
2186
- *
2187
- * @param methodName name of the method to delegate to
2188
- * @param target @see reactive
2189
- * @param callback @see reactive
2190
- */
2191
- function makeKeyObserver(methodName, target, callback) {
2192
- return (key) => {
2193
- key = toRaw(key);
2194
- observeTargetKey(target, key, callback);
2195
- return possiblyReactive(target[methodName](key), callback);
2196
- };
2197
- }
2198
- /**
2199
- * Creates an iterable that will delegate to the underlying iteration method and
2200
- * observe keys as necessary.
2201
- *
2202
- * @param methodName name of the method to delegate to
2203
- * @param target @see reactive
2204
- * @param callback @see reactive
2205
- */
2206
- function makeIteratorObserver(methodName, target, callback) {
2207
- return function* () {
2208
- observeTargetKey(target, KEYCHANGES, callback);
2209
- const keys = target.keys();
2210
- for (const item of target[methodName]()) {
2211
- const key = keys.next().value;
2212
- observeTargetKey(target, key, callback);
2213
- yield possiblyReactive(item, callback);
2214
- }
2215
- };
2216
- }
2217
- /**
2218
- * Creates a forEach function that will delegate to forEach on the underlying
2219
- * collection while observing key changes, and keys as they're iterated over,
2220
- * and making the passed keys/values reactive.
2221
- *
2222
- * @param target @see reactive
2223
- * @param callback @see reactive
2224
- */
2225
- function makeForEachObserver(target, callback) {
2226
- return function forEach(forEachCb, thisArg) {
2227
- observeTargetKey(target, KEYCHANGES, callback);
2228
- target.forEach(function (val, key, targetObj) {
2229
- observeTargetKey(target, key, callback);
2230
- forEachCb.call(thisArg, possiblyReactive(val, callback), possiblyReactive(key, callback), possiblyReactive(targetObj, callback));
2231
- }, thisArg);
2232
- };
2233
- }
2234
- /**
2235
- * Creates a function that will delegate to an underlying method, and check if
2236
- * that method has modified the presence or value of a key, and notify the
2237
- * reactives appropriately.
2238
- *
2239
- * @param setterName name of the method to delegate to
2240
- * @param getterName name of the method which should be used to retrieve the
2241
- * value before calling the delegate method for comparison purposes
2242
- * @param target @see reactive
2243
- */
2244
- function delegateAndNotify(setterName, getterName, target) {
2245
- return (key, value) => {
2246
- key = toRaw(key);
2247
- const hadKey = target.has(key);
2248
- const originalValue = target[getterName](key);
2249
- const ret = target[setterName](key, value);
2250
- const hasKey = target.has(key);
2251
- if (hadKey !== hasKey) {
2252
- notifyReactives(target, KEYCHANGES);
2253
- }
2254
- if (originalValue !== target[getterName](key)) {
2255
- notifyReactives(target, key);
2256
- }
2257
- return ret;
2258
- };
2259
- }
2260
- /**
2261
- * Creates a function that will clear the underlying collection and notify that
2262
- * the keys of the collection have changed.
2263
- *
2264
- * @param target @see reactive
2265
- */
2266
- function makeClearNotifier(target) {
2267
- return () => {
2268
- const allKeys = [...target.keys()];
2269
- target.clear();
2270
- notifyReactives(target, KEYCHANGES);
2271
- for (const key of allKeys) {
2272
- notifyReactives(target, key);
2273
- }
2274
- };
2275
- }
2276
- /**
2277
- * Maps raw type of an object to an object containing functions that can be used
2278
- * to build an appropritate proxy handler for that raw type. Eg: when making a
2279
- * reactive set, calling the has method should mark the key that is being
2280
- * retrieved as observed, and calling the add or delete method should notify the
2281
- * reactives that the key which is being added or deleted has been modified.
2282
- */
2283
- const rawTypeToFuncHandlers = {
2284
- Set: (target, callback) => ({
2285
- has: makeKeyObserver("has", target, callback),
2286
- add: delegateAndNotify("add", "has", target),
2287
- delete: delegateAndNotify("delete", "has", target),
2288
- keys: makeIteratorObserver("keys", target, callback),
2289
- values: makeIteratorObserver("values", target, callback),
2290
- entries: makeIteratorObserver("entries", target, callback),
2291
- [Symbol.iterator]: makeIteratorObserver(Symbol.iterator, target, callback),
2292
- forEach: makeForEachObserver(target, callback),
2293
- clear: makeClearNotifier(target),
2294
- get size() {
2295
- observeTargetKey(target, KEYCHANGES, callback);
2296
- return target.size;
2297
- },
2298
- }),
2299
- Map: (target, callback) => ({
2300
- has: makeKeyObserver("has", target, callback),
2301
- get: makeKeyObserver("get", target, callback),
2302
- set: delegateAndNotify("set", "get", target),
2303
- delete: delegateAndNotify("delete", "has", target),
2304
- keys: makeIteratorObserver("keys", target, callback),
2305
- values: makeIteratorObserver("values", target, callback),
2306
- entries: makeIteratorObserver("entries", target, callback),
2307
- [Symbol.iterator]: makeIteratorObserver(Symbol.iterator, target, callback),
2308
- forEach: makeForEachObserver(target, callback),
2309
- clear: makeClearNotifier(target),
2310
- get size() {
2311
- observeTargetKey(target, KEYCHANGES, callback);
2312
- return target.size;
2313
- },
2314
- }),
2315
- WeakMap: (target, callback) => ({
2316
- has: makeKeyObserver("has", target, callback),
2317
- get: makeKeyObserver("get", target, callback),
2318
- set: delegateAndNotify("set", "get", target),
2319
- delete: delegateAndNotify("delete", "has", target),
2320
- }),
2321
- };
2322
- /**
2323
- * Creates a proxy handler for collections (Set/Map/WeakMap)
2324
- *
2325
- * @param callback @see reactive
2326
- * @param target @see reactive
2327
- * @returns a proxy handler object
2328
- */
2329
- function collectionsProxyHandler(target, callback, targetRawType) {
2330
- // TODO: if performance is an issue we can create the special handlers lazily when each
2331
- // property is read.
2332
- const specialHandlers = rawTypeToFuncHandlers[targetRawType](target, callback);
2333
- return Object.assign(basicProxyHandler(callback), {
2334
- // FIXME: probably broken when part of prototype chain since we ignore the receiver
2335
- get(target, key) {
2336
- if (objectHasOwnProperty.call(specialHandlers, key)) {
2337
- return specialHandlers[key];
2338
- }
2339
- observeTargetKey(target, key, callback);
2340
- return possiblyReactive(target[key], callback);
2341
- },
2342
- });
2343
- }
2344
-
2345
- let currentNode = null;
2346
- function saveCurrent() {
2347
- let n = currentNode;
2348
- return () => {
2349
- currentNode = n;
2350
- };
2351
- }
2352
- function getCurrent() {
2353
- if (!currentNode) {
2354
- throw new OwlError("No active component (a hook function should only be called in 'setup')");
2355
- }
2356
- return currentNode;
2357
- }
2358
- function useComponent() {
2359
- return currentNode.component;
2360
- }
2361
- /**
2362
- * Apply default props (only top level).
2363
- */
2364
- function applyDefaultProps(props, defaultProps) {
2365
- for (let propName in defaultProps) {
2366
- if (props[propName] === undefined) {
2367
- props[propName] = defaultProps[propName];
2368
- }
2369
- }
2370
- }
2371
- // -----------------------------------------------------------------------------
2372
- // Integration with reactivity system (useState)
2373
- // -----------------------------------------------------------------------------
2374
- const batchedRenderFunctions = new WeakMap();
2375
- /**
2376
- * Creates a reactive object that will be observed by the current component.
2377
- * Reading data from the returned object (eg during rendering) will cause the
2378
- * component to subscribe to that data and be rerendered when it changes.
2379
- *
2380
- * @param state the state to observe
2381
- * @returns a reactive object that will cause the component to re-render on
2382
- * relevant changes
2383
- * @see reactive
2384
- */
2385
- function useState(state) {
2386
- const node = getCurrent();
2387
- let render = batchedRenderFunctions.get(node);
2388
- if (!render) {
2389
- render = batched(node.render.bind(node, false));
2390
- batchedRenderFunctions.set(node, render);
2391
- // manual implementation of onWillDestroy to break cyclic dependency
2392
- node.willDestroy.push(clearReactivesForCallback.bind(null, render));
2393
- }
2394
- return reactive(state, render);
2395
- }
2396
- class ComponentNode {
2397
- constructor(C, props, app, parent, parentKey) {
2398
- this.fiber = null;
2399
- this.bdom = null;
2400
- this.status = 0 /* NEW */;
2401
- this.forceNextRender = false;
2402
- this.nextProps = null;
2403
- this.children = Object.create(null);
2404
- this.refs = {};
2405
- this.willStart = [];
2406
- this.willUpdateProps = [];
2407
- this.willUnmount = [];
2408
- this.mounted = [];
2409
- this.willPatch = [];
2410
- this.patched = [];
2411
- this.willDestroy = [];
2412
- currentNode = this;
2413
- this.app = app;
2414
- this.parent = parent;
2415
- this.props = props;
2416
- this.parentKey = parentKey;
2417
- const defaultProps = C.defaultProps;
2418
- props = Object.assign({}, props);
2419
- if (defaultProps) {
2420
- applyDefaultProps(props, defaultProps);
2421
- }
2422
- const env = (parent && parent.childEnv) || app.env;
2423
- this.childEnv = env;
2424
- for (const key in props) {
2425
- const prop = props[key];
2426
- if (prop && typeof prop === "object" && targets.has(prop)) {
2427
- props[key] = useState(prop);
2428
- }
2429
- }
2430
- this.component = new C(props, env, this);
2431
- const ctx = Object.assign(Object.create(this.component), { this: this.component });
2432
- this.renderFn = app.getTemplate(C.template).bind(this.component, ctx, this);
2433
- this.component.setup();
2434
- currentNode = null;
2435
- }
2436
- mountComponent(target, options) {
2437
- const fiber = new MountFiber(this, target, options);
2438
- this.app.scheduler.addFiber(fiber);
2439
- this.initiateRender(fiber);
2440
- }
2441
- async initiateRender(fiber) {
2442
- this.fiber = fiber;
2443
- if (this.mounted.length) {
2444
- fiber.root.mounted.push(fiber);
2445
- }
2446
- const component = this.component;
2447
- try {
2448
- await Promise.all(this.willStart.map((f) => f.call(component)));
2449
- }
2450
- catch (e) {
2451
- this.app.handleError({ node: this, error: e });
2452
- return;
2453
- }
2454
- if (this.status === 0 /* NEW */ && this.fiber === fiber) {
2455
- fiber.render();
2456
- }
2457
- }
2458
- async render(deep) {
2459
- if (this.status >= 2 /* CANCELLED */) {
2460
- return;
2461
- }
2462
- let current = this.fiber;
2463
- if (current && (current.root.locked || current.bdom === true)) {
2464
- await Promise.resolve();
2465
- // situation may have changed after the microtask tick
2466
- current = this.fiber;
2467
- }
2468
- if (current) {
2469
- if (!current.bdom && !fibersInError.has(current)) {
2470
- if (deep) {
2471
- // we want the render from this point on to be with deep=true
2472
- current.deep = deep;
2473
- }
2474
- return;
2475
- }
2476
- // if current rendering was with deep=true, we want this one to be the same
2477
- deep = deep || current.deep;
2145
+ // if current rendering was with deep=true, we want this one to be the same
2146
+ deep = deep || current.deep;
2478
2147
  }
2479
2148
  else if (!this.bdom) {
2480
2149
  return;
@@ -2484,7 +2153,7 @@
2484
2153
  this.fiber = fiber;
2485
2154
  this.app.scheduler.addFiber(fiber);
2486
2155
  await Promise.resolve();
2487
- if (this.status >= 2 /* CANCELLED */) {
2156
+ if (this.status >= 2 /* STATUS.CANCELLED */) {
2488
2157
  return;
2489
2158
  }
2490
2159
  // We only want to actually render the component if the following two
@@ -2508,14 +2177,14 @@
2508
2177
  this.app.scheduler.scheduleDestroy(this);
2509
2178
  }
2510
2179
  _cancel() {
2511
- this.status = 2 /* CANCELLED */;
2180
+ this.status = 2 /* STATUS.CANCELLED */;
2512
2181
  const children = this.children;
2513
2182
  for (let childKey in children) {
2514
2183
  children[childKey]._cancel();
2515
2184
  }
2516
2185
  }
2517
2186
  destroy() {
2518
- let shouldRemove = this.status === 1 /* MOUNTED */;
2187
+ let shouldRemove = this.status === 1 /* STATUS.MOUNTED */;
2519
2188
  this._destroy();
2520
2189
  if (shouldRemove) {
2521
2190
  this.bdom.remove();
@@ -2523,7 +2192,7 @@
2523
2192
  }
2524
2193
  _destroy() {
2525
2194
  const component = this.component;
2526
- if (this.status === 1 /* MOUNTED */) {
2195
+ if (this.status === 1 /* STATUS.MOUNTED */) {
2527
2196
  for (let cb of this.willUnmount) {
2528
2197
  cb.call(component);
2529
2198
  }
@@ -2541,33 +2210,23 @@
2541
2210
  this.app.handleError({ error: e, node: this });
2542
2211
  }
2543
2212
  }
2544
- this.status = 3 /* DESTROYED */;
2213
+ this.status = 3 /* STATUS.DESTROYED */;
2545
2214
  }
2546
2215
  async updateAndRender(props, parentFiber) {
2547
- this.nextProps = props;
2548
2216
  props = Object.assign({}, props);
2549
2217
  // update
2550
2218
  const fiber = makeChildFiber(this, parentFiber);
2551
2219
  this.fiber = fiber;
2552
2220
  const component = this.component;
2553
- const defaultProps = component.constructor.defaultProps;
2554
- if (defaultProps) {
2555
- applyDefaultProps(props, defaultProps);
2556
- }
2557
- currentNode = this;
2558
- for (const key in props) {
2559
- const prop = props[key];
2560
- if (prop && typeof prop === "object" && targets.has(prop)) {
2561
- props[key] = useState(prop);
2562
- }
2563
- }
2564
- currentNode = null;
2565
- const prom = Promise.all(this.willUpdateProps.map((f) => f.call(component, props)));
2221
+ let prom;
2222
+ untrack(() => {
2223
+ prom = Promise.all(this.willUpdateProps.map((f) => f.call(component, props)));
2224
+ });
2566
2225
  await prom;
2567
2226
  if (fiber !== this.fiber) {
2568
2227
  return;
2569
2228
  }
2570
- component.props = props;
2229
+ this.props = props;
2571
2230
  fiber.render();
2572
2231
  const parentRoot = parentFiber.root;
2573
2232
  if (this.willPatch.length) {
@@ -2602,18 +2261,6 @@
2602
2261
  this.fiber = null;
2603
2262
  }
2604
2263
  }
2605
- /**
2606
- * Sets a ref to a given HTMLElement.
2607
- *
2608
- * @param name the name of the ref to set
2609
- * @param el the HTMLElement to set the ref to. The ref is not set if the el
2610
- * is null, but useRef will not return elements that are not in the DOM
2611
- */
2612
- setRef(name, el) {
2613
- if (el) {
2614
- this.refs[name] = el;
2615
- }
2616
- }
2617
2264
  // ---------------------------------------------------------------------------
2618
2265
  // Block DOM methods
2619
2266
  // ---------------------------------------------------------------------------
@@ -2625,7 +2272,7 @@
2625
2272
  const bdom = this.fiber.bdom;
2626
2273
  this.bdom = bdom;
2627
2274
  bdom.mount(parent, anchor);
2628
- this.status = 1 /* MOUNTED */;
2275
+ this.status = 1 /* STATUS.MOUNTED */;
2629
2276
  this.fiber.appliedToDom = true;
2630
2277
  this.children = this.fiber.childrenMap;
2631
2278
  this.fiber = null;
@@ -2642,7 +2289,6 @@
2642
2289
  // by the component will be patched independently in the appropriate
2643
2290
  // fiber.complete
2644
2291
  this._patch();
2645
- this.props = this.nextProps;
2646
2292
  }
2647
2293
  }
2648
2294
  _patch() {
@@ -2664,120 +2310,130 @@
2664
2310
  remove() {
2665
2311
  this.bdom.remove();
2666
2312
  }
2667
- // ---------------------------------------------------------------------------
2668
- // Some debug helpers
2669
- // ---------------------------------------------------------------------------
2670
- get name() {
2671
- return this.component.constructor.name;
2672
- }
2673
- get subscriptions() {
2674
- const render = batchedRenderFunctions.get(this);
2675
- return render ? getSubscriptions(render) : [];
2676
- }
2677
2313
  }
2678
2314
 
2679
- const TIMEOUT = Symbol("timeout");
2680
- const HOOK_TIMEOUT = {
2681
- onWillStart: 3000,
2682
- onWillUpdateProps: 3000,
2683
- };
2684
- function wrapError(fn, hookName) {
2685
- const error = new OwlError();
2686
- const timeoutError = new OwlError();
2687
- const node = getCurrent();
2688
- return (...args) => {
2689
- const onError = (cause) => {
2690
- error.cause = cause;
2691
- error.message =
2692
- cause instanceof Error
2693
- ? `The following error occurred in ${hookName}: "${cause.message}"`
2694
- : `Something that is not an Error was thrown in ${hookName} (see this Error's "cause" property)`;
2695
- throw error;
2696
- };
2697
- let result;
2698
- try {
2699
- result = fn(...args);
2315
+ let currentPluginManager = null;
2316
+ const _getCurrentPluginManager = () => currentPluginManager;
2317
+ class Plugin {
2318
+ setup() { }
2319
+ }
2320
+ Plugin.id = "";
2321
+ class PluginManager {
2322
+ constructor(parent) {
2323
+ var _a;
2324
+ this.children = [];
2325
+ this.onDestroyCb = [];
2326
+ this.status = 0 /* STATUS.NEW */;
2327
+ this.parent = parent;
2328
+ (_a = this.parent) === null || _a === void 0 ? void 0 : _a.children.push(this);
2329
+ this.plugins = this.parent ? Object.create(this.parent.plugins) : {};
2330
+ }
2331
+ destroy() {
2332
+ for (let children of this.children) {
2333
+ children.destroy();
2700
2334
  }
2701
- catch (cause) {
2702
- onError(cause);
2335
+ const cbs = this.onDestroyCb;
2336
+ while (cbs.length) {
2337
+ cbs.pop()();
2703
2338
  }
2704
- if (!(result instanceof Promise)) {
2705
- return result;
2339
+ this.status = 3 /* STATUS.DESTROYED */;
2340
+ }
2341
+ getPluginById(id) {
2342
+ return this.plugins[id] || null;
2343
+ }
2344
+ getPlugin(pluginType) {
2345
+ return this.getPluginById(pluginType.id);
2346
+ }
2347
+ startPlugins(pluginTypes) {
2348
+ const previousManager = currentPluginManager;
2349
+ currentPluginManager = this;
2350
+ const plugins = [];
2351
+ // instantiate plugins
2352
+ for (const pluginType of pluginTypes) {
2353
+ if (!pluginType.id) {
2354
+ currentPluginManager = previousManager;
2355
+ throw new OwlError(`Plugin "${pluginType.name}" has no id`);
2356
+ }
2357
+ if (this.plugins.hasOwnProperty(pluginType.id)) {
2358
+ continue;
2359
+ }
2360
+ const plugin = new pluginType();
2361
+ this.plugins[pluginType.id] = plugin;
2362
+ plugins.push(plugin);
2706
2363
  }
2707
- const timeout = HOOK_TIMEOUT[hookName];
2708
- if (timeout) {
2709
- const fiber = node.fiber;
2710
- Promise.race([
2711
- result.catch(() => { }),
2712
- new Promise((resolve) => setTimeout(() => resolve(TIMEOUT), timeout)),
2713
- ]).then((res) => {
2714
- if (res === TIMEOUT && node.fiber === fiber && node.status <= 2) {
2715
- timeoutError.message = `${hookName}'s promise hasn't resolved after ${timeout / 1000} seconds`;
2716
- console.log(timeoutError);
2717
- }
2718
- });
2364
+ // setup phase
2365
+ for (let p of plugins) {
2366
+ p.setup();
2719
2367
  }
2720
- return result.catch(onError);
2721
- };
2368
+ currentPluginManager = previousManager;
2369
+ if (!currentPluginManager) {
2370
+ this.status = 1 /* STATUS.MOUNTED */;
2371
+ }
2372
+ return plugins;
2373
+ }
2722
2374
  }
2375
+ function plugin(pluginType) {
2376
+ // getCurrent will throw if we're not in a component
2377
+ const manager = currentPluginManager || getCurrent().pluginManager;
2378
+ let plugin = manager.getPluginById(pluginType.id);
2379
+ if (!plugin) {
2380
+ if (manager === currentPluginManager) {
2381
+ manager.startPlugins([pluginType]);
2382
+ plugin = manager.getPluginById(pluginType.id);
2383
+ }
2384
+ else {
2385
+ throw new OwlError(`Unknown plugin "${pluginType.id}"`);
2386
+ }
2387
+ }
2388
+ return plugin;
2389
+ }
2390
+
2723
2391
  // -----------------------------------------------------------------------------
2724
2392
  // hooks
2725
2393
  // -----------------------------------------------------------------------------
2394
+ function decorate(node, f, hookName) {
2395
+ const result = f.bind(node.component);
2396
+ if (node.app.dev) {
2397
+ const suffix = f.name ? ` <${f.name}>` : "";
2398
+ Reflect.defineProperty(result, "name", {
2399
+ value: hookName + suffix,
2400
+ });
2401
+ }
2402
+ return result;
2403
+ }
2726
2404
  function onWillStart(fn) {
2727
2405
  const node = getCurrent();
2728
- const decorate = node.app.dev ? wrapError : (fn) => fn;
2729
- node.willStart.push(decorate(fn.bind(node.component), "onWillStart"));
2406
+ node.willStart.push(decorate(node, fn, "onWillStart"));
2730
2407
  }
2731
2408
  function onWillUpdateProps(fn) {
2732
2409
  const node = getCurrent();
2733
- const decorate = node.app.dev ? wrapError : (fn) => fn;
2734
- node.willUpdateProps.push(decorate(fn.bind(node.component), "onWillUpdateProps"));
2410
+ node.willUpdateProps.push(decorate(node, fn, "onWillUpdateProps"));
2735
2411
  }
2736
2412
  function onMounted(fn) {
2737
2413
  const node = getCurrent();
2738
- const decorate = node.app.dev ? wrapError : (fn) => fn;
2739
- node.mounted.push(decorate(fn.bind(node.component), "onMounted"));
2414
+ node.mounted.push(decorate(node, fn, "onMounted"));
2740
2415
  }
2741
2416
  function onWillPatch(fn) {
2742
2417
  const node = getCurrent();
2743
- const decorate = node.app.dev ? wrapError : (fn) => fn;
2744
- node.willPatch.unshift(decorate(fn.bind(node.component), "onWillPatch"));
2418
+ node.willPatch.unshift(decorate(node, fn, "onWillPatch"));
2745
2419
  }
2746
2420
  function onPatched(fn) {
2747
2421
  const node = getCurrent();
2748
- const decorate = node.app.dev ? wrapError : (fn) => fn;
2749
- node.patched.push(decorate(fn.bind(node.component), "onPatched"));
2422
+ node.patched.push(decorate(node, fn, "onPatched"));
2750
2423
  }
2751
2424
  function onWillUnmount(fn) {
2752
2425
  const node = getCurrent();
2753
- const decorate = node.app.dev ? wrapError : (fn) => fn;
2754
- node.willUnmount.unshift(decorate(fn.bind(node.component), "onWillUnmount"));
2426
+ node.willUnmount.unshift(decorate(node, fn, "onWillUnmount"));
2755
2427
  }
2756
2428
  function onWillDestroy(fn) {
2757
- const node = getCurrent();
2758
- const decorate = node.app.dev ? wrapError : (fn) => fn;
2759
- node.willDestroy.push(decorate(fn.bind(node.component), "onWillDestroy"));
2760
- }
2761
- function onWillRender(fn) {
2762
- const node = getCurrent();
2763
- const renderFn = node.renderFn;
2764
- const decorate = node.app.dev ? wrapError : (fn) => fn;
2765
- fn = decorate(fn.bind(node.component), "onWillRender");
2766
- node.renderFn = () => {
2767
- fn();
2768
- return renderFn();
2769
- };
2770
- }
2771
- function onRendered(fn) {
2772
- const node = getCurrent();
2773
- const renderFn = node.renderFn;
2774
- const decorate = node.app.dev ? wrapError : (fn) => fn;
2775
- fn = decorate(fn.bind(node.component), "onRendered");
2776
- node.renderFn = () => {
2777
- const result = renderFn();
2778
- fn();
2779
- return result;
2780
- };
2429
+ const pm = _getCurrentPluginManager();
2430
+ if (pm) {
2431
+ pm.onDestroyCb.push(fn);
2432
+ }
2433
+ else {
2434
+ const node = getCurrent();
2435
+ node.willDestroy.unshift(decorate(node, fn, "onWillDestroy"));
2436
+ }
2781
2437
  }
2782
2438
  function onError(callback) {
2783
2439
  const node = getCurrent();
@@ -2790,9 +2446,7 @@
2790
2446
  }
2791
2447
 
2792
2448
  class Component {
2793
- constructor(props, env, node) {
2794
- this.props = props;
2795
- this.env = env;
2449
+ constructor(node) {
2796
2450
  this.__owl__ = node;
2797
2451
  }
2798
2452
  setup() { }
@@ -2802,83 +2456,6 @@
2802
2456
  }
2803
2457
  Component.template = "";
2804
2458
 
2805
- const VText = text("").constructor;
2806
- class VPortal extends VText {
2807
- constructor(selector, content) {
2808
- super("");
2809
- this.target = null;
2810
- this.selector = selector;
2811
- this.content = content;
2812
- }
2813
- mount(parent, anchor) {
2814
- super.mount(parent, anchor);
2815
- this.target = document.querySelector(this.selector);
2816
- if (this.target) {
2817
- this.content.mount(this.target, null);
2818
- }
2819
- else {
2820
- this.content.mount(parent, anchor);
2821
- }
2822
- }
2823
- beforeRemove() {
2824
- this.content.beforeRemove();
2825
- }
2826
- remove() {
2827
- if (this.content) {
2828
- super.remove();
2829
- this.content.remove();
2830
- this.content = null;
2831
- }
2832
- }
2833
- patch(other) {
2834
- super.patch(other);
2835
- if (this.content) {
2836
- this.content.patch(other.content, true);
2837
- }
2838
- else {
2839
- this.content = other.content;
2840
- this.content.mount(this.target, null);
2841
- }
2842
- }
2843
- }
2844
- /**
2845
- * kind of similar to <t t-slot="default"/>, but it wraps it around a VPortal
2846
- */
2847
- function portalTemplate(app, bdom, helpers) {
2848
- let { callSlot } = helpers;
2849
- return function template(ctx, node, key = "") {
2850
- return new VPortal(ctx.props.target, callSlot(ctx, node, key, "default", false, null));
2851
- };
2852
- }
2853
- class Portal extends Component {
2854
- setup() {
2855
- const node = this.__owl__;
2856
- onMounted(() => {
2857
- const portal = node.bdom;
2858
- if (!portal.target) {
2859
- const target = document.querySelector(this.props.target);
2860
- if (target) {
2861
- portal.content.moveBeforeDOMNode(target.firstChild, target);
2862
- }
2863
- else {
2864
- throw new OwlError("invalid portal target");
2865
- }
2866
- }
2867
- });
2868
- onWillUnmount(() => {
2869
- const portal = node.bdom;
2870
- portal.remove();
2871
- });
2872
- }
2873
- }
2874
- Portal.template = "__portal__";
2875
- Portal.props = {
2876
- target: {
2877
- type: String,
2878
- },
2879
- slots: true,
2880
- };
2881
-
2882
2459
  // -----------------------------------------------------------------------------
2883
2460
  // helpers
2884
2461
  // -----------------------------------------------------------------------------
@@ -2929,7 +2506,7 @@
2929
2506
  if (Array.isArray(schema)) {
2930
2507
  schema = toSchema(schema);
2931
2508
  }
2932
- obj = toRaw(obj);
2509
+ // obj = toRaw(obj);
2933
2510
  let errors = [];
2934
2511
  // check if each value in obj has correct shape
2935
2512
  for (let key in obj) {
@@ -3002,33 +2579,555 @@
3002
2579
  if (typeof value !== "object" || Array.isArray(value)) {
3003
2580
  result = `'${key}' is not an object`;
3004
2581
  }
3005
- else {
3006
- const errors = validateSchema(value, descr.shape);
3007
- if (errors.length) {
3008
- result = `'${key}' doesn't have the correct shape (${errors.join(", ")})`;
3009
- }
2582
+ else {
2583
+ const errors = validateSchema(value, descr.shape);
2584
+ if (errors.length) {
2585
+ result = `'${key}' doesn't have the correct shape (${errors.join(", ")})`;
2586
+ }
2587
+ }
2588
+ }
2589
+ else if ("values" in descr) {
2590
+ if (typeof value !== "object" || Array.isArray(value)) {
2591
+ result = `'${key}' is not an object`;
2592
+ }
2593
+ else {
2594
+ const errors = Object.entries(value)
2595
+ .map(([key, value]) => validateType(key, value, descr.values))
2596
+ .filter(Boolean);
2597
+ if (errors.length) {
2598
+ result = `some of the values in '${key}' are invalid (${errors.join(", ")})`;
2599
+ }
2600
+ }
2601
+ }
2602
+ if ("type" in descr && !result) {
2603
+ result = validateType(key, value, descr.type);
2604
+ }
2605
+ if ("validate" in descr && !result) {
2606
+ result = !descr.validate(value) ? `'${key}' is not valid` : null;
2607
+ }
2608
+ return result;
2609
+ }
2610
+
2611
+ function validateProps(componentName, props, validation, keys) {
2612
+ const propsToValidate = Object.create(null);
2613
+ const errors = [];
2614
+ for (const key of keys) {
2615
+ if (key in props) {
2616
+ propsToValidate[key] = props[key];
2617
+ }
2618
+ if (propsToValidate[key] === undefined &&
2619
+ !Array.isArray(validation) &&
2620
+ validation[key].defaultValue !== undefined) {
2621
+ if (!validation[key].optional) {
2622
+ errors.push(`A default value cannot be defined for the mandatory prop '${key}'`);
2623
+ continue;
2624
+ }
2625
+ propsToValidate[key] = validation[key].defaultValue;
2626
+ }
2627
+ }
2628
+ errors.push(...validateSchema(propsToValidate, validation));
2629
+ if (errors.length) {
2630
+ throw new OwlError(`Invalid props for component '${componentName}': ` + errors.join(", "));
2631
+ // node.app.handleError({
2632
+ // error: new OwlError(`Invalid props for component '${componentName}': ` + errors.join(", ")),
2633
+ // node,
2634
+ // });
2635
+ }
2636
+ }
2637
+ function props(validation) {
2638
+ const node = getCurrent();
2639
+ const isSchemaValidated = validation && !Array.isArray(validation);
2640
+ function getProp(key) {
2641
+ if (isSchemaValidated && node.props[key] === undefined) {
2642
+ return validation[key].defaultValue;
2643
+ }
2644
+ return node.props[key];
2645
+ }
2646
+ const result = Object.create(null);
2647
+ function applyPropGetters(keys) {
2648
+ for (const key of keys) {
2649
+ Reflect.defineProperty(result, key, {
2650
+ enumerable: true,
2651
+ get: getProp.bind(null, key),
2652
+ });
2653
+ }
2654
+ }
2655
+ if (validation) {
2656
+ const keys = (isSchemaValidated ? Object.keys(validation) : validation).map((key) => key.endsWith("?") ? key.slice(0, -1) : key);
2657
+ applyPropGetters(keys);
2658
+ if (node.app.dev) {
2659
+ validateProps(node.name, node.props, validation, keys);
2660
+ node.willUpdateProps.push((np) => {
2661
+ validateProps(node.name, np, validation, keys);
2662
+ });
2663
+ }
2664
+ }
2665
+ else {
2666
+ applyPropGetters(Object.keys(node.props));
2667
+ node.willUpdateProps.push((np) => {
2668
+ for (let key in result) {
2669
+ Reflect.deleteProperty(result, key);
2670
+ }
2671
+ applyPropGetters(Object.keys(np));
2672
+ });
2673
+ }
2674
+ return result;
2675
+ }
2676
+
2677
+ const VText = text("").constructor;
2678
+ class VPortal extends VText {
2679
+ constructor(selector, content) {
2680
+ super("");
2681
+ this.target = null;
2682
+ this.selector = selector;
2683
+ this.content = content;
2684
+ }
2685
+ mount(parent, anchor) {
2686
+ super.mount(parent, anchor);
2687
+ this.target = document.querySelector(this.selector);
2688
+ if (this.target) {
2689
+ this.content.mount(this.target, null);
2690
+ }
2691
+ else {
2692
+ this.content.mount(parent, anchor);
2693
+ }
2694
+ }
2695
+ beforeRemove() {
2696
+ this.content.beforeRemove();
2697
+ }
2698
+ remove() {
2699
+ if (this.content) {
2700
+ super.remove();
2701
+ this.content.remove();
2702
+ this.content = null;
2703
+ }
2704
+ }
2705
+ patch(other) {
2706
+ super.patch(other);
2707
+ if (this.content) {
2708
+ this.content.patch(other.content, true);
2709
+ }
2710
+ else {
2711
+ this.content = other.content;
2712
+ this.content.mount(this.target, null);
2713
+ }
2714
+ }
2715
+ }
2716
+ /**
2717
+ * kind of similar to <t t-slot="default"/>, but it wraps it around a VPortal
2718
+ */
2719
+ function portalTemplate(app, bdom, helpers) {
2720
+ let { callSlot } = helpers;
2721
+ return function template(ctx, node, key = "") {
2722
+ return new VPortal(ctx.this.props.target, callSlot(ctx, node, key, "default", false, null));
2723
+ };
2724
+ }
2725
+ class Portal extends Component {
2726
+ constructor() {
2727
+ super(...arguments);
2728
+ this.props = props({
2729
+ target: String,
2730
+ slots: true,
2731
+ });
2732
+ }
2733
+ setup() {
2734
+ const node = this.__owl__;
2735
+ onMounted(() => {
2736
+ const portal = node.bdom;
2737
+ if (!portal.target) {
2738
+ const target = document.querySelector(node.props.target);
2739
+ if (target) {
2740
+ portal.content.moveBeforeDOMNode(target.firstChild, target);
2741
+ }
2742
+ else {
2743
+ throw new OwlError("invalid portal target");
2744
+ }
2745
+ }
2746
+ });
2747
+ onWillUnmount(() => {
2748
+ const portal = node.bdom;
2749
+ portal.remove();
2750
+ });
2751
+ }
2752
+ }
2753
+ Portal.template = "__portal__";
2754
+
2755
+ // Special key to subscribe to, to be notified of key creation/deletion
2756
+ const KEYCHANGES = Symbol("Key changes");
2757
+ const objectToString = Object.prototype.toString;
2758
+ const objectHasOwnProperty = Object.prototype.hasOwnProperty;
2759
+ // Use arrays because Array.includes is faster than Set.has for small arrays
2760
+ const SUPPORTED_RAW_TYPES = ["Object", "Array", "Set", "Map", "WeakMap"];
2761
+ const COLLECTION_RAW_TYPES = ["Set", "Map", "WeakMap"];
2762
+ /**
2763
+ * extract "RawType" from strings like "[object RawType]" => this lets us ignore
2764
+ * many native objects such as Promise (whose toString is [object Promise])
2765
+ * or Date ([object Date]), while also supporting collections without using
2766
+ * instanceof in a loop
2767
+ *
2768
+ * @param obj the object to check
2769
+ * @returns the raw type of the object
2770
+ */
2771
+ function rawType(obj) {
2772
+ return objectToString.call(toRaw(obj)).slice(8, -1);
2773
+ }
2774
+ /**
2775
+ * Checks whether a given value can be made into a proxy object.
2776
+ *
2777
+ * @param value the value to check
2778
+ * @returns whether the value can be made proxy
2779
+ */
2780
+ function canBeMadeReactive(value) {
2781
+ if (typeof value !== "object") {
2782
+ return false;
2783
+ }
2784
+ return SUPPORTED_RAW_TYPES.includes(rawType(value));
2785
+ }
2786
+ /**
2787
+ * Creates a proxy from the given object/callback if possible and returns it,
2788
+ * returns the original object otherwise.
2789
+ *
2790
+ * @param value the value make proxy
2791
+ * @returns a proxy for the given object when possible, the original otherwise
2792
+ */
2793
+ function possiblyReactive(val) {
2794
+ return canBeMadeReactive(val) ? proxy(val) : val;
2795
+ }
2796
+ const skipped = new WeakSet();
2797
+ /**
2798
+ * Mark an object or array so that it is ignored by the reactivity system
2799
+ *
2800
+ * @param value the value to mark
2801
+ * @returns the object itself
2802
+ */
2803
+ function markRaw(value) {
2804
+ skipped.add(value);
2805
+ return value;
2806
+ }
2807
+ /**
2808
+ * Given a proxy objet, return the raw (non proxy) underlying object
2809
+ *
2810
+ * @param value a proxy value
2811
+ * @returns the underlying value
2812
+ */
2813
+ function toRaw(value) {
2814
+ return targets.has(value) ? targets.get(value) : value;
2815
+ }
2816
+ const targetToKeysToAtomItem = new WeakMap();
2817
+ function getTargetKeyAtom(target, key) {
2818
+ let keyToAtomItem = targetToKeysToAtomItem.get(target);
2819
+ if (!keyToAtomItem) {
2820
+ keyToAtomItem = new Map();
2821
+ targetToKeysToAtomItem.set(target, keyToAtomItem);
2822
+ }
2823
+ let atom = keyToAtomItem.get(key);
2824
+ if (!atom) {
2825
+ atom = {
2826
+ value: undefined,
2827
+ observers: new Set(),
2828
+ };
2829
+ keyToAtomItem.set(key, atom);
2830
+ }
2831
+ return atom;
2832
+ }
2833
+ /**
2834
+ * Observes a given key on a target with an callback. The callback will be
2835
+ * called when the given key changes on the target.
2836
+ *
2837
+ * @param target the target whose key should be observed
2838
+ * @param key the key to observe (or Symbol(KEYCHANGES) for key creation
2839
+ * or deletion)
2840
+ * @param callback the function to call when the key changes
2841
+ */
2842
+ function onReadTargetKey(target, key) {
2843
+ onReadAtom(getTargetKeyAtom(target, key));
2844
+ }
2845
+ /**
2846
+ * Notify Reactives that are observing a given target that a key has changed on
2847
+ * the target.
2848
+ *
2849
+ * @param target target whose Reactives should be notified that the target was
2850
+ * changed.
2851
+ * @param key the key that changed (or Symbol `KEYCHANGES` if a key was created
2852
+ * or deleted)
2853
+ */
2854
+ function onWriteTargetKey(target, key) {
2855
+ const keyToAtomItem = targetToKeysToAtomItem.get(target);
2856
+ if (!keyToAtomItem) {
2857
+ return;
2858
+ }
2859
+ const atom = keyToAtomItem.get(key);
2860
+ if (!atom) {
2861
+ return;
2862
+ }
2863
+ onWriteAtom(atom);
2864
+ }
2865
+ // Maps proxy objects to the underlying target
2866
+ const targets = new WeakMap();
2867
+ const proxyCache = new WeakMap();
2868
+ /**
2869
+ * Creates a reactive proxy for an object. Reading data on the proxy object
2870
+ * subscribes to changes to the data. Writing data on the object will cause the
2871
+ * notify callback to be called if there are suscriptions to that data. Nested
2872
+ * objects and arrays are automatically made reactive as well.
2873
+ *
2874
+ * Whenever you are notified of a change, all subscriptions are cleared, and if
2875
+ * you would like to be notified of any further changes, you should go read
2876
+ * the underlying data again. We assume that if you don't go read it again after
2877
+ * being notified, it means that you are no longer interested in that data.
2878
+ *
2879
+ * Subscriptions:
2880
+ * + Reading a property on an object will subscribe you to changes in the value
2881
+ * of that property.
2882
+ * + Accessing an object's keys (eg with Object.keys or with `for..in`) will
2883
+ * subscribe you to the creation/deletion of keys. Checking the presence of a
2884
+ * key on the object with 'in' has the same effect.
2885
+ * - getOwnPropertyDescriptor does not currently subscribe you to the property.
2886
+ * This is a choice that was made because changing a key's value will trigger
2887
+ * this trap and we do not want to subscribe by writes. This also means that
2888
+ * Object.hasOwnProperty doesn't subscribe as it goes through this trap.
2889
+ *
2890
+ * @param target the object for which to create a proxy proxy
2891
+ * @param callback the function to call when an observed property of the
2892
+ * proxy has changed
2893
+ * @returns a proxy that tracks changes to it
2894
+ */
2895
+ function proxy(target) {
2896
+ if (!canBeMadeReactive(target)) {
2897
+ throw new OwlError(`Cannot make the given value reactive`);
2898
+ }
2899
+ if (skipped.has(target)) {
2900
+ return target;
2901
+ }
2902
+ if (targets.has(target)) {
2903
+ // target is reactive, create a reactive on the underlying object instead
2904
+ return target;
2905
+ }
2906
+ const reactive = proxyCache.get(target);
2907
+ if (reactive)
2908
+ return reactive;
2909
+ const targetRawType = rawType(target);
2910
+ const handler = COLLECTION_RAW_TYPES.includes(targetRawType)
2911
+ ? collectionsProxyHandler(target, targetRawType)
2912
+ : basicProxyHandler();
2913
+ const proxy = new Proxy(target, handler);
2914
+ proxyCache.set(target, proxy);
2915
+ targets.set(proxy, target);
2916
+ return proxy;
2917
+ }
2918
+ /**
2919
+ * Creates a basic proxy handler for regular objects and arrays.
2920
+ *
2921
+ * @param callback @see proxy
2922
+ * @returns a proxy handler object
2923
+ */
2924
+ function basicProxyHandler() {
2925
+ return {
2926
+ get(target, key, receiver) {
2927
+ // non-writable non-configurable properties cannot be made proxy
2928
+ const desc = Object.getOwnPropertyDescriptor(target, key);
2929
+ if (desc && !desc.writable && !desc.configurable) {
2930
+ return Reflect.get(target, key, receiver);
2931
+ }
2932
+ onReadTargetKey(target, key);
2933
+ return possiblyReactive(Reflect.get(target, key, receiver));
2934
+ },
2935
+ set(target, key, value, receiver) {
2936
+ const hadKey = objectHasOwnProperty.call(target, key);
2937
+ const originalValue = Reflect.get(target, key, receiver);
2938
+ const ret = Reflect.set(target, key, toRaw(value), receiver);
2939
+ if (!hadKey && objectHasOwnProperty.call(target, key)) {
2940
+ onWriteTargetKey(target, KEYCHANGES);
2941
+ }
2942
+ // While Array length may trigger the set trap, it's not actually set by this
2943
+ // method but is updated behind the scenes, and the trap is not called with the
2944
+ // new value. We disable the "same-value-optimization" for it because of that.
2945
+ if (originalValue !== Reflect.get(target, key, receiver) ||
2946
+ (key === "length" && Array.isArray(target))) {
2947
+ onWriteTargetKey(target, key);
2948
+ }
2949
+ return ret;
2950
+ },
2951
+ deleteProperty(target, key) {
2952
+ const ret = Reflect.deleteProperty(target, key);
2953
+ // TODO: only notify when something was actually deleted
2954
+ onWriteTargetKey(target, KEYCHANGES);
2955
+ onWriteTargetKey(target, key);
2956
+ return ret;
2957
+ },
2958
+ ownKeys(target) {
2959
+ onReadTargetKey(target, KEYCHANGES);
2960
+ return Reflect.ownKeys(target);
2961
+ },
2962
+ has(target, key) {
2963
+ // TODO: this observes all key changes instead of only the presence of the argument key
2964
+ // observing the key itself would observe value changes instead of presence changes
2965
+ // so we may need a finer grained system to distinguish observing value vs presence.
2966
+ onReadTargetKey(target, KEYCHANGES);
2967
+ return Reflect.has(target, key);
2968
+ },
2969
+ };
2970
+ }
2971
+ /**
2972
+ * Creates a function that will observe the key that is passed to it when called
2973
+ * and delegates to the underlying method.
2974
+ *
2975
+ * @param methodName name of the method to delegate to
2976
+ * @param target @see proxy
2977
+ * @param callback @see proxy
2978
+ */
2979
+ function makeKeyObserver(methodName, target) {
2980
+ return (key) => {
2981
+ key = toRaw(key);
2982
+ onReadTargetKey(target, key);
2983
+ return possiblyReactive(target[methodName](key));
2984
+ };
2985
+ }
2986
+ /**
2987
+ * Creates an iterable that will delegate to the underlying iteration method and
2988
+ * observe keys as necessary.
2989
+ *
2990
+ * @param methodName name of the method to delegate to
2991
+ * @param target @see proxy
2992
+ * @param callback @see proxy
2993
+ */
2994
+ function makeIteratorObserver(methodName, target) {
2995
+ return function* () {
2996
+ onReadTargetKey(target, KEYCHANGES);
2997
+ const keys = target.keys();
2998
+ for (const item of target[methodName]()) {
2999
+ const key = keys.next().value;
3000
+ onReadTargetKey(target, key);
3001
+ yield possiblyReactive(item);
3002
+ }
3003
+ };
3004
+ }
3005
+ /**
3006
+ * Creates a forEach function that will delegate to forEach on the underlying
3007
+ * collection while observing key changes, and keys as they're iterated over,
3008
+ * and making the passed keys/values proxy.
3009
+ *
3010
+ * @param target @see proxy
3011
+ * @param callback @see proxy
3012
+ */
3013
+ function makeForEachObserver(target) {
3014
+ return function forEach(forEachCb, thisArg) {
3015
+ onReadTargetKey(target, KEYCHANGES);
3016
+ target.forEach(function (val, key, targetObj) {
3017
+ onReadTargetKey(target, key);
3018
+ forEachCb.call(thisArg, possiblyReactive(val), possiblyReactive(key), possiblyReactive(targetObj));
3019
+ }, thisArg);
3020
+ };
3021
+ }
3022
+ /**
3023
+ * Creates a function that will delegate to an underlying method, and check if
3024
+ * that method has modified the presence or value of a key, and notify the
3025
+ * proxys appropriately.
3026
+ *
3027
+ * @param setterName name of the method to delegate to
3028
+ * @param getterName name of the method which should be used to retrieve the
3029
+ * value before calling the delegate method for comparison purposes
3030
+ * @param target @see proxy
3031
+ */
3032
+ function delegateAndNotify(setterName, getterName, target) {
3033
+ return (key, value) => {
3034
+ key = toRaw(key);
3035
+ const hadKey = target.has(key);
3036
+ const originalValue = target[getterName](key);
3037
+ const ret = target[setterName](key, value);
3038
+ const hasKey = target.has(key);
3039
+ if (hadKey !== hasKey) {
3040
+ onWriteTargetKey(target, KEYCHANGES);
3041
+ }
3042
+ if (originalValue !== target[getterName](key)) {
3043
+ onWriteTargetKey(target, key);
3010
3044
  }
3011
- }
3012
- else if ("values" in descr) {
3013
- if (typeof value !== "object" || Array.isArray(value)) {
3014
- result = `'${key}' is not an object`;
3045
+ return ret;
3046
+ };
3047
+ }
3048
+ /**
3049
+ * Creates a function that will clear the underlying collection and notify that
3050
+ * the keys of the collection have changed.
3051
+ *
3052
+ * @param target @see proxy
3053
+ */
3054
+ function makeClearNotifier(target) {
3055
+ return () => {
3056
+ const allKeys = [...target.keys()];
3057
+ target.clear();
3058
+ onWriteTargetKey(target, KEYCHANGES);
3059
+ for (const key of allKeys) {
3060
+ onWriteTargetKey(target, key);
3015
3061
  }
3016
- else {
3017
- const errors = Object.entries(value)
3018
- .map(([key, value]) => validateType(key, value, descr.values))
3019
- .filter(Boolean);
3020
- if (errors.length) {
3021
- result = `some of the values in '${key}' are invalid (${errors.join(", ")})`;
3062
+ };
3063
+ }
3064
+ /**
3065
+ * Maps raw type of an object to an object containing functions that can be used
3066
+ * to build an appropritate proxy handler for that raw type. Eg: when making a
3067
+ * proxy set, calling the has method should mark the key that is being
3068
+ * retrieved as observed, and calling the add or delete method should notify the
3069
+ * proxys that the key which is being added or deleted has been modified.
3070
+ */
3071
+ const rawTypeToFuncHandlers = {
3072
+ Set: (target) => ({
3073
+ has: makeKeyObserver("has", target),
3074
+ add: delegateAndNotify("add", "has", target),
3075
+ delete: delegateAndNotify("delete", "has", target),
3076
+ keys: makeIteratorObserver("keys", target),
3077
+ values: makeIteratorObserver("values", target),
3078
+ entries: makeIteratorObserver("entries", target),
3079
+ [Symbol.iterator]: makeIteratorObserver(Symbol.iterator, target),
3080
+ forEach: makeForEachObserver(target),
3081
+ clear: makeClearNotifier(target),
3082
+ get size() {
3083
+ onReadTargetKey(target, KEYCHANGES);
3084
+ return target.size;
3085
+ },
3086
+ }),
3087
+ Map: (target) => ({
3088
+ has: makeKeyObserver("has", target),
3089
+ get: makeKeyObserver("get", target),
3090
+ set: delegateAndNotify("set", "get", target),
3091
+ delete: delegateAndNotify("delete", "has", target),
3092
+ keys: makeIteratorObserver("keys", target),
3093
+ values: makeIteratorObserver("values", target),
3094
+ entries: makeIteratorObserver("entries", target),
3095
+ [Symbol.iterator]: makeIteratorObserver(Symbol.iterator, target),
3096
+ forEach: makeForEachObserver(target),
3097
+ clear: makeClearNotifier(target),
3098
+ get size() {
3099
+ onReadTargetKey(target, KEYCHANGES);
3100
+ return target.size;
3101
+ },
3102
+ }),
3103
+ WeakMap: (target) => ({
3104
+ has: makeKeyObserver("has", target),
3105
+ get: makeKeyObserver("get", target),
3106
+ set: delegateAndNotify("set", "get", target),
3107
+ delete: delegateAndNotify("delete", "has", target),
3108
+ }),
3109
+ };
3110
+ /**
3111
+ * Creates a proxy handler for collections (Set/Map/WeakMap)
3112
+ *
3113
+ * @param callback @see proxy
3114
+ * @param target @see proxy
3115
+ * @returns a proxy handler object
3116
+ */
3117
+ function collectionsProxyHandler(target, targetRawType) {
3118
+ // TODO: if performance is an issue we can create the special handlers lazily when each
3119
+ // property is read.
3120
+ const specialHandlers = rawTypeToFuncHandlers[targetRawType](target);
3121
+ return Object.assign(basicProxyHandler(), {
3122
+ // FIXME: probably broken when part of prototype chain since we ignore the receiver
3123
+ get(target, key) {
3124
+ if (objectHasOwnProperty.call(specialHandlers, key)) {
3125
+ return specialHandlers[key];
3022
3126
  }
3023
- }
3024
- }
3025
- if ("type" in descr && !result) {
3026
- result = validateType(key, value, descr.type);
3027
- }
3028
- if ("validate" in descr && !result) {
3029
- result = !descr.validate(value) ? `'${key}' is not valid` : null;
3030
- }
3031
- return result;
3127
+ onReadTargetKey(target, key);
3128
+ return possiblyReactive(target[key]);
3129
+ },
3130
+ });
3032
3131
  }
3033
3132
 
3034
3133
  const ObjectCreate = Object.create;
@@ -3041,7 +3140,7 @@
3041
3140
  }
3042
3141
  function callSlot(ctx, parent, key, name, dynamic, extra, defaultContent) {
3043
3142
  key = key + "__slot_" + name;
3044
- const slots = ctx.props.slots || {};
3143
+ const slots = ctx.__owl__.props.slots || {};
3045
3144
  const { __render, __ctx, __scope } = slots[name] || {};
3046
3145
  const slotScope = ObjectCreate(__ctx || {});
3047
3146
  if (__scope) {
@@ -3146,84 +3245,52 @@
3146
3245
  }
3147
3246
  let safeKey;
3148
3247
  let block;
3149
- switch (typeof value) {
3150
- case "object":
3151
- if (value instanceof Markup) {
3152
- safeKey = `string_safe`;
3153
- block = html(value);
3154
- }
3155
- else if (value instanceof LazyValue) {
3156
- safeKey = `lazy_value`;
3157
- block = value.evaluate();
3158
- }
3159
- else if (value instanceof String) {
3160
- safeKey = "string_unsafe";
3161
- block = text(value);
3162
- }
3163
- else {
3164
- // Assuming it is a block
3165
- safeKey = "block_safe";
3166
- block = value;
3167
- }
3168
- break;
3169
- case "string":
3170
- safeKey = "string_unsafe";
3171
- block = text(value);
3172
- break;
3173
- default:
3174
- safeKey = "string_unsafe";
3175
- block = text(String(value));
3248
+ if (value instanceof Markup) {
3249
+ safeKey = `string_safe`;
3250
+ block = html(value);
3251
+ }
3252
+ else if (value instanceof LazyValue) {
3253
+ safeKey = `lazy_value`;
3254
+ block = value.evaluate();
3255
+ }
3256
+ else {
3257
+ safeKey = "string_unsafe";
3258
+ block = text(value);
3176
3259
  }
3177
3260
  return toggler(safeKey, block);
3178
3261
  }
3179
- /**
3180
- * Validate the component props (or next props) against the (static) props
3181
- * description. This is potentially an expensive operation: it may needs to
3182
- * visit recursively the props and all the children to check if they are valid.
3183
- * This is why it is only done in 'dev' mode.
3184
- */
3185
- function validateProps(name, props, comp) {
3186
- const ComponentClass = typeof name !== "string"
3187
- ? name
3188
- : comp.constructor.components[name];
3189
- if (!ComponentClass) {
3190
- // this is an error, wrong component. We silently return here instead so the
3191
- // error is triggered by the usual path ('component' function)
3192
- return;
3262
+ function createRef(ref) {
3263
+ if (!ref) {
3264
+ throw new OwlError(`Ref is undefined or null`);
3193
3265
  }
3194
- const schema = ComponentClass.props;
3195
- if (!schema) {
3196
- if (comp.__owl__.app.warnIfNoStaticProps) {
3197
- console.warn(`Component '${ComponentClass.name}' does not have a static props description`);
3198
- }
3199
- return;
3266
+ let add;
3267
+ let remove;
3268
+ if (ref.add && ref.remove) {
3269
+ add = ref.add.bind(ref);
3270
+ remove = ref.remove.bind(ref);
3200
3271
  }
3201
- const defaultProps = ComponentClass.defaultProps;
3202
- if (defaultProps) {
3203
- let isMandatory = (name) => Array.isArray(schema)
3204
- ? schema.includes(name)
3205
- : name in schema && !("*" in schema) && !isOptional(schema[name]);
3206
- for (let p in defaultProps) {
3207
- if (isMandatory(p)) {
3208
- throw new OwlError(`A default value cannot be defined for a mandatory prop (name: '${p}', component: ${ComponentClass.name})`);
3209
- }
3210
- }
3272
+ else if (ref.set) {
3273
+ add = ref.set.bind(ref);
3274
+ remove = () => ref.set(null);
3211
3275
  }
3212
- const errors = validateSchema(props, schema);
3213
- if (errors.length) {
3214
- throw new OwlError(`Invalid props for component '${ComponentClass.name}': ` + errors.join(", "));
3276
+ else {
3277
+ throw new OwlError(`Ref should implement either a 'set' function or 'add' and 'remove' functions`);
3215
3278
  }
3216
- }
3217
- function makeRefWrapper(node) {
3218
- let refNames = new Set();
3219
- return (name, fn) => {
3220
- if (refNames.has(name)) {
3221
- throw new OwlError(`Cannot set the same ref more than once in the same component, ref "${name}" was set multiple times in ${node.name}`);
3279
+ return (el, previousEl) => {
3280
+ if (previousEl) {
3281
+ remove(previousEl);
3282
+ }
3283
+ if (el) {
3284
+ add(el);
3222
3285
  }
3223
- refNames.add(name);
3224
- return fn;
3225
3286
  };
3226
3287
  }
3288
+ function modelExpr(value) {
3289
+ if (!value.set || typeof value !== "function") {
3290
+ throw new OwlError(`Invalid t-model expression: expression should evaluate to a function with a 'set' method defined on it`);
3291
+ }
3292
+ return value;
3293
+ }
3227
3294
  const helpers = {
3228
3295
  withDefault,
3229
3296
  zero: Symbol("zero"),
@@ -3235,13 +3302,13 @@
3235
3302
  setContextValue,
3236
3303
  shallowEqual,
3237
3304
  toNumber,
3238
- validateProps,
3239
3305
  LazyValue,
3240
3306
  safeOutput,
3241
3307
  createCatcher,
3242
3308
  markRaw,
3243
3309
  OwlError,
3244
- makeRefWrapper,
3310
+ createRef,
3311
+ modelExpr,
3245
3312
  };
3246
3313
 
3247
3314
  /**
@@ -3282,6 +3349,9 @@
3282
3349
 
3283
3350
  const bdom = { text, createBlock, list, multi, html, toggler, comment };
3284
3351
  class TemplateSet {
3352
+ static registerTemplate(name, fn) {
3353
+ globalTemplates[name] = fn;
3354
+ }
3285
3355
  constructor(config = {}) {
3286
3356
  this.rawTemplates = Object.create(globalTemplates);
3287
3357
  this.templates = {};
@@ -3304,9 +3374,6 @@
3304
3374
  this.runtimeUtils = { ...helpers, __globals__: config.globalValues || {} };
3305
3375
  this.hasGlobalValues = Boolean(config.globalValues && Object.keys(config.globalValues).length);
3306
3376
  }
3307
- static registerTemplate(name, fn) {
3308
- globalTemplates[name] = fn;
3309
- }
3310
3377
  addTemplate(name, template) {
3311
3378
  if (name in this.rawTemplates) {
3312
3379
  // this check can be expensive, just silently ignore double definitions outside dev mode
@@ -3617,7 +3684,7 @@
3617
3684
  stack.pop();
3618
3685
  }
3619
3686
  let isVar = token.type === "SYMBOL" && !RESERVED_WORDS.includes(token.value);
3620
- if (token.type === "SYMBOL" && !RESERVED_WORDS.includes(token.value)) {
3687
+ if (isVar) {
3621
3688
  if (prevToken) {
3622
3689
  // normalize missing tokens: {a} should be equivalent to {a:a}
3623
3690
  if (groupType === "LEFT_BRACE" &&
@@ -3809,7 +3876,6 @@
3809
3876
  this.hasRoot = false;
3810
3877
  this.hasCache = false;
3811
3878
  this.shouldProtectScope = false;
3812
- this.hasRefWrapper = false;
3813
3879
  this.name = name;
3814
3880
  this.on = on || null;
3815
3881
  }
@@ -3829,9 +3895,6 @@
3829
3895
  result.push(` ctx = Object.create(ctx);`);
3830
3896
  result.push(` ctx[isBoundary] = 1`);
3831
3897
  }
3832
- if (this.hasRefWrapper) {
3833
- result.push(` let refWrapper = makeRefWrapper(this.__owl__);`);
3834
- }
3835
3898
  if (this.hasCache) {
3836
3899
  result.push(` let cache = ctx.cache || {};`);
3837
3900
  result.push(` let nextCache = ctx.cache = {};`);
@@ -3898,7 +3961,7 @@
3898
3961
  }
3899
3962
  generateCode() {
3900
3963
  const ast = this.ast;
3901
- this.isDebug = ast.type === 12 /* TDebug */;
3964
+ this.isDebug = ast.type === 11 /* ASTType.TDebug */;
3902
3965
  BlockDescription.nextBlockId = 1;
3903
3966
  nextDataIds = {};
3904
3967
  this.compileAST(ast, {
@@ -4054,43 +4117,41 @@
4054
4117
  */
4055
4118
  compileAST(ast, ctx) {
4056
4119
  switch (ast.type) {
4057
- case 1 /* Comment */:
4120
+ case 1 /* ASTType.Comment */:
4058
4121
  return this.compileComment(ast, ctx);
4059
- case 0 /* Text */:
4122
+ case 0 /* ASTType.Text */:
4060
4123
  return this.compileText(ast, ctx);
4061
- case 2 /* DomNode */:
4124
+ case 2 /* ASTType.DomNode */:
4062
4125
  return this.compileTDomNode(ast, ctx);
4063
- case 4 /* TEsc */:
4064
- return this.compileTEsc(ast, ctx);
4065
- case 8 /* TOut */:
4126
+ case 7 /* ASTType.TOut */:
4066
4127
  return this.compileTOut(ast, ctx);
4067
- case 5 /* TIf */:
4128
+ case 4 /* ASTType.TIf */:
4068
4129
  return this.compileTIf(ast, ctx);
4069
- case 9 /* TForEach */:
4130
+ case 8 /* ASTType.TForEach */:
4070
4131
  return this.compileTForeach(ast, ctx);
4071
- case 10 /* TKey */:
4132
+ case 9 /* ASTType.TKey */:
4072
4133
  return this.compileTKey(ast, ctx);
4073
- case 3 /* Multi */:
4134
+ case 3 /* ASTType.Multi */:
4074
4135
  return this.compileMulti(ast, ctx);
4075
- case 7 /* TCall */:
4136
+ case 6 /* ASTType.TCall */:
4076
4137
  return this.compileTCall(ast, ctx);
4077
- case 15 /* TCallBlock */:
4138
+ case 14 /* ASTType.TCallBlock */:
4078
4139
  return this.compileTCallBlock(ast, ctx);
4079
- case 6 /* TSet */:
4140
+ case 5 /* ASTType.TSet */:
4080
4141
  return this.compileTSet(ast, ctx);
4081
- case 11 /* TComponent */:
4142
+ case 10 /* ASTType.TComponent */:
4082
4143
  return this.compileComponent(ast, ctx);
4083
- case 12 /* TDebug */:
4144
+ case 11 /* ASTType.TDebug */:
4084
4145
  return this.compileDebug(ast, ctx);
4085
- case 13 /* TLog */:
4146
+ case 12 /* ASTType.TLog */:
4086
4147
  return this.compileLog(ast, ctx);
4087
- case 14 /* TSlot */:
4088
- return this.compileTSlot(ast, ctx);
4089
- case 16 /* TTranslation */:
4148
+ case 13 /* ASTType.TCallSlot */:
4149
+ return this.compileTCallSlot(ast, ctx);
4150
+ case 15 /* ASTType.TTranslation */:
4090
4151
  return this.compileTTranslation(ast, ctx);
4091
- case 17 /* TTranslationContext */:
4152
+ case 16 /* ASTType.TTranslationContext */:
4092
4153
  return this.compileTTranslationContext(ast, ctx);
4093
- case 18 /* TPortal */:
4154
+ case 17 /* ASTType.TPortal */:
4094
4155
  return this.compileTPortal(ast, ctx);
4095
4156
  }
4096
4157
  }
@@ -4141,7 +4202,7 @@
4141
4202
  });
4142
4203
  }
4143
4204
  else {
4144
- const createFn = ast.type === 0 /* Text */ ? xmlDoc.createTextNode : xmlDoc.createComment;
4205
+ const createFn = ast.type === 0 /* ASTType.Text */ ? xmlDoc.createTextNode : xmlDoc.createComment;
4145
4206
  block.insert(createFn.call(xmlDoc, value));
4146
4207
  }
4147
4208
  return block.varName;
@@ -4235,14 +4296,11 @@
4235
4296
  // t-model
4236
4297
  let tModelSelectedExpr;
4237
4298
  if (ast.model) {
4238
- const { hasDynamicChildren, baseExpr, expr, eventType, shouldNumberize, shouldTrim, targetAttr, specialInitTargetAttr, } = ast.model;
4239
- const baseExpression = compileExpr(baseExpr);
4240
- const bExprId = generateId("bExpr");
4241
- this.define(bExprId, baseExpression);
4299
+ const { hasDynamicChildren, expr, eventType, shouldNumberize, shouldTrim, targetAttr, specialInitTargetAttr, } = ast.model;
4242
4300
  const expression = compileExpr(expr);
4243
4301
  const exprId = generateId("expr");
4244
- this.define(exprId, expression);
4245
- const fullExpression = `${bExprId}[${exprId}]`;
4302
+ this.helpers.add("modelExpr");
4303
+ this.define(exprId, `modelExpr(${expression})`);
4246
4304
  let idx;
4247
4305
  if (specialInitTargetAttr) {
4248
4306
  let targetExpr = targetAttr in attrs && `'${attrs[targetAttr]}'`;
@@ -4253,23 +4311,23 @@
4253
4311
  targetExpr = compileExpr(dynamicTgExpr);
4254
4312
  }
4255
4313
  }
4256
- idx = block.insertData(`${fullExpression} === ${targetExpr}`, "prop");
4314
+ idx = block.insertData(`${exprId}() === ${targetExpr}`, "prop");
4257
4315
  attrs[`block-property-${idx}`] = specialInitTargetAttr;
4258
4316
  }
4259
4317
  else if (hasDynamicChildren) {
4260
4318
  const bValueId = generateId("bValue");
4261
4319
  tModelSelectedExpr = `${bValueId}`;
4262
- this.define(tModelSelectedExpr, fullExpression);
4320
+ this.define(tModelSelectedExpr, `${exprId}()`);
4263
4321
  }
4264
4322
  else {
4265
- idx = block.insertData(`${fullExpression}`, "prop");
4323
+ idx = block.insertData(`${exprId}()`, "prop");
4266
4324
  attrs[`block-property-${idx}`] = targetAttr;
4267
4325
  }
4268
4326
  this.helpers.add("toNumber");
4269
4327
  let valueCode = `ev.target.${targetAttr}`;
4270
4328
  valueCode = shouldTrim ? `${valueCode}.trim()` : valueCode;
4271
4329
  valueCode = shouldNumberize ? `toNumber(${valueCode})` : valueCode;
4272
- const handler = `[(ev) => { ${fullExpression} = ${valueCode}; }]`;
4330
+ const handler = `[(ev) => { ${exprId}.set(${valueCode}); }]`;
4273
4331
  idx = block.insertData(handler, "hdlr");
4274
4332
  attrs[`block-handler-${idx}`] = eventType;
4275
4333
  }
@@ -4281,19 +4339,9 @@
4281
4339
  }
4282
4340
  // t-ref
4283
4341
  if (ast.ref) {
4284
- if (this.dev) {
4285
- this.helpers.add("makeRefWrapper");
4286
- this.target.hasRefWrapper = true;
4287
- }
4288
- const isDynamic = INTERP_REGEXP.test(ast.ref);
4289
- let name = `\`${ast.ref}\``;
4290
- if (isDynamic) {
4291
- name = replaceDynamicParts(ast.ref, (expr) => this.captureExpression(expr, true));
4292
- }
4293
- let setRefStr = `(el) => this.__owl__.setRef((${name}), el)`;
4294
- if (this.dev) {
4295
- setRefStr = `refWrapper(${name}, ${setRefStr})`;
4296
- }
4342
+ const refExpr = compileExpr(ast.ref);
4343
+ this.helpers.add("createRef");
4344
+ const setRefStr = `createRef(${refExpr})`;
4297
4345
  const idx = block.insertData(setRefStr, "ref");
4298
4346
  attrs["block-ref"] = String(idx);
4299
4347
  }
@@ -4347,32 +4395,6 @@
4347
4395
  }
4348
4396
  return block.varName;
4349
4397
  }
4350
- compileTEsc(ast, ctx) {
4351
- let { block, forceNewBlock } = ctx;
4352
- let expr;
4353
- if (ast.expr === "0") {
4354
- this.helpers.add("zero");
4355
- expr = `ctx[zero]`;
4356
- }
4357
- else {
4358
- expr = compileExpr(ast.expr);
4359
- if (ast.defaultValue) {
4360
- this.helpers.add("withDefault");
4361
- // FIXME: defaultValue is not translated
4362
- expr = `withDefault(${expr}, ${toStringExpression(ast.defaultValue)})`;
4363
- }
4364
- }
4365
- if (!block || forceNewBlock) {
4366
- block = this.createBlock(block, "text", ctx);
4367
- this.insertBlock(`text(${expr})`, block, { ...ctx, forceNewBlock: forceNewBlock && !block });
4368
- }
4369
- else {
4370
- const idx = block.insertData(expr, "txt");
4371
- const text = xmlDoc.createElement(`block-text-${idx}`);
4372
- block.insert(text);
4373
- }
4374
- return block.varName;
4375
- }
4376
4398
  compileTOut(ast, ctx) {
4377
4399
  let { block } = ctx;
4378
4400
  if (block) {
@@ -4388,7 +4410,7 @@
4388
4410
  let bodyValue = null;
4389
4411
  bodyValue = BlockDescription.nextBlockId;
4390
4412
  const subCtx = createContext(ctx);
4391
- this.compileAST({ type: 3 /* Multi */, content: ast.body }, subCtx);
4413
+ this.compileAST({ type: 3 /* ASTType.Multi */, content: ast.body }, subCtx);
4392
4414
  this.helpers.add("safeOutput");
4393
4415
  blockStr = `safeOutput(${compileExpr(ast.expr)}, b${bodyValue})`;
4394
4416
  }
@@ -4593,7 +4615,7 @@
4593
4615
  let ctxVar = ctx.ctxVar || "ctx";
4594
4616
  if (ast.context) {
4595
4617
  ctxVar = generateId("ctx");
4596
- this.addLine(`let ${ctxVar} = ${compileExpr(ast.context)};`);
4618
+ this.addLine(`let ${ctxVar} = {this: ${compileExpr(ast.context)}, __owl__: this.__owl__};`);
4597
4619
  }
4598
4620
  const isDynamic = INTERP_REGEXP.test(ast.name);
4599
4621
  const subTemplate = isDynamic ? interpolate(ast.name) : "`" + ast.name + "`";
@@ -4606,7 +4628,7 @@
4606
4628
  this.addLine(`${ctxVar}[isBoundary] = 1;`);
4607
4629
  this.helpers.add("isBoundary");
4608
4630
  const subCtx = createContext(ctx, { ctxVar });
4609
- const bl = this.compileMulti({ type: 3 /* Multi */, content: ast.body }, subCtx);
4631
+ const bl = this.compileMulti({ type: 3 /* ASTType.Multi */, content: ast.body }, subCtx);
4610
4632
  if (bl) {
4611
4633
  this.helpers.add("zero");
4612
4634
  this.addLine(`${ctxVar}[zero] = ${bl};`);
@@ -4654,7 +4676,7 @@
4654
4676
  const expr = ast.value ? compileExpr(ast.value || "") : "null";
4655
4677
  if (ast.body) {
4656
4678
  this.helpers.add("LazyValue");
4657
- const bodyAst = { type: 3 /* Multi */, content: ast.body };
4679
+ const bodyAst = { type: 3 /* ASTType.Multi */, content: ast.body };
4658
4680
  const name = this.compileInNewTarget("value", bodyAst, ctx);
4659
4681
  let key = this.target.currentKey(ctx);
4660
4682
  let value = `new LazyValue(${name}, ctx, this, node, ${key})`;
@@ -4793,9 +4815,6 @@
4793
4815
  else {
4794
4816
  expr = `\`${ast.name}\``;
4795
4817
  }
4796
- if (this.dev) {
4797
- this.addLine(`helpers.validateProps(${expr}, ${propVar}, this);`);
4798
- }
4799
4818
  if (block && (ctx.forceNewBlock === false || ctx.tKeyExpr)) {
4800
4819
  // todo: check the forcenewblock condition
4801
4820
  this.insertAnchor(block);
@@ -4850,7 +4869,7 @@
4850
4869
  this.staticDefs.push({ id: name, expr: `createCatcher(${JSON.stringify(spec)})` });
4851
4870
  return `${name}(${expr}, [${handlers.join(",")}])`;
4852
4871
  }
4853
- compileTSlot(ast, ctx) {
4872
+ compileTCallSlot(ast, ctx) {
4854
4873
  this.helpers.add("callSlot");
4855
4874
  let { block } = ctx;
4856
4875
  let blockString;
@@ -4967,7 +4986,7 @@
4967
4986
  }
4968
4987
  function _parse(xml, ctx) {
4969
4988
  normalizeXML(xml);
4970
- return parseNode(xml, ctx) || { type: 0 /* Text */, value: "" };
4989
+ return parseNode(xml, ctx) || { type: 0 /* ASTType.Text */, value: "" };
4971
4990
  }
4972
4991
  function parseNode(node, ctx) {
4973
4992
  if (!(node instanceof Element)) {
@@ -4983,9 +5002,8 @@
4983
5002
  parseTTranslation(node, ctx) ||
4984
5003
  parseTTranslationContext(node, ctx) ||
4985
5004
  parseTKey(node, ctx) ||
4986
- parseTEscNode(node, ctx) ||
4987
5005
  parseTOutNode(node, ctx) ||
4988
- parseTSlot(node, ctx) ||
5006
+ parseTCallSlot(node, ctx) ||
4989
5007
  parseComponent(node, ctx) ||
4990
5008
  parseDOMNode(node, ctx) ||
4991
5009
  parseTSetNode(node, ctx) ||
@@ -5010,10 +5028,10 @@
5010
5028
  if (!ctx.inPreTag && lineBreakRE.test(value) && !value.trim()) {
5011
5029
  return null;
5012
5030
  }
5013
- return { type: 0 /* Text */, value };
5031
+ return { type: 0 /* ASTType.Text */, value };
5014
5032
  }
5015
5033
  else if (node.nodeType === Node.COMMENT_NODE) {
5016
- return { type: 1 /* Comment */, value: node.textContent || "" };
5034
+ return { type: 1 /* ASTType.Comment */, value: node.textContent || "" };
5017
5035
  }
5018
5036
  return null;
5019
5037
  }
@@ -5054,7 +5072,7 @@
5054
5072
  node.removeAttribute("t-debug");
5055
5073
  const content = parseNode(node, ctx);
5056
5074
  const ast = {
5057
- type: 12 /* TDebug */,
5075
+ type: 11 /* ASTType.TDebug */,
5058
5076
  content,
5059
5077
  };
5060
5078
  if (content === null || content === void 0 ? void 0 : content.hasNoRepresentation) {
@@ -5067,7 +5085,7 @@
5067
5085
  node.removeAttribute("t-log");
5068
5086
  const content = parseNode(node, ctx);
5069
5087
  const ast = {
5070
- type: 13 /* TLog */,
5088
+ type: 12 /* ASTType.TLog */,
5071
5089
  expr,
5072
5090
  content,
5073
5091
  };
@@ -5081,8 +5099,6 @@
5081
5099
  // -----------------------------------------------------------------------------
5082
5100
  // Regular dom node
5083
5101
  // -----------------------------------------------------------------------------
5084
- const hasDotAtTheEnd = /\.[\w_]+\s*$/;
5085
- const hasBracketsAtTheEnd = /\[[^\[]+\]\s*$/;
5086
5102
  const ROOT_SVG_TAGS = new Set(["svg", "g", "path"]);
5087
5103
  function parseDOMNode(node, ctx) {
5088
5104
  const { tagName } = node;
@@ -5119,20 +5135,6 @@
5119
5135
  if (!["input", "select", "textarea"].includes(tagName)) {
5120
5136
  throw new OwlError("The t-model directive only works with <input>, <textarea> and <select>");
5121
5137
  }
5122
- let baseExpr, expr;
5123
- if (hasDotAtTheEnd.test(value)) {
5124
- const index = value.lastIndexOf(".");
5125
- baseExpr = value.slice(0, index);
5126
- expr = `'${value.slice(index + 1)}'`;
5127
- }
5128
- else if (hasBracketsAtTheEnd.test(value)) {
5129
- const index = value.lastIndexOf("[");
5130
- baseExpr = value.slice(0, index);
5131
- expr = value.slice(index + 1, -1);
5132
- }
5133
- else {
5134
- throw new OwlError(`Invalid t-model expression: "${value}" (it should be assignable)`);
5135
- }
5136
5138
  const typeAttr = node.getAttribute("type");
5137
5139
  const isInput = tagName === "input";
5138
5140
  const isSelect = tagName === "select";
@@ -5143,8 +5145,7 @@
5143
5145
  const hasNumberMod = attr.includes(".number");
5144
5146
  const eventType = isRadioInput ? "click" : isSelect || hasLazyMod ? "change" : "input";
5145
5147
  model = {
5146
- baseExpr,
5147
- expr,
5148
+ expr: value,
5148
5149
  targetAttr: isCheckboxInput ? "checked" : "value",
5149
5150
  specialInitTargetAttr: isRadioInput ? "checked" : null,
5150
5151
  eventType,
@@ -5186,7 +5187,7 @@
5186
5187
  }
5187
5188
  const children = parseChildren(node, ctx);
5188
5189
  return {
5189
- type: 2 /* DomNode */,
5190
+ type: 2 /* ASTType.DomNode */,
5190
5191
  tag: tagName,
5191
5192
  dynamicTag,
5192
5193
  attrs,
@@ -5199,55 +5200,26 @@
5199
5200
  };
5200
5201
  }
5201
5202
  // -----------------------------------------------------------------------------
5202
- // t-esc
5203
- // -----------------------------------------------------------------------------
5204
- function parseTEscNode(node, ctx) {
5205
- if (!node.hasAttribute("t-esc")) {
5206
- return null;
5207
- }
5208
- const escValue = node.getAttribute("t-esc");
5209
- node.removeAttribute("t-esc");
5210
- const tesc = {
5211
- type: 4 /* TEsc */,
5212
- expr: escValue,
5213
- defaultValue: node.textContent || "",
5214
- };
5215
- let ref = node.getAttribute("t-ref");
5216
- node.removeAttribute("t-ref");
5217
- const ast = parseNode(node, ctx);
5218
- if (!ast) {
5219
- return tesc;
5220
- }
5221
- if (ast.type === 2 /* DomNode */) {
5222
- return {
5223
- ...ast,
5224
- ref,
5225
- content: [tesc],
5226
- };
5227
- }
5228
- return tesc;
5229
- }
5230
- // -----------------------------------------------------------------------------
5231
5203
  // t-out
5232
5204
  // -----------------------------------------------------------------------------
5233
5205
  function parseTOutNode(node, ctx) {
5234
- if (!node.hasAttribute("t-out") && !node.hasAttribute("t-raw")) {
5206
+ if (!node.hasAttribute("t-out") && !node.hasAttribute("t-esc")) {
5235
5207
  return null;
5236
5208
  }
5237
- if (node.hasAttribute("t-raw")) {
5238
- console.warn(`t-raw has been deprecated in favor of t-out. If the value to render is not wrapped by the "markup" function, it will be escaped`);
5209
+ if (node.hasAttribute("t-esc")) {
5210
+ console.warn(`t-esc has been deprecated in favor of t-out. If the value to render is not wrapped by the "markup" function, it will be escaped`);
5239
5211
  }
5240
- const expr = (node.getAttribute("t-out") || node.getAttribute("t-raw"));
5212
+ const expr = (node.getAttribute("t-out") || node.getAttribute("t-esc"));
5241
5213
  node.removeAttribute("t-out");
5242
- node.removeAttribute("t-raw");
5243
- const tOut = { type: 8 /* TOut */, expr, body: null };
5214
+ node.removeAttribute("t-esc");
5215
+ const tOut = { type: 7 /* ASTType.TOut */, expr, body: null };
5244
5216
  const ref = node.getAttribute("t-ref");
5245
5217
  node.removeAttribute("t-ref");
5246
5218
  const ast = parseNode(node, ctx);
5247
5219
  if (!ast) {
5248
5220
  return tOut;
5249
5221
  }
5250
- if (ast.type === 2 /* DomNode */) {
5222
+ if (ast.type === 2 /* ASTType.DomNode */) {
5251
5223
  tOut.body = ast.content.length ? ast.content : null;
5252
5224
  return {
5253
5225
  ...ast,
@@ -5286,7 +5258,7 @@
5286
5258
  const hasNoIndex = hasNoTCall && !html.includes(`${elem}_index`);
5287
5259
  const hasNoValue = hasNoTCall && !html.includes(`${elem}_value`);
5288
5260
  return {
5289
- type: 9 /* TForEach */,
5261
+ type: 8 /* ASTType.TForEach */,
5290
5262
  collection,
5291
5263
  elem,
5292
5264
  body,
@@ -5309,7 +5281,7 @@
5309
5281
  return null;
5310
5282
  }
5311
5283
  const ast = {
5312
- type: 10 /* TKey */,
5284
+ type: 9 /* ASTType.TKey */,
5313
5285
  expr: key,
5314
5286
  content,
5315
5287
  };
@@ -5331,12 +5303,12 @@
5331
5303
  node.removeAttribute("t-call-context");
5332
5304
  if (node.tagName !== "t") {
5333
5305
  const ast = parseNode(node, ctx);
5334
- const tcall = { type: 7 /* TCall */, name: subTemplate, body: null, context };
5335
- if (ast && ast.type === 2 /* DomNode */) {
5306
+ const tcall = { type: 6 /* ASTType.TCall */, name: subTemplate, body: null, context };
5307
+ if (ast && ast.type === 2 /* ASTType.DomNode */) {
5336
5308
  ast.content = [tcall];
5337
5309
  return ast;
5338
5310
  }
5339
- if (ast && ast.type === 11 /* TComponent */) {
5311
+ if (ast && ast.type === 10 /* ASTType.TComponent */) {
5340
5312
  return {
5341
5313
  ...ast,
5342
5314
  slots: {
@@ -5353,7 +5325,7 @@
5353
5325
  }
5354
5326
  const body = parseChildren(node, ctx);
5355
5327
  return {
5356
- type: 7 /* TCall */,
5328
+ type: 6 /* ASTType.TCall */,
5357
5329
  name: subTemplate,
5358
5330
  body: body.length ? body : null,
5359
5331
  context,
@@ -5368,7 +5340,7 @@
5368
5340
  }
5369
5341
  const name = node.getAttribute("t-call-block");
5370
5342
  return {
5371
- type: 15 /* TCallBlock */,
5343
+ type: 14 /* ASTType.TCallBlock */,
5372
5344
  name,
5373
5345
  };
5374
5346
  }
@@ -5381,7 +5353,7 @@
5381
5353
  }
5382
5354
  const condition = node.getAttribute("t-if");
5383
5355
  node.removeAttribute("t-if");
5384
- const content = parseNode(node, ctx) || { type: 0 /* Text */, value: "" };
5356
+ const content = parseNode(node, ctx) || { type: 0 /* ASTType.Text */, value: "" };
5385
5357
  let nextElement = node.nextElementSibling;
5386
5358
  // t-elifs
5387
5359
  const tElifs = [];
@@ -5404,7 +5376,7 @@
5404
5376
  nextElement.remove();
5405
5377
  }
5406
5378
  return {
5407
- type: 5 /* TIf */,
5379
+ type: 4 /* ASTType.TIf */,
5408
5380
  condition,
5409
5381
  content,
5410
5382
  tElif: tElifs.length ? tElifs : null,
@@ -5425,7 +5397,7 @@
5425
5397
  if (node.textContent !== node.innerHTML) {
5426
5398
  body = parseChildren(node, ctx);
5427
5399
  }
5428
- return { type: 6 /* TSet */, name, value, defaultValue, body, hasNoRepresentation: true };
5400
+ return { type: 5 /* ASTType.TSet */, name, value, defaultValue, body, hasNoRepresentation: true };
5429
5401
  }
5430
5402
  // -----------------------------------------------------------------------------
5431
5403
  // Components
@@ -5554,7 +5526,7 @@
5554
5526
  }
5555
5527
  }
5556
5528
  return {
5557
- type: 11 /* TComponent */,
5529
+ type: 10 /* ASTType.TComponent */,
5558
5530
  name,
5559
5531
  isDynamic,
5560
5532
  dynamicProps,
@@ -5567,12 +5539,12 @@
5567
5539
  // -----------------------------------------------------------------------------
5568
5540
  // Slots
5569
5541
  // -----------------------------------------------------------------------------
5570
- function parseTSlot(node, ctx) {
5571
- if (!node.hasAttribute("t-slot")) {
5542
+ function parseTCallSlot(node, ctx) {
5543
+ if (!node.hasAttribute("t-call-slot")) {
5572
5544
  return null;
5573
5545
  }
5574
- const name = node.getAttribute("t-slot");
5575
- node.removeAttribute("t-slot");
5546
+ const name = node.getAttribute("t-call-slot");
5547
+ node.removeAttribute("t-call-slot");
5576
5548
  let attrs = null;
5577
5549
  let attrsTranslationCtx = null;
5578
5550
  let on = null;
@@ -5593,7 +5565,7 @@
5593
5565
  }
5594
5566
  }
5595
5567
  return {
5596
- type: 14 /* TSlot */,
5568
+ type: 13 /* ASTType.TCallSlot */,
5597
5569
  name,
5598
5570
  attrs,
5599
5571
  attrsTranslationCtx,
@@ -5605,7 +5577,7 @@
5605
5577
  // Translation
5606
5578
  // -----------------------------------------------------------------------------
5607
5579
  function wrapInTTranslationAST(r) {
5608
- const ast = { type: 16 /* TTranslation */, content: r };
5580
+ const ast = { type: 15 /* ASTType.TTranslation */, content: r };
5609
5581
  if (r === null || r === void 0 ? void 0 : r.hasNoRepresentation) {
5610
5582
  ast.hasNoRepresentation = true;
5611
5583
  }
@@ -5617,7 +5589,7 @@
5617
5589
  }
5618
5590
  node.removeAttribute("t-translation");
5619
5591
  const result = parseNode(node, ctx);
5620
- if ((result === null || result === void 0 ? void 0 : result.type) === 3 /* Multi */) {
5592
+ if ((result === null || result === void 0 ? void 0 : result.type) === 3 /* ASTType.Multi */) {
5621
5593
  const children = result.content.map(wrapInTTranslationAST);
5622
5594
  return makeASTMulti(children);
5623
5595
  }
@@ -5628,7 +5600,7 @@
5628
5600
  // -----------------------------------------------------------------------------
5629
5601
  function wrapInTTranslationContextAST(r, translationCtx) {
5630
5602
  const ast = {
5631
- type: 17 /* TTranslationContext */,
5603
+ type: 16 /* ASTType.TTranslationContext */,
5632
5604
  content: r,
5633
5605
  translationCtx,
5634
5606
  };
@@ -5644,7 +5616,7 @@
5644
5616
  }
5645
5617
  node.removeAttribute("t-translation-context");
5646
5618
  const result = parseNode(node, ctx);
5647
- if ((result === null || result === void 0 ? void 0 : result.type) === 3 /* Multi */) {
5619
+ if ((result === null || result === void 0 ? void 0 : result.type) === 3 /* ASTType.Multi */) {
5648
5620
  const children = result.content.map((c) => wrapInTTranslationContextAST(c, translationCtx));
5649
5621
  return makeASTMulti(children);
5650
5622
  }
@@ -5662,12 +5634,12 @@
5662
5634
  const content = parseNode(node, ctx);
5663
5635
  if (!content) {
5664
5636
  return {
5665
- type: 0 /* Text */,
5637
+ type: 0 /* ASTType.Text */,
5666
5638
  value: "",
5667
5639
  };
5668
5640
  }
5669
5641
  return {
5670
- type: 18 /* TPortal */,
5642
+ type: 17 /* ASTType.TPortal */,
5671
5643
  target,
5672
5644
  content,
5673
5645
  };
@@ -5683,7 +5655,7 @@
5683
5655
  for (let child of node.childNodes) {
5684
5656
  const childAst = parseNode(child, ctx);
5685
5657
  if (childAst) {
5686
- if (childAst.type === 3 /* Multi */) {
5658
+ if (childAst.type === 3 /* ASTType.Multi */) {
5687
5659
  children.push(...childAst.content);
5688
5660
  }
5689
5661
  else {
@@ -5694,7 +5666,7 @@
5694
5666
  return children;
5695
5667
  }
5696
5668
  function makeASTMulti(children) {
5697
- const ast = { type: 3 /* Multi */, content: children };
5669
+ const ast = { type: 3 /* ASTType.Multi */, content: children };
5698
5670
  if (children.every((c) => c.hasNoRepresentation)) {
5699
5671
  ast.hasNoRepresentation = true;
5700
5672
  }
@@ -5755,28 +5727,26 @@
5755
5727
  }
5756
5728
  }
5757
5729
  /**
5758
- * Normalizes the content of an Element so that t-esc directives on components
5759
- * are removed and instead places a <t t-esc=""> as the default slot of the
5730
+ * Normalizes the content of an Element so that t-out directives on components
5731
+ * are removed and instead places a <t t-out=""> as the default slot of the
5760
5732
  * component. Also throws if the component already has content. This function
5761
5733
  * modifies the Element in place.
5762
5734
  *
5763
5735
  * @param el the element containing the tree that should be normalized
5764
5736
  */
5765
- function normalizeTEscTOut(el) {
5766
- for (const d of ["t-esc", "t-out"]) {
5767
- const elements = [...el.querySelectorAll(`[${d}]`)].filter((el) => el.tagName[0] === el.tagName[0].toUpperCase() || el.hasAttribute("t-component"));
5768
- for (const el of elements) {
5769
- if (el.childNodes.length) {
5770
- throw new OwlError(`Cannot have ${d} on a component that already has content`);
5771
- }
5772
- const value = el.getAttribute(d);
5773
- el.removeAttribute(d);
5774
- const t = el.ownerDocument.createElement("t");
5775
- if (value != null) {
5776
- t.setAttribute(d, value);
5777
- }
5778
- el.appendChild(t);
5737
+ function normalizeTOut(el) {
5738
+ const elements = [...el.querySelectorAll(`[t-out]`)].filter((el) => el.tagName[0] === el.tagName[0].toUpperCase() || el.hasAttribute("t-component"));
5739
+ for (const el of elements) {
5740
+ if (el.childNodes.length) {
5741
+ throw new OwlError(`Cannot have t-out on a component that already has content`);
5742
+ }
5743
+ const value = el.getAttribute("t-out");
5744
+ el.removeAttribute("t-out");
5745
+ const t = el.ownerDocument.createElement("t");
5746
+ if (value != null) {
5747
+ t.setAttribute("t-out", value);
5779
5748
  }
5749
+ el.appendChild(t);
5780
5750
  }
5781
5751
  }
5782
5752
  /**
@@ -5787,7 +5757,7 @@
5787
5757
  */
5788
5758
  function normalizeXML(el) {
5789
5759
  normalizeTIf(el);
5790
- normalizeTEscTOut(el);
5760
+ normalizeTOut(el);
5791
5761
  }
5792
5762
 
5793
5763
  function compile(template, options = {
@@ -5798,7 +5768,7 @@
5798
5768
  // some work
5799
5769
  const hasSafeContext = template instanceof Node
5800
5770
  ? !(template instanceof Element) || template.querySelector("[t-set], [t-call]") === null
5801
- : !template.includes("t-set") && !template.includes("t-call");
5771
+ : !template.includes("t-set=") && !template.includes("t-call=");
5802
5772
  // code generation
5803
5773
  const codeGenerator = new CodeGenerator(ast, { ...options, hasSafeContext });
5804
5774
  const code = codeGenerator.generateCode();
@@ -5816,7 +5786,7 @@
5816
5786
  }
5817
5787
 
5818
5788
  // do not modify manually. This file is generated by the release script.
5819
- const version = "2.8.1";
5789
+ const version = "3.0.0-alpha";
5820
5790
 
5821
5791
  // -----------------------------------------------------------------------------
5822
5792
  // Scheduler
@@ -5848,7 +5818,7 @@
5848
5818
  let renders = this.delayedRenders;
5849
5819
  this.delayedRenders = [];
5850
5820
  for (let f of renders) {
5851
- if (f.root && f.node.status !== 3 /* DESTROYED */ && f.node.fiber === f) {
5821
+ if (f.root && f.node.status !== 3 /* STATUS.DESTROYED */ && f.node.fiber === f) {
5852
5822
  f.render();
5853
5823
  }
5854
5824
  }
@@ -5871,7 +5841,7 @@
5871
5841
  this.processFiber(task);
5872
5842
  }
5873
5843
  for (let task of this.tasks) {
5874
- if (task.node.status === 3 /* DESTROYED */) {
5844
+ if (task.node.status === 3 /* STATUS.DESTROYED */) {
5875
5845
  this.tasks.delete(task);
5876
5846
  }
5877
5847
  }
@@ -5887,7 +5857,7 @@
5887
5857
  this.tasks.delete(fiber);
5888
5858
  return;
5889
5859
  }
5890
- if (fiber.node.status === 3 /* DESTROYED */) {
5860
+ if (fiber.node.status === 3 /* STATUS.DESTROYED */) {
5891
5861
  this.tasks.delete(fiber);
5892
5862
  return;
5893
5863
  }
@@ -5912,105 +5882,94 @@
5912
5882
 
5913
5883
  let hasBeenLogged = false;
5914
5884
  const apps = new Set();
5915
- window.__OWL_DEVTOOLS__ || (window.__OWL_DEVTOOLS__ = { apps, Fiber, RootFiber, toRaw, reactive });
5885
+ window.__OWL_DEVTOOLS__ || (window.__OWL_DEVTOOLS__ = { apps, Fiber, RootFiber, toRaw, proxy });
5916
5886
  class App extends TemplateSet {
5917
- constructor(Root, config = {}) {
5887
+ constructor(config = {}) {
5918
5888
  super(config);
5919
5889
  this.scheduler = new Scheduler();
5920
- this.subRoots = new Set();
5921
- this.root = null;
5890
+ this.roots = new Set();
5922
5891
  this.name = config.name || "";
5923
- this.Root = Root;
5924
5892
  apps.add(this);
5893
+ this.pluginManager = config.pluginManager || new PluginManager(null);
5894
+ if (config.plugins) {
5895
+ this.pluginManager.startPlugins(config.plugins);
5896
+ }
5925
5897
  if (config.test) {
5926
5898
  this.dev = true;
5927
5899
  }
5928
- this.warnIfNoStaticProps = config.warnIfNoStaticProps || false;
5929
5900
  if (this.dev && !config.test && !hasBeenLogged) {
5930
5901
  console.info(`Owl is running in 'dev' mode.`);
5931
5902
  hasBeenLogged = true;
5932
5903
  }
5933
- const env = config.env || {};
5934
- const descrs = Object.getOwnPropertyDescriptors(env);
5935
- this.env = Object.freeze(Object.create(Object.getPrototypeOf(env), descrs));
5936
- this.props = config.props || {};
5937
- }
5938
- mount(target, options) {
5939
- const root = this.createRoot(this.Root, { props: this.props });
5940
- this.root = root.node;
5941
- this.subRoots.delete(root.node);
5942
- return root.mount(target, options);
5943
5904
  }
5944
5905
  createRoot(Root, config = {}) {
5945
5906
  const props = config.props || {};
5946
- // hack to make sure the sub root get the sub env if necessary. for owl 3,
5947
- // would be nice to rethink the initialization process to make sure that
5948
- // we can create a ComponentNode and give it explicitely the env, instead
5949
- // of looking it up in the app
5950
- const env = this.env;
5951
- if (config.env) {
5952
- this.env = config.env;
5953
- }
5907
+ let resolve;
5908
+ let reject;
5909
+ const promise = new Promise((res, rej) => {
5910
+ resolve = res;
5911
+ reject = rej;
5912
+ });
5954
5913
  const restore = saveCurrent();
5955
- const node = this.makeNode(Root, props);
5956
- restore();
5957
- if (config.env) {
5958
- this.env = env;
5914
+ let node;
5915
+ let error = null;
5916
+ try {
5917
+ node = this.makeNode(Root, props);
5959
5918
  }
5960
- this.subRoots.add(node);
5961
- return {
5962
- node,
5919
+ catch (e) {
5920
+ error = e;
5921
+ reject(e);
5922
+ }
5923
+ finally {
5924
+ restore();
5925
+ }
5926
+ const root = {
5927
+ node: node,
5928
+ promise,
5963
5929
  mount: (target, options) => {
5964
- App.validateTarget(target);
5965
- if (this.dev) {
5966
- validateProps(Root, props, { __owl__: { app: this } });
5930
+ if (error) {
5931
+ return promise;
5967
5932
  }
5968
- const prom = this.mountNode(node, target, options);
5969
- return prom;
5933
+ App.validateTarget(target);
5934
+ this.mountNode(node, target, resolve, reject, options);
5935
+ return promise;
5970
5936
  },
5971
5937
  destroy: () => {
5972
- this.subRoots.delete(node);
5938
+ this.roots.delete(root);
5973
5939
  node.destroy();
5974
5940
  this.scheduler.processTasks();
5975
5941
  },
5976
5942
  };
5943
+ this.roots.add(root);
5944
+ return root;
5977
5945
  }
5978
5946
  makeNode(Component, props) {
5979
5947
  return new ComponentNode(Component, props, this, null, null);
5980
5948
  }
5981
- mountNode(node, target, options) {
5982
- const promise = new Promise((resolve, reject) => {
5983
- let isResolved = false;
5984
- // manually set a onMounted callback.
5985
- // that way, we are independant from the current node.
5986
- node.mounted.push(() => {
5987
- resolve(node.component);
5988
- isResolved = true;
5989
- });
5990
- // Manually add the last resort error handler on the node
5991
- let handlers = nodeErrorHandlers.get(node);
5992
- if (!handlers) {
5993
- handlers = [];
5994
- nodeErrorHandlers.set(node, handlers);
5995
- }
5996
- handlers.unshift((e) => {
5997
- if (!isResolved) {
5998
- reject(e);
5999
- }
6000
- throw e;
6001
- });
5949
+ mountNode(node, target, resolve, reject, options) {
5950
+ // Manually add the last resort error handler on the node
5951
+ let handlers = nodeErrorHandlers.get(node);
5952
+ if (!handlers) {
5953
+ handlers = [];
5954
+ nodeErrorHandlers.set(node, handlers);
5955
+ }
5956
+ handlers.unshift((e, finalize) => {
5957
+ const finalError = finalize();
5958
+ reject(finalError);
5959
+ });
5960
+ // manually set a onMounted callback.
5961
+ // that way, we are independant from the current node.
5962
+ node.mounted.push(() => {
5963
+ resolve(node.component);
5964
+ handlers.shift();
6002
5965
  });
6003
5966
  node.mountComponent(target, options);
6004
- return promise;
6005
5967
  }
6006
5968
  destroy() {
6007
- if (this.root) {
6008
- for (let subroot of this.subRoots) {
6009
- subroot.destroy();
6010
- }
6011
- this.root.destroy();
6012
- this.scheduler.processTasks();
5969
+ for (let root of this.roots) {
5970
+ root.destroy();
6013
5971
  }
5972
+ this.scheduler.processTasks();
6014
5973
  apps.delete(this);
6015
5974
  }
6016
5975
  createComponent(name, isStatic, hasSlotsProp, hasDynamicPropList, propList) {
@@ -6089,7 +6048,9 @@
6089
6048
  App.apps = apps;
6090
6049
  App.version = version;
6091
6050
  async function mount(C, target, config = {}) {
6092
- return new App(C, config).mount(target, config);
6051
+ const app = new App(config);
6052
+ const root = app.createRoot(C, config);
6053
+ return root.mount(target, config);
6093
6054
  }
6094
6055
 
6095
6056
  const mainEventHandler = (data, ev, currentTarget) => {
@@ -6130,72 +6091,227 @@
6130
6091
  throw new OwlError(`Invalid handler (expected a function, received: '${handler}')`);
6131
6092
  }
6132
6093
  let node = data[1] ? data[1].__owl__ : null;
6133
- if (node ? node.status === 1 /* MOUNTED */ : true) {
6094
+ if (node ? node.status === 1 /* STATUS.MOUNTED */ : true) {
6134
6095
  handler.call(node ? node.component : null, ev);
6135
6096
  }
6136
6097
  }
6137
6098
  return stopped;
6138
6099
  };
6139
6100
 
6140
- function status(component) {
6141
- switch (component.__owl__.status) {
6142
- case 0 /* NEW */:
6143
- return "new";
6144
- case 2 /* CANCELLED */:
6145
- return "cancelled";
6146
- case 1 /* MOUNTED */:
6147
- return "mounted";
6148
- case 3 /* DESTROYED */:
6149
- return "destroyed";
6101
+ function computed(fn, opts) {
6102
+ // todo: handle cleanup
6103
+ let derivedComputation;
6104
+ return () => {
6105
+ derivedComputation !== null && derivedComputation !== void 0 ? derivedComputation : (derivedComputation = {
6106
+ state: ComputationState.STALE,
6107
+ sources: new Set(),
6108
+ compute: () => {
6109
+ onWriteAtom(derivedComputation);
6110
+ return fn();
6111
+ },
6112
+ isDerived: true,
6113
+ value: undefined,
6114
+ observers: new Set(),
6115
+ name: opts === null || opts === void 0 ? void 0 : opts.name,
6116
+ });
6117
+ updateComputation(derivedComputation);
6118
+ return derivedComputation.value;
6119
+ };
6120
+ }
6121
+
6122
+ function signal(value, opts) {
6123
+ const atom = {
6124
+ value,
6125
+ observers: new Set(),
6126
+ name: opts === null || opts === void 0 ? void 0 : opts.name,
6127
+ };
6128
+ const read = () => {
6129
+ onReadAtom(atom);
6130
+ return atom.value;
6131
+ };
6132
+ const write = (newValue) => {
6133
+ if (typeof newValue === "function") {
6134
+ newValue = newValue(atom.value);
6135
+ }
6136
+ if (Object.is(atom.value, newValue))
6137
+ return;
6138
+ atom.value = newValue;
6139
+ onWriteAtom(atom);
6140
+ };
6141
+ read.set = write;
6142
+ read.update = (updater) => {
6143
+ if (updater) {
6144
+ write(updater(atom.value));
6145
+ }
6146
+ else {
6147
+ onWriteAtom(atom);
6148
+ }
6149
+ };
6150
+ return read;
6151
+ }
6152
+
6153
+ class Resource {
6154
+ constructor(name, type) {
6155
+ this._items = signal([]);
6156
+ this.items = computed(() => {
6157
+ return this._items()
6158
+ .sort((el1, el2) => el1[0] - el2[0])
6159
+ .map((elem) => elem[1]);
6160
+ });
6161
+ this._name = name || "resource";
6162
+ this._type = type;
6163
+ }
6164
+ add(item, sequence = 50) {
6165
+ if (this._type) {
6166
+ const error = validateType("item", item, this._type);
6167
+ if (error) {
6168
+ throw new Error(`Invalid type: ${error} (resource '${this._name}')`);
6169
+ }
6170
+ }
6171
+ this._items().push([sequence, item]);
6172
+ this._items.update();
6173
+ return this;
6174
+ }
6175
+ remove(item) {
6176
+ const items = this._items().filter(([seq, val]) => val !== item);
6177
+ this._items.set(items);
6178
+ return this;
6179
+ }
6180
+ has(item) {
6181
+ return this._items().some(([s, value]) => value === item);
6182
+ }
6183
+ }
6184
+ function useResource(r, elements) {
6185
+ for (let elem of elements) {
6186
+ r.add(elem);
6150
6187
  }
6188
+ onWillDestroy(() => {
6189
+ for (let elem of elements) {
6190
+ r.remove(elem);
6191
+ }
6192
+ });
6151
6193
  }
6152
6194
 
6153
- // -----------------------------------------------------------------------------
6154
- // useRef
6155
- // -----------------------------------------------------------------------------
6156
- /**
6157
- * The purpose of this hook is to allow components to get a reference to a sub
6158
- * html node or component.
6159
- */
6160
- function useRef(name) {
6161
- const node = getCurrent();
6162
- const refs = node.refs;
6163
- return {
6164
- get el() {
6165
- const el = refs[name];
6166
- return inOwnerDocument(el) ? el : null;
6195
+ class Registry {
6196
+ constructor(name, type) {
6197
+ this._map = signal(Object.create(null));
6198
+ this.entries = computed(() => {
6199
+ const entries = Object.entries(this._map())
6200
+ .sort((el1, el2) => el1[1][0] - el2[1][0])
6201
+ .map(([str, elem]) => [str, elem[1]]);
6202
+ return entries;
6203
+ });
6204
+ this.items = computed(() => this.entries().map((e) => e[1]));
6205
+ this._name = name || "registry";
6206
+ this._type = type;
6207
+ }
6208
+ addById(item, sequence = 50) {
6209
+ if (!item.id) {
6210
+ throw new Error(`Item should have an id key`);
6211
+ }
6212
+ return this.add(item.id, item, sequence);
6213
+ }
6214
+ add(key, value, sequence = 50) {
6215
+ if (this._type) {
6216
+ const error = validateType(key, value, this._type);
6217
+ // todo: move error handling in validation.js
6218
+ if (error) {
6219
+ throw new Error("Invalid type: " + error);
6220
+ }
6221
+ }
6222
+ this._map()[key] = [sequence, value];
6223
+ this._map.update();
6224
+ return this;
6225
+ }
6226
+ get(key, defaultValue) {
6227
+ const hasKey = key in this._map();
6228
+ if (!hasKey && arguments.length < 2) {
6229
+ throw new Error(`KeyNotFoundError: Cannot find key "${key}" in this registry`);
6230
+ }
6231
+ return hasKey ? this._map()[key][1] : defaultValue;
6232
+ }
6233
+ remove(key) {
6234
+ delete this._map()[key];
6235
+ this._map.update();
6236
+ }
6237
+ has(key) {
6238
+ return key in this._map();
6239
+ }
6240
+ }
6241
+
6242
+ function status() {
6243
+ const pm = _getCurrentPluginManager();
6244
+ const node = pm || getCurrent();
6245
+ return () => {
6246
+ switch (node.status) {
6247
+ case 0 /* STATUS.NEW */:
6248
+ return "new";
6249
+ case 2 /* STATUS.CANCELLED */:
6250
+ return "cancelled";
6251
+ case 1 /* STATUS.MOUNTED */:
6252
+ return pm ? "started" : "mounted";
6253
+ case 3 /* STATUS.DESTROYED */:
6254
+ return "destroyed";
6255
+ }
6256
+ };
6257
+ }
6258
+
6259
+ function effect(fn, opts) {
6260
+ var _a, _b, _c;
6261
+ const effectComputation = {
6262
+ state: ComputationState.STALE,
6263
+ value: undefined,
6264
+ compute() {
6265
+ // In case the cleanup read an atom.
6266
+ // todo: test it
6267
+ setComputation(undefined);
6268
+ // CurrentComputation = undefined!;
6269
+ // `removeSources` is made by `runComputation`.
6270
+ unsubscribeEffect(effectComputation);
6271
+ setComputation(effectComputation);
6272
+ // CurrentComputation = effectComputation;
6273
+ return fn();
6167
6274
  },
6275
+ sources: new Set(),
6276
+ childrenEffect: [],
6277
+ name: opts === null || opts === void 0 ? void 0 : opts.name,
6278
+ };
6279
+ (_c = (_b = (_a = getCurrentComputation()) === null || _a === void 0 ? void 0 : _a.childrenEffect) === null || _b === void 0 ? void 0 : _b.push) === null || _c === void 0 ? void 0 : _c.call(_b, effectComputation);
6280
+ updateComputation(effectComputation);
6281
+ // Remove sources and unsubscribe
6282
+ return () => {
6283
+ // In case the cleanup read an atom.
6284
+ // todo: test it
6285
+ const previousComputation = getCurrentComputation();
6286
+ setComputation(undefined);
6287
+ unsubscribeEffect(effectComputation);
6288
+ setComputation(previousComputation);
6168
6289
  };
6169
6290
  }
6291
+ function unsubscribeEffect(effectComputation) {
6292
+ removeSources(effectComputation);
6293
+ cleanupEffect(effectComputation);
6294
+ for (const children of effectComputation.childrenEffect) {
6295
+ // Consider it executed to avoid it's re-execution
6296
+ // todo: make a test for it
6297
+ children.state = ComputationState.EXECUTED;
6298
+ removeSources(children);
6299
+ unsubscribeEffect(children);
6300
+ }
6301
+ effectComputation.childrenEffect.length = 0;
6302
+ }
6303
+ function cleanupEffect(computation) {
6304
+ // the computation.value of an effect is a cleanup function
6305
+ const cleanupFn = computation.value;
6306
+ if (cleanupFn && typeof cleanupFn === "function") {
6307
+ cleanupFn();
6308
+ computation.value = undefined;
6309
+ }
6310
+ }
6311
+
6170
6312
  // -----------------------------------------------------------------------------
6171
- // useEnv and useSubEnv
6313
+ // useEffect
6172
6314
  // -----------------------------------------------------------------------------
6173
- /**
6174
- * This hook is useful as a building block for some customized hooks, that may
6175
- * need a reference to the env of the component calling them.
6176
- */
6177
- function useEnv() {
6178
- return getCurrent().component.env;
6179
- }
6180
- function extendEnv(currentEnv, extension) {
6181
- const env = Object.create(currentEnv);
6182
- const descrs = Object.getOwnPropertyDescriptors(extension);
6183
- return Object.freeze(Object.defineProperties(env, descrs));
6184
- }
6185
- /**
6186
- * This hook is a simple way to let components use a sub environment. Note that
6187
- * like for all hooks, it is important that this is only called in the
6188
- * constructor method.
6189
- */
6190
- function useSubEnv(envExtension) {
6191
- const node = getCurrent();
6192
- node.component.env = extendEnv(node.component.env, envExtension);
6193
- useChildSubEnv(envExtension);
6194
- }
6195
- function useChildSubEnv(envExtension) {
6196
- const node = getCurrent();
6197
- node.childEnv = extendEnv(node.childEnv, envExtension);
6198
- }
6199
6315
  /**
6200
6316
  * This hook will run a callback when a component is mounted and patched, and
6201
6317
  * will run a cleanup function before patching and before unmounting the
@@ -6209,32 +6325,15 @@
6209
6325
  * again. The default value returns an array containing only NaN because
6210
6326
  * NaN !== NaN, which will cause the effect to rerun on every patch.
6211
6327
  */
6212
- function useEffect(effect, computeDependencies = () => [NaN]) {
6213
- let cleanup;
6214
- let dependencies;
6215
- onMounted(() => {
6216
- dependencies = computeDependencies();
6217
- cleanup = effect(...dependencies);
6218
- });
6219
- onPatched(() => {
6220
- const newDeps = computeDependencies();
6221
- const shouldReapply = newDeps.some((val, i) => val !== dependencies[i]);
6222
- if (shouldReapply) {
6223
- dependencies = newDeps;
6224
- if (cleanup) {
6225
- cleanup();
6226
- }
6227
- cleanup = effect(...dependencies);
6228
- }
6229
- });
6230
- onWillUnmount(() => cleanup && cleanup());
6328
+ function useEffect(fn) {
6329
+ onWillDestroy(effect(fn));
6231
6330
  }
6232
6331
  // -----------------------------------------------------------------------------
6233
- // useExternalListener
6332
+ // useListener
6234
6333
  // -----------------------------------------------------------------------------
6235
6334
  /**
6236
6335
  * When a component needs to listen to DOM Events on element(s) that are not
6237
- * part of his hierarchy, we can use the `useExternalListener` hook.
6336
+ * part of his hierarchy, we can use the `useListener` hook.
6238
6337
  * It will correctly add and remove the event listener, whenever the
6239
6338
  * component is mounted and unmounted.
6240
6339
  *
@@ -6243,13 +6342,20 @@
6243
6342
  *
6244
6343
  * Usage:
6245
6344
  * in the constructor of the OWL component that needs to be notified,
6246
- * `useExternalListener(window, 'click', this._doSomething);`
6345
+ * `useListener(window, 'click', this._doSomething);`
6247
6346
  * */
6248
- function useExternalListener(target, eventName, handler, eventParams) {
6347
+ function useListener(target, eventName, handler, eventParams) {
6249
6348
  const node = getCurrent();
6250
6349
  const boundHandler = handler.bind(node.component);
6251
6350
  onMounted(() => target.addEventListener(eventName, boundHandler, eventParams));
6252
6351
  onWillUnmount(() => target.removeEventListener(eventName, boundHandler, eventParams));
6352
+ }
6353
+ function usePlugins(Plugins) {
6354
+ const node = getCurrent();
6355
+ const manager = new PluginManager(node.pluginManager);
6356
+ node.pluginManager = manager;
6357
+ onWillDestroy(() => manager.destroy());
6358
+ return manager.startPlugins(Plugins);
6253
6359
  }
6254
6360
 
6255
6361
  config.shouldNormalizeDom = false;
@@ -6288,35 +6394,39 @@
6288
6394
  exports.Component = Component;
6289
6395
  exports.EventBus = EventBus;
6290
6396
  exports.OwlError = OwlError;
6397
+ exports.Plugin = Plugin;
6398
+ exports.PluginManager = PluginManager;
6399
+ exports.Registry = Registry;
6400
+ exports.Resource = Resource;
6291
6401
  exports.__info__ = __info__;
6292
6402
  exports.batched = batched;
6293
6403
  exports.blockDom = blockDom;
6404
+ exports.computed = computed;
6405
+ exports.effect = effect;
6294
6406
  exports.htmlEscape = htmlEscape;
6295
- exports.loadFile = loadFile;
6296
6407
  exports.markRaw = markRaw;
6297
6408
  exports.markup = markup;
6298
6409
  exports.mount = mount;
6299
6410
  exports.onError = onError;
6300
6411
  exports.onMounted = onMounted;
6301
6412
  exports.onPatched = onPatched;
6302
- exports.onRendered = onRendered;
6303
6413
  exports.onWillDestroy = onWillDestroy;
6304
6414
  exports.onWillPatch = onWillPatch;
6305
- exports.onWillRender = onWillRender;
6306
6415
  exports.onWillStart = onWillStart;
6307
6416
  exports.onWillUnmount = onWillUnmount;
6308
6417
  exports.onWillUpdateProps = onWillUpdateProps;
6309
- exports.reactive = reactive;
6418
+ exports.plugin = plugin;
6419
+ exports.props = props;
6420
+ exports.proxy = proxy;
6421
+ exports.signal = signal;
6310
6422
  exports.status = status;
6311
6423
  exports.toRaw = toRaw;
6312
- exports.useChildSubEnv = useChildSubEnv;
6424
+ exports.untrack = untrack;
6313
6425
  exports.useComponent = useComponent;
6314
6426
  exports.useEffect = useEffect;
6315
- exports.useEnv = useEnv;
6316
- exports.useExternalListener = useExternalListener;
6317
- exports.useRef = useRef;
6318
- exports.useState = useState;
6319
- exports.useSubEnv = useSubEnv;
6427
+ exports.useListener = useListener;
6428
+ exports.usePlugins = usePlugins;
6429
+ exports.useResource = useResource;
6320
6430
  exports.validate = validate;
6321
6431
  exports.validateType = validateType;
6322
6432
  exports.whenReady = whenReady;
@@ -6325,8 +6435,8 @@
6325
6435
  Object.defineProperty(exports, '__esModule', { value: true });
6326
6436
 
6327
6437
 
6328
- __info__.date = '2025-09-23T07:17:45.055Z';
6329
- __info__.hash = '5211116';
6438
+ __info__.date = '2025-12-22T15:42:39.839Z';
6439
+ __info__.hash = '169a8cc';
6330
6440
  __info__.url = 'https://github.com/odoo/owl';
6331
6441
 
6332
6442