[REL] v2.2.11

# v2.2.11

 - [FIX] compiler: better support for arrow function and function call
 - [IMP] owl-vision: Autocomplete and added missing owl directives
 - [IMP] index: export batched utility function
 - [FIX] playground: correctly escape backslashes and interpolation sigils
 - [FIX] compiler: correctly escape special characters in template literals
 - [FIX] Typo docs
 - [FIX] runtime: don't emit async hook warnings when cancelled/destroyed
This commit is contained in:
Romeo Fragomeli
2024-06-17 15:31:21 +02:00
parent e7f405cc97
commit 66a801393f
4 changed files with 37 additions and 29 deletions
+34 -26
View File
@@ -2622,7 +2622,7 @@ function wrapError(fn, hookName) {
result.catch(() => { }), result.catch(() => { }),
new Promise((resolve) => setTimeout(() => resolve(TIMEOUT), 3000)), new Promise((resolve) => setTimeout(() => resolve(TIMEOUT), 3000)),
]).then((res) => { ]).then((res) => {
if (res === TIMEOUT && node.fiber === fiber) { if (res === TIMEOUT && node.fiber === fiber && node.status <= 2) {
console.warn(timeoutError); console.warn(timeoutError);
} }
}); });
@@ -3512,7 +3512,7 @@ function compileExprToArray(expr) {
const localVars = new Set(); const localVars = new Set();
const tokens = tokenize(expr); const tokens = tokenize(expr);
let i = 0; let i = 0;
let stack = []; // to track last opening [ or { let stack = []; // to track last opening (, [ or {
while (i < tokens.length) { while (i < tokens.length) {
let token = tokens[i]; let token = tokens[i];
let prevToken = tokens[i - 1]; let prevToken = tokens[i - 1];
@@ -3521,10 +3521,12 @@ function compileExprToArray(expr) {
switch (token.type) { switch (token.type) {
case "LEFT_BRACE": case "LEFT_BRACE":
case "LEFT_BRACKET": case "LEFT_BRACKET":
case "LEFT_PAREN":
stack.push(token.type); stack.push(token.type);
break; break;
case "RIGHT_BRACE": case "RIGHT_BRACE":
case "RIGHT_BRACKET": case "RIGHT_BRACKET":
case "RIGHT_PAREN":
stack.pop(); stack.pop();
} }
let isVar = token.type === "SYMBOL" && !RESERVED_WORDS.includes(token.value); let isVar = token.type === "SYMBOL" && !RESERVED_WORDS.includes(token.value);
@@ -3636,6 +3638,13 @@ function isProp(tag, key) {
} }
return false; return false;
} }
/**
* Returns a template literal that evaluates to str. You can add interpolation
* sigils into the string if required
*/
function toStringExpression(str) {
return `\`${str.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/, "\\${")}\``;
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// BlockDescription // BlockDescription
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -3816,15 +3825,14 @@ class CodeGenerator {
mainCode.push(``); mainCode.push(``);
for (let block of this.blocks) { for (let block of this.blocks) {
if (block.dom) { if (block.dom) {
let xmlString = block.asXmlString(); let xmlString = toStringExpression(block.asXmlString());
xmlString = xmlString.replace(/\\/g, "\\\\").replace(/`/g, "\\`");
if (block.dynamicTagName) { if (block.dynamicTagName) {
xmlString = xmlString.replace(/^<\w+/, `<\${tag || '${block.dom.nodeName}'}`); xmlString = xmlString.replace(/^`<\w+/, `\`<\${tag || '${block.dom.nodeName}'}`);
xmlString = xmlString.replace(/\w+>$/, `\${tag || '${block.dom.nodeName}'}>`); xmlString = xmlString.replace(/\w+>`$/, `\${tag || '${block.dom.nodeName}'}>\``);
mainCode.push(`let ${block.blockName} = tag => createBlock(\`${xmlString}\`);`); mainCode.push(`let ${block.blockName} = tag => createBlock(${xmlString});`);
} }
else { else {
mainCode.push(`let ${block.blockName} = createBlock(\`${xmlString}\`);`); mainCode.push(`let ${block.blockName} = createBlock(${xmlString});`);
} }
} }
} }
@@ -4002,7 +4010,7 @@ class CodeGenerator {
const isNewBlock = !block || forceNewBlock; const isNewBlock = !block || forceNewBlock;
if (isNewBlock) { if (isNewBlock) {
block = this.createBlock(block, "comment", ctx); block = this.createBlock(block, "comment", ctx);
this.insertBlock(`comment(\`${ast.value}\`)`, block, { this.insertBlock(`comment(${toStringExpression(ast.value)})`, block, {
...ctx, ...ctx,
forceNewBlock: forceNewBlock && !block, forceNewBlock: forceNewBlock && !block,
}); });
@@ -4024,7 +4032,7 @@ class CodeGenerator {
} }
if (!block || forceNewBlock) { if (!block || forceNewBlock) {
block = this.createBlock(block, "text", ctx); block = this.createBlock(block, "text", ctx);
this.insertBlock(`text(\`${value}\`)`, block, { this.insertBlock(`text(${toStringExpression(value)})`, block, {
...ctx, ...ctx,
forceNewBlock: forceNewBlock && !block, forceNewBlock: forceNewBlock && !block,
}); });
@@ -4245,7 +4253,8 @@ class CodeGenerator {
expr = compileExpr(ast.expr); expr = compileExpr(ast.expr);
if (ast.defaultValue) { if (ast.defaultValue) {
this.helpers.add("withDefault"); this.helpers.add("withDefault");
expr = `withDefault(${expr}, \`${ast.defaultValue}\`)`; // FIXME: defaultValue is not translated
expr = `withDefault(${expr}, ${toStringExpression(ast.defaultValue)})`;
} }
} }
if (!block || forceNewBlock) { if (!block || forceNewBlock) {
@@ -4498,7 +4507,7 @@ class CodeGenerator {
this.addLine(`${ctxVar}[zero] = ${bl};`); this.addLine(`${ctxVar}[zero] = ${bl};`);
} }
} }
const key = `key + \`${this.generateComponentKey()}\``; const key = this.generateComponentKey();
if (isDynamic) { if (isDynamic) {
const templateVar = generateId("template"); const templateVar = generateId("template");
if (!this.staticDefs.find((d) => d.id === "call")) { if (!this.staticDefs.find((d) => d.id === "call")) {
@@ -4550,12 +4559,12 @@ class CodeGenerator {
else { else {
let value; let value;
if (ast.defaultValue) { if (ast.defaultValue) {
const defaultValue = ctx.translate ? this.translate(ast.defaultValue) : ast.defaultValue; const defaultValue = toStringExpression(ctx.translate ? this.translate(ast.defaultValue) : ast.defaultValue);
if (ast.value) { if (ast.value) {
value = `withDefault(${expr}, \`${defaultValue}\`)`; value = `withDefault(${expr}, ${defaultValue})`;
} }
else { else {
value = `\`${defaultValue}\``; value = defaultValue;
} }
} }
else { else {
@@ -4566,12 +4575,12 @@ class CodeGenerator {
} }
return null; return null;
} }
generateComponentKey() { generateComponentKey(currentKey = "key") {
const parts = [generateId("__")]; const parts = [generateId("__")];
for (let i = 0; i < this.target.loopLevel; i++) { for (let i = 0; i < this.target.loopLevel; i++) {
parts.push(`\${key${i + 1}}`); parts.push(`\${key${i + 1}}`);
} }
return parts.join("__"); return `${currentKey} + \`${parts.join("__")}\``;
} }
/** /**
* Formats a prop name and value into a string suitable to be inserted in the * Formats a prop name and value into a string suitable to be inserted in the
@@ -4662,7 +4671,6 @@ class CodeGenerator {
this.addLine(`${propVar}.slots = markRaw(Object.assign(${slotDef}, ${propVar}.slots))`); this.addLine(`${propVar}.slots = markRaw(Object.assign(${slotDef}, ${propVar}.slots))`);
} }
// cmap key // cmap key
const key = this.generateComponentKey();
let expr; let expr;
if (ast.isDynamic) { if (ast.isDynamic) {
expr = generateId("Comp"); expr = generateId("Comp");
@@ -4678,7 +4686,7 @@ class CodeGenerator {
// todo: check the forcenewblock condition // todo: check the forcenewblock condition
this.insertAnchor(block); this.insertAnchor(block);
} }
let keyArg = `key + \`${key}\``; let keyArg = this.generateComponentKey();
if (ctx.tKeyExpr) { if (ctx.tKeyExpr) {
keyArg = `${ctx.tKeyExpr} + ${keyArg}`; keyArg = `${ctx.tKeyExpr} + ${keyArg}`;
} }
@@ -4751,7 +4759,7 @@ class CodeGenerator {
} }
let key = this.target.loopLevel ? `key${this.target.loopLevel}` : "key"; let key = this.target.loopLevel ? `key${this.target.loopLevel}` : "key";
if (isMultiple) { if (isMultiple) {
key = `${key} + \`${this.generateComponentKey()}\``; key = this.generateComponentKey(key);
} }
const props = ast.attrs ? this.formatPropObject(ast.attrs) : []; const props = ast.attrs ? this.formatPropObject(ast.attrs) : [];
const scope = this.getPropString(props, dynProps); const scope = this.getPropString(props, dynProps);
@@ -4792,7 +4800,6 @@ class CodeGenerator {
} }
let { block } = ctx; let { block } = ctx;
const name = this.compileInNewTarget("slot", ast.content, ctx); const name = this.compileInNewTarget("slot", ast.content, ctx);
const key = this.generateComponentKey();
let ctxStr = "ctx"; let ctxStr = "ctx";
if (this.target.loopLevel || !this.hasSafeContext) { if (this.target.loopLevel || !this.hasSafeContext) {
ctxStr = generateId("ctx"); ctxStr = generateId("ctx");
@@ -4805,7 +4812,8 @@ class CodeGenerator {
expr: `app.createComponent(null, false, true, false, false)`, expr: `app.createComponent(null, false, true, false, false)`,
}); });
const target = compileExpr(ast.target); const target = compileExpr(ast.target);
const blockString = `${id}({target: ${target},slots: {'default': {__render: ${name}.bind(this), __ctx: ${ctxStr}}}}, key + \`${key}\`, node, ctx, Portal)`; const key = this.generateComponentKey();
const blockString = `${id}({target: ${target},slots: {'default': {__render: ${name}.bind(this), __ctx: ${ctxStr}}}}, ${key}, node, ctx, Portal)`;
if (block) { if (block) {
this.insertAnchor(block); this.insertAnchor(block);
} }
@@ -5538,7 +5546,7 @@ function compile(template, options = {}) {
} }
// do not modify manually. This file is generated by the release script. // do not modify manually. This file is generated by the release script.
const version = "2.2.10"; const version = "2.2.11";
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Scheduler // Scheduler
@@ -5964,9 +5972,9 @@ TemplateSet.prototype._compileTemplate = function _compileTemplate(name, templat
}); });
}; };
export { App, Component, EventBus, OwlError, __info__, blockDom, loadFile, markRaw, markup, mount, onError, onMounted, onPatched, onRendered, onWillDestroy, onWillPatch, onWillRender, onWillStart, onWillUnmount, onWillUpdateProps, reactive, status, toRaw, useChildSubEnv, useComponent, useEffect, useEnv, useExternalListener, useRef, useState, useSubEnv, validate, validateType, whenReady, xml }; export { App, Component, EventBus, OwlError, __info__, batched, blockDom, loadFile, markRaw, markup, mount, onError, onMounted, onPatched, onRendered, onWillDestroy, onWillPatch, onWillRender, onWillStart, onWillUnmount, onWillUpdateProps, reactive, status, toRaw, useChildSubEnv, useComponent, useEffect, useEnv, useExternalListener, useRef, useState, useSubEnv, validate, validateType, whenReady, xml };
__info__.date = '2024-04-02T10:25:32.577Z'; __info__.date = '2024-06-17T13:31:12.099Z';
__info__.hash = '97b69f1'; __info__.hash = 'e7f405c';
__info__.url = 'https://github.com/odoo/owl'; __info__.url = 'https://github.com/odoo/owl';
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@odoo/owl", "name": "@odoo/owl",
"version": "2.2.10", "version": "2.2.11",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@odoo/owl", "name": "@odoo/owl",
"version": "2.2.10", "version": "2.2.11",
"description": "Odoo Web Library (OWL)", "description": "Odoo Web Library (OWL)",
"main": "dist/owl.cjs.js", "main": "dist/owl.cjs.js",
"module": "dist/owl.es.js", "module": "dist/owl.es.js",
+1 -1
View File
@@ -1,2 +1,2 @@
// do not modify manually. This file is generated by the release script. // do not modify manually. This file is generated by the release script.
export const version = "2.2.10"; export const version = "2.2.11";