From 66a801393fc55c0fc960cd49d77bb56af2533e2f Mon Sep 17 00:00:00 2001 From: Romeo Fragomeli Date: Mon, 17 Jun 2024 15:31:21 +0200 Subject: [PATCH] [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 --- docs/owl.js | 60 +++++++++++++++++++++++++++-------------------- package-lock.json | 2 +- package.json | 2 +- src/version.ts | 2 +- 4 files changed, 37 insertions(+), 29 deletions(-) diff --git a/docs/owl.js b/docs/owl.js index d35fde291..0b67c66d1 100644 --- a/docs/owl.js +++ b/docs/owl.js @@ -2622,7 +2622,7 @@ function wrapError(fn, hookName) { result.catch(() => { }), new Promise((resolve) => setTimeout(() => resolve(TIMEOUT), 3000)), ]).then((res) => { - if (res === TIMEOUT && node.fiber === fiber) { + if (res === TIMEOUT && node.fiber === fiber && node.status <= 2) { console.warn(timeoutError); } }); @@ -3512,7 +3512,7 @@ function compileExprToArray(expr) { const localVars = new Set(); const tokens = tokenize(expr); let i = 0; - let stack = []; // to track last opening [ or { + let stack = []; // to track last opening (, [ or { while (i < tokens.length) { let token = tokens[i]; let prevToken = tokens[i - 1]; @@ -3521,10 +3521,12 @@ function compileExprToArray(expr) { switch (token.type) { case "LEFT_BRACE": case "LEFT_BRACKET": + case "LEFT_PAREN": stack.push(token.type); break; case "RIGHT_BRACE": case "RIGHT_BRACKET": + case "RIGHT_PAREN": stack.pop(); } let isVar = token.type === "SYMBOL" && !RESERVED_WORDS.includes(token.value); @@ -3636,6 +3638,13 @@ function isProp(tag, key) { } 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 // ----------------------------------------------------------------------------- @@ -3816,15 +3825,14 @@ class CodeGenerator { mainCode.push(``); for (let block of this.blocks) { if (block.dom) { - let xmlString = block.asXmlString(); - xmlString = xmlString.replace(/\\/g, "\\\\").replace(/`/g, "\\`"); + let xmlString = toStringExpression(block.asXmlString()); if (block.dynamicTagName) { - xmlString = xmlString.replace(/^<\w+/, `<\${tag || '${block.dom.nodeName}'}`); - xmlString = xmlString.replace(/\w+>$/, `\${tag || '${block.dom.nodeName}'}>`); - mainCode.push(`let ${block.blockName} = tag => createBlock(\`${xmlString}\`);`); + xmlString = xmlString.replace(/^`<\w+/, `\`<\${tag || '${block.dom.nodeName}'}`); + xmlString = xmlString.replace(/\w+>`$/, `\${tag || '${block.dom.nodeName}'}>\``); + mainCode.push(`let ${block.blockName} = tag => createBlock(${xmlString});`); } 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; if (isNewBlock) { block = this.createBlock(block, "comment", ctx); - this.insertBlock(`comment(\`${ast.value}\`)`, block, { + this.insertBlock(`comment(${toStringExpression(ast.value)})`, block, { ...ctx, forceNewBlock: forceNewBlock && !block, }); @@ -4024,7 +4032,7 @@ class CodeGenerator { } if (!block || forceNewBlock) { block = this.createBlock(block, "text", ctx); - this.insertBlock(`text(\`${value}\`)`, block, { + this.insertBlock(`text(${toStringExpression(value)})`, block, { ...ctx, forceNewBlock: forceNewBlock && !block, }); @@ -4245,7 +4253,8 @@ class CodeGenerator { expr = compileExpr(ast.expr); if (ast.defaultValue) { this.helpers.add("withDefault"); - expr = `withDefault(${expr}, \`${ast.defaultValue}\`)`; + // FIXME: defaultValue is not translated + expr = `withDefault(${expr}, ${toStringExpression(ast.defaultValue)})`; } } if (!block || forceNewBlock) { @@ -4498,7 +4507,7 @@ class CodeGenerator { this.addLine(`${ctxVar}[zero] = ${bl};`); } } - const key = `key + \`${this.generateComponentKey()}\``; + const key = this.generateComponentKey(); if (isDynamic) { const templateVar = generateId("template"); if (!this.staticDefs.find((d) => d.id === "call")) { @@ -4550,12 +4559,12 @@ class CodeGenerator { else { let value; 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) { - value = `withDefault(${expr}, \`${defaultValue}\`)`; + value = `withDefault(${expr}, ${defaultValue})`; } else { - value = `\`${defaultValue}\``; + value = defaultValue; } } else { @@ -4566,12 +4575,12 @@ class CodeGenerator { } return null; } - generateComponentKey() { + generateComponentKey(currentKey = "key") { const parts = [generateId("__")]; for (let i = 0; i < this.target.loopLevel; i++) { 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 @@ -4662,7 +4671,6 @@ class CodeGenerator { this.addLine(`${propVar}.slots = markRaw(Object.assign(${slotDef}, ${propVar}.slots))`); } // cmap key - const key = this.generateComponentKey(); let expr; if (ast.isDynamic) { expr = generateId("Comp"); @@ -4678,7 +4686,7 @@ class CodeGenerator { // todo: check the forcenewblock condition this.insertAnchor(block); } - let keyArg = `key + \`${key}\``; + let keyArg = this.generateComponentKey(); if (ctx.tKeyExpr) { keyArg = `${ctx.tKeyExpr} + ${keyArg}`; } @@ -4751,7 +4759,7 @@ class CodeGenerator { } let key = this.target.loopLevel ? `key${this.target.loopLevel}` : "key"; if (isMultiple) { - key = `${key} + \`${this.generateComponentKey()}\``; + key = this.generateComponentKey(key); } const props = ast.attrs ? this.formatPropObject(ast.attrs) : []; const scope = this.getPropString(props, dynProps); @@ -4792,7 +4800,6 @@ class CodeGenerator { } let { block } = ctx; const name = this.compileInNewTarget("slot", ast.content, ctx); - const key = this.generateComponentKey(); let ctxStr = "ctx"; if (this.target.loopLevel || !this.hasSafeContext) { ctxStr = generateId("ctx"); @@ -4805,7 +4812,8 @@ class CodeGenerator { expr: `app.createComponent(null, false, true, false, false)`, }); 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) { this.insertAnchor(block); } @@ -5538,7 +5546,7 @@ function compile(template, options = {}) { } // do not modify manually. This file is generated by the release script. -const version = "2.2.10"; +const version = "2.2.11"; // ----------------------------------------------------------------------------- // 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__.hash = '97b69f1'; +__info__.date = '2024-06-17T13:31:12.099Z'; +__info__.hash = 'e7f405c'; __info__.url = 'https://github.com/odoo/owl'; diff --git a/package-lock.json b/package-lock.json index b9ef8b8be..b2f8799c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@odoo/owl", - "version": "2.2.10", + "version": "2.2.11", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 908aa77c8..971839967 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@odoo/owl", - "version": "2.2.10", + "version": "2.2.11", "description": "Odoo Web Library (OWL)", "main": "dist/owl.cjs.js", "module": "dist/owl.es.js", diff --git a/src/version.ts b/src/version.ts index 4729d577d..3f519f1c5 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1,2 +1,2 @@ // do not modify manually. This file is generated by the release script. -export const version = "2.2.10"; +export const version = "2.2.11";