[IMP] update owl to v1.4.0

This commit is contained in:
Géry Debongnie
2021-07-06 10:28:21 +02:00
parent 2a53139313
commit 0f4e20367c
+136 -31
View File
@@ -205,7 +205,7 @@
let tokenizeString = function (expr) {
let s = expr[0];
let start = s;
if (s !== "'" && s !== '"') {
if (s !== "'" && s !== '"' && s !== "`") {
return false;
}
let i = 1;
@@ -227,6 +227,17 @@
throw new Error("Invalid expression");
}
s += start;
if (start === "`") {
return {
type: "TEMPLATE_STRING",
value: s,
replace(replacer) {
return s.replace(/\$\{(.*?)\}/g, (match, group) => {
return "${" + replacer(group) + "}";
});
},
};
}
return { type: "VALUE", value: s };
};
let tokenizeNumber = function (expr) {
@@ -320,6 +331,8 @@
//------------------------------------------------------------------------------
// Expression "evaluator"
//------------------------------------------------------------------------------
const isLeftSeparator = (token) => token && (token.type === "LEFT_BRACE" || token.type === "COMMA");
const isRightSeparator = (token) => token && (token.type === "RIGHT_BRACE" || token.type === "COMMA");
/**
* This is the main function exported by this file. This is the code that will
* process an expression (given as a string) and returns another expression with
@@ -348,13 +361,19 @@
function compileExprToArray(expr, scope) {
scope = Object.create(scope);
const tokens = tokenize(expr);
for (let i = 0; i < tokens.length; i++) {
let i = 0;
while (i < tokens.length) {
let token = tokens[i];
let prevToken = tokens[i - 1];
let nextToken = tokens[i + 1];
let isVar = token.type === "SYMBOL" && !RESERVED_WORDS.includes(token.value);
if (token.type === "SYMBOL" && !RESERVED_WORDS.includes(token.value)) {
if (prevToken) {
// normalize missing tokens: {a} should be equivalent to {a:a}
if (isLeftSeparator(prevToken) && isRightSeparator(nextToken)) {
tokens.splice(i + 1, 0, { type: "COLON", value: ":" }, { ...token });
nextToken = tokens[i + 1];
}
if (prevToken.type === "OPERATOR" && prevToken.value === ".") {
isVar = false;
}
@@ -365,6 +384,9 @@
}
}
}
if (token.type === "TEMPLATE_STRING") {
token.value = token.replace((expr) => compileExpr(expr, scope));
}
if (nextToken && nextToken.type === "OPERATOR" && nextToken.value === "=>") {
if (token.type === "RIGHT_PAREN") {
let j = i - 1;
@@ -390,6 +412,7 @@
token.value = `scope['${token.value}']`;
}
}
i++;
}
return tokens;
}
@@ -1401,20 +1424,38 @@
}
const UTILS = {
zero: Symbol("zero"),
toObj(expr) {
toClassObj(expr) {
const result = {};
if (typeof expr === "string") {
// we transform here a list of classes into an object:
// 'hey you' becomes {hey: true, you: true}
expr = expr.trim();
if (!expr) {
return {};
}
let words = expr.split(/\s+/);
let result = {};
for (let i = 0; i < words.length; i++) {
result[words[i]] = true;
}
return result;
}
return expr;
// this is already an object, but we may need to split keys:
// {'a': true, 'b c': true} should become {a: true, b: true, c: true}
for (let key in expr) {
const value = expr[key];
const words = key.split(/\s+/);
for (let word of words) {
result[word] = value;
}
}
return result;
},
combine(context, scope) {
const clone = Object.create(context);
for (let k in scope) {
clone[k] = scope[k];
}
return clone;
},
shallowEqual,
addNameSpace(vnode) {
@@ -1832,13 +1873,21 @@
}
}
if (node.nodeName !== "t" || node.hasAttribute("t-tag")) {
let nodeID = this._compileGenericNode(node, ctx, withHandlers);
ctx = ctx.withParent(nodeID);
let nodeHooks = {};
let addNodeHook = function (hook, handler) {
nodeHooks[hook] = nodeHooks[hook] || [];
nodeHooks[hook].push(handler);
};
if (node.tagName === "select" && node.hasAttribute("t-att-value")) {
const value = node.getAttribute("t-att-value");
let exprId = ctx.generateID();
ctx.addLine(`let expr${exprId} = ${ctx.formatExpression(value)};`);
let expr = `expr${exprId}`;
node.setAttribute("t-att-value", expr);
addNodeHook("create", `n.elm.value=${expr};`);
}
let nodeID = this._compileGenericNode(node, ctx, withHandlers);
ctx = ctx.withParent(nodeID);
for (let { directive, value, fullName } of validDirectives) {
if (directive.atNodeCreation) {
directive.atNodeCreation({
@@ -1909,16 +1958,18 @@
isProp = key === "selected" || key === "disabled";
break;
case "textarea":
isProp = key === "readonly" || key === "disabled";
isProp = key === "readonly" || key === "disabled" || key === "value";
break;
case "select":
isProp = key === "disabled" || key === "value";
break;
case "button":
case "select":
case "optgroup":
isProp = key === "disabled";
break;
}
if (isProp) {
props.push(`${key}: _${val}`);
props.push(`${key}: ${val}`);
}
}
let classObj = "";
@@ -1953,7 +2004,7 @@
name = '"' + name + '"';
}
attrs.push(`${name}: _${attID}`);
handleProperties(name, attID);
handleProperties(name, `_${attID}`);
}
}
// dynamic attributes
@@ -1963,7 +2014,7 @@
let formattedValue = typeof v === "string" ? ctx.formatExpression(v) : `scope.${v.id}`;
if (attName === "class") {
ctx.rootContext.shouldDefineUtils = true;
formattedValue = `utils.toObj(${formattedValue})`;
formattedValue = `utils.toClassObj(${formattedValue})`;
if (classObj) {
ctx.addLine(`Object.assign(${classObj}, ${formattedValue})`);
}
@@ -1988,9 +2039,15 @@
const attrIndex = attrs.findIndex((att) => att.startsWith(attName + ":"));
attrs.splice(attrIndex, 1);
}
ctx.addLine(`let _${attID} = ${formattedValue};`);
attrs.push(`${attName}: _${attID}`);
handleProperties(attName, attID);
if (node.nodeName === "select" && attName === "value") {
attrs.push(`${attName}: ${v}`);
handleProperties(attName, v);
}
else {
ctx.addLine(`let _${attID} = ${formattedValue};`);
attrs.push(`${attName}: _${attID}`);
handleProperties(attName, "_" + attID);
}
}
}
if (name.startsWith("t-attf-")) {
@@ -2540,6 +2597,7 @@
// we need to capture every variable in it
putInCache = false;
code = ctx.captureExpression(value);
code = `const res = (() => { return ${code} })(); if (typeof res === 'function') { res(e) }`;
}
const modCode = mods.map((mod) => modcodes[mod]).join("");
let handler = `function (e) {if (context.__owl__.status === ${5 /* DESTROYED */}){return}${modCode}${code}}`;
@@ -2647,7 +2705,15 @@
const durations = (styles.transitionDuration || "").split(", ");
const timeout = getTimeout(delays, durations);
if (timeout > 0) {
elm.addEventListener("transitionend", cb, { once: true });
const transitionEndCB = () => {
if (!elm.parentNode)
return;
cb();
browser.clearTimeout(fallbackTimeout);
elm.removeEventListener("transitionend", transitionEndCB);
};
elm.addEventListener("transitionend", transitionEndCB, { once: true });
const fallbackTimeout = browser.setTimeout(transitionEndCB, timeout + 1);
}
else {
cb();
@@ -2818,8 +2884,10 @@
set(mode) {
QWeb.dev = mode === "dev";
if (QWeb.dev) {
const url = `https://github.com/odoo/owl/blob/master/doc/reference/config.md#mode`;
console.warn(`Owl is running in 'dev' mode. This is not suitable for production use. See ${url} for more information.`);
console.info(`Owl is running in 'dev' mode.
This is not suitable for production use.
See https://github.com/odoo/owl/blob/master/doc/reference/config.md#mode for more information.`);
}
else {
console.log(`Owl is now running in 'prod' mode.`);
@@ -3085,7 +3153,19 @@
.map((k) => k + ":" + props[k])
.join(",");
let componentID = ctx.generateID();
const templateKey = ctx.generateTemplateKey();
let hasDefinedKey = false;
let templateKey;
if (node.tagName === "t" && !node.hasAttribute("t-key") && value.match(INTERP_REGEXP)) {
defineComponentKey();
const id = ctx.generateID();
// the ___ is to make sure we have no possible conflict with normal
// template keys
ctx.addLine(`let k${id} = '___' + componentKey${componentID}`);
templateKey = `k${id}`;
}
else {
templateKey = ctx.generateTemplateKey();
}
let ref = node.getAttribute("t-ref");
let refExpr = "";
let refKey = "";
@@ -3130,7 +3210,7 @@
if (tattClass) {
let tattExpr = ctx.formatExpression(tattClass);
if (tattExpr[0] !== "{" || tattExpr[tattExpr.length - 1] !== "}") {
tattExpr = `utils.toObj(${tattExpr})`;
tattExpr = `utils.toClassObj(${tattExpr})`;
}
if (classAttr) {
ctx.addLine(`Object.assign(${classObj}, ${tattExpr})`);
@@ -3167,7 +3247,7 @@
}
if (hasDynamicProps) {
const dynamicProp = ctx.formatExpression(node.getAttribute("t-props"));
ctx.addLine(`let props${componentID} = Object.assign({${propStr}}, ${dynamicProp});`);
ctx.addLine(`let props${componentID} = Object.assign({}, ${dynamicProp}, {${propStr}});`);
}
else {
ctx.addLine(`let props${componentID} = {${propStr}};`);
@@ -3182,7 +3262,7 @@
}
// SLOTS
const hasSlots = node.childNodes.length;
let scope = hasSlots ? `Object.assign(Object.create(context), scope)` : "undefined";
let scope = hasSlots ? `utils.combine(context, scope)` : "undefined";
ctx.addIf(`w${componentID}`);
// need to update component
let styleCode = "";
@@ -3199,9 +3279,15 @@
}
ctx.addElse();
// new component
function defineComponentKey() {
if (!hasDefinedKey) {
const interpValue = ctx.interpolate(value);
ctx.addLine(`let componentKey${componentID} = ${interpValue};`);
hasDefinedKey = true;
}
}
defineComponentKey();
const contextualValue = value.match(INTERP_REGEXP) ? "false" : ctx.formatExpression(value);
const interpValue = ctx.interpolate(value);
ctx.addLine(`let componentKey${componentID} = ${interpValue};`);
ctx.addLine(`let W${componentID} = ${contextualValue} || context.constructor.components[componentKey${componentID}] || QWeb.components[componentKey${componentID}];`);
// maybe only do this in dev mode...
ctx.addLine(`if (!W${componentID}) {throw new Error('Cannot find the definition of component "' + componentKey${componentID} + '"')}`);
@@ -3261,12 +3347,17 @@
}
}
if (clone.childNodes.length) {
let hasContent = false;
const t = clone.ownerDocument.createElement("t");
for (let child of Object.values(clone.childNodes)) {
hasContent =
hasContent || (child instanceof Text ? Boolean(child.textContent.trim().length) : true);
t.appendChild(child);
}
const slotFn = qweb._compile(`slot_default_template`, { elem: t, hasParent: true });
QWeb.slots[`${slotId}_default`] = slotFn;
if (hasContent) {
const slotFn = qweb._compile(`slot_default_template`, { elem: t, hasParent: true });
QWeb.slots[`${slotId}_default`] = slotFn;
}
}
}
ctx.addLine(`let fiber = w${componentID}.__prepare(extra.fiber, ${scope}, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; ${createHook}});`);
@@ -3687,7 +3778,15 @@
this.root.counter = 0;
this.root.error = error;
scheduler.flush();
root.destroy();
// at this point, the state of the application is corrupted and we could
// have a lot of issues or crashes. So we destroy the application in a try
// catch and swallow these errors because the fiber is already in error,
// and this is the actual issue that needs to be solved, not those followup
// errors.
try {
root.destroy();
}
catch (e) { }
}
}
}
@@ -5225,7 +5324,10 @@
const initialParams = this.currentParams;
const result = await this.matchAndApplyRules(path);
if (result.type === "match") {
const finalPath = this.routeToPath(result.route, result.params);
let finalPath = this.routeToPath(result.route, result.params);
if (path.indexOf("?") > -1) {
finalPath += "?" + path.split("?")[1];
}
const isPopStateEvent = ev && ev instanceof PopStateEvent;
if (!isPopStateEvent) {
this.setUrlFromPath(finalPath);
@@ -5326,6 +5428,9 @@
if (route.path === "*") {
return {};
}
if (path.indexOf("?") > -1) {
path = path.split("?")[0];
}
if (path.startsWith("#")) {
path = path.slice(1);
}
@@ -5410,9 +5515,9 @@
exports.utils = utils;
__info__.version = '1.3.2';
__info__.date = '2021-06-18T08:42:12.863Z';
__info__.hash = '2e83c73';
__info__.version = '1.4.0';
__info__.date = '2021-07-06T08:28:15.916Z';
__info__.hash = '6faaa6c';
__info__.url = 'https://github.com/odoo/owl';