@odoo/owl 2.0.0-beta-11 → 2.0.0-beta-15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/owl.cjs.js +146 -140
- package/dist/owl.es.js +146 -141
- package/dist/owl.iife.js +146 -140
- package/dist/owl.iife.min.js +1 -1
- package/dist/types/compiler/code_generator.d.ts +3 -2
- package/dist/types/compiler/inline_expressions.d.ts +0 -22
- package/dist/types/owl.d.ts +5 -2
- package/dist/types/runtime/component_node.d.ts +0 -1
- package/dist/types/runtime/error_handling.d.ts +3 -0
- package/dist/types/runtime/index.d.ts +1 -0
- package/dist/types/runtime/template_helpers.d.ts +2 -0
- package/package.json +1 -2
package/dist/owl.cjs.js
CHANGED
|
@@ -81,6 +81,68 @@ function toggler(key, child) {
|
|
|
81
81
|
return new VToggler(key, child);
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
// Custom error class that wraps error that happen in the owl lifecycle
|
|
85
|
+
class OwlError extends Error {
|
|
86
|
+
}
|
|
87
|
+
// Maps fibers to thrown errors
|
|
88
|
+
const fibersInError = new WeakMap();
|
|
89
|
+
const nodeErrorHandlers = new WeakMap();
|
|
90
|
+
function _handleError(node, error) {
|
|
91
|
+
if (!node) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
const fiber = node.fiber;
|
|
95
|
+
if (fiber) {
|
|
96
|
+
fibersInError.set(fiber, error);
|
|
97
|
+
}
|
|
98
|
+
const errorHandlers = nodeErrorHandlers.get(node);
|
|
99
|
+
if (errorHandlers) {
|
|
100
|
+
let handled = false;
|
|
101
|
+
// execute in the opposite order
|
|
102
|
+
for (let i = errorHandlers.length - 1; i >= 0; i--) {
|
|
103
|
+
try {
|
|
104
|
+
errorHandlers[i](error);
|
|
105
|
+
handled = true;
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
catch (e) {
|
|
109
|
+
error = e;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (handled) {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return _handleError(node.parent, error);
|
|
117
|
+
}
|
|
118
|
+
function handleError(params) {
|
|
119
|
+
let { error } = params;
|
|
120
|
+
// Wrap error if it wasn't wrapped by wrapError (ie when not in dev mode)
|
|
121
|
+
if (!(error instanceof OwlError)) {
|
|
122
|
+
error = Object.assign(new OwlError("An error occured in the owl lifecycle"), { cause: error });
|
|
123
|
+
}
|
|
124
|
+
const node = "node" in params ? params.node : params.fiber.node;
|
|
125
|
+
const fiber = "fiber" in params ? params.fiber : node.fiber;
|
|
126
|
+
// resets the fibers on components if possible. This is important so that
|
|
127
|
+
// new renderings can be properly included in the initial one, if any.
|
|
128
|
+
let current = fiber;
|
|
129
|
+
do {
|
|
130
|
+
current.node.fiber = current;
|
|
131
|
+
current = current.parent;
|
|
132
|
+
} while (current);
|
|
133
|
+
fibersInError.set(fiber.root, error);
|
|
134
|
+
const handled = _handleError(node, error);
|
|
135
|
+
if (!handled) {
|
|
136
|
+
console.warn(`[Owl] Unhandled error. Destroying the root component`);
|
|
137
|
+
try {
|
|
138
|
+
node.app.destroy();
|
|
139
|
+
}
|
|
140
|
+
catch (e) {
|
|
141
|
+
console.error(e);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
84
146
|
const { setAttribute: elemSetAttribute, removeAttribute } = Element.prototype;
|
|
85
147
|
const tokenList = DOMTokenList.prototype;
|
|
86
148
|
const tokenListAdd = tokenList.add;
|
|
@@ -218,7 +280,7 @@ function updateClass(val, oldVal) {
|
|
|
218
280
|
function makePropSetter(name) {
|
|
219
281
|
return function setProp(value) {
|
|
220
282
|
// support 0, fallback to empty string for other falsy values
|
|
221
|
-
this[name] = value === 0 ? 0 : value
|
|
283
|
+
this[name] = value === 0 ? 0 : value ? value.valueOf() : "";
|
|
222
284
|
};
|
|
223
285
|
}
|
|
224
286
|
function isProp(tag, key) {
|
|
@@ -702,7 +764,7 @@ function buildTree(node, parent = null, domParentTree = null) {
|
|
|
702
764
|
};
|
|
703
765
|
}
|
|
704
766
|
}
|
|
705
|
-
throw new
|
|
767
|
+
throw new OwlError("boom");
|
|
706
768
|
}
|
|
707
769
|
function addRef(tree) {
|
|
708
770
|
tree.isRef = true;
|
|
@@ -1386,61 +1448,6 @@ function remove(vnode, withBeforeRemove = false) {
|
|
|
1386
1448
|
vnode.remove();
|
|
1387
1449
|
}
|
|
1388
1450
|
|
|
1389
|
-
// Maps fibers to thrown errors
|
|
1390
|
-
const fibersInError = new WeakMap();
|
|
1391
|
-
const nodeErrorHandlers = new WeakMap();
|
|
1392
|
-
function _handleError(node, error) {
|
|
1393
|
-
if (!node) {
|
|
1394
|
-
return false;
|
|
1395
|
-
}
|
|
1396
|
-
const fiber = node.fiber;
|
|
1397
|
-
if (fiber) {
|
|
1398
|
-
fibersInError.set(fiber, error);
|
|
1399
|
-
}
|
|
1400
|
-
const errorHandlers = nodeErrorHandlers.get(node);
|
|
1401
|
-
if (errorHandlers) {
|
|
1402
|
-
let handled = false;
|
|
1403
|
-
// execute in the opposite order
|
|
1404
|
-
for (let i = errorHandlers.length - 1; i >= 0; i--) {
|
|
1405
|
-
try {
|
|
1406
|
-
errorHandlers[i](error);
|
|
1407
|
-
handled = true;
|
|
1408
|
-
break;
|
|
1409
|
-
}
|
|
1410
|
-
catch (e) {
|
|
1411
|
-
error = e;
|
|
1412
|
-
}
|
|
1413
|
-
}
|
|
1414
|
-
if (handled) {
|
|
1415
|
-
return true;
|
|
1416
|
-
}
|
|
1417
|
-
}
|
|
1418
|
-
return _handleError(node.parent, error);
|
|
1419
|
-
}
|
|
1420
|
-
function handleError(params) {
|
|
1421
|
-
const error = params.error;
|
|
1422
|
-
const node = "node" in params ? params.node : params.fiber.node;
|
|
1423
|
-
const fiber = "fiber" in params ? params.fiber : node.fiber;
|
|
1424
|
-
// resets the fibers on components if possible. This is important so that
|
|
1425
|
-
// new renderings can be properly included in the initial one, if any.
|
|
1426
|
-
let current = fiber;
|
|
1427
|
-
do {
|
|
1428
|
-
current.node.fiber = current;
|
|
1429
|
-
current = current.parent;
|
|
1430
|
-
} while (current);
|
|
1431
|
-
fibersInError.set(fiber.root, error);
|
|
1432
|
-
const handled = _handleError(node, error);
|
|
1433
|
-
if (!handled) {
|
|
1434
|
-
console.warn(`[Owl] Unhandled error. Destroying the root component`);
|
|
1435
|
-
try {
|
|
1436
|
-
node.app.destroy();
|
|
1437
|
-
}
|
|
1438
|
-
catch (e) {
|
|
1439
|
-
console.error(e);
|
|
1440
|
-
}
|
|
1441
|
-
}
|
|
1442
|
-
}
|
|
1443
|
-
|
|
1444
1451
|
function makeChildFiber(node, parent) {
|
|
1445
1452
|
let current = node.fiber;
|
|
1446
1453
|
if (current) {
|
|
@@ -1479,7 +1486,7 @@ function makeRootFiber(node) {
|
|
|
1479
1486
|
return fiber;
|
|
1480
1487
|
}
|
|
1481
1488
|
function throwOnRender() {
|
|
1482
|
-
throw new
|
|
1489
|
+
throw new OwlError("Attempted to render cancelled fiber");
|
|
1483
1490
|
}
|
|
1484
1491
|
/**
|
|
1485
1492
|
* @returns number of not-yet rendered fibers cancelled
|
|
@@ -1856,7 +1863,7 @@ const reactiveCache = new WeakMap();
|
|
|
1856
1863
|
*/
|
|
1857
1864
|
function reactive(target, callback = () => { }) {
|
|
1858
1865
|
if (!canBeMadeReactive(target)) {
|
|
1859
|
-
throw new
|
|
1866
|
+
throw new OwlError(`Cannot make the given value reactive`);
|
|
1860
1867
|
}
|
|
1861
1868
|
if (SKIP in target) {
|
|
1862
1869
|
return target;
|
|
@@ -2125,10 +2132,10 @@ function batched(callback) {
|
|
|
2125
2132
|
}
|
|
2126
2133
|
function validateTarget(target) {
|
|
2127
2134
|
if (!(target instanceof HTMLElement)) {
|
|
2128
|
-
throw new
|
|
2135
|
+
throw new OwlError("Cannot mount component: the target is not a valid DOM element");
|
|
2129
2136
|
}
|
|
2130
2137
|
if (!document.body.contains(target)) {
|
|
2131
|
-
throw new
|
|
2138
|
+
throw new OwlError("Cannot mount a component on a detached dom node");
|
|
2132
2139
|
}
|
|
2133
2140
|
}
|
|
2134
2141
|
class EventBus extends EventTarget {
|
|
@@ -2149,7 +2156,7 @@ function whenReady(fn) {
|
|
|
2149
2156
|
async function loadFile(url) {
|
|
2150
2157
|
const result = await fetch(url);
|
|
2151
2158
|
if (!result.ok) {
|
|
2152
|
-
throw new
|
|
2159
|
+
throw new OwlError("Error while fetching xml templates");
|
|
2153
2160
|
}
|
|
2154
2161
|
return await result.text();
|
|
2155
2162
|
}
|
|
@@ -2171,7 +2178,7 @@ function markup(value) {
|
|
|
2171
2178
|
let currentNode = null;
|
|
2172
2179
|
function getCurrent() {
|
|
2173
2180
|
if (!currentNode) {
|
|
2174
|
-
throw new
|
|
2181
|
+
throw new OwlError("No active component (a hook function should only be called in 'setup')");
|
|
2175
2182
|
}
|
|
2176
2183
|
return currentNode;
|
|
2177
2184
|
}
|
|
@@ -2233,7 +2240,6 @@ class ComponentNode {
|
|
|
2233
2240
|
this.parent = parent;
|
|
2234
2241
|
this.props = props;
|
|
2235
2242
|
this.parentKey = parentKey;
|
|
2236
|
-
this.level = parent ? parent.level + 1 : 0;
|
|
2237
2243
|
const defaultProps = C.defaultProps;
|
|
2238
2244
|
props = Object.assign({}, props);
|
|
2239
2245
|
if (defaultProps) {
|
|
@@ -2466,17 +2472,24 @@ class ComponentNode {
|
|
|
2466
2472
|
|
|
2467
2473
|
const TIMEOUT = Symbol("timeout");
|
|
2468
2474
|
function wrapError(fn, hookName) {
|
|
2469
|
-
const error = new
|
|
2470
|
-
const timeoutError = new
|
|
2475
|
+
const error = new OwlError(`The following error occurred in ${hookName}: `);
|
|
2476
|
+
const timeoutError = new OwlError(`${hookName}'s promise hasn't resolved after 3 seconds`);
|
|
2471
2477
|
const node = getCurrent();
|
|
2472
2478
|
return (...args) => {
|
|
2479
|
+
const onError = (cause) => {
|
|
2480
|
+
if (cause instanceof Error) {
|
|
2481
|
+
error.cause = cause;
|
|
2482
|
+
error.message += `"${cause.message}"`;
|
|
2483
|
+
}
|
|
2484
|
+
throw error;
|
|
2485
|
+
};
|
|
2473
2486
|
try {
|
|
2474
2487
|
const result = fn(...args);
|
|
2475
2488
|
if (result instanceof Promise) {
|
|
2476
2489
|
if (hookName === "onWillStart" || hookName === "onWillUpdateProps") {
|
|
2477
2490
|
const fiber = node.fiber;
|
|
2478
2491
|
Promise.race([
|
|
2479
|
-
result,
|
|
2492
|
+
result.catch(() => { }),
|
|
2480
2493
|
new Promise((resolve) => setTimeout(() => resolve(TIMEOUT), 3000)),
|
|
2481
2494
|
]).then((res) => {
|
|
2482
2495
|
if (res === TIMEOUT && node.fiber === fiber) {
|
|
@@ -2484,21 +2497,12 @@ function wrapError(fn, hookName) {
|
|
|
2484
2497
|
}
|
|
2485
2498
|
});
|
|
2486
2499
|
}
|
|
2487
|
-
return result.catch(
|
|
2488
|
-
error.cause = cause;
|
|
2489
|
-
if (cause instanceof Error) {
|
|
2490
|
-
error.message += `"${cause.message}"`;
|
|
2491
|
-
}
|
|
2492
|
-
throw error;
|
|
2493
|
-
});
|
|
2500
|
+
return result.catch(onError);
|
|
2494
2501
|
}
|
|
2495
2502
|
return result;
|
|
2496
2503
|
}
|
|
2497
2504
|
catch (cause) {
|
|
2498
|
-
|
|
2499
|
-
error.message += `"${cause.message}"`;
|
|
2500
|
-
}
|
|
2501
|
-
throw error;
|
|
2505
|
+
onError(cause);
|
|
2502
2506
|
}
|
|
2503
2507
|
};
|
|
2504
2508
|
}
|
|
@@ -2602,7 +2606,7 @@ class VPortal extends VText {
|
|
|
2602
2606
|
}
|
|
2603
2607
|
this.target = el && el.querySelector(this.selector);
|
|
2604
2608
|
if (!this.target) {
|
|
2605
|
-
throw new
|
|
2609
|
+
throw new OwlError("invalid portal target");
|
|
2606
2610
|
}
|
|
2607
2611
|
}
|
|
2608
2612
|
this.realBDom.mount(this.target, null);
|
|
@@ -2696,7 +2700,7 @@ function toSchema(spec) {
|
|
|
2696
2700
|
function validate(obj, spec) {
|
|
2697
2701
|
let errors = validateSchema(obj, spec);
|
|
2698
2702
|
if (errors.length) {
|
|
2699
|
-
throw new
|
|
2703
|
+
throw new OwlError("Invalid object: " + errors.join(", "));
|
|
2700
2704
|
}
|
|
2701
2705
|
}
|
|
2702
2706
|
/**
|
|
@@ -2849,7 +2853,7 @@ function prepareList(collection) {
|
|
|
2849
2853
|
keys = Object.values(collection);
|
|
2850
2854
|
}
|
|
2851
2855
|
else {
|
|
2852
|
-
throw new
|
|
2856
|
+
throw new OwlError("Invalid loop expression");
|
|
2853
2857
|
}
|
|
2854
2858
|
const n = values.length;
|
|
2855
2859
|
return [keys, values, n, new Array(n)];
|
|
@@ -2955,7 +2959,7 @@ function multiRefSetter(refs, name) {
|
|
|
2955
2959
|
if (el) {
|
|
2956
2960
|
count++;
|
|
2957
2961
|
if (count > 1) {
|
|
2958
|
-
throw new
|
|
2962
|
+
throw new OwlError("Cannot have 2 elements with same ref name at the same time");
|
|
2959
2963
|
}
|
|
2960
2964
|
}
|
|
2961
2965
|
if (count === 0 || el) {
|
|
@@ -2992,13 +2996,13 @@ function validateProps(name, props, parent) {
|
|
|
2992
2996
|
: name in schema && !("*" in schema) && !isOptional(schema[name]);
|
|
2993
2997
|
for (let p in defaultProps) {
|
|
2994
2998
|
if (isMandatory(p)) {
|
|
2995
|
-
throw new
|
|
2999
|
+
throw new OwlError(`A default value cannot be defined for a mandatory prop (name: '${p}', component: ${ComponentClass.name})`);
|
|
2996
3000
|
}
|
|
2997
3001
|
}
|
|
2998
3002
|
}
|
|
2999
3003
|
const errors = validateSchema(props, schema);
|
|
3000
3004
|
if (errors.length) {
|
|
3001
|
-
throw new
|
|
3005
|
+
throw new OwlError(`Invalid props for component '${ComponentClass.name}': ` + errors.join(", "));
|
|
3002
3006
|
}
|
|
3003
3007
|
}
|
|
3004
3008
|
const helpers = {
|
|
@@ -3019,6 +3023,7 @@ const helpers = {
|
|
|
3019
3023
|
bind,
|
|
3020
3024
|
createCatcher,
|
|
3021
3025
|
markRaw,
|
|
3026
|
+
OwlError,
|
|
3022
3027
|
};
|
|
3023
3028
|
|
|
3024
3029
|
const bdom = { text, createBlock, list, multi, html, toggler, comment };
|
|
@@ -3046,7 +3051,7 @@ function parseXML$1(xml) {
|
|
|
3046
3051
|
}
|
|
3047
3052
|
}
|
|
3048
3053
|
}
|
|
3049
|
-
throw new
|
|
3054
|
+
throw new OwlError(msg);
|
|
3050
3055
|
}
|
|
3051
3056
|
return doc;
|
|
3052
3057
|
}
|
|
@@ -3077,7 +3082,7 @@ class TemplateSet {
|
|
|
3077
3082
|
if (currentAsString === newAsString) {
|
|
3078
3083
|
return;
|
|
3079
3084
|
}
|
|
3080
|
-
throw new
|
|
3085
|
+
throw new OwlError(`Template ${name} already defined with different content`);
|
|
3081
3086
|
}
|
|
3082
3087
|
this.rawTemplates[name] = template;
|
|
3083
3088
|
}
|
|
@@ -3102,7 +3107,7 @@ class TemplateSet {
|
|
|
3102
3107
|
extraInfo = ` (for component "${componentName}")`;
|
|
3103
3108
|
}
|
|
3104
3109
|
catch { }
|
|
3105
|
-
throw new
|
|
3110
|
+
throw new OwlError(`Missing template: "${name}"${extraInfo}`);
|
|
3106
3111
|
}
|
|
3107
3112
|
const isFn = typeof rawTemplate === "function" && !(rawTemplate instanceof Element);
|
|
3108
3113
|
const templateFn = isFn ? rawTemplate : this._compileTemplate(name, rawTemplate);
|
|
@@ -3118,7 +3123,7 @@ class TemplateSet {
|
|
|
3118
3123
|
return this.templates[name];
|
|
3119
3124
|
}
|
|
3120
3125
|
_compileTemplate(name, template) {
|
|
3121
|
-
throw new
|
|
3126
|
+
throw new OwlError(`Unable to compile a template. Please use owl full build instead`);
|
|
3122
3127
|
}
|
|
3123
3128
|
callTemplate(owner, subTemplate, ctx, parent, key) {
|
|
3124
3129
|
const template = this.getTemplate(subTemplate);
|
|
@@ -3200,14 +3205,14 @@ let tokenizeString = function (expr) {
|
|
|
3200
3205
|
i++;
|
|
3201
3206
|
cur = expr[i];
|
|
3202
3207
|
if (!cur) {
|
|
3203
|
-
throw new
|
|
3208
|
+
throw new OwlError("Invalid expression");
|
|
3204
3209
|
}
|
|
3205
3210
|
s += cur;
|
|
3206
3211
|
}
|
|
3207
3212
|
i++;
|
|
3208
3213
|
}
|
|
3209
3214
|
if (expr[i] !== start) {
|
|
3210
|
-
throw new
|
|
3215
|
+
throw new OwlError("Invalid expression");
|
|
3211
3216
|
}
|
|
3212
3217
|
s += start;
|
|
3213
3218
|
if (start === "`") {
|
|
@@ -3314,7 +3319,7 @@ function tokenize(expr) {
|
|
|
3314
3319
|
error = e; // Silence all errors and throw a generic error below
|
|
3315
3320
|
}
|
|
3316
3321
|
if (current.length || error) {
|
|
3317
|
-
throw new
|
|
3322
|
+
throw new OwlError(`Tokenizer error: could not tokenize \`${expr}\``);
|
|
3318
3323
|
}
|
|
3319
3324
|
return result;
|
|
3320
3325
|
}
|
|
@@ -3678,8 +3683,8 @@ class CodeGenerator {
|
|
|
3678
3683
|
define(varName, expr) {
|
|
3679
3684
|
this.addLine(`const ${varName} = ${expr};`);
|
|
3680
3685
|
}
|
|
3681
|
-
insertAnchor(block) {
|
|
3682
|
-
const tag = `block-child-${
|
|
3686
|
+
insertAnchor(block, index = block.children.length) {
|
|
3687
|
+
const tag = `block-child-${index}`;
|
|
3683
3688
|
const anchor = xmlDoc.createElement(tag);
|
|
3684
3689
|
block.insert(anchor);
|
|
3685
3690
|
}
|
|
@@ -3865,7 +3870,7 @@ class CodeGenerator {
|
|
|
3865
3870
|
.slice(1)
|
|
3866
3871
|
.map((m) => {
|
|
3867
3872
|
if (!MODS.has(m)) {
|
|
3868
|
-
throw new
|
|
3873
|
+
throw new OwlError(`Unknown event modifier: '${m}'`);
|
|
3869
3874
|
}
|
|
3870
3875
|
return `"${m}"`;
|
|
3871
3876
|
});
|
|
@@ -3907,13 +3912,18 @@ class CodeGenerator {
|
|
|
3907
3912
|
attrs["block-attribute-" + idx] = attrName;
|
|
3908
3913
|
}
|
|
3909
3914
|
else if (key.startsWith("t-att")) {
|
|
3915
|
+
attrName = key === "t-att" ? null : key.slice(6);
|
|
3910
3916
|
expr = compileExpr(ast.attrs[key]);
|
|
3917
|
+
if (attrName && isProp(ast.tag, attrName)) {
|
|
3918
|
+
// we force a new string or new boolean to bypass the equality check in blockdom when patching same value
|
|
3919
|
+
const C = attrName === "value" ? "String" : "Boolean";
|
|
3920
|
+
expr = `new ${C}(${expr})`;
|
|
3921
|
+
}
|
|
3911
3922
|
const idx = block.insertData(expr, "attr");
|
|
3912
3923
|
if (key === "t-att") {
|
|
3913
3924
|
attrs[`block-attributes`] = String(idx);
|
|
3914
3925
|
}
|
|
3915
3926
|
else {
|
|
3916
|
-
attrName = key.slice(6);
|
|
3917
3927
|
attrs[`block-attribute-${idx}`] = attrName;
|
|
3918
3928
|
}
|
|
3919
3929
|
}
|
|
@@ -4090,9 +4100,18 @@ class CodeGenerator {
|
|
|
4090
4100
|
}
|
|
4091
4101
|
this.insertBlock(blockStr, block, ctx);
|
|
4092
4102
|
}
|
|
4103
|
+
compileTIfBranch(content, block, ctx) {
|
|
4104
|
+
this.target.indentLevel++;
|
|
4105
|
+
let childN = block.children.length;
|
|
4106
|
+
this.compileAST(content, createContext(ctx, { block, index: ctx.index }));
|
|
4107
|
+
if (block.children.length > childN) {
|
|
4108
|
+
// we have some content => need to insert an anchor at correct index
|
|
4109
|
+
this.insertAnchor(block, childN);
|
|
4110
|
+
}
|
|
4111
|
+
this.target.indentLevel--;
|
|
4112
|
+
}
|
|
4093
4113
|
compileTIf(ast, ctx, nextNode) {
|
|
4094
|
-
let { block, forceNewBlock
|
|
4095
|
-
let currentIndex = index;
|
|
4114
|
+
let { block, forceNewBlock } = ctx;
|
|
4096
4115
|
const codeIdx = this.target.code.length;
|
|
4097
4116
|
const isNewBlock = !block || (block.type !== "multi" && forceNewBlock);
|
|
4098
4117
|
if (block) {
|
|
@@ -4102,28 +4121,16 @@ class CodeGenerator {
|
|
|
4102
4121
|
block = this.createBlock(block, "multi", ctx);
|
|
4103
4122
|
}
|
|
4104
4123
|
this.addLine(`if (${compileExpr(ast.condition)}) {`);
|
|
4105
|
-
this.
|
|
4106
|
-
this.insertAnchor(block);
|
|
4107
|
-
const subCtx = createContext(ctx, { block, index: currentIndex });
|
|
4108
|
-
this.compileAST(ast.content, subCtx);
|
|
4109
|
-
this.target.indentLevel--;
|
|
4124
|
+
this.compileTIfBranch(ast.content, block, ctx);
|
|
4110
4125
|
if (ast.tElif) {
|
|
4111
4126
|
for (let clause of ast.tElif) {
|
|
4112
4127
|
this.addLine(`} else if (${compileExpr(clause.condition)}) {`);
|
|
4113
|
-
this.
|
|
4114
|
-
this.insertAnchor(block);
|
|
4115
|
-
const subCtx = createContext(ctx, { block, index: currentIndex });
|
|
4116
|
-
this.compileAST(clause.content, subCtx);
|
|
4117
|
-
this.target.indentLevel--;
|
|
4128
|
+
this.compileTIfBranch(clause.content, block, ctx);
|
|
4118
4129
|
}
|
|
4119
4130
|
}
|
|
4120
4131
|
if (ast.tElse) {
|
|
4121
4132
|
this.addLine(`} else {`);
|
|
4122
|
-
this.
|
|
4123
|
-
this.insertAnchor(block);
|
|
4124
|
-
const subCtx = createContext(ctx, { block, index: currentIndex });
|
|
4125
|
-
this.compileAST(ast.tElse, subCtx);
|
|
4126
|
-
this.target.indentLevel--;
|
|
4133
|
+
this.compileTIfBranch(ast.tElse, block, ctx);
|
|
4127
4134
|
}
|
|
4128
4135
|
this.addLine("}");
|
|
4129
4136
|
if (isNewBlock) {
|
|
@@ -4184,7 +4191,8 @@ class CodeGenerator {
|
|
|
4184
4191
|
this.define(`key${this.target.loopLevel}`, ast.key ? compileExpr(ast.key) : loopVar);
|
|
4185
4192
|
if (this.dev) {
|
|
4186
4193
|
// Throw error on duplicate keys in dev mode
|
|
4187
|
-
this.
|
|
4194
|
+
this.helpers.add("OwlError");
|
|
4195
|
+
this.addLine(`if (keys${block.id}.has(key${this.target.loopLevel})) { throw new OwlError(\`Got duplicate key in t-foreach: \${key${this.target.loopLevel}}\`)}`);
|
|
4188
4196
|
this.addLine(`keys${block.id}.add(key${this.target.loopLevel});`);
|
|
4189
4197
|
}
|
|
4190
4198
|
let id;
|
|
@@ -4398,7 +4406,7 @@ class CodeGenerator {
|
|
|
4398
4406
|
value = `bind(ctx, ${value || undefined})`;
|
|
4399
4407
|
}
|
|
4400
4408
|
else {
|
|
4401
|
-
throw new
|
|
4409
|
+
throw new OwlError("Invalid prop suffix");
|
|
4402
4410
|
}
|
|
4403
4411
|
}
|
|
4404
4412
|
name = /^[a-z_]+$/i.test(name) ? name : `'${name}'`;
|
|
@@ -4591,9 +4599,6 @@ class CodeGenerator {
|
|
|
4591
4599
|
}
|
|
4592
4600
|
}
|
|
4593
4601
|
|
|
4594
|
-
// -----------------------------------------------------------------------------
|
|
4595
|
-
// AST Type definition
|
|
4596
|
-
// -----------------------------------------------------------------------------
|
|
4597
4602
|
// -----------------------------------------------------------------------------
|
|
4598
4603
|
// Parser
|
|
4599
4604
|
// -----------------------------------------------------------------------------
|
|
@@ -4702,7 +4707,7 @@ function parseDOMNode(node, ctx) {
|
|
|
4702
4707
|
return null;
|
|
4703
4708
|
}
|
|
4704
4709
|
if (tagName.startsWith("block-")) {
|
|
4705
|
-
throw new
|
|
4710
|
+
throw new OwlError(`Invalid tag name: '${tagName}'`);
|
|
4706
4711
|
}
|
|
4707
4712
|
ctx = Object.assign({}, ctx);
|
|
4708
4713
|
if (tagName === "pre") {
|
|
@@ -4721,14 +4726,14 @@ function parseDOMNode(node, ctx) {
|
|
|
4721
4726
|
const value = node.getAttribute(attr);
|
|
4722
4727
|
if (attr.startsWith("t-on")) {
|
|
4723
4728
|
if (attr === "t-on") {
|
|
4724
|
-
throw new
|
|
4729
|
+
throw new OwlError("Missing event name with t-on directive");
|
|
4725
4730
|
}
|
|
4726
4731
|
on = on || {};
|
|
4727
4732
|
on[attr.slice(5)] = value;
|
|
4728
4733
|
}
|
|
4729
4734
|
else if (attr.startsWith("t-model")) {
|
|
4730
4735
|
if (!["input", "select", "textarea"].includes(tagName)) {
|
|
4731
|
-
throw new
|
|
4736
|
+
throw new OwlError("The t-model directive only works with <input>, <textarea> and <select>");
|
|
4732
4737
|
}
|
|
4733
4738
|
let baseExpr, expr;
|
|
4734
4739
|
if (hasDotAtTheEnd.test(value)) {
|
|
@@ -4742,7 +4747,7 @@ function parseDOMNode(node, ctx) {
|
|
|
4742
4747
|
expr = value.slice(index + 1, -1);
|
|
4743
4748
|
}
|
|
4744
4749
|
else {
|
|
4745
|
-
throw new
|
|
4750
|
+
throw new OwlError(`Invalid t-model expression: "${value}" (it should be assignable)`);
|
|
4746
4751
|
}
|
|
4747
4752
|
const typeAttr = node.getAttribute("type");
|
|
4748
4753
|
const isInput = tagName === "input";
|
|
@@ -4772,11 +4777,11 @@ function parseDOMNode(node, ctx) {
|
|
|
4772
4777
|
}
|
|
4773
4778
|
}
|
|
4774
4779
|
else if (attr.startsWith("block-")) {
|
|
4775
|
-
throw new
|
|
4780
|
+
throw new OwlError(`Invalid attribute: '${attr}'`);
|
|
4776
4781
|
}
|
|
4777
4782
|
else if (attr !== "t-name") {
|
|
4778
4783
|
if (attr.startsWith("t-") && !attr.startsWith("t-att")) {
|
|
4779
|
-
throw new
|
|
4784
|
+
throw new OwlError(`Unknown QWeb directive: '${attr}'`);
|
|
4780
4785
|
}
|
|
4781
4786
|
const tModel = ctx.tModelInfo;
|
|
4782
4787
|
if (tModel && ["t-att-value", "t-attf-value"].includes(attr)) {
|
|
@@ -4827,7 +4832,7 @@ function parseTEscNode(node, ctx) {
|
|
|
4827
4832
|
};
|
|
4828
4833
|
}
|
|
4829
4834
|
if (ast.type === 11 /* TComponent */) {
|
|
4830
|
-
throw new
|
|
4835
|
+
throw new OwlError("t-esc is not supported on Component nodes");
|
|
4831
4836
|
}
|
|
4832
4837
|
return tesc;
|
|
4833
4838
|
}
|
|
@@ -4875,7 +4880,7 @@ function parseTForEach(node, ctx) {
|
|
|
4875
4880
|
node.removeAttribute("t-as");
|
|
4876
4881
|
const key = node.getAttribute("t-key");
|
|
4877
4882
|
if (!key) {
|
|
4878
|
-
throw new
|
|
4883
|
+
throw new OwlError(`"Directive t-foreach should always be used with a t-key!" (expression: t-foreach="${collection}" t-as="${elem}")`);
|
|
4879
4884
|
}
|
|
4880
4885
|
node.removeAttribute("t-key");
|
|
4881
4886
|
const memo = node.getAttribute("t-memo") || "";
|
|
@@ -5035,7 +5040,7 @@ function parseComponent(node, ctx) {
|
|
|
5035
5040
|
const firstLetter = name[0];
|
|
5036
5041
|
let isDynamic = node.hasAttribute("t-component");
|
|
5037
5042
|
if (isDynamic && name !== "t") {
|
|
5038
|
-
throw new
|
|
5043
|
+
throw new OwlError(`Directive 't-component' can only be used on <t> nodes (used on a <${name}>)`);
|
|
5039
5044
|
}
|
|
5040
5045
|
if (!(firstLetter === firstLetter.toUpperCase() || isDynamic)) {
|
|
5041
5046
|
return null;
|
|
@@ -5059,7 +5064,7 @@ function parseComponent(node, ctx) {
|
|
|
5059
5064
|
}
|
|
5060
5065
|
else {
|
|
5061
5066
|
const message = directiveErrorMap.get(name.split("-").slice(0, 2).join("-"));
|
|
5062
|
-
throw new
|
|
5067
|
+
throw new OwlError(message || `unsupported directive on Component: ${name}`);
|
|
5063
5068
|
}
|
|
5064
5069
|
}
|
|
5065
5070
|
else {
|
|
@@ -5074,7 +5079,7 @@ function parseComponent(node, ctx) {
|
|
|
5074
5079
|
const slotNodes = Array.from(clone.querySelectorAll("[t-set-slot]"));
|
|
5075
5080
|
for (let slotNode of slotNodes) {
|
|
5076
5081
|
if (slotNode.tagName !== "t") {
|
|
5077
|
-
throw new
|
|
5082
|
+
throw new OwlError(`Directive 't-set-slot' can only be used on <t> nodes (used on a <${slotNode.tagName}>)`);
|
|
5078
5083
|
}
|
|
5079
5084
|
const name = slotNode.getAttribute("t-set-slot");
|
|
5080
5085
|
// check if this is defined in a sub component (in which case it should
|
|
@@ -5239,25 +5244,25 @@ function normalizeTIf(el) {
|
|
|
5239
5244
|
let nattr = (name) => +!!node.getAttribute(name);
|
|
5240
5245
|
if (prevElem && (pattr("t-if") || pattr("t-elif"))) {
|
|
5241
5246
|
if (pattr("t-foreach")) {
|
|
5242
|
-
throw new
|
|
5247
|
+
throw new OwlError("t-if cannot stay at the same level as t-foreach when using t-elif or t-else");
|
|
5243
5248
|
}
|
|
5244
5249
|
if (["t-if", "t-elif", "t-else"].map(nattr).reduce(function (a, b) {
|
|
5245
5250
|
return a + b;
|
|
5246
5251
|
}) > 1) {
|
|
5247
|
-
throw new
|
|
5252
|
+
throw new OwlError("Only one conditional branching directive is allowed per node");
|
|
5248
5253
|
}
|
|
5249
5254
|
// All text (with only spaces) and comment nodes (nodeType 8) between
|
|
5250
5255
|
// branch nodes are removed
|
|
5251
5256
|
let textNode;
|
|
5252
5257
|
while ((textNode = node.previousSibling) !== prevElem) {
|
|
5253
5258
|
if (textNode.nodeValue.trim().length && textNode.nodeType !== 8) {
|
|
5254
|
-
throw new
|
|
5259
|
+
throw new OwlError("text is not allowed between branching directives");
|
|
5255
5260
|
}
|
|
5256
5261
|
textNode.remove();
|
|
5257
5262
|
}
|
|
5258
5263
|
}
|
|
5259
5264
|
else {
|
|
5260
|
-
throw new
|
|
5265
|
+
throw new OwlError("t-elif and t-else directives must be preceded by a t-if or t-elif directive");
|
|
5261
5266
|
}
|
|
5262
5267
|
}
|
|
5263
5268
|
}
|
|
@@ -5273,7 +5278,7 @@ function normalizeTEsc(el) {
|
|
|
5273
5278
|
const elements = [...el.querySelectorAll("[t-esc]")].filter((el) => el.tagName[0] === el.tagName[0].toUpperCase() || el.hasAttribute("t-component"));
|
|
5274
5279
|
for (const el of elements) {
|
|
5275
5280
|
if (el.childNodes.length) {
|
|
5276
|
-
throw new
|
|
5281
|
+
throw new OwlError("Cannot have t-esc on a component that already has content");
|
|
5277
5282
|
}
|
|
5278
5283
|
const value = el.getAttribute("t-esc");
|
|
5279
5284
|
el.removeAttribute("t-esc");
|
|
@@ -5325,7 +5330,7 @@ function parseXML(xml) {
|
|
|
5325
5330
|
}
|
|
5326
5331
|
}
|
|
5327
5332
|
}
|
|
5328
|
-
throw new
|
|
5333
|
+
throw new OwlError(msg);
|
|
5329
5334
|
}
|
|
5330
5335
|
return doc;
|
|
5331
5336
|
}
|
|
@@ -5379,7 +5384,7 @@ const mainEventHandler = (data, ev, currentTarget) => {
|
|
|
5379
5384
|
if (Object.hasOwnProperty.call(data, 0)) {
|
|
5380
5385
|
const handler = data[0];
|
|
5381
5386
|
if (typeof handler !== "function") {
|
|
5382
|
-
throw new
|
|
5387
|
+
throw new OwlError(`Invalid handler (expected a function, received: '${handler}')`);
|
|
5383
5388
|
}
|
|
5384
5389
|
let node = data[1] ? data[1].__owl__ : null;
|
|
5385
5390
|
if (node ? node.status === 1 /* MOUNTED */ : true) {
|
|
@@ -5563,10 +5568,10 @@ class App extends TemplateSet {
|
|
|
5563
5568
|
if (isStatic) {
|
|
5564
5569
|
C = parent.constructor.components[name];
|
|
5565
5570
|
if (!C) {
|
|
5566
|
-
throw new
|
|
5571
|
+
throw new OwlError(`Cannot find the definition of component "${name}"`);
|
|
5567
5572
|
}
|
|
5568
5573
|
else if (!(C.prototype instanceof Component)) {
|
|
5569
|
-
throw new
|
|
5574
|
+
throw new OwlError(`"${name}" is not a Component. It must inherit from the Component class`);
|
|
5570
5575
|
}
|
|
5571
5576
|
}
|
|
5572
5577
|
node = new ComponentNode(C, props, this, ctx, key);
|
|
@@ -5725,6 +5730,7 @@ TemplateSet.prototype._compileTemplate = function _compileTemplate(name, templat
|
|
|
5725
5730
|
exports.App = App;
|
|
5726
5731
|
exports.Component = Component;
|
|
5727
5732
|
exports.EventBus = EventBus;
|
|
5733
|
+
exports.OwlError = OwlError;
|
|
5728
5734
|
exports.__info__ = __info__;
|
|
5729
5735
|
exports.blockDom = blockDom;
|
|
5730
5736
|
exports.loadFile = loadFile;
|
|
@@ -5757,7 +5763,7 @@ exports.whenReady = whenReady;
|
|
|
5757
5763
|
exports.xml = xml;
|
|
5758
5764
|
|
|
5759
5765
|
|
|
5760
|
-
__info__.version = '2.0.0-beta-
|
|
5761
|
-
__info__.date = '2022-
|
|
5762
|
-
__info__.hash = '
|
|
5766
|
+
__info__.version = '2.0.0-beta-15';
|
|
5767
|
+
__info__.date = '2022-07-20T08:02:36.538Z';
|
|
5768
|
+
__info__.hash = '588b655';
|
|
5763
5769
|
__info__.url = 'https://github.com/odoo/owl';
|