[REL] v2.5.0

# v2.5.0

 - [IMP] app: add global values at compile time
 - [IMP] parser: add support for custom directives
 - [FIX] runtime: properly handle error caused in mounted hook
This commit is contained in:
Romeo Fragomeli
2024-11-25 10:30:53 +01:00
parent 6b2486473f
commit e6768501cd
4 changed files with 67 additions and 17 deletions
+63 -13
View File
@@ -3227,6 +3227,9 @@ class TemplateSet {
}
}
this.getRawTemplate = config.getTemplate;
this.customDirectives = config.customDirectives || {};
this.runtimeUtils = { ...helpers, __globals__: config.globalValues || {} };
this.hasGlobalValues = Boolean(config.globalValues && Object.keys(config.globalValues).length);
}
static registerTemplate(name, fn) {
globalTemplates[name] = fn;
@@ -3283,7 +3286,7 @@ class TemplateSet {
this.templates[name] = function (context, parent) {
return templates[name].call(this, context, parent);
};
const template = templateFn(this, bdom, helpers);
const template = templateFn(this, bdom, this.runtimeUtils);
this.templates[name] = template;
}
return this.templates[name];
@@ -3334,7 +3337,7 @@ TemplateSet.registerTemplate("__portal__", portalTemplate);
//------------------------------------------------------------------------------
// Misc types, constants and helpers
//------------------------------------------------------------------------------
const RESERVED_WORDS = "true,false,NaN,null,undefined,debugger,console,window,in,instanceof,new,function,return,eval,void,Math,RegExp,Array,Object,Date".split(",");
const RESERVED_WORDS = "true,false,NaN,null,undefined,debugger,console,window,in,instanceof,new,function,return,eval,void,Math,RegExp,Array,Object,Date,__globals__".split(",");
const WORD_REPLACEMENT = Object.assign(Object.create(null), {
and: "&&",
or: "||",
@@ -3806,6 +3809,9 @@ class CodeGenerator {
this.dev = options.dev || false;
this.ast = ast;
this.templateName = options.name;
if (options.hasGlobalValues) {
this.helpers.add("__globals__");
}
}
generateCode() {
const ast = this.ast;
@@ -4844,29 +4850,33 @@ class CodeGenerator {
// Parser
// -----------------------------------------------------------------------------
const cache = new WeakMap();
function parse(xml) {
function parse(xml, customDir) {
const ctx = {
inPreTag: false,
customDirectives: customDir,
};
if (typeof xml === "string") {
const elem = parseXML(`<t>${xml}</t>`).firstChild;
return _parse(elem);
return _parse(elem, ctx);
}
let ast = cache.get(xml);
if (!ast) {
// we clone here the xml to prevent modifying it in place
ast = _parse(xml.cloneNode(true));
ast = _parse(xml.cloneNode(true), ctx);
cache.set(xml, ast);
}
return ast;
}
function _parse(xml) {
function _parse(xml, ctx) {
normalizeXML(xml);
const ctx = { inPreTag: false };
return parseNode(xml, ctx) || { type: 0 /* Text */, value: "" };
}
function parseNode(node, ctx) {
if (!(node instanceof Element)) {
return parseTextCommentNode(node, ctx);
}
return (parseTDebugLog(node, ctx) ||
return (parseTCustom(node, ctx) ||
parseTDebugLog(node, ctx) ||
parseTForEach(node, ctx) ||
parseTIf(node, ctx) ||
parseTPortal(node, ctx) ||
@@ -4908,6 +4918,35 @@ function parseTextCommentNode(node, ctx) {
}
return null;
}
function parseTCustom(node, ctx) {
if (!ctx.customDirectives) {
return null;
}
const nodeAttrsNames = node.getAttributeNames();
for (let attr of nodeAttrsNames) {
if (attr === "t-custom" || attr === "t-custom-") {
throw new OwlError("Missing custom directive name with t-custom directive");
}
if (attr.startsWith("t-custom-")) {
const directiveName = attr.split(".")[0].slice(9);
const customDirective = ctx.customDirectives[directiveName];
if (!customDirective) {
throw new OwlError(`Custom directive "${directiveName}" is not defined`);
}
const value = node.getAttribute(attr);
const modifier = attr.split(".").length > 1 ? attr.split(".")[1] : undefined;
node.removeAttribute(attr);
try {
customDirective(node, value, modifier);
}
catch (error) {
throw new OwlError(`Custom directive "${directiveName}" throw the following error: ${error}`);
}
return parseNode(node, ctx);
}
}
return null;
}
// -----------------------------------------------------------------------------
// debugging
// -----------------------------------------------------------------------------
@@ -5539,9 +5578,11 @@ function normalizeXML(el) {
normalizeTEscTOut(el);
}
function compile(template, options = {}) {
function compile(template, options = {
hasGlobalValues: false,
}) {
// parsing
const ast = parse(template);
const ast = parse(template, options.customDirectives);
// some work
const hasSafeContext = template instanceof Node
? !(template instanceof Element) || template.querySelector("[t-set], [t-call]") === null
@@ -5563,7 +5604,7 @@ function compile(template, options = {}) {
}
// do not modify manually. This file is generated by the release script.
const version = "2.4.1";
const version = "2.5.0";
// -----------------------------------------------------------------------------
// Scheduler
@@ -5642,10 +5683,17 @@ class Scheduler {
if (!hasError) {
fiber.complete();
}
// at this point, the fiber should have been applied to the DOM, so we can
// remove it from the task list. If it is not the case, it means that there
// was an error and an error handler triggered a new rendering that recycled
// the fiber, so in that case, we actually want to keep the fiber around,
// otherwise it will just be ignored.
if (fiber.appliedToDom) {
this.tasks.delete(fiber);
}
}
}
}
// capture the value of requestAnimationFrame as soon as possible, to avoid
// interactions with other code, such as test frameworks that override them
Scheduler.requestAnimationFrame = window.requestAnimationFrame.bind(window);
@@ -6026,12 +6074,14 @@ TemplateSet.prototype._compileTemplate = function _compileTemplate(name, templat
dev: this.dev,
translateFn: this.translateFn,
translatableAttributes: this.translatableAttributes,
customDirectives: this.customDirectives,
hasGlobalValues: this.hasGlobalValues,
});
};
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-10-31T09:42:30.824Z';
__info__.hash = 'b8d09e5';
__info__.date = '2024-11-25T09:30:45.930Z';
__info__.hash = '6b24864';
__info__.url = 'https://github.com/odoo/owl';
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@odoo/owl",
"version": "2.4.1",
"version": "2.5.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@odoo/owl",
"version": "2.4.1",
"version": "2.5.0",
"description": "Odoo Web Library (OWL)",
"main": "dist/owl.cjs.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.
export const version = "2.4.1";
export const version = "2.5.0";