The directives t-esc/t-out both discard the ASTs that are not of DomNode
type. Since the directives t-translation and t-translation-context both
creates AST wrappers of type TTranslation and TTranslationContext
respectively, mix t-esc/t-out and t-translation/t-translation-context
does not work. For example parse
<span t-esc="'Hello'" t-translation="off"/>
gives the AST
{
type: ASTType.TEsc,
expr: "'Hello'",
defaultValue: "",
}
This makes the span not been rendered in the end.
We fix that problem.
ARIA attributes containing human-readable text should be translated.
This commit adds human-readable ARIA attributes to the list of the
attributes translated by OWL.
# v2.7.0
- [IMP] runtime/utils: export htmlEscape and add tests
- [FIX] utils: Correct validation of mount target in shadow DOM/iframe
- [IMP] runtime: add markup tag function
markup tag function requires markup awareness to determine whether a
given parameter should be escaped or not.
This implies that pre-escaped content should be properly marked'ed up to
avoid double escaping. Having to manually wrap all calls to escape
with markup is cumbersome and prone to issues (on top of having to be
validated by the security team for no reason).
This commit introduces a markup-aware escape function to resolve those
issues.
Previously, `validateTarget` only checked if the target element or its
host (if it was a ShadowRoot) was directly contained in the document body.
This failed in cases where the target element was nested inside a shadow
DOM, which itself was attached to the document.
This commit introduces a new helper `isAttachedToDocument` that
traverses through parent nodes and shadow roots to ensure that the
target is ultimately attached to the given document.
Additionally, it now throws a clear error if `document.defaultView` is
missing, indicating that the target document is detached or invalid.
This ensures proper validation of mount targets, including complex
scenarios with shadow roots and iframes.
Allows markup to be called as a tag function. The interpolated strings
are then safely escaped for injection in HTML code.
Example usage:
```js
const maliciousInput = "<script>alert('💥💥')</script>";
const value = markup`<b>${maliciousInput}</b>`;
// no problem, maliciousInput is properly escaped
```
This commit removes an AST change during the code generation of slots.
Before, the `compileTSlot` function deleted the `t-props` attribute
directly on `ast.attrs`. This creates wrong code when compiling a
second time as the `t-props` attribute does not exist anymore.
The owl dev info message may be useful, but does not bring that much
value. Also, this is even slightly annoying while debugging odoo, since
it is common to have to go to dev mode, and the message takes some
visual space, which is a distraction. In this commit, we simplify it to
just warn that owl is in dev mode.
A new directive t-tanslation-context and a new family of directives (of the form
t-translation-context-...) are introduced to allow to translate terms in contexts.
Set t-translation-context="fr" on a node makes every call of the translation
function to be done with "fr" as seconde parameter when translating an
attribute/content within that node or its children (if no closer
t-translation-context directive is found).
A directive t-translation-context-attr="fr" can be used on a node to target its
attribute "attr". For example, if a div has an attribute title="a title",
use t-translation-context-title="pt" will make "a title" to be translated
in the context "pt". Note that this takes precedence over any other directive
t-translation-context found on a parent (or the div itself).
The translation function is in charge of the interpretation of the context: OWL
does not associate any meaning with a translation context.
Previously, we made it possible to compile templates ahead of time as
this was required by the owl devtools. Unfortunately, no effort was made
at the time to make this ahead-of-time compiler outside of the owl repo.
This commit refactors the AoT template compiler by splitting it in two
parts: a typescript module that gets bundled in the dist folder that
gives a programmatic way to call the standalone compiler from another
javascript module by importing it, and a command line utility that is
exposed as an executable files available to npm run-script of dependent
modules (configured in the bin field of owl's package.json).
This allows a simple workflow for modules wanting to use owl:
`npm install @odoo/owl`
add a script to your package.json that calls compile_owl_templates on
your source files, eg:
```jsonc
{
// ...
"scripts": {
"build": "compile_owl_templates src -o dist/templates.js"
}
}
```
`npm run build`
Without this, you only get the following error:
```
UncaughtPromiseError > OwlError
Uncaught promise > Invalid prop suffix
OwlError: Invalid prop suffix
```
Which is not helping much to find the problematic code.
The error recovery process is necessary, but is particularly subtle. An
error may occur basically any time some user code is called, which may
leave the internal owl state in an invalid situation.
In this commit, we try to improve Owl behaviour when a crash occurs in a
onMounted hook. Before this commit, it was possible for other component
mounted hooks to be called even though the component was not mounted, or
for willUnmount hooks to be called even though the component was
mounted, but its onMounted hooks had not been called.
All these situations need to be handled when we catch an error. In this
commit, we try to clean more internal state in the error handling code,
to make Owl more consistent.
# 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, add a new configuration on the App: globalValues.
It's a global object of elements available at compilations.
For instance:
```js
const app = new App(SomeComponent, {
globalValues: {
plop: (string: any) => {
steps.push(string);
},
},
});
```
The plop function will be available at the compilation, so it can be
used on the templates :
```xml
<div t-on-click="() => __globals__.plop('click')" class="my-div"/>
```
This commit adds the support for custom directives. To use the
custom directive, an Object of functions needs to be configured on
the owl APP:
```js
new App(..., {
customDirectives: {
test_directive: function (el, value) {
el.setAttribute("t-on-click", value);
return el;
}
}
});
```
The functions will be called when a custom directive with the name of the
function is found. The original element will be replaced with the one
returned by the function.
This :
```xml
<div t-custom-test_directive="click" />
```
will be replace by :
```xml
<div t-on-click="value"/>
```
issue : https://github.com/odoo/owl/issues/1650
If a component onMounted hook causes an error, and an error handler
tries to reinitiate a new correct render, the fiber may be recycled. But
the scheduler removes it from its own set of tasks, so it will be
ignored.
We simply check for this condition before removing it from the tasks
set.
# v2.4.1
- [FIX] app: make subroots more robust
- [IMP] devtools: show objects class name and add special extension cases
- [IMP] devtools: allow expansion of empty class
- [IMP] Bump support for Node 20+
This commit fixes two issues with subroots:
1. creating a subroot create a new component node synchronously. This
would causes issues if the creation was done in the setup of a
component, since in that case, owl would reset the current component to
null right after, which would cause all calls to hooks to fail. This is
fixed by restoring the previous component node right after creating a
root.
2. the destroy method for roots calls the scheduler processTasks.
However, the processTasks method was not safe to reentrant calls,
which would in some cases crashes owl. For example, if a destroy is done
while a new component is mounted, the mount method would be called
twice.
This is fixed by ignoring the processTasks if we are currently
processing tasks. It works because the "for ... of" loop will still
process all new tasks in the current iteration.
# v2.4.0
- [IMP] owl: add basic support for sub roots
- [IMP] make set of timeout-able hooks (and their timeouts) clearer by using a const map
- [IMP] devtools: add support for file urls on chrome
In this commit, we extend the owl App class to support multiple sub
roots. This is useful for situations where we want to mount sub
components in non-managed DOM. This is exactly what the Knowledge app is
doing, with mounting views in an html editor.
Currently, this requires some difficult and fragile hacks, and still,
the result is that it is very easy to mix components from the main App
and a SubApp. But Knowledge does not actually care about creating a sub
app. It only needs the possibility to mount sub components in dynamic
places.
closes#1640
Also unnest the handling of `result` via guard clauses, and generate
messages as close as possible to use site, keeping the error
construction itself where it currently is as the goal is specifically
to point back to the *definition* site for the hook function.
Before this commit, when willStart/willUpdateProps took more than
3s, a console.warn was done. In odoo, when a warning is logged
during a test, the test fails and the build is considered as "in
error".
There's a component that loads several resources (sequentially) in
its onWillStart, which *sometimes* takes more than 3s, making
builds fail non deterministically. Since a recent change (which
adds another call in the problematic onWillStart), the warning gets
logged quite often.
A quick fix is necessary, so we change the warn into a log, which
won't make build fail.
We may consider alternatives in the future though:
- add a parameter to onWillStart, to disable the timeout, or to
specify the delay (which is 3s by default)
- do not warn in test mode
Previously, if you wanted to pass a prop and have it be translated, you
had to either to the translation manually in JS, or use a workaround
with t-set and a body so that Owl would translate it for you, and then
pass the t-set variable as a prop. This is quite inconvenient and is a
common use case.
This commit introduces the `.translate` suffix to solve this issue. When
a prop uses this suffix, it is treated as a string instead of a JS
expression, avoiding the need for quotes as well as their escaping and
allowing extraction tools such as babel to generate a clean string as
the term's translation id. This is also more ergonomic. This suffix is
available for both component props and slot props.
This change will still require some work in Odoo to correctly extract
the terms for props using this suffix.
This commit fixes inline expressions when we have these conditions:
* inline expression that contains an arrow function
* inside the arrow function call a function with multiple arguments (3+)
* the second argument (not the first and not the last) must be a variable
In the `compileExprToArray` we have a code to handle missing tokens in an object
e.g.: {a} (equivalent to {a:a})
When OWL match all 3 conditions listed above we execute the code to handle
the missing tokens and so it alter the tokens and adds a new token:
`{ type: "COLON", value: ":" }`
This result in a Javascript compilation error:
OwlError: Failed to compile template "XXX": missing ) after argument list
To fix the error and avoid execute the code to handle the missing tokens, now,
we track also the parentheses in our local stack.
Previously, there were a few places where the compiler would create
strings from template content and emit them as template literals, but
didn't properly escape characters or character sequences with special
meanings, in particular: backslashes, backticks, and interpolation
sigils.
This commit fixes this in:
- block creation (interpolation sigils were not escaped)
- text node creation (no escaping was performed)
- comment not creation (no escaping was performed)
- default values for t-esc (no escaping was performed)
- body of a t-set (no escaping was performed)
Previously, the async hook warning that's emitted when async hooks take
too long to resolve would be emitted even if the component was
destroyed or cancelled. We used to check if the node's fiber was still
the current fiber as a proxy for cancellation, but this is insufficient
in the case where an app might be destroyed.
This commit also wraps the content of some of the warning tests into a
try/finally block so that a failure of these tests doesn't pollute
setTimeout and console.warn and cause other tests to timeout one after
another.
# v2.2.10
- [IMP] runtime: allow using any class as a type in props validation
- Update reactivity.md
- [IMP] owl-vision: Better snippets
- [FIX] docs: code in example cannot run
Previously, we had a fixed whitelist for types that were allowed during
props validation. The implementation however supports using arbitrary
classes, and in practice it's desirable to do so, and already done when
not using typescript (when using typescript, it will error if the class
is not whitelisted), eg in Odoo, we use "Element" for the arch in the
standard view props, but this causes all view controllers to fail type
checking because it's not whitelisted.
This commit simply replaces existing constructors by a generic
constructor type, and adds a test with a validation success and a test
with a validation failure.
While Sets have better lookup complexity than arrays, because of the
large constant factors, small arrays can perform better than small sets
when checking for inclusion.
In practice, replacing both of the raw types sets with arrays can
improve performance of reactive-heavy workloads by as much as 30%.
Considering the reactivity code is very hot when rendering data-heavy
components, and the low impact on readability of the fix, the
cost-benefit analysis is clearly in favour of making the fix.
A template set can now obtain a template by calling the function
getTemplate (if any) received in the initial config.
The returned value can be an Element instance, a template string, a
function (i.e. a compiled template), or null. In the last case, owl will
look into the template set rawTemplates object.
Before to this commit, the .trim modifier did not work correctly. The value
in the model is always trim at each input event but not the visual value
in the input. This difference between the visual and the model value cause
few strange bug.
After reflection, we think that we always want to be in .lazy when we use .trim.
Because we want to trim the final value during the onchange event and not
at each input event. If we do it at each input event, we can't write more then
one word easily.
So this commit change the behavior of the .trim modifier to always be in .lazy
For some reason the code of parseXML was duplicated, despite being
exactly the same except for some whitespace. Move it out into a common
utils file.
closes#1569
# v2.2.7
- [IMP] types: correctly support `Function` type for props-validation
- [IMP] app: allow to instantiate templates lazily
- [FIX] compiler: compile named slot in t-component in named slot
- [IMP] devtools: update the devtools documentation
- [IMP] owl-vision: syntax scripts, single quotes attributes and slot props highlight and switch below command
- [IMP] doc: improve documentation of useExternalListener
- [FIX] devtools: remove highlights when out of devtools
- [IMP] devtools: highlight component on select
- [ADD] owl-vision: vscode extension initial commit
Previously, having a Function as a type in the static props description
of a component would only work if the component was not the root
component, as the static props description on Component was "any",
whereas the static props description on ComponentConstructor was
"Schema". This meant that static props description on non-root
components was not type-checked, and on root components it was
type-checked only on the mount call.
This commit makes it so that static type description is of type "Schema"
on Component, now causing static props description to be type-checked,
and adds `typeof Function` to the `BaseType` union, which allows
declaring that a component expects a function as a prop.
Closes#1448
With this commit, the templates that are given while instantiating the
App class can be an object with templates not yet parsed (i.e. string,
not Document). This allow to instantiate the App class with templates
that are not yet parsed, and these templates will be parsed only when
needed.
Part of task-id 3601257
Previously, if a t-set-slot was inside a t-component itself inside a
t-set-slot the parser would crash, because the slot is removed from the
template before compiling its content, causing a further check's
assumption to be broken (the t-set-slot remains connected to the
component's xml node)