@odoo/owl 3.0.0-alpha.19 → 3.0.0-alpha.20
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/compile_templates.mjs +150 -177
- package/dist/owl.cjs.js +3423 -3452
- package/dist/owl.es.js +3423 -3452
- package/dist/owl.iife.js +3423 -3452
- package/dist/owl.iife.min.js +1 -1
- package/dist/types/compiler/code_generator.d.ts +6 -22
- package/dist/types/compiler/index.d.ts +1 -1
- package/dist/types/compiler/inline_expressions.d.ts +0 -1
- package/dist/types/compiler/parser.d.ts +10 -6
- package/dist/types/owl.d.ts +53 -68
- package/dist/types/runtime/app.d.ts +1 -3
- package/dist/types/runtime/blockdom/index.d.ts +0 -1
- package/dist/types/runtime/index.d.ts +2 -2
- package/dist/types/runtime/plugin_hooks.d.ts +2 -1
- package/dist/types/runtime/plugin_manager.d.ts +2 -2
- package/dist/types/runtime/reactivity/async_computed.d.ts +1 -0
- package/dist/types/runtime/reactivity/computations.d.ts +1 -1
- package/dist/types/runtime/reactivity/proxy.d.ts +0 -1
- package/dist/types/runtime/reactivity/signal.d.ts +13 -8
- package/dist/types/runtime/rendering/template_helpers.d.ts +10 -9
- package/dist/types/runtime/suspense.d.ts +5 -0
- package/dist/types/runtime/template_set.d.ts +2 -5
- package/dist/types/runtime/types.d.ts +10 -5
- package/dist/types/runtime/utils.d.ts +1 -1
- package/package.json +1 -1
|
@@ -328,6 +328,7 @@ function interpolate(s) {
|
|
|
328
328
|
return replaceDynamicParts(s, compileExpr);
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
+
const zero = Symbol("zero");
|
|
331
332
|
const whitespaceRE = /\s+/g;
|
|
332
333
|
// using a non-html document so that <inner/outer>HTML serializes as XML instead
|
|
333
334
|
// of HTML (as we will parse it as xml later)
|
|
@@ -446,10 +447,11 @@ class CodeTarget {
|
|
|
446
447
|
name;
|
|
447
448
|
indentLevel = 0;
|
|
448
449
|
loopLevel = 0;
|
|
450
|
+
loopCtxVars = [];
|
|
451
|
+
tSetVars = new Map();
|
|
449
452
|
code = [];
|
|
450
453
|
hasRoot = false;
|
|
451
|
-
|
|
452
|
-
shouldProtectScope = false;
|
|
454
|
+
needsScopeProtection = false;
|
|
453
455
|
on;
|
|
454
456
|
constructor(name, on) {
|
|
455
457
|
this.name = name;
|
|
@@ -467,13 +469,8 @@ class CodeTarget {
|
|
|
467
469
|
generateCode() {
|
|
468
470
|
let result = [];
|
|
469
471
|
result.push(`function ${this.name}(ctx, node, key = "") {`);
|
|
470
|
-
if (this.
|
|
472
|
+
if (this.needsScopeProtection) {
|
|
471
473
|
result.push(` ctx = Object.create(ctx);`);
|
|
472
|
-
result.push(` ctx[isBoundary] = 1`);
|
|
473
|
-
}
|
|
474
|
-
if (this.hasCache) {
|
|
475
|
-
result.push(` let cache = ctx.cache || {};`);
|
|
476
|
-
result.push(` let nextCache = ctx.cache = {};`);
|
|
477
474
|
}
|
|
478
475
|
for (let line of this.code) {
|
|
479
476
|
result.push(line);
|
|
@@ -506,7 +503,6 @@ const translationRE = /^(\s*)([\s\S]+?)(\s*)$/;
|
|
|
506
503
|
class CodeGenerator {
|
|
507
504
|
blocks = [];
|
|
508
505
|
nextBlockId = 1;
|
|
509
|
-
hasSafeContext;
|
|
510
506
|
isDebug = false;
|
|
511
507
|
targets = [];
|
|
512
508
|
target = new CodeTarget("template");
|
|
@@ -532,7 +528,6 @@ class CodeGenerator {
|
|
|
532
528
|
}
|
|
533
529
|
this.translatableAttributes = [...attrs];
|
|
534
530
|
}
|
|
535
|
-
this.hasSafeContext = options.hasSafeContext || false;
|
|
536
531
|
this.dev = options.dev || false;
|
|
537
532
|
this.ast = ast;
|
|
538
533
|
this.templateName = options.name;
|
|
@@ -549,7 +544,6 @@ class CodeGenerator {
|
|
|
549
544
|
block: null,
|
|
550
545
|
index: 0,
|
|
551
546
|
forceNewBlock: false,
|
|
552
|
-
isLast: true,
|
|
553
547
|
translate: true,
|
|
554
548
|
translationCtx: "",
|
|
555
549
|
tKeyExpr: null,
|
|
@@ -656,39 +650,6 @@ class CodeGenerator {
|
|
|
656
650
|
this.define(block.varName, blockExpr);
|
|
657
651
|
}
|
|
658
652
|
}
|
|
659
|
-
/**
|
|
660
|
-
* Captures variables that are used inside of an expression. This is useful
|
|
661
|
-
* because in compiled code, almost all variables are accessed through the ctx
|
|
662
|
-
* object. In the case of functions, that lookup in the context can be delayed
|
|
663
|
-
* which can cause issues if the value has changed since the function was
|
|
664
|
-
* defined.
|
|
665
|
-
*
|
|
666
|
-
* @param expr the expression to capture
|
|
667
|
-
* @param forceCapture whether the expression should capture its scope even if
|
|
668
|
-
* it doesn't contain a function. Useful when the expression will be used as
|
|
669
|
-
* a function body.
|
|
670
|
-
* @returns a new expression that uses the captured values
|
|
671
|
-
*/
|
|
672
|
-
captureExpression(expr, forceCapture = false) {
|
|
673
|
-
if (!forceCapture && !expr.includes("=>")) {
|
|
674
|
-
return compileExpr(expr);
|
|
675
|
-
}
|
|
676
|
-
const tokens = compileExprToArray(expr);
|
|
677
|
-
const mapping = new Map();
|
|
678
|
-
return tokens
|
|
679
|
-
.map((tok) => {
|
|
680
|
-
if (tok.varName && !tok.isLocal) {
|
|
681
|
-
if (!mapping.has(tok.varName)) {
|
|
682
|
-
const varId = generateId("v");
|
|
683
|
-
mapping.set(tok.varName, varId);
|
|
684
|
-
this.define(varId, tok.value);
|
|
685
|
-
}
|
|
686
|
-
tok.value = mapping.get(tok.varName);
|
|
687
|
-
}
|
|
688
|
-
return tok.value;
|
|
689
|
-
})
|
|
690
|
-
.join("");
|
|
691
|
-
}
|
|
692
653
|
translate(str, translationCtx) {
|
|
693
654
|
const match = translationRE.exec(str);
|
|
694
655
|
return match[1] + this.translateFn(match[2], translationCtx) + match[3];
|
|
@@ -802,7 +763,28 @@ class CodeGenerator {
|
|
|
802
763
|
if (modifiers.length) {
|
|
803
764
|
modifiersCode = `${modifiers.join(",")}, `;
|
|
804
765
|
}
|
|
805
|
-
|
|
766
|
+
const compiled = compileExpr(handler);
|
|
767
|
+
if (!compiled.trim()) {
|
|
768
|
+
return `[${modifiersCode}, ctx]`;
|
|
769
|
+
}
|
|
770
|
+
let hoistedExpr;
|
|
771
|
+
const arrowMatch = compiled.match(/^(\([^)]*\))\s*=>/);
|
|
772
|
+
const bareArrowMatch = !arrowMatch && compiled.match(/^([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=>/);
|
|
773
|
+
if (arrowMatch) {
|
|
774
|
+
const inner = arrowMatch[1].slice(1, -1).trim();
|
|
775
|
+
const rest = compiled.slice(arrowMatch[0].length);
|
|
776
|
+
hoistedExpr = inner ? `(ctx,${inner})=>${rest}` : `(ctx)=>${rest}`;
|
|
777
|
+
}
|
|
778
|
+
else if (bareArrowMatch) {
|
|
779
|
+
const rest = compiled.slice(bareArrowMatch[0].length);
|
|
780
|
+
hoistedExpr = `(ctx,${bareArrowMatch[1]})=>${rest}`;
|
|
781
|
+
}
|
|
782
|
+
else {
|
|
783
|
+
hoistedExpr = `(ctx, ev) => (${compiled}).call(ctx['this'], ev)`;
|
|
784
|
+
}
|
|
785
|
+
const id = generateId("hdlr_fn");
|
|
786
|
+
this.staticDefs.push({ id, expr: hoistedExpr });
|
|
787
|
+
return `[${modifiersCode}${id}, ctx]`;
|
|
806
788
|
}
|
|
807
789
|
compileTDomNode(ast, ctx) {
|
|
808
790
|
let { block, forceNewBlock } = ctx;
|
|
@@ -907,7 +889,7 @@ class CodeGenerator {
|
|
|
907
889
|
let valueCode = `ev.target.${targetAttr}`;
|
|
908
890
|
valueCode = shouldTrim ? `${valueCode}.trim()` : valueCode;
|
|
909
891
|
valueCode = shouldNumberize ? `toNumber(${valueCode})` : valueCode;
|
|
910
|
-
const handler = `[(ev) => { ${exprId}.set(${valueCode}); }]`;
|
|
892
|
+
const handler = `[(ctx, ev) => { ${exprId}.set(${valueCode}); }, ctx]`;
|
|
911
893
|
idx = block.insertData(handler, "hdlr");
|
|
912
894
|
attrs[`block-handler-${idx}`] = eventType;
|
|
913
895
|
}
|
|
@@ -945,7 +927,6 @@ class CodeGenerator {
|
|
|
945
927
|
block,
|
|
946
928
|
index: block.childNumber,
|
|
947
929
|
forceNewBlock: false,
|
|
948
|
-
isLast: ctx.isLast && i === children.length - 1,
|
|
949
930
|
tKeyExpr: ctx.tKeyExpr,
|
|
950
931
|
nameSpace,
|
|
951
932
|
tModelSelectedExpr,
|
|
@@ -975,6 +956,16 @@ class CodeGenerator {
|
|
|
975
956
|
}
|
|
976
957
|
return block.varName;
|
|
977
958
|
}
|
|
959
|
+
compileZero() {
|
|
960
|
+
this.helpers.add("zero");
|
|
961
|
+
const isMultiple = this.slotNames.has(zero);
|
|
962
|
+
this.slotNames.add(zero);
|
|
963
|
+
let key = this.target.loopLevel ? `key${this.target.loopLevel}` : "key";
|
|
964
|
+
if (isMultiple) {
|
|
965
|
+
key = this.generateComponentKey(key);
|
|
966
|
+
}
|
|
967
|
+
return `ctx[zero]?.(node, ${key}) || text("")`;
|
|
968
|
+
}
|
|
978
969
|
compileTOut(ast, ctx) {
|
|
979
970
|
let { block } = ctx;
|
|
980
971
|
if (block) {
|
|
@@ -983,8 +974,7 @@ class CodeGenerator {
|
|
|
983
974
|
block = this.createBlock(block, "html", ctx);
|
|
984
975
|
let blockStr;
|
|
985
976
|
if (ast.expr === "0") {
|
|
986
|
-
this.
|
|
987
|
-
blockStr = `ctx[zero]`;
|
|
977
|
+
blockStr = this.compileZero();
|
|
988
978
|
}
|
|
989
979
|
else if (ast.body) {
|
|
990
980
|
let bodyValue = null;
|
|
@@ -1064,7 +1054,9 @@ class CodeGenerator {
|
|
|
1064
1054
|
block = this.createBlock(block, "list", ctx);
|
|
1065
1055
|
this.target.loopLevel++;
|
|
1066
1056
|
const loopVar = `i${this.target.loopLevel}`;
|
|
1067
|
-
|
|
1057
|
+
const ctxVar = generateId("ctx");
|
|
1058
|
+
this.addLine(`const ${ctxVar} = ctx;`);
|
|
1059
|
+
this.target.loopCtxVars.push(ctxVar);
|
|
1068
1060
|
const vals = `v_block${block.id}`;
|
|
1069
1061
|
const keys = `k_block${block.id}`;
|
|
1070
1062
|
const l = `l_block${block.id}`;
|
|
@@ -1077,17 +1069,18 @@ class CodeGenerator {
|
|
|
1077
1069
|
}
|
|
1078
1070
|
this.addLine(`for (let ${loopVar} = 0; ${loopVar} < ${l}; ${loopVar}++) {`);
|
|
1079
1071
|
this.target.indentLevel++;
|
|
1072
|
+
this.addLine(`let ctx = Object.create(${ctxVar});`);
|
|
1080
1073
|
this.addLine(`ctx[\`${ast.elem}\`] = ${keys}[${loopVar}];`);
|
|
1081
|
-
if (!ast.
|
|
1074
|
+
if (!(ast.noFlags & 1 /* ForEachNoFlag.First */)) {
|
|
1082
1075
|
this.addLine(`ctx[\`${ast.elem}_first\`] = ${loopVar} === 0;`);
|
|
1083
1076
|
}
|
|
1084
|
-
if (!ast.
|
|
1077
|
+
if (!(ast.noFlags & 2 /* ForEachNoFlag.Last */)) {
|
|
1085
1078
|
this.addLine(`ctx[\`${ast.elem}_last\`] = ${loopVar} === ${keys}.length - 1;`);
|
|
1086
1079
|
}
|
|
1087
|
-
if (!ast.
|
|
1080
|
+
if (!(ast.noFlags & 4 /* ForEachNoFlag.Index */)) {
|
|
1088
1081
|
this.addLine(`ctx[\`${ast.elem}_index\`] = ${loopVar};`);
|
|
1089
1082
|
}
|
|
1090
|
-
if (!ast.
|
|
1083
|
+
if (!(ast.noFlags & 8 /* ForEachNoFlag.Value */)) {
|
|
1091
1084
|
this.addLine(`ctx[\`${ast.elem}_value\`] = ${vals}[${loopVar}];`);
|
|
1092
1085
|
}
|
|
1093
1086
|
this.define(`key${this.target.loopLevel}`, ast.key ? compileExpr(ast.key) : loopVar);
|
|
@@ -1097,35 +1090,12 @@ class CodeGenerator {
|
|
|
1097
1090
|
this.addLine(`if (keys${block.id}.has(String(key${this.target.loopLevel}))) { throw new OwlError(\`Got duplicate key in t-foreach: \${key${this.target.loopLevel}}\`)}`);
|
|
1098
1091
|
this.addLine(`keys${block.id}.add(String(key${this.target.loopLevel}));`);
|
|
1099
1092
|
}
|
|
1100
|
-
let id;
|
|
1101
|
-
if (ast.memo) {
|
|
1102
|
-
this.target.hasCache = true;
|
|
1103
|
-
id = generateId();
|
|
1104
|
-
this.define(`memo${id}`, compileExpr(ast.memo));
|
|
1105
|
-
this.define(`vnode${id}`, `cache[key${this.target.loopLevel}];`);
|
|
1106
|
-
this.addLine(`if (vnode${id}) {`);
|
|
1107
|
-
this.target.indentLevel++;
|
|
1108
|
-
this.addLine(`if (shallowEqual(vnode${id}.memo, memo${id})) {`);
|
|
1109
|
-
this.target.indentLevel++;
|
|
1110
|
-
this.addLine(`${c}[${loopVar}] = vnode${id};`);
|
|
1111
|
-
this.addLine(`nextCache[key${this.target.loopLevel}] = vnode${id};`);
|
|
1112
|
-
this.addLine(`continue;`);
|
|
1113
|
-
this.target.indentLevel--;
|
|
1114
|
-
this.addLine("}");
|
|
1115
|
-
this.target.indentLevel--;
|
|
1116
|
-
this.addLine("}");
|
|
1117
|
-
}
|
|
1118
1093
|
const subCtx = createContext(ctx, { block, index: loopVar });
|
|
1119
1094
|
this.compileAST(ast.body, subCtx);
|
|
1120
|
-
if (ast.memo) {
|
|
1121
|
-
this.addLine(`nextCache[key${this.target.loopLevel}] = Object.assign(${c}[${loopVar}], {memo: memo${id}});`);
|
|
1122
|
-
}
|
|
1123
1095
|
this.target.indentLevel--;
|
|
1124
1096
|
this.target.loopLevel--;
|
|
1097
|
+
this.target.loopCtxVars.pop();
|
|
1125
1098
|
this.addLine(`}`);
|
|
1126
|
-
if (!ctx.isLast) {
|
|
1127
|
-
this.addLine(`ctx = ctx.__proto__;`);
|
|
1128
|
-
}
|
|
1129
1099
|
this.insertBlock("l", block, ctx);
|
|
1130
1100
|
return block.varName;
|
|
1131
1101
|
}
|
|
@@ -1163,7 +1133,6 @@ class CodeGenerator {
|
|
|
1163
1133
|
block,
|
|
1164
1134
|
index,
|
|
1165
1135
|
forceNewBlock,
|
|
1166
|
-
isLast: ctx.isLast && i === l - 1,
|
|
1167
1136
|
});
|
|
1168
1137
|
this.compileAST(child, subCtx);
|
|
1169
1138
|
if (forceNewBlock) {
|
|
@@ -1192,11 +1161,9 @@ class CodeGenerator {
|
|
|
1192
1161
|
}
|
|
1193
1162
|
compileTCall(ast, ctx) {
|
|
1194
1163
|
let { block, forceNewBlock } = ctx;
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
this.addLine(`let ${ctxVar} = {this: ${compileExpr(ast.context)}, __owl__: this.__owl__};`);
|
|
1199
|
-
}
|
|
1164
|
+
const attrs = ast.attrs
|
|
1165
|
+
? this.formatPropObject(ast.attrs, ast.attrsTranslationCtx, ctx.translationCtx)
|
|
1166
|
+
: [];
|
|
1200
1167
|
const isDynamic = INTERP_REGEXP.test(ast.name);
|
|
1201
1168
|
const subTemplate = isDynamic ? interpolate(ast.name) : "`" + ast.name + "`";
|
|
1202
1169
|
if (block && !forceNewBlock) {
|
|
@@ -1204,39 +1171,39 @@ class CodeGenerator {
|
|
|
1204
1171
|
}
|
|
1205
1172
|
block = this.createBlock(block, "multi", ctx);
|
|
1206
1173
|
if (ast.body) {
|
|
1207
|
-
this.
|
|
1208
|
-
|
|
1209
|
-
this.
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
if (bl) {
|
|
1213
|
-
this.helpers.add("zero");
|
|
1214
|
-
this.addLine(`${ctxVar}[zero] = ${bl};`);
|
|
1215
|
-
}
|
|
1174
|
+
const name = this.compileInNewTarget("callBody", ast.body, ctx);
|
|
1175
|
+
const zeroStr = generateId("lazyBlock");
|
|
1176
|
+
this.define(zeroStr, `${name}.bind(this, ctx)`);
|
|
1177
|
+
this.helpers.add("zero");
|
|
1178
|
+
attrs.push(`[zero]: ${zeroStr}`);
|
|
1216
1179
|
}
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1180
|
+
let ctxExpr;
|
|
1181
|
+
const ctxString = `{${attrs.join(", ")}}`;
|
|
1182
|
+
if (ast.context) {
|
|
1183
|
+
const dynCtxVar = generateId("ctx");
|
|
1184
|
+
this.addLine(`const ${dynCtxVar} = ${compileExpr(ast.context)};`);
|
|
1185
|
+
if (ast.attrs) {
|
|
1186
|
+
ctxExpr = `Object.assign({}, ${dynCtxVar}${attrs.length ? ", " + ctxString : ""})`;
|
|
1187
|
+
}
|
|
1188
|
+
else {
|
|
1189
|
+
const thisCtx = `{this: ${dynCtxVar}, __owl__: this.__owl__}`;
|
|
1190
|
+
ctxExpr = `Object.assign({}, ${dynCtxVar}, ${thisCtx}${attrs.length ? ", " + ctxString : ""})`;
|
|
1222
1191
|
}
|
|
1223
|
-
this.define(templateVar, subTemplate);
|
|
1224
|
-
this.insertBlock(`call(this, ${templateVar}, ${ctxVar}, node, ${key})`, block, {
|
|
1225
|
-
...ctx,
|
|
1226
|
-
forceNewBlock: !block,
|
|
1227
|
-
});
|
|
1228
1192
|
}
|
|
1229
1193
|
else {
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
if (ast.body && !ctx.isLast) {
|
|
1238
|
-
this.addLine(`${ctxVar} = ${ctxVar}.__proto__;`);
|
|
1194
|
+
if (attrs.length === 0) {
|
|
1195
|
+
ctxExpr = "ctx";
|
|
1196
|
+
}
|
|
1197
|
+
else {
|
|
1198
|
+
ctxExpr = `Object.assign(Object.create(ctx), ${ctxString})`;
|
|
1199
|
+
}
|
|
1239
1200
|
}
|
|
1201
|
+
const key = this.generateComponentKey();
|
|
1202
|
+
this.helpers.add("callTemplate");
|
|
1203
|
+
this.insertBlock(`callTemplate(${subTemplate}, this, app, ${ctxExpr}, node, ${key})`, block, {
|
|
1204
|
+
...ctx,
|
|
1205
|
+
forceNewBlock: !block,
|
|
1206
|
+
});
|
|
1240
1207
|
return block.varName;
|
|
1241
1208
|
}
|
|
1242
1209
|
compileTCallBlock(ast, ctx) {
|
|
@@ -1251,9 +1218,10 @@ class CodeGenerator {
|
|
|
1251
1218
|
return block.varName;
|
|
1252
1219
|
}
|
|
1253
1220
|
compileTSet(ast, ctx) {
|
|
1254
|
-
this.target.shouldProtectScope = true;
|
|
1255
|
-
this.helpers.add("isBoundary").add("withDefault");
|
|
1256
1221
|
const expr = ast.value ? compileExpr(ast.value || "") : "null";
|
|
1222
|
+
const isOuterScope = this.target.loopLevel === 0;
|
|
1223
|
+
const defLevel = this.target.tSetVars.get(ast.name);
|
|
1224
|
+
const isReassignment = defLevel !== undefined && this.target.loopLevel > defLevel;
|
|
1257
1225
|
if (ast.body) {
|
|
1258
1226
|
this.helpers.add("LazyValue");
|
|
1259
1227
|
const bodyAst = { type: 3 /* ASTType.Multi */, content: ast.body };
|
|
@@ -1261,13 +1229,27 @@ class CodeGenerator {
|
|
|
1261
1229
|
let key = this.target.currentKey(ctx);
|
|
1262
1230
|
let value = `new LazyValue(${name}, ctx, this, node, ${key})`;
|
|
1263
1231
|
value = ast.value ? (value ? `withDefault(${expr}, ${value})` : expr) : value;
|
|
1264
|
-
this.
|
|
1232
|
+
this.helpers.add("withDefault");
|
|
1233
|
+
if (isReassignment) {
|
|
1234
|
+
const ctxVar = this.target.loopCtxVars[defLevel];
|
|
1235
|
+
this.addLine(`${ctxVar}[\`${ast.name}\`] = ${value};`);
|
|
1236
|
+
}
|
|
1237
|
+
else if (isOuterScope) {
|
|
1238
|
+
this.target.needsScopeProtection = true;
|
|
1239
|
+
this.addLine(`ctx[\`${ast.name}\`] = ${value};`);
|
|
1240
|
+
this.target.tSetVars.set(ast.name, 0);
|
|
1241
|
+
}
|
|
1242
|
+
else {
|
|
1243
|
+
this.addLine(`ctx[\`${ast.name}\`] = ${value};`);
|
|
1244
|
+
this.target.tSetVars.set(ast.name, this.target.loopLevel);
|
|
1245
|
+
}
|
|
1265
1246
|
}
|
|
1266
1247
|
else {
|
|
1267
1248
|
let value;
|
|
1268
1249
|
if (ast.defaultValue) {
|
|
1269
1250
|
const defaultValue = toStringExpression(ctx.translate ? this.translate(ast.defaultValue, ctx.translationCtx) : ast.defaultValue);
|
|
1270
1251
|
if (ast.value) {
|
|
1252
|
+
this.helpers.add("withDefault");
|
|
1271
1253
|
value = `withDefault(${expr}, ${defaultValue})`;
|
|
1272
1254
|
}
|
|
1273
1255
|
else {
|
|
@@ -1277,8 +1259,19 @@ class CodeGenerator {
|
|
|
1277
1259
|
else {
|
|
1278
1260
|
value = expr;
|
|
1279
1261
|
}
|
|
1280
|
-
|
|
1281
|
-
|
|
1262
|
+
if (isReassignment) {
|
|
1263
|
+
const ctxVar = this.target.loopCtxVars[defLevel];
|
|
1264
|
+
this.addLine(`${ctxVar}["${ast.name}"] = ${value};`);
|
|
1265
|
+
}
|
|
1266
|
+
else if (isOuterScope) {
|
|
1267
|
+
this.target.needsScopeProtection = true;
|
|
1268
|
+
this.addLine(`ctx["${ast.name}"] = ${value};`);
|
|
1269
|
+
this.target.tSetVars.set(ast.name, 0);
|
|
1270
|
+
}
|
|
1271
|
+
else {
|
|
1272
|
+
this.addLine(`ctx["${ast.name}"] = ${value};`);
|
|
1273
|
+
this.target.tSetVars.set(ast.name, this.target.loopLevel);
|
|
1274
|
+
}
|
|
1282
1275
|
}
|
|
1283
1276
|
return null;
|
|
1284
1277
|
}
|
|
@@ -1306,7 +1299,7 @@ class CodeGenerator {
|
|
|
1306
1299
|
value = toStringExpression(this.translateFn(value, attrTranslationCtx));
|
|
1307
1300
|
}
|
|
1308
1301
|
else {
|
|
1309
|
-
value =
|
|
1302
|
+
value = compileExpr(value);
|
|
1310
1303
|
}
|
|
1311
1304
|
if (name.includes(".")) {
|
|
1312
1305
|
let [_name, suffix] = name.split(".");
|
|
@@ -1345,19 +1338,13 @@ class CodeGenerator {
|
|
|
1345
1338
|
// slots
|
|
1346
1339
|
let slotDef = "";
|
|
1347
1340
|
if (ast.slots) {
|
|
1348
|
-
let ctxStr = "ctx";
|
|
1349
|
-
if (this.target.loopLevel || !this.hasSafeContext) {
|
|
1350
|
-
ctxStr = generateId("ctx");
|
|
1351
|
-
this.helpers.add("capture");
|
|
1352
|
-
this.define(ctxStr, `capture(ctx)`);
|
|
1353
|
-
}
|
|
1354
1341
|
let slotStr = [];
|
|
1355
1342
|
for (let slotName in ast.slots) {
|
|
1356
1343
|
const slotAst = ast.slots[slotName];
|
|
1357
1344
|
const params = [];
|
|
1358
1345
|
if (slotAst.content) {
|
|
1359
1346
|
const name = this.compileInNewTarget("slot", slotAst.content, ctx, slotAst.on);
|
|
1360
|
-
params.push(`__render: ${name}.bind(this), __ctx:
|
|
1347
|
+
params.push(`__render: ${name}.bind(this), __ctx: ctx`);
|
|
1361
1348
|
}
|
|
1362
1349
|
const scope = ast.slots[slotName].scope;
|
|
1363
1350
|
if (scope) {
|
|
@@ -1411,9 +1398,10 @@ class CodeGenerator {
|
|
|
1411
1398
|
propList.push(`"${name}"`);
|
|
1412
1399
|
}
|
|
1413
1400
|
}
|
|
1401
|
+
this.helpers.add("createComponent");
|
|
1414
1402
|
this.staticDefs.push({
|
|
1415
1403
|
id,
|
|
1416
|
-
expr: `
|
|
1404
|
+
expr: `createComponent(app, ${ast.isDynamic ? null : expr}, ${!ast.isDynamic}, ${!!ast.slots}, ${!!ast.dynamicProps}, [${propList}])`,
|
|
1417
1405
|
});
|
|
1418
1406
|
if (ast.isDynamic) {
|
|
1419
1407
|
// If the component class changes, this can cause delayed renders to go
|
|
@@ -1515,25 +1503,18 @@ class CodeGenerator {
|
|
|
1515
1503
|
return null;
|
|
1516
1504
|
}
|
|
1517
1505
|
compileTPortal(ast, ctx) {
|
|
1518
|
-
|
|
1519
|
-
this.staticDefs.push({ id: "Portal", expr: `app.Portal` });
|
|
1520
|
-
}
|
|
1506
|
+
this.helpers.add("Portal");
|
|
1521
1507
|
let { block } = ctx;
|
|
1522
1508
|
const name = this.compileInNewTarget("slot", ast.content, ctx);
|
|
1523
|
-
let ctxStr = "ctx";
|
|
1524
|
-
if (this.target.loopLevel || !this.hasSafeContext) {
|
|
1525
|
-
ctxStr = generateId("ctx");
|
|
1526
|
-
this.helpers.add("capture");
|
|
1527
|
-
this.define(ctxStr, `capture(ctx)`);
|
|
1528
|
-
}
|
|
1529
1509
|
let id = generateId("comp");
|
|
1510
|
+
this.helpers.add("createComponent");
|
|
1530
1511
|
this.staticDefs.push({
|
|
1531
1512
|
id,
|
|
1532
|
-
expr: `
|
|
1513
|
+
expr: `createComponent(app, null, false, true, false, false)`,
|
|
1533
1514
|
});
|
|
1534
1515
|
const target = compileExpr(ast.target);
|
|
1535
1516
|
const key = this.generateComponentKey();
|
|
1536
|
-
const blockString = `${id}({target: ${target},slots: {'default': {__render: ${name}.bind(this), __ctx:
|
|
1517
|
+
const blockString = `${id}({target: ${target},slots: {'default': {__render: ${name}.bind(this), __ctx: ctx}}}, ${key}, node, ctx, Portal)`;
|
|
1537
1518
|
if (block) {
|
|
1538
1519
|
this.insertAnchor(block);
|
|
1539
1520
|
}
|
|
@@ -1613,10 +1594,10 @@ function parseNode(node, ctx) {
|
|
|
1613
1594
|
parseTForEach(node, ctx) ||
|
|
1614
1595
|
parseTIf(node, ctx) ||
|
|
1615
1596
|
parseTPortal(node, ctx) ||
|
|
1616
|
-
parseTCall(node, ctx) ||
|
|
1617
|
-
parseTCallBlock(node) ||
|
|
1618
1597
|
parseTTranslation(node, ctx) ||
|
|
1619
1598
|
parseTTranslationContext(node, ctx) ||
|
|
1599
|
+
parseTCall(node, ctx) ||
|
|
1600
|
+
parseTCallBlock(node) ||
|
|
1620
1601
|
parseTKey(node, ctx) ||
|
|
1621
1602
|
parseTOutNode(node, ctx) ||
|
|
1622
1603
|
parseTCallSlot(node, ctx) ||
|
|
@@ -1862,28 +1843,27 @@ function parseTForEach(node, ctx) {
|
|
|
1862
1843
|
throw new OwlError(`"Directive t-foreach should always be used with a t-key!" (expression: t-foreach="${collection}" t-as="${elem}")`);
|
|
1863
1844
|
}
|
|
1864
1845
|
node.removeAttribute("t-key");
|
|
1865
|
-
const memo = node.getAttribute("t-memo") || "";
|
|
1866
|
-
node.removeAttribute("t-memo");
|
|
1867
1846
|
const body = parseNode(node, ctx);
|
|
1868
1847
|
if (!body) {
|
|
1869
1848
|
return null;
|
|
1870
1849
|
}
|
|
1871
1850
|
const hasNoTCall = !html.includes("t-call");
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1851
|
+
let noFlags = 0;
|
|
1852
|
+
if (hasNoTCall && !html.includes(`${elem}_first`))
|
|
1853
|
+
noFlags |= 1 /* ForEachNoFlag.First */;
|
|
1854
|
+
if (hasNoTCall && !html.includes(`${elem}_last`))
|
|
1855
|
+
noFlags |= 2 /* ForEachNoFlag.Last */;
|
|
1856
|
+
if (hasNoTCall && !html.includes(`${elem}_index`))
|
|
1857
|
+
noFlags |= 4 /* ForEachNoFlag.Index */;
|
|
1858
|
+
if (hasNoTCall && !html.includes(`${elem}_value`))
|
|
1859
|
+
noFlags |= 8 /* ForEachNoFlag.Value */;
|
|
1876
1860
|
return {
|
|
1877
1861
|
type: 8 /* ASTType.TForEach */,
|
|
1878
1862
|
collection,
|
|
1879
1863
|
elem,
|
|
1880
1864
|
body,
|
|
1881
|
-
memo,
|
|
1882
1865
|
key,
|
|
1883
|
-
|
|
1884
|
-
hasNoLast,
|
|
1885
|
-
hasNoIndex,
|
|
1886
|
-
hasNoValue,
|
|
1866
|
+
noFlags,
|
|
1887
1867
|
};
|
|
1888
1868
|
}
|
|
1889
1869
|
function parseTKey(node, ctx) {
|
|
@@ -1913,37 +1893,34 @@ function parseTCall(node, ctx) {
|
|
|
1913
1893
|
if (!node.hasAttribute("t-call")) {
|
|
1914
1894
|
return null;
|
|
1915
1895
|
}
|
|
1896
|
+
if (node.tagName !== "t") {
|
|
1897
|
+
throw new OwlError(`Directive 't-call' can only be used on <t> nodes (used on a <${node.tagName}>)`);
|
|
1898
|
+
}
|
|
1916
1899
|
const subTemplate = node.getAttribute("t-call");
|
|
1917
1900
|
const context = node.getAttribute("t-call-context");
|
|
1918
1901
|
node.removeAttribute("t-call");
|
|
1919
1902
|
node.removeAttribute("t-call-context");
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
content: tcall,
|
|
1933
|
-
scope: null,
|
|
1934
|
-
on: null,
|
|
1935
|
-
attrs: null,
|
|
1936
|
-
attrsTranslationCtx: null,
|
|
1937
|
-
},
|
|
1938
|
-
},
|
|
1939
|
-
};
|
|
1903
|
+
let attrs = null;
|
|
1904
|
+
let attrsTranslationCtx = null;
|
|
1905
|
+
for (let attributeName of node.getAttributeNames()) {
|
|
1906
|
+
const value = node.getAttribute(attributeName);
|
|
1907
|
+
if (attributeName.startsWith("t-translation-context-")) {
|
|
1908
|
+
const attrName = attributeName.slice(22);
|
|
1909
|
+
attrsTranslationCtx = attrsTranslationCtx || {};
|
|
1910
|
+
attrsTranslationCtx[attrName] = value;
|
|
1911
|
+
}
|
|
1912
|
+
else {
|
|
1913
|
+
attrs = attrs || {};
|
|
1914
|
+
attrs[attributeName] = value;
|
|
1940
1915
|
}
|
|
1941
1916
|
}
|
|
1942
|
-
const body =
|
|
1917
|
+
const body = parseChildNodes(node, ctx);
|
|
1943
1918
|
return {
|
|
1944
1919
|
type: 6 /* ASTType.TCall */,
|
|
1945
1920
|
name: subTemplate,
|
|
1946
|
-
|
|
1921
|
+
attrs,
|
|
1922
|
+
attrsTranslationCtx,
|
|
1923
|
+
body,
|
|
1947
1924
|
context,
|
|
1948
1925
|
};
|
|
1949
1926
|
}
|
|
@@ -2385,12 +2362,8 @@ function compile(template, options = {
|
|
|
2385
2362
|
}) {
|
|
2386
2363
|
// parsing
|
|
2387
2364
|
const ast = parse(template, options.customDirectives);
|
|
2388
|
-
// some work
|
|
2389
|
-
const hasSafeContext = template instanceof Node
|
|
2390
|
-
? !(template instanceof Element) || template.querySelector("[t-set], [t-call]") === null
|
|
2391
|
-
: !template.includes("t-set=") && !template.includes("t-call=");
|
|
2392
2365
|
// code generation
|
|
2393
|
-
const codeGenerator = new CodeGenerator(ast,
|
|
2366
|
+
const codeGenerator = new CodeGenerator(ast, options);
|
|
2394
2367
|
const code = codeGenerator.generateCode();
|
|
2395
2368
|
// template function
|
|
2396
2369
|
try {
|