Compare commits

..

1 Commits

Author SHA1 Message Date
Simon Genin (ges) de805d0d80 [FIX/IMP]: t-foreach can interpret any iterable.
t-foreach accepted only arrays and object.
Now, iterables and objects are accepted.

Closes #720
2020-10-13 15:37:24 +02:00
27 changed files with 989 additions and 1069 deletions
-27
View File
@@ -1,27 +0,0 @@
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Node.js CI
on:
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [10.x, 12.x, 14.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npm run test
- run: npm run prettier
+1 -3
View File
@@ -26,6 +26,4 @@ node_modules
# Extras temp file # Extras temp file
/tools/owl.js /tools/owl.js
release-notes.md release-notes.md
.rpt2_cache
+2 -1
View File
@@ -121,7 +121,8 @@ npm install @odoo/owl
If you want to use a simple `<script>` tag, the last release can be downloaded here: If you want to use a simple `<script>` tag, the last release can be downloaded here:
- [owl-1.0.13](https://github.com/odoo/owl/releases/tag/v1.0.13) - [owl-1.0.11.js](https://github.com/odoo/owl/releases/download/v1.0.11/owl.js)
- [owl-1.0.11.min.js](https://github.com/odoo/owl/releases/download/v1.0.11/owl.min.js)
## License ## License
+2 -19
View File
@@ -2,10 +2,9 @@
The Owl framework is designed to work in many situations. However, it is The Owl framework is designed to work in many situations. However, it is
sometimes necessary to customize some behaviour. This is done by using the sometimes necessary to customize some behaviour. This is done by using the
global `config` object. It provides two settings: global `config` object. It currently has one key:
- [`mode`](#mode) (default value: `prod`), - [`mode`](#mode).
- [`enableTransitions`](#enabletransitions) (default value: `true`).
## Mode ## Mode
@@ -26,19 +25,3 @@ So, changing this setting is best done at startup.
An important job done by the `dev` mode is to validate props for each component An important job done by the `dev` mode is to validate props for each component
creation and update. Also, extra props will cause an error. creation and update. Also, extra props will cause an error.
## `enableTransitions`
Transitions are usually nice, but they can cause issues in some specific cases,
such as automated tests. It is uncomfortable having to wait for a transition
to end before moving to the next step.
To solve this issue, Owl can be configured to ignore the `t-transition` directive.
To do that, one only needs to set the `enableTransitions` flag to false:
```js
owl.config.enableTransitions = false;
```
Note that it suffers from the same drawback as the "dev" mode: all compiled
templates, if any, will keep their current behaviours.
-14
View File
@@ -78,20 +78,6 @@ The `t-on` directive allows to prebind its arguments. For example,
Here, `expr` is a valid Owl expression, so it could be `true` or some variable Here, `expr` is a valid Owl expression, so it could be `true` or some variable
from the rendering context. from the rendering context.
### Type Hinting
Note that if you work with Typescript, the `trigger` method is generic on the type of the payload.
You can then describe the type of the event, so you will see typing errors...
```typescript
this.trigger<MyCustomPayload>("my-custom-event", payload);
```
```typescript
myCustomEventHandler(ev: OwlEvent<MyCustomPayload>) { ... }
```
## Inline Event Handlers ## Inline Event Handlers
One can also directly specify inline statements. For example, One can also directly specify inline statements. For example,
-28
View File
@@ -15,7 +15,6 @@
- [Dynamic Attributes](#dynamic-attributes) - [Dynamic Attributes](#dynamic-attributes)
- [Loops](#loops) - [Loops](#loops)
- [Rendering Sub Templates](#rendering-sub-templates) - [Rendering Sub Templates](#rendering-sub-templates)
- [Dynamic Sub Templates](#dynamic-sub-templates)
- [Translations](#translations) - [Translations](#translations)
- [Debugging](#debugging) - [Debugging](#debugging)
@@ -453,17 +452,6 @@ are all equivalent:
If there is no `t-key` directive, Owl will use the index as a default key. If there is no `t-key` directive, Owl will use the index as a default key.
Note: the `t-foreach` directive only accepts arrays (lists) or objects. It does
not work with other iterables, such as `Set`. However, it is only a matter of
using the `...` javascript operator. For example:
```xml
<t t-foreach="...items" t-as="item">...</t>
```
The `...` operator will convert the `Set` (or any other iterables) into a list,
which will work with Owl QWeb.
### Rendering Sub Templates ### Rendering Sub Templates
QWeb templates can be used for top level rendering, but they can also be used QWeb templates can be used for top level rendering, but they can also be used
@@ -521,22 +509,6 @@ This can be used to define variables scoped to a sub template:
<!-- "var" does not exist here --> <!-- "var" does not exist here -->
``` ```
### Dynamic sub templates
The `t-call` directive can also be used to dynamically call a sub template,
using string interpolation. For example:
```xml
<div t-name="main-template">
<t t-call="{{template}}">
<em>content</em>
</t>
</div>
```
Here, the name of the template is obtained from the `template` value in the
template rendering context.
### Translations ### Translations
By default, QWeb specify that templates should be translated. If this behaviour By default, QWeb specify that templates should be translated. If this behaviour
+13 -11
View File
@@ -1,20 +1,23 @@
{ {
"name": "@odoo/owl", "name": "@odoo/owl",
"version": "1.0.13", "version": "1.0.11",
"description": "Odoo Web Library (OWL)", "description": "Odoo Web Library (OWL)",
"main": "dist/owl.cjs.js", "main": "dist/owl.js",
"browser": "dist/owl.iife.js",
"module": "dist/owl.es.js",
"types": "dist/types/index.d.ts", "types": "dist/types/index.d.ts",
"files": [ "files": [
"dist" "dist/types/",
"dist/owl.js",
"dist/owl-iife.js"
], ],
"engines": { "engines": {
"node": ">=10.15.3" "node": ">=10.15.3"
}, },
"scripts": { "scripts": {
"build:js": "tsc --target esnext --module es6 --outDir dist/owl",
"build:bundle": "rollup -c", "build:bundle": "rollup -c",
"build": "npm run build:bundle", "build": "npm run build:js && npm run build:bundle",
"buildcommonjs": "npm run build:js && npm run build:bundle -- -f cjs",
"minify": "uglifyjs dist/owl.js -o dist/owl.min.js --compress --mangle",
"test": "jest", "test": "jest",
"test:watch": "jest --watch", "test:watch": "jest --watch",
"tools:serve": "python3 tools/server.py || python tools/server.py", "tools:serve": "python3 tools/server.py || python tools/server.py",
@@ -22,7 +25,7 @@
"pretools:watch": "npm run build", "pretools:watch": "npm run build",
"tools:watch": "npm-run-all --parallel tools:serve \"build:* -- --watch\"", "tools:watch": "npm-run-all --parallel tools:serve \"build:* -- --watch\"",
"prettier": "prettier {src/*.ts,src/**/*.ts,tests/*.ts,tests/**/*.ts,doc/*.md,doc/**/*.md} --write", "prettier": "prettier {src/*.ts,src/**/*.ts,tests/*.ts,tests/**/*.ts,doc/*.md,doc/**/*.md} --write",
"publish": "npm run build && npm publish", "publish": "npm run build && mv dist/owl.js dist/owl-iife.js && npm run buildcommonjs && npm publish",
"release": "node tools/release.js" "release": "node tools/release.js"
}, },
"repository": { "repository": {
@@ -35,9 +38,9 @@
"url": "https://github.com/odoo/owl/issues" "url": "https://github.com/odoo/owl/issues"
}, },
"homepage": "https://github.com/odoo/owl#readme", "homepage": "https://github.com/odoo/owl#readme",
"dependencies": {},
"devDependencies": { "devDependencies": {
"@types/jest": "^23.3.14", "@types/jest": "^23.3.12",
"@types/node": "^14.11.8",
"chalk": "^3.0.0", "chalk": "^3.0.0",
"cpx": "^1.5.0", "cpx": "^1.5.0",
"git-rev-sync": "^1.12.0", "git-rev-sync": "^1.12.0",
@@ -48,8 +51,7 @@
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"prettier": "^2.0.4", "prettier": "^2.0.4",
"rollup": "^1.6.0", "rollup": "^1.6.0",
"rollup-plugin-terser": "^7.0.2", "rollup-plugin-typescript2": "^0.20.1",
"rollup-plugin-typescript2": "^0.27.3",
"sass": "^1.16.1", "sass": "^1.16.1",
"source-map-support": "^0.5.10", "source-map-support": "^0.5.10",
"ts-jest": "^23.10.5", "ts-jest": "^23.10.5",
+1 -1
View File
@@ -1,6 +1,6 @@
# 🦉 OWL Roadmap 🦉 # 🦉 OWL Roadmap 🦉
- Current version: 1.0.13 - Current version: 1.0.11
- Status: stable - Status: stable
This roadmap is only an attempt at predicting Owl's future. Everything may This roadmap is only an attempt at predicting Owl's future. Everything may
+10 -66
View File
@@ -1,70 +1,14 @@
import pkg from "./package.json"; import { version } from "./package.json";
import git from "git-rev-sync"; import git from "git-rev-sync";
import typescript from 'rollup-plugin-typescript2';
import { terser } from "rollup-plugin-terser";
const name = "owl";
const extend = true;
/**
* Meta data to be added on the __info__ object.
* Used to let external tools know the current owl version.
*/
const outro = `
__info__.version = '${pkg.version}';
__info__.date = '${new Date().toISOString()}';
__info__.hash = '${git.short()}';
__info__.url = 'https://github.com/odoo/owl';
`;
/**
* Generate from a string depicting a path a new path for the minified version.
* @param {string} pkgFileName file name
*/
function generateMinifiedNameFromPkgName(pkgFileName) {
const parts = pkgFileName.split('.');
parts.splice(parts.length - 1, 0, "min");
return parts.join('.');
}
/**
* Get the rollup config based on the arguments
* @param {string} format format of the bundle
* @param {string} generatedFileName generated file name
* @param {boolean} minified should it be minified
*/
function getConfigForFormat(format, generatedFileName, minified = false) {
return {
file: minified ? generateMinifiedNameFromPkgName(generatedFileName) : generatedFileName,
format: format,
name: name,
extend: extend,
outro: outro,
plugins: minified ? [terser()] : [],
indent: ' ', // indent with 4 spaces
};
}
// rollup.config.js
export default { export default {
input: "src/index.ts", input: "dist/owl/index.js",
output: [ output: {
file: "dist/owl.js",
/** format: "iife",
* Read about module formats: name: "owl",
* https://auth0.com/blog/javascript-module-systems-showdown/ extend: true,
* https://medium.com/@kelin2025/so-you-wanna-use-es6-modules-714f48b3a953 outro: `exports.__info__.version = '${version}';\nexports.__info__.date = '${new Date().toISOString()}';\nexports.__info__.hash = '${git.short()}';\nexports.__info__.url = 'https://github.com/odoo/owl';`
*/ }
getConfigForFormat('esm', pkg.module),
getConfigForFormat('esm', pkg.module, true),
getConfigForFormat('cjs', pkg.main),
getConfigForFormat('cjs', pkg.main, true),
getConfigForFormat('iife', pkg.browser),
getConfigForFormat('iife', pkg.browser, true),
],
plugins: [
typescript({
useTsconfigDeclarationDir: true
}),
]
}; };
+4 -4
View File
@@ -433,8 +433,8 @@ export class Component<Props extends {} = any, T extends Env = Env> {
* up to the parent DOM nodes. Thus, it must be called between mounted() and * up to the parent DOM nodes. Thus, it must be called between mounted() and
* willUnmount(). * willUnmount().
*/ */
trigger<T = any>(eventType: string, payload?: T) { trigger(eventType: string, payload?: any) {
this.__trigger<T>(this, eventType, payload); this.__trigger(this, eventType, payload);
} }
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
@@ -512,9 +512,9 @@ export class Component<Props extends {} = any, T extends Env = Env> {
* Private trigger method, allows to choose the component which triggered * Private trigger method, allows to choose the component which triggered
* the event in the first place * the event in the first place
*/ */
__trigger<T>(component: Component, eventType: string, payload?: T) { __trigger(component: Component, eventType: string, payload?: any) {
if (this.el) { if (this.el) {
const ev = new OwlEvent<T>(component, eventType, { const ev = new OwlEvent(component, eventType, {
bubbles: true, bubbles: true,
cancelable: true, cancelable: true,
detail: payload, detail: payload,
+3 -6
View File
@@ -227,9 +227,7 @@ QWeb.addDirective({
if (name.startsWith("t-on-")) { if (name.startsWith("t-on-")) {
events.push([name, value]); events.push([name, value]);
} else if (name === "t-transition") { } else if (name === "t-transition") {
if (QWeb.enableTransitions) { transition = value;
transition = value;
}
} else if (!name.startsWith("t-")) { } else if (!name.startsWith("t-")) {
if (name !== "class" && name !== "style") { if (name !== "class" && name !== "style") {
// this is a prop! // this is a prop!
@@ -436,7 +434,6 @@ QWeb.addDirective({
isInSubComponent = true; isInSubComponent = true;
break; break;
} }
el = el.parentElement;
} }
if (isInSubComponent) { if (isInSubComponent) {
continue; continue;
@@ -449,7 +446,7 @@ QWeb.addDirective({
slotNode.removeAttribute("t-set-slot"); slotNode.removeAttribute("t-set-slot");
slotNode.parentElement!.removeChild(slotNode); slotNode.parentElement!.removeChild(slotNode);
const slotFn = qweb._compile(`slot_${key}_template`, { elem: slotNode, hasParent: true }); const slotFn = qweb._compile(`slot_${key}_template`, slotNode, ctx);
QWeb.slots[`${slotId}_${key}`] = slotFn; QWeb.slots[`${slotId}_${key}`] = slotFn;
} }
} }
@@ -458,7 +455,7 @@ QWeb.addDirective({
for (let child of Object.values(clone.childNodes)) { for (let child of Object.values(clone.childNodes)) {
t.appendChild(child); t.appendChild(child);
} }
const slotFn = qweb._compile(`slot_default_template`, { elem: t, hasParent: true }); const slotFn = qweb._compile(`slot_default_template`, t, ctx);
QWeb.slots[`${slotId}_default`] = slotFn; QWeb.slots[`${slotId}_default`] = slotFn;
} }
} }
-10
View File
@@ -8,7 +8,6 @@ import { QWeb } from "./qweb/index";
interface Config { interface Config {
mode: string; mode: string;
enableTransitions: boolean;
} }
export const config = {} as Config; export const config = {} as Config;
@@ -29,12 +28,3 @@ Object.defineProperty(config, "mode", {
} }
}, },
}); });
Object.defineProperty(config, "enableTransitions", {
get() {
return QWeb.enableTransitions;
},
set(value: boolean) {
QWeb.enableTransitions = value;
},
});
+18 -28
View File
@@ -1,4 +1,4 @@
import { CompilationContext, INTERP_REGEXP } from "./compilation_context"; import { CompilationContext } from "./compilation_context";
import { QWeb } from "./qweb"; import { QWeb } from "./qweb";
import { htmlToVDOM } from "../vdom/html_to_vdom"; import { htmlToVDOM } from "../vdom/html_to_vdom";
import { QWebVar } from "./expression_parser"; import { QWebVar } from "./expression_parser";
@@ -224,36 +224,19 @@ QWeb.addDirective({
ctx.rootContext.shouldDefineScope = true; ctx.rootContext.shouldDefineScope = true;
ctx.rootContext.shouldDefineUtils = true; ctx.rootContext.shouldDefineUtils = true;
const subTemplate = node.getAttribute("t-call")!; const subTemplate = node.getAttribute("t-call")!;
const isDynamic = INTERP_REGEXP.test(subTemplate);
const nodeTemplate = qweb.templates[subTemplate]; const nodeTemplate = qweb.templates[subTemplate];
if (!isDynamic && !nodeTemplate) { if (!nodeTemplate) {
throw new Error(`Cannot find template "${subTemplate}" (t-call)`); throw new Error(`Cannot find template "${subTemplate}" (t-call)`);
} }
// Step 2: compile target template in sub templates // Step 2: compile target template in sub templates
// ------------------------------------------------ // ------------------------------------------------
let subIdstr: string; let subId = qweb.subTemplates[subTemplate];
if (isDynamic) { if (!subId) {
const _id = ctx.generateID(); subId = QWeb.nextId++;
ctx.addLine(`let tname${_id} = ${ctx.interpolate(subTemplate)};`); qweb.subTemplates[subTemplate] = subId;
ctx.addLine(`let tid${_id} = this.subTemplates[tname${_id}];`); const subTemplateFn = qweb._compile(subTemplate, nodeTemplate.elem, ctx, true);
ctx.addIf(`!tid${_id}`); QWeb.subTemplates[subId] = subTemplateFn;
ctx.addLine(`tid${_id} = this.constructor.nextId++;`);
ctx.addLine(`this.subTemplates[tname${_id}] = tid${_id};`);
ctx.addLine(
`this.constructor.subTemplates[tid${_id}] = this._compile(tname${_id}, {hasParent: true, defineKey: true});`
);
ctx.closeIf();
subIdstr = `tid${_id}`;
} else {
let subId = qweb.subTemplates[subTemplate];
if (!subId) {
subId = QWeb.nextId++;
qweb.subTemplates[subTemplate] = subId;
const subTemplateFn = qweb._compile(subTemplate, { hasParent: true, defineKey: true });
QWeb.subTemplates[subId] = subTemplateFn;
}
subIdstr = `'${subId}'`;
} }
// Step 3: compile t-call body if necessary // Step 3: compile t-call body if necessary
@@ -268,13 +251,16 @@ QWeb.addDirective({
for (let attr of ["t-if", "t-else", "t-elif", "t-call"]) { for (let attr of ["t-if", "t-else", "t-elif", "t-call"]) {
nodeCopy.removeAttribute(attr); nodeCopy.removeAttribute(attr);
} }
const parentNode = ctx.parentNode;
ctx.parentNode = "__0";
// this local scope is intended to trap c__0 // this local scope is intended to trap c__0
ctx.addLine(`{`); ctx.addLine(`{`);
ctx.indent(); ctx.indent();
ctx.addLine("let c__0 = [];"); ctx.addLine("let c__0 = [];");
qweb._compileNode(nodeCopy, ctx.subContext("parentNode", "__0")); qweb._compileNode(nodeCopy, ctx);
ctx.rootContext.shouldDefineUtils = true; ctx.rootContext.shouldDefineUtils = true;
ctx.addLine("scope[utils.zero] = c__0;"); ctx.addLine("scope[utils.zero] = c__0;");
ctx.parentNode = parentNode;
ctx.dedent(); ctx.dedent();
ctx.addLine(`}`); ctx.addLine(`}`);
} }
@@ -286,12 +272,12 @@ QWeb.addDirective({
const parentNode = ctx.parentNode ? `c${ctx.parentNode}` : "result"; const parentNode = ctx.parentNode ? `c${ctx.parentNode}` : "result";
const extra = `Object.assign({}, extra, {parentNode: ${parentNode}, parent: ${parentComponent}, key: ${key}})`; const extra = `Object.assign({}, extra, {parentNode: ${parentNode}, parent: ${parentComponent}, key: ${key}})`;
if (ctx.parentNode) { if (ctx.parentNode) {
ctx.addLine(`this.constructor.subTemplates[${subIdstr}].call(this, scope, ${extra});`); ctx.addLine(`this.constructor.subTemplates['${subId}'].call(this, scope, ${extra});`);
} else { } else {
// this is a t-call with no parentnode, we need to extract the result // this is a t-call with no parentnode, we need to extract the result
ctx.rootContext.shouldDefineResult = true; ctx.rootContext.shouldDefineResult = true;
ctx.addLine(`result = []`); ctx.addLine(`result = []`);
ctx.addLine(`this.constructor.subTemplates[${subIdstr}].call(this, scope, ${extra});`); ctx.addLine(`this.constructor.subTemplates['${subId}'].call(this, scope, ${extra});`);
ctx.addLine(`result = result[0]`); ctx.addLine(`result = result[0]`);
} }
@@ -326,9 +312,13 @@ QWeb.addDirective({
let valuesID = ctx.generateID(); let valuesID = ctx.generateID();
ctx.addLine(`let _${keysID} = _${valuesID} = _${arrayID};`); ctx.addLine(`let _${keysID} = _${valuesID} = _${arrayID};`);
ctx.addIf(`!(_${arrayID} instanceof Array)`); ctx.addIf(`!(_${arrayID} instanceof Array)`);
ctx.addIf(`typeof _${arrayID}[Symbol.iterator] === 'function'`);
ctx.addLine(`_${keysID} = [..._${arrayID}];`)
ctx.addElse();
ctx.addLine(`_${keysID} = Object.keys(_${arrayID});`); ctx.addLine(`_${keysID} = Object.keys(_${arrayID});`);
ctx.addLine(`_${valuesID} = Object.values(_${arrayID});`); ctx.addLine(`_${valuesID} = Object.values(_${arrayID});`);
ctx.closeIf(); ctx.closeIf();
ctx.closeIf();
ctx.addLine(`let _length${keysID} = _${keysID}.length;`); ctx.addLine(`let _length${keysID} = _${keysID}.length;`);
let varsID = ctx.startProtectScope(true); let varsID = ctx.startProtectScope(true);
const loopVar = `i${ctx.loopNumber}`; const loopVar = `i${ctx.loopNumber}`;
-3
View File
@@ -205,9 +205,6 @@ QWeb.addDirective({
name: "transition", name: "transition",
priority: 96, priority: 96,
atNodeCreation({ ctx, value, addNodeHook }) { atNodeCreation({ ctx, value, addNodeHook }) {
if (!QWeb.enableTransitions) {
return;
}
ctx.rootContext.shouldDefineUtils = true; ctx.rootContext.shouldDefineUtils = true;
let name = value; let name = value;
const hooks = { const hooks = {
+9 -13
View File
@@ -212,7 +212,6 @@ export class QWeb extends EventBus {
h = h; h = h;
// dev mode enables better error messages or more costly validations // dev mode enables better error messages or more costly validations
static dev: boolean = false; static dev: boolean = false;
static enableTransitions: boolean = true;
// slots contains sub templates defined with t-set inside t-component nodes, and // slots contains sub templates defined with t-set inside t-component nodes, and
// are meant to be used by the t-slot directive. // are meant to be used by the t-slot directive.
@@ -313,7 +312,7 @@ export class QWeb extends EventBus {
const template = { const template = {
elem, elem,
fn: function (this: QWeb, context, extra) { fn: function (this: QWeb, context, extra) {
const compiledFunction = this._compile(name); const compiledFunction = this._compile(name, elem);
template.fn = compiledFunction; template.fn = compiledFunction;
return compiledFunction.call(this, context, extra); return compiledFunction.call(this, context, extra);
}, },
@@ -418,33 +417,30 @@ export class QWeb extends EventBus {
_compile( _compile(
name: string, name: string,
options: { elem: Element,
elem?: Element; parentContext?: CompilationContext,
hasParent?: boolean; defineKey?: boolean
defineKey?: boolean;
} = {}
): CompiledTemplate { ): CompiledTemplate {
const elem = options.elem || this.templates[name].elem;
const isDebug = elem.attributes.hasOwnProperty("t-debug"); const isDebug = elem.attributes.hasOwnProperty("t-debug");
const ctx = new CompilationContext(name); const ctx = new CompilationContext(name);
if (elem.tagName !== "t") { if (elem.tagName !== "t") {
ctx.shouldDefineResult = false; ctx.shouldDefineResult = false;
} }
if (options.hasParent) { if (parentContext) {
ctx.variables = Object.create(null); ctx.variables = Object.create(parentContext.variables);
ctx.parentNode = ctx.generateID(); ctx.parentNode = parentContext.parentNode || ctx.generateID();
ctx.allowMultipleRoots = true; ctx.allowMultipleRoots = true;
ctx.hasParentWidget = true; ctx.hasParentWidget = true;
ctx.shouldDefineResult = false; ctx.shouldDefineResult = false;
ctx.addLine(`let c${ctx.parentNode} = extra.parentNode;`); ctx.addLine(`let c${ctx.parentNode} = extra.parentNode;`);
if (options.defineKey) { if (defineKey) {
ctx.addLine(`let key0 = extra.key || "";`); ctx.addLine(`let key0 = extra.key || "";`);
ctx.hasKey0 = true; ctx.hasKey0 = true;
} }
} }
this._compileNode(elem, ctx); this._compileNode(elem, ctx);
if (!options.hasParent) { if (!parentContext) {
if (ctx.shouldDefineResult) { if (ctx.shouldDefineResult) {
ctx.addLine(`return result;`); ctx.addLine(`return result;`);
} else { } else {
-3
View File
@@ -13,9 +13,6 @@ export function htmlToVDOM(html: string): VNode[] {
function htmlToVNode(node: ChildNode): VNode { function htmlToVNode(node: ChildNode): VNode {
if (!(node instanceof Element)) { if (!(node instanceof Element)) {
if (node instanceof Comment) {
return h("!", node.textContent);
}
return { text: node.textContent! } as VNode; return { text: node.textContent! } as VNode;
} }
const attrs = {}; const attrs = {};
+8 -8
View File
@@ -1,15 +1,15 @@
import { Component, Env } from "../src/component/component"; import { Component, Env } from "../src/component/component";
import { useRef, useState } from "../src/hooks";
import { QWeb } from "../src/qweb/index"; import { QWeb } from "../src/qweb/index";
import { useState, useRef } from "../src/hooks";
import { xml } from "../src/tags"; import { xml } from "../src/tags";
import { import {
makeDeferred, makeDeferred,
makeTestEnv,
makeTestFixture, makeTestFixture,
nextFrame, makeTestEnv,
patchNextFrame, patchNextFrame,
renderToDOM, renderToDOM,
unpatchNextFrame, unpatchNextFrame,
nextTick,
} from "./helpers"; } from "./helpers";
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -420,29 +420,29 @@ describe("animations", () => {
widget.state.flag = true; widget.state.flag = true;
await nextFrame(); await nextTick();
widget.el!.querySelector("span")!.dispatchEvent(new Event("transitionend")); widget.el!.querySelector("span")!.dispatchEvent(new Event("transitionend"));
expect(fixture.innerHTML).toBe('<div><span class="">blue</span></div>'); expect(fixture.innerHTML).toBe('<div><span class="">blue</span></div>');
expect(QWeb.utils.transitionInsert).toBeCalledTimes(1); expect(QWeb.utils.transitionInsert).toBeCalledTimes(1);
widget.state.flag = false; widget.state.flag = false;
await nextFrame(); await nextTick();
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe(
'<div><span class="chimay-leave-active chimay-leave-to" data-owl-key="__3__">blue</span></div>' '<div><span class="chimay-leave-active chimay-leave-to" data-owl-key="__3__">blue</span></div>'
); );
expect(QWeb.utils.transitionInsert).toBeCalledTimes(1); expect(QWeb.utils.transitionInsert).toBeCalledTimes(1);
widget.state.flag = true; widget.state.flag = true;
await nextFrame(); await nextTick();
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe(
'<div><span class="chimay-enter-active chimay-enter-to" data-owl-key="__3__">blue</span></div>' '<div><span class="chimay-enter-active chimay-enter-to" data-owl-key="__3__">blue</span></div>'
); );
expect(QWeb.utils.transitionInsert).toBeCalledTimes(2); expect(QWeb.utils.transitionInsert).toBeCalledTimes(2);
widget.state.flag = false; widget.state.flag = false;
await nextFrame(); await nextTick();
widget.state.flag = true; widget.state.flag = true;
await nextFrame(); await nextTick();
expect(QWeb.utils.transitionInsert).toBeCalledTimes(3); expect(QWeb.utils.transitionInsert).toBeCalledTimes(3);
widget.el!.querySelector("span")!.dispatchEvent(new Event("transitionend")); widget.el!.querySelector("span")!.dispatchEvent(new Event("transitionend"));
@@ -53,8 +53,12 @@ exports[`basic widget properties reconciliation alg works for t-foreach in t-for
if (!_2) { throw new Error('QWeb error: Invalid loop expression')} if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
let _3 = _4 = _2; let _3 = _4 = _2;
if (!(_2 instanceof Array)) { if (!(_2 instanceof Array)) {
_3 = Object.keys(_2); if (typeof _2[Symbol.iterator] === 'function') {
_4 = Object.values(_2); _3 = [..._2];
} else {
_3 = Object.keys(_2);
_4 = Object.values(_2);
}
} }
let _length3 = _3.length; let _length3 = _3.length;
let _origScope5 = scope; let _origScope5 = scope;
@@ -70,8 +74,12 @@ exports[`basic widget properties reconciliation alg works for t-foreach in t-for
if (!_6) { throw new Error('QWeb error: Invalid loop expression')} if (!_6) { throw new Error('QWeb error: Invalid loop expression')}
let _7 = _8 = _6; let _7 = _8 = _6;
if (!(_6 instanceof Array)) { if (!(_6 instanceof Array)) {
_7 = Object.keys(_6); if (typeof _6[Symbol.iterator] === 'function') {
_8 = Object.values(_6); _7 = [..._6];
} else {
_7 = Object.keys(_6);
_8 = Object.values(_6);
}
} }
let _length7 = _7.length; let _length7 = _7.length;
let _origScope9 = scope; let _origScope9 = scope;
@@ -272,8 +280,12 @@ exports[`composition sub components with some state rendered in a loop 1`] = `
if (!_2) { throw new Error('QWeb error: Invalid loop expression')} if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
let _3 = _4 = _2; let _3 = _4 = _2;
if (!(_2 instanceof Array)) { if (!(_2 instanceof Array)) {
_3 = Object.keys(_2); if (typeof _2[Symbol.iterator] === 'function') {
_4 = Object.values(_2); _3 = [..._2];
} else {
_3 = Object.keys(_2);
_4 = Object.values(_2);
}
} }
let _length3 = _3.length; let _length3 = _3.length;
let _origScope5 = scope; let _origScope5 = scope;
@@ -534,11 +546,11 @@ exports[`other directives with t-component slot setted value (with t-set) not ac
w3.__owl__.pvnode = pvnode; w3.__owl__.pvnode = pvnode;
} }
w3.__owl__.parentLastFiberId = extra.fiber.id; w3.__owl__.parentLastFiberId = extra.fiber.id;
let c6 = [], p6 = {key:6}; let c5 = [], p5 = {key:5};
let vn6 = h('p', p6, c6); let vn5 = h('p', p5, c5);
c1.push(vn6); c1.push(vn5);
if (scope.iter != null) { if (scope.iter != null) {
c6.push({text: scope.iter}); c5.push({text: scope.iter});
} }
return vn1; return vn1;
}" }"
@@ -558,8 +570,12 @@ exports[`other directives with t-component t-on expression captured in t-foreach
if (!_2) { throw new Error('QWeb error: Invalid loop expression')} if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
let _3 = _4 = _2; let _3 = _4 = _2;
if (!(_2 instanceof Array)) { if (!(_2 instanceof Array)) {
_3 = Object.keys(_2); if (typeof _2[Symbol.iterator] === 'function') {
_4 = Object.values(_2); _3 = [..._2];
} else {
_3 = Object.keys(_2);
_4 = Object.values(_2);
}
} }
let _length3 = _3.length; let _length3 = _3.length;
let _origScope5 = scope; let _origScope5 = scope;
@@ -600,8 +616,12 @@ exports[`other directives with t-component t-on expression in t-foreach 1`] = `
if (!_2) { throw new Error('QWeb error: Invalid loop expression')} if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
let _3 = _4 = _2; let _3 = _4 = _2;
if (!(_2 instanceof Array)) { if (!(_2 instanceof Array)) {
_3 = Object.keys(_2); if (typeof _2[Symbol.iterator] === 'function') {
_4 = Object.values(_2); _3 = [..._2];
} else {
_3 = Object.keys(_2);
_4 = Object.values(_2);
}
} }
let _length3 = _3.length; let _length3 = _3.length;
let _origScope5 = scope; let _origScope5 = scope;
@@ -652,8 +672,12 @@ exports[`other directives with t-component t-on expression in t-foreach with t-s
if (!_2) { throw new Error('QWeb error: Invalid loop expression')} if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
let _3 = _4 = _2; let _3 = _4 = _2;
if (!(_2 instanceof Array)) { if (!(_2 instanceof Array)) {
_3 = Object.keys(_2); if (typeof _2[Symbol.iterator] === 'function') {
_4 = Object.values(_2); _3 = [..._2];
} else {
_3 = Object.keys(_2);
_4 = Object.values(_2);
}
} }
let _length3 = _3.length; let _length3 = _3.length;
let _origScope5 = scope; let _origScope5 = scope;
@@ -705,8 +729,12 @@ exports[`other directives with t-component t-on method call in t-foreach 1`] = `
if (!_2) { throw new Error('QWeb error: Invalid loop expression')} if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
let _3 = _4 = _2; let _3 = _4 = _2;
if (!(_2 instanceof Array)) { if (!(_2 instanceof Array)) {
_3 = Object.keys(_2); if (typeof _2[Symbol.iterator] === 'function') {
_4 = Object.values(_2); _3 = [..._2];
} else {
_3 = Object.keys(_2);
_4 = Object.values(_2);
}
} }
let _length3 = _3.length; let _length3 = _3.length;
let _origScope5 = scope; let _origScope5 = scope;
@@ -1253,16 +1281,16 @@ exports[`other directives with t-component t-set can't alter from within callee
if (scope.iter != null) { if (scope.iter != null) {
c2.push({text: scope.iter}); c2.push({text: scope.iter});
} }
let _origScope6 = scope; let _origScope4 = scope;
scope = Object.create(scope); scope = Object.create(scope);
scope.__access_mode__ = 'ro'; scope.__access_mode__ = 'ro';
this.constructor.subTemplates['2'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__7__'})); this.constructor.subTemplates['2'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__5__'}));
scope = _origScope6; scope = _origScope4;
let c8 = [], p8 = {key:8}; let c6 = [], p6 = {key:6};
let vn8 = h('p', p8, c8); let vn6 = h('p', p6, c6);
c1.push(vn8); c1.push(vn6);
if (scope.iter != null) { if (scope.iter != null) {
c8.push({text: scope.iter}); c6.push({text: scope.iter});
} }
return vn1; return vn1;
}" }"
@@ -1284,7 +1312,7 @@ exports[`other directives with t-component t-set can't alter in t-call body 1`]
if (scope.iter != null) { if (scope.iter != null) {
c2.push({text: scope.iter}); c2.push({text: scope.iter});
} }
let _origScope6 = scope; let _origScope4 = scope;
scope = Object.create(scope); scope = Object.create(scope);
scope.__access_mode__ = 'ro'; scope.__access_mode__ = 'ro';
{ {
@@ -1293,14 +1321,14 @@ exports[`other directives with t-component t-set can't alter in t-call body 1`]
utils.getScope(scope, 'iter').iter = 'inCall'; utils.getScope(scope, 'iter').iter = 'inCall';
scope[utils.zero] = c__0; scope[utils.zero] = c__0;
} }
this.constructor.subTemplates['2'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__7__'})); this.constructor.subTemplates['2'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__5__'}));
} }
scope = _origScope6; scope = _origScope4;
let c8 = [], p8 = {key:8}; let c6 = [], p6 = {key:6};
let vn8 = h('p', p8, c8); let vn6 = h('p', p6, c6);
c1.push(vn8); c1.push(vn6);
if (scope.iter != null) { if (scope.iter != null) {
c8.push({text: scope.iter}); c6.push({text: scope.iter});
} }
return vn1; return vn1;
}" }"
@@ -1371,8 +1399,12 @@ exports[`other directives with t-component t-set outside modified in t-foreach 1
if (!_2) { throw new Error('QWeb error: Invalid loop expression')} if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
let _3 = _4 = _2; let _3 = _4 = _2;
if (!(_2 instanceof Array)) { if (!(_2 instanceof Array)) {
_3 = Object.keys(_2); if (typeof _2[Symbol.iterator] === 'function') {
_4 = Object.values(_2); _3 = [..._2];
} else {
_3 = Object.keys(_2);
_4 = Object.values(_2);
}
} }
let _length3 = _3.length; let _length3 = _3.length;
let _origScope5 = scope; let _origScope5 = scope;
@@ -1455,11 +1487,11 @@ exports[`random stuff/miscellaneous can inject values in tagged templates 1`] =
let h = this.h; let h = this.h;
let c1 = [], p1 = {key:1}; let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1); let vn1 = h('div', p1, c1);
let _origScope5 = scope; let _origScope4 = scope;
scope = Object.create(scope); scope = Object.create(scope);
scope.__access_mode__ = 'ro'; scope.__access_mode__ = 'ro';
this.constructor.subTemplates['3'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__6__'})); this.constructor.subTemplates['3'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__5__'}));
scope = _origScope5; scope = _origScope4;
return vn1; return vn1;
}" }"
`; `;
@@ -1521,8 +1553,12 @@ exports[`random stuff/miscellaneous t-on with handler bound to dynamic argument
if (!_2) { throw new Error('QWeb error: Invalid loop expression')} if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
let _3 = _4 = _2; let _3 = _4 = _2;
if (!(_2 instanceof Array)) { if (!(_2 instanceof Array)) {
_3 = Object.keys(_2); if (typeof _2[Symbol.iterator] === 'function') {
_4 = Object.values(_2); _3 = [..._2];
} else {
_3 = Object.keys(_2);
_4 = Object.values(_2);
}
} }
let _length3 = _3.length; let _length3 = _3.length;
let _origScope5 = scope; let _origScope5 = scope;
@@ -1574,15 +1610,15 @@ exports[`t-call handlers are properly bound through a t-call 1`] = `
// Template name: \\"sub\\" // Template name: \\"sub\\"
let utils = this.constructor.utils; let utils = this.constructor.utils;
let h = this.h; let h = this.h;
let c2 = extra.parentNode; let c1 = extra.parentNode;
let key0 = extra.key || \\"\\"; let key0 = extra.key || \\"\\";
let c3 = [], p3 = {key:\`\${key0}_3\`,on:{}}; let c2 = [], p2 = {key:\`\${key0}_2\`,on:{}};
let vn3 = h('p', p3, c3); let vn2 = h('p', p2, c2);
c2.push(vn3); c1.push(vn2);
let k4 = \`click__4__\${key0}__\`; let k3 = \`click__3__\${key0}__\`;
extra.handlers[k4] = extra.handlers[k4] || function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['update'](e);}; extra.handlers[k3] = extra.handlers[k3] || function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['update'](e);};
p3.on['click'] = extra.handlers[k4]; p2.on['click'] = extra.handlers[k3];
c3.push({text: \`lucas\`}); c2.push({text: \`lucas\`});
}" }"
`; `;
@@ -1593,14 +1629,14 @@ exports[`t-call handlers with arguments are properly bound through a t-call 1`]
let utils = this.constructor.utils; let utils = this.constructor.utils;
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let c2 = extra.parentNode; let c1 = extra.parentNode;
let key0 = extra.key || \\"\\"; let key0 = extra.key || \\"\\";
let c3 = [], p3 = {key:\`\${key0}_3\`,on:{}}; let c2 = [], p2 = {key:\`\${key0}_2\`,on:{}};
let vn3 = h('p', p3, c3); let vn2 = h('p', p2, c2);
c2.push(vn3); c1.push(vn2);
let args4 = [scope['a']]; let args3 = [scope['a']];
p3.on['click'] = function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['update'](...args4, e);}; p2.on['click'] = function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['update'](...args3, e);};
c3.push({text: \`lucas\`}); c2.push({text: \`lucas\`});
}" }"
`; `;
@@ -1613,32 +1649,32 @@ exports[`t-call parent is set within t-call 1`] = `
let parent = extra.parent; let parent = extra.parent;
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let c2 = extra.parentNode; let c1 = extra.parentNode;
let key0 = extra.key || \\"\\"; let key0 = extra.key || \\"\\";
// Component 'Child' // Component 'Child'
let k4 = \`__4__\${key0}__\`; let k3 = \`__3__\${key0}__\`;
let w3 = k4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k4]] : false; let w2 = k3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k3]] : false;
let props3 = {}; let props2 = {};
if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
w3.destroy(); w2.destroy();
w3 = false; w2 = false;
} }
if (w3) { if (w2) {
w3.__updateProps(props3, extra.fiber, undefined); w2.__updateProps(props2, extra.fiber, undefined);
let pvnode = w3.__owl__.pvnode; let pvnode = w2.__owl__.pvnode;
c2.push(pvnode); c1.push(pvnode);
} else { } else {
let componentKey3 = \`Child\`; let componentKey2 = \`Child\`;
let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| scope['Child']; let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| scope['Child'];
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
w3 = new W3(parent, props3); w2 = new W2(parent, props2);
parent.__owl__.cmap[k4] = w3.__owl__.id; parent.__owl__.cmap[k3] = w2.__owl__.id;
let fiber = w3.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}}); let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}});
c2.push(pvnode); c1.push(pvnode);
w3.__owl__.pvnode = pvnode; w2.__owl__.pvnode = pvnode;
} }
w3.__owl__.parentLastFiberId = extra.fiber.id; w2.__owl__.parentLastFiberId = extra.fiber.id;
}" }"
`; `;
@@ -1797,8 +1833,12 @@ exports[`t-model directive in a t-foreach 1`] = `
if (!_2) { throw new Error('QWeb error: Invalid loop expression')} if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
let _3 = _4 = _2; let _3 = _4 = _2;
if (!(_2 instanceof Array)) { if (!(_2 instanceof Array)) {
_3 = Object.keys(_2); if (typeof _2[Symbol.iterator] === 'function') {
_4 = Object.values(_2); _3 = [..._2];
} else {
_3 = Object.keys(_2);
_4 = Object.values(_2);
}
} }
let _length3 = _3.length; let _length3 = _3.length;
let _origScope5 = scope; let _origScope5 = scope;
@@ -1837,8 +1877,12 @@ exports[`t-model directive in a t-foreach, part 2 1`] = `
if (!_2) { throw new Error('QWeb error: Invalid loop expression')} if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
let _3 = _4 = _2; let _3 = _4 = _2;
if (!(_2 instanceof Array)) { if (!(_2 instanceof Array)) {
_3 = Object.keys(_2); if (typeof _2[Symbol.iterator] === 'function') {
_4 = Object.values(_2); _3 = [..._2];
} else {
_3 = Object.keys(_2);
_4 = Object.values(_2);
}
} }
let _length3 = _3.length; let _length3 = _3.length;
let _origScope5 = scope; let _origScope5 = scope;
+135 -129
View File
@@ -44,23 +44,23 @@ exports[`t-slot directive can define and call slots 2`] = `
) { ) {
// Template name: \\"Dialog\\" // Template name: \\"Dialog\\"
let h = this.h; let h = this.h;
let c6 = [], p6 = {key:6};
let vn6 = h('div', p6, c6);
let c7 = [], p7 = {key:7};
let vn7 = h('div', p7, c7);
c6.push(vn7);
const slot8 = this.constructor.slots[context.__owl__.slotId + '_' + 'header'];
if (slot8) {
slot8.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c7, parent: extra.parent || context}));
}
let c9 = [], p9 = {key:9}; let c9 = [], p9 = {key:9};
let vn9 = h('div', p9, c9); let vn9 = h('div', p9, c9);
let c10 = [], p10 = {key:10}; c6.push(vn9);
let vn10 = h('div', p10, c10); const slot10 = this.constructor.slots[context.__owl__.slotId + '_' + 'footer'];
c9.push(vn10); if (slot10) {
const slot11 = this.constructor.slots[context.__owl__.slotId + '_' + 'header']; slot10.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c9, parent: extra.parent || context}));
if (slot11) {
slot11.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c10, parent: extra.parent || context}));
} }
let c12 = [], p12 = {key:12}; return vn6;
let vn12 = h('div', p12, c12);
c9.push(vn12);
const slot13 = this.constructor.slots[context.__owl__.slotId + '_' + 'footer'];
if (slot13) {
slot13.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c12, parent: extra.parent || context}));
}
return vn9;
}" }"
`; `;
@@ -69,11 +69,11 @@ exports[`t-slot directive can define and call slots 3`] = `
) { ) {
// Template name: \\"slot_header_template\\" // Template name: \\"slot_header_template\\"
let h = this.h; let h = this.h;
let c4 = extra.parentNode; let c1 = extra.parentNode;
let c5 = [], p5 = {key:5}; let c4 = [], p4 = {key:4};
let vn5 = h('span', p5, c5); let vn4 = h('span', p4, c4);
c4.push(vn5); c1.push(vn4);
c5.push({text: \`header\`}); c4.push({text: \`header\`});
}" }"
`; `;
@@ -82,11 +82,11 @@ exports[`t-slot directive can define and call slots 4`] = `
) { ) {
// Template name: \\"slot_footer_template\\" // Template name: \\"slot_footer_template\\"
let h = this.h; let h = this.h;
let c6 = extra.parentNode; let c1 = extra.parentNode;
let c7 = [], p7 = {key:7}; let c5 = [], p5 = {key:5};
let vn7 = h('span', p7, c7); let vn5 = h('span', p5, c5);
c6.push(vn7); c1.push(vn5);
c7.push({text: \`footer\`}); c5.push({text: \`footer\`});
}" }"
`; `;
@@ -134,23 +134,23 @@ exports[`t-slot directive can define and call slots using old t-set keyword 2`]
) { ) {
// Template name: \\"__template__1\\" // Template name: \\"__template__1\\"
let h = this.h; let h = this.h;
let c6 = [], p6 = {key:6};
let vn6 = h('div', p6, c6);
let c7 = [], p7 = {key:7};
let vn7 = h('div', p7, c7);
c6.push(vn7);
const slot8 = this.constructor.slots[context.__owl__.slotId + '_' + 'header'];
if (slot8) {
slot8.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c7, parent: extra.parent || context}));
}
let c9 = [], p9 = {key:9}; let c9 = [], p9 = {key:9};
let vn9 = h('div', p9, c9); let vn9 = h('div', p9, c9);
let c10 = [], p10 = {key:10}; c6.push(vn9);
let vn10 = h('div', p10, c10); const slot10 = this.constructor.slots[context.__owl__.slotId + '_' + 'footer'];
c9.push(vn10); if (slot10) {
const slot11 = this.constructor.slots[context.__owl__.slotId + '_' + 'header']; slot10.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c9, parent: extra.parent || context}));
if (slot11) {
slot11.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c10, parent: extra.parent || context}));
} }
let c12 = [], p12 = {key:12}; return vn6;
let vn12 = h('div', p12, c12);
c9.push(vn12);
const slot13 = this.constructor.slots[context.__owl__.slotId + '_' + 'footer'];
if (slot13) {
slot13.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c12, parent: extra.parent || context}));
}
return vn9;
}" }"
`; `;
@@ -159,11 +159,11 @@ exports[`t-slot directive can define and call slots using old t-set keyword 3`]
) { ) {
// Template name: \\"slot_header_template\\" // Template name: \\"slot_header_template\\"
let h = this.h; let h = this.h;
let c4 = extra.parentNode; let c1 = extra.parentNode;
let c5 = [], p5 = {key:5}; let c4 = [], p4 = {key:4};
let vn5 = h('span', p5, c5); let vn4 = h('span', p4, c4);
c4.push(vn5); c1.push(vn4);
c5.push({text: \`header\`}); c4.push({text: \`header\`});
}" }"
`; `;
@@ -172,11 +172,11 @@ exports[`t-slot directive can define and call slots using old t-set keyword 4`]
) { ) {
// Template name: \\"slot_footer_template\\" // Template name: \\"slot_footer_template\\"
let h = this.h; let h = this.h;
let c6 = extra.parentNode; let c1 = extra.parentNode;
let c7 = [], p7 = {key:7}; let c5 = [], p5 = {key:5};
let vn7 = h('span', p7, c7); let vn5 = h('span', p5, c5);
c6.push(vn7); c1.push(vn5);
c7.push({text: \`footer\`}); c5.push({text: \`footer\`});
}" }"
`; `;
@@ -185,11 +185,11 @@ exports[`t-slot directive content is the default slot 1`] = `
) { ) {
// Template name: \\"slot_default_template\\" // Template name: \\"slot_default_template\\"
let h = this.h; let h = this.h;
let c4 = extra.parentNode; let c1 = extra.parentNode;
let c5 = [], p5 = {key:5}; let c4 = [], p4 = {key:4};
let vn5 = h('span', p5, c5); let vn4 = h('span', p4, c4);
c4.push(vn5); c1.push(vn4);
c5.push({text: \`sts rocks\`}); c4.push({text: \`sts rocks\`});
}" }"
`; `;
@@ -215,8 +215,8 @@ exports[`t-slot directive default slot work with text nodes 1`] = `
) { ) {
// Template name: \\"slot_default_template\\" // Template name: \\"slot_default_template\\"
let h = this.h; let h = this.h;
let c4 = extra.parentNode; let c1 = extra.parentNode;
c4.push({text: \`sts rocks\`}); c1.push({text: \`sts rocks\`});
}" }"
`; `;
@@ -225,15 +225,15 @@ exports[`t-slot directive multiple roots are allowed in a default slot 1`] = `
) { ) {
// Template name: \\"slot_default_template\\" // Template name: \\"slot_default_template\\"
let h = this.h; let h = this.h;
let c4 = extra.parentNode; let c1 = extra.parentNode;
let c4 = [], p4 = {key:4};
let vn4 = h('span', p4, c4);
c1.push(vn4);
c4.push({text: \`sts\`});
let c5 = [], p5 = {key:5}; let c5 = [], p5 = {key:5};
let vn5 = h('span', p5, c5); let vn5 = h('span', p5, c5);
c4.push(vn5); c1.push(vn5);
c5.push({text: \`sts\`}); c5.push({text: \`rocks\`});
let c6 = [], p6 = {key:6};
let vn6 = h('span', p6, c6);
c4.push(vn6);
c6.push({text: \`rocks\`});
}" }"
`; `;
@@ -242,15 +242,15 @@ exports[`t-slot directive multiple roots are allowed in a named slot 1`] = `
) { ) {
// Template name: \\"slot_content_template\\" // Template name: \\"slot_content_template\\"
let h = this.h; let h = this.h;
let c4 = extra.parentNode; let c1 = extra.parentNode;
let c4 = [], p4 = {key:4};
let vn4 = h('span', p4, c4);
c1.push(vn4);
c4.push({text: \`sts\`});
let c5 = [], p5 = {key:5}; let c5 = [], p5 = {key:5};
let vn5 = h('span', p5, c5); let vn5 = h('span', p5, c5);
c4.push(vn5); c1.push(vn5);
c5.push({text: \`sts\`}); c5.push({text: \`rocks\`});
let c6 = [], p6 = {key:6};
let vn6 = h('span', p6, c6);
c4.push(vn6);
c6.push({text: \`rocks\`});
}" }"
`; `;
@@ -278,22 +278,22 @@ exports[`t-slot directive refs are properly bound in slots 1`] = `
let utils = this.constructor.utils; let utils = this.constructor.utils;
context.__owl__.refs = context.__owl__.refs || {}; context.__owl__.refs = context.__owl__.refs || {};
let h = this.h; let h = this.h;
let c8 = extra.parentNode; let c1 = extra.parentNode;
let c9 = [], p9 = {key:9,on:{}}; let c8 = [], p8 = {key:8,on:{}};
let vn9 = h('button', p9, c9); let vn8 = h('button', p8, c8);
c8.push(vn9); c1.push(vn8);
extra.handlers['click__10__'] = extra.handlers['click__10__'] || function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['doSomething'](e);}; extra.handlers['click__9__'] = extra.handlers['click__9__'] || function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['doSomething'](e);};
p9.on['click'] = extra.handlers['click__10__']; p8.on['click'] = extra.handlers['click__9__'];
const ref11 = \`myButton\`; const ref10 = \`myButton\`;
p9.hook = { p8.hook = {
create: (_, n) => { create: (_, n) => {
context.__owl__.refs[ref11] = n.elm; context.__owl__.refs[ref10] = n.elm;
}, },
destroy: () => { destroy: () => {
delete context.__owl__.refs[ref11]; delete context.__owl__.refs[ref10];
}, },
}; };
c9.push({text: \`do something\`}); c8.push({text: \`do something\`});
}" }"
`; `;
@@ -303,13 +303,13 @@ exports[`t-slot directive slots are rendered with proper context 1`] = `
// Template name: \\"slot_footer_template\\" // Template name: \\"slot_footer_template\\"
let utils = this.constructor.utils; let utils = this.constructor.utils;
let h = this.h; let h = this.h;
let c8 = extra.parentNode; let c1 = extra.parentNode;
let c9 = [], p9 = {key:9,on:{}}; let c8 = [], p8 = {key:8,on:{}};
let vn9 = h('button', p9, c9); let vn8 = h('button', p8, c8);
c8.push(vn9); c1.push(vn8);
extra.handlers['click__10__'] = extra.handlers['click__10__'] || function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['doSomething'](e);}; extra.handlers['click__9__'] = extra.handlers['click__9__'] || function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['doSomething'](e);};
p9.on['click'] = extra.handlers['click__10__']; p8.on['click'] = extra.handlers['click__9__'];
c9.push({text: \`do something\`}); c8.push({text: \`do something\`});
}" }"
`; `;
@@ -319,14 +319,14 @@ exports[`t-slot directive slots are rendered with proper context, part 2 1`] = `
// Template name: \\"Link\\" // Template name: \\"Link\\"
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let _12 = scope['props'].to; let _11 = scope['props'].to;
let c13 = [], p13 = {key:13,attrs:{href: _12}}; let c12 = [], p12 = {key:12,attrs:{href: _11}};
let vn13 = h('a', p13, c13); let vn12 = h('a', p12, c12);
const slot14 = this.constructor.slots[context.__owl__.slotId + '_' + 'default']; const slot13 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
if (slot14) { if (slot13) {
slot14.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c13, parent: extra.parent || context})); slot13.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c12, parent: extra.parent || context}));
} }
return vn13; return vn12;
}" }"
`; `;
@@ -348,8 +348,12 @@ exports[`t-slot directive slots are rendered with proper context, part 2 2`] = `
if (!_3) { throw new Error('QWeb error: Invalid loop expression')} if (!_3) { throw new Error('QWeb error: Invalid loop expression')}
let _4 = _5 = _3; let _4 = _5 = _3;
if (!(_3 instanceof Array)) { if (!(_3 instanceof Array)) {
_4 = Object.keys(_3); if (typeof _3[Symbol.iterator] === 'function') {
_5 = Object.values(_3); _4 = [..._3];
} else {
_4 = Object.keys(_3);
_5 = Object.values(_3);
}
} }
let _length4 = _4.length; let _length4 = _4.length;
let _origScope6 = scope; let _origScope6 = scope;
@@ -401,11 +405,11 @@ exports[`t-slot directive slots are rendered with proper context, part 2 3`] = `
// Template name: \\"slot_default_template\\" // Template name: \\"slot_default_template\\"
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let c10 = extra.parentNode; let c7 = extra.parentNode;
c10.push({text: \`User \`}); c7.push({text: \`User \`});
let _11 = scope['user'].name; let _10 = scope['user'].name;
if (_11 != null) { if (_10 != null) {
c10.push({text: _11}); c7.push({text: _10});
} }
}" }"
`; `;
@@ -416,14 +420,14 @@ exports[`t-slot directive slots are rendered with proper context, part 3 1`] = `
// Template name: \\"Link\\" // Template name: \\"Link\\"
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let _12 = scope['props'].to; let _10 = scope['props'].to;
let c13 = [], p13 = {key:13,attrs:{href: _12}}; let c11 = [], p11 = {key:11,attrs:{href: _10}};
let vn13 = h('a', p13, c13); let vn11 = h('a', p11, c11);
const slot14 = this.constructor.slots[context.__owl__.slotId + '_' + 'default']; const slot12 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
if (slot14) { if (slot12) {
slot14.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c13, parent: extra.parent || context})); slot12.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c11, parent: extra.parent || context}));
} }
return vn13; return vn11;
}" }"
`; `;
@@ -445,8 +449,12 @@ exports[`t-slot directive slots are rendered with proper context, part 3 2`] = `
if (!_3) { throw new Error('QWeb error: Invalid loop expression')} if (!_3) { throw new Error('QWeb error: Invalid loop expression')}
let _4 = _5 = _3; let _4 = _5 = _3;
if (!(_3 instanceof Array)) { if (!(_3 instanceof Array)) {
_4 = Object.keys(_3); if (typeof _3[Symbol.iterator] === 'function') {
_5 = Object.values(_3); _4 = [..._3];
} else {
_4 = Object.keys(_3);
_5 = Object.values(_3);
}
} }
let _length4 = _4.length; let _length4 = _4.length;
let _origScope6 = scope; let _origScope6 = scope;
@@ -499,10 +507,9 @@ exports[`t-slot directive slots are rendered with proper context, part 3 3`] = `
// Template name: \\"slot_default_template\\" // Template name: \\"slot_default_template\\"
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let c10 = extra.parentNode; let c7 = extra.parentNode;
let _11 = scope['userdescr']; if (scope.userdescr != null) {
if (_11 != null) { c7.push({text: scope.userdescr});
c10.push({text: _11});
} }
}" }"
`; `;
@@ -553,10 +560,9 @@ exports[`t-slot directive slots are rendered with proper context, part 4 2`] = `
// Template name: \\"slot_default_template\\" // Template name: \\"slot_default_template\\"
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let c4 = extra.parentNode; let c1 = extra.parentNode;
let _5 = scope['userdescr']; if (scope.userdescr != null) {
if (_5 != null) { c1.push({text: scope.userdescr});
c4.push({text: _5});
} }
}" }"
`; `;
@@ -566,13 +572,13 @@ exports[`t-slot directive t-set t-value in a slot 1`] = `
) { ) {
// Template name: \\"__template__1\\" // Template name: \\"__template__1\\"
let h = this.h; let h = this.h;
let c5 = [], p5 = {key:5}; let c4 = [], p4 = {key:4};
let vn5 = h('span', p5, c5); let vn4 = h('span', p4, c4);
const slot6 = this.constructor.slots[context.__owl__.slotId + '_' + 'default']; const slot5 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
if (slot6) { if (slot5) {
slot6.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c5, parent: extra.parent || context})); slot5.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c4, parent: extra.parent || context}));
} }
return vn5; return vn4;
}" }"
`; `;
@@ -583,12 +589,12 @@ exports[`t-slot directive template can just return a slot 1`] = `
let utils = this.constructor.utils; let utils = this.constructor.utils;
let result; let result;
let h = this.h; let h = this.h;
const slot7 = this.constructor.slots[context.__owl__.slotId + '_' + 'default']; const slot6 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
if (slot7) { if (slot6) {
let children8= [] let children7= []
result = {} result = {}
slot7.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: children8, parent: extra.parent || context})); slot6.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: children7, parent: extra.parent || context}));
utils.defineProxy(result, children8[0]); utils.defineProxy(result, children7[0]);
} }
return result; return result;
}" }"
-33
View File
@@ -1052,37 +1052,4 @@ describe("t-slot directive", () => {
"<div><child><p>Ablip</p>default2<child>default1<p>Bblip</p></child></child></div>" "<div><child><p>Ablip</p>default2<child>default1<p>Bblip</p></child></child></div>"
); );
}); });
test("named slot inside slot, part 3", async () => {
class Child extends Component {
static template = xml`
<div>
<t t-slot="brol"/>
<t t-slot="default"/>
</div>`;
}
class Parent extends Component {
static template = xml`
<div>
<Child>
<t t-set-slot="brol">
<p>A<t t-esc="value"/></p>
</t>
<Child>
<t>
<t t-set-slot="brol">
<p>B<t t-esc="value"/></p>
</t>
</t>
</Child>
</Child>
</div>`;
static components = { Child };
value = "blip";
}
const parent = new Parent();
await parent.mount(fixture);
expect(fixture.innerHTML).toBe("<div><div><p>Ablip</p><div><p>Bblip</p></div></div></div>");
});
}); });
+3 -8
View File
@@ -37,13 +37,8 @@ export function nextMicroTick(): Promise<void> {
} }
export async function nextTick(): Promise<void> { export async function nextTick(): Promise<void> {
await new Promise((resolve) => scheduler.requestAnimationFrame(resolve));
await new Promise((resolve) => setTimeout(resolve)); await new Promise((resolve) => setTimeout(resolve));
await new Promise((resolve) => scheduler.requestAnimationFrame(resolve));
}
export async function nextFrame(): Promise<void> {
await new Promise((resolve) => scheduler.requestAnimationFrame(resolve));
await new Promise((resolve) => scheduler.requestAnimationFrame(resolve));
} }
export function makeTestFixture() { export function makeTestFixture() {
@@ -132,7 +127,7 @@ export function renderToString(
// is useful for animations tests, as we hook before repaints to trigger // is useful for animations tests, as we hook before repaints to trigger
// animations (thanks to requestAnimationFrame). Patching nextFrame allows to // animations (thanks to requestAnimationFrame). Patching nextFrame allows to
// simulate calls to this hook. One must not forget to unpatch afterwards. // simulate calls to this hook. One must not forget to unpatch afterwards.
let _nextFrame = QWeb.utils.nextFrame; let nextFrame = QWeb.utils.nextFrame;
export function patchNextFrame(f: Function) { export function patchNextFrame(f: Function) {
QWeb.utils.nextFrame = (cb: () => void) => { QWeb.utils.nextFrame = (cb: () => void) => {
setTimeout(() => f(cb)); setTimeout(() => f(cb));
@@ -140,7 +135,7 @@ export function patchNextFrame(f: Function) {
} }
export function unpatchNextFrame() { export function unpatchNextFrame() {
QWeb.utils.nextFrame = _nextFrame; QWeb.utils.nextFrame = nextFrame;
} }
export async function editInput(input: HTMLInputElement | HTMLTextAreaElement, value: string) { export async function editInput(input: HTMLInputElement | HTMLTextAreaElement, value: string) {
File diff suppressed because it is too large Load Diff
+38 -27
View File
@@ -238,13 +238,6 @@ describe("t-raw", () => {
"<span><span>hello</span><ok>world</ok></span>" "<span><span>hello</span><ok>world</ok></span>"
); );
}); });
test("t-raw with comment", () => {
qweb.addTemplate("test", `<span><t t-raw="var"/></span>`);
expect(renderToString(qweb, "test", { var: "<p>text<!-- top secret --></p>" })).toBe(
"<span><p>text<!-- top secret --></p></span>"
);
});
}); });
describe("t-set", () => { describe("t-set", () => {
@@ -1095,26 +1088,6 @@ describe("t-call (template calling", () => {
const expected = "<div><p>yip yip</p></div>"; const expected = "<div><p>yip yip</p></div>";
expect(renderToString(qweb, "main")).toBe(expected); expect(renderToString(qweb, "main")).toBe(expected);
}); });
test("t-call with body content as root of a template", () => {
qweb.addTemplate("antony", `<foo><t t-raw="0"/></foo>`);
qweb.addTemplate("main", `<t><t t-call="antony"><p>antony</p></t></t>`);
const expected = "<foo><p>antony</p></foo>";
expect(renderToString(qweb, "main")).toBe(expected);
});
test("dynamic t-call", () => {
qweb.addTemplate("foo", `<foo><t t-esc="val"/></foo>`);
qweb.addTemplate("bar", `<bar><t t-esc="val"/></bar>`);
qweb.addTemplate("main", `<div><t t-call="{{template}}"/></div>`);
const expected = "<div><foo>foo</foo></div>";
expect(renderToString(qweb, "main", { template: "foo", val: "foo" })).toBe(expected);
const expected2 = "<div><bar>quux</bar></div>";
expect(renderToString(qweb, "main", { template: "bar", val: "quux" })).toBe(expected2);
// duplicate call because there was a specific bug with some id that was
// incremented each rendering.
expect(renderToString(qweb, "main", { template: "bar", val: "quux" })).toBe(expected2);
});
}); });
describe("foreach", () => { describe("foreach", () => {
@@ -1290,6 +1263,44 @@ describe("foreach", () => {
); );
console.warn = consoleWarn; console.warn = consoleWarn;
}); });
test('t-foreach supports custom iterators', () => {
qweb.addTemplate(
"test",
`
<div>
<t t-foreach="iterator" t-as="item">
<t t-esc="item"/>
</t>
</div>`
);
let iterator: any = [1, 2, 3];
let result = trim(renderToString(qweb, "test", { iterator } ));
let expected = `<div>123</div>`;
expect(result).toBe(expected);
iterator = { 1: 1, 2: 2, 3:3 };
result = trim(renderToString(qweb, "test", { iterator } ));
expected = `<div>123</div>`;
expect(result).toBe(expected);
iterator = new Set([1, 2, 3]);
result = trim(renderToString(qweb, "test", { iterator } ));
expected = `<div>123</div>`;
expect(result).toBe(expected);
iterator = {}
iterator[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
}
result = trim(renderToString(qweb, "test", { iterator } ));
expected = `<div>123</div>`;
expect(result).toBe(expected);
});
}); });
describe("misc", () => { describe("misc", () => {
+10 -10
View File
@@ -7,16 +7,16 @@ exports[`Link component can render simple cases 1`] = `
let utils = this.constructor.utils; let utils = this.constructor.utils;
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let _5 = utils.toObj({'router-link-active':scope['isActive']}); let _4 = utils.toObj({'router-link-active':scope['isActive']});
let _6 = scope['href']; let _5 = scope['href'];
let c7 = [], p7 = {key:7,attrs:{href: _6},class:_5,on:{}}; let c6 = [], p6 = {key:6,attrs:{href: _5},class:_4,on:{}};
let vn7 = h('a', p7, c7); let vn6 = h('a', p6, c6);
extra.handlers['click__8__'] = extra.handlers['click__8__'] || function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['navigate'](e);}; extra.handlers['click__7__'] = extra.handlers['click__7__'] || function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['navigate'](e);};
p7.on['click'] = extra.handlers['click__8__']; p6.on['click'] = extra.handlers['click__7__'];
const slot9 = this.constructor.slots[context.__owl__.slotId + '_' + 'default']; const slot8 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
if (slot9) { if (slot8) {
slot9.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c7, parent: extra.parent || context})); slot8.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c6, parent: extra.parent || context}));
} }
return vn7; return vn6;
}" }"
`; `;
+59 -24
View File
@@ -3,9 +3,10 @@ const readline = require("readline");
const fs = require("fs"); const fs = require("fs");
const exec = require("child_process").exec; const exec = require("child_process").exec;
const chalk = require("chalk"); const chalk = require("chalk");
const GitHub = require("github-api");
const REL_NOTES_FILE = `release-notes.md`; const REL_NOTES_FILE = `release-notes.md`;
const STEPS = 8; const STEPS = 10;
const rl = readline.createInterface({ const rl = readline.createInterface({
input: process.stdin, input: process.stdin,
@@ -27,8 +28,7 @@ async function startRelease() {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
log(`Step 1/${STEPS}: collecting info...`); log(`Step 1/${STEPS}: collecting info...`);
const current = package.version; const current = package.version;
let next = await ask("Next version: "); const next = await ask("Next version: ");
if (next[0] === 'v') next = next.substring(1);
let file = await ask(`Release notes (${REL_NOTES_FILE}): `); let file = await ask(`Release notes (${REL_NOTES_FILE}): `);
file = file || REL_NOTES_FILE; file = file || REL_NOTES_FILE;
let content; let content;
@@ -39,12 +39,11 @@ async function startRelease() {
log("Cannot find release notes... Aborting"); log("Cannot find release notes... Aborting");
return; return;
} }
let shouldBeDraft = await ask(`Should be a draft [y/n] ? (n)`);
let draft = "" // Todo: add playground update feature
if (shouldBeDraft.toLowerCase() === 'y') // let shouldUpdateStr = await ask("Update Playground? (y/n)");
{ // const shouldUpdatePlayground = shouldUpdateStr === "y";
draft = "--draft"; const token = await ask("Github token: ");
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
log(`Step 2/${STEPS}: running tests...`); log(`Step 2/${STEPS}: running tests...`);
@@ -53,12 +52,14 @@ async function startRelease() {
log("Test suite does not pass. Aborting."); log("Test suite does not pass. Aborting.");
return; return;
} }
await ask("Ready for next step...");
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
log(`Step 3/${STEPS}: updating package.json, readme.md and roadmap.md...`); log(`Step 3/${STEPS}: updating package.json, readme.md and roadmap.md...`);
await replaceInFile("./package.json", current, next); await replaceInFile("./package.json", current, next);
await replaceInFile("./README.md", current, next); await replaceInFile("./README.md", current, next);
await replaceInFile("./roadmap.md", current, next); await replaceInFile("./roadmap.md", current, next);
await ask("Ready for next step...");
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
log(`Step 4/${STEPS}: creating git commit...`); log(`Step 4/${STEPS}: creating git commit...`);
@@ -67,40 +68,61 @@ async function startRelease() {
log("Git commit failed. Aborting."); log("Git commit failed. Aborting.");
return; return;
} }
await ask("Ready for next step...");
// ---------------------------------------------------------------------------- // -------------------j--------------------------------------------------------
log(`Step 5/${STEPS}: building owl...`); log(`Step 5/${STEPS}: building owl (iife version)...`);
await execCommand("npm run prettier");
const buildResult = await execCommand("npm run build"); const buildResult = await execCommand("npm run build");
if (buildResult !== 0) { if (buildResult !== 0) {
log("Build failed. Aborting."); log("Build failed. Aborting.");
return; return;
} }
await ask("Ready for next step...");
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
log(`Step 6/${STEPS}: pushing on github...`); log(`Step 6/${STEPS}: minifying owl...`);
const minifyResult = await execCommand("npm run minify");
if (minifyResult !== 0) {
log("Minify failed. Aborting.");
return;
}
await ask("Ready for next step...");
// ---------------------------------------------------------------------------
log(`Step 7/${STEPS}: pushing on github...`);
const pushResult = await execCommand("git push"); const pushResult = await execCommand("git push");
if (pushResult !== 0) { if (pushResult !== 0) {
log("git push failed. Aborting."); log("git push failed. Aborting.");
return; return;
} }
await ask("Ready for next step...");
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
log(`Step 8/${STEPS}: publishing release notes on github...`);
const options = {
tag_name: `v${next}`,
name: `v${next}`,
body: content,
draft: true // todo: remove this someday
};
const result = await createRelease(token, options);
await ask("Ready for next step...");
log(`Step 7/${STEPS}: Creating the release...`); // ---------------------------------------------------------------------------
const relaseResult = await execCommand(`gh release create v${next} dist/*.js ${draft} -F release-notes.md`); log(`Step 9/${STEPS}: adding assets to release...`);
if (relaseResult !== 0) { await ask("Please add owl.js and owl.min.js to draft release, then confirm");
log("github release failed. Aborting."); // todo: do this with curl
return; // curl \
} // -H "Authorization: token $GITHUB_TOKEN" \
// -H "Content-Type: $(file -b --mime-type $FILE)" \
// --data-binary @$FILE \
// "https://uploads.github.com/repos/hubot/singularity/releases/123/assets?name=$(basename $FILE)"
log(`Step 8/${STEPS}: publishing module on npm...`); // ---------------------------------------------------------------------------
log(`Step 10/${STEPS}: publishing module on npm...`);
await execCommand("npm run publish"); await execCommand("npm run publish");
log("Owl Release process completed! Thank you for your patience");
await execCommand(`gh release view`);
await execCommand(`gh release view -w`);
log("Owl Release process completed! Thank you for your patience");
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -177,3 +199,16 @@ async function replaceInFile(file, from, to) {
}); });
}); });
} }
function createRelease(token, options) {
return new Promise((resolve, reject) => {
var gh = new GitHub({ token });
gh.getRepo("odoo", "owl").createRelease(options, (err, result, req) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
}
+2 -2
View File
@@ -14,9 +14,9 @@ URL = 'http://{0}:{1}/tools'.format(HOST, PORT)
# With this, we can simply copy the playground folder as is in the gh-page when # With this, we can simply copy the playground folder as is in the gh-page when
# we want to update the playground. # we want to update the playground.
class OWLHandler(SimpleHTTPRequestHandler): class OWLHandler(SimpleHTTPRequestHandler):
def do_GET(self): def do_GET(self):
if self.path == '/tools/owl.js': if self.path == '/tools/owl.js':
self.path = '/dist/owl.iife.js' self.path = '/dist/owl.js'
return SimpleHTTPRequestHandler.do_GET(self) return SimpleHTTPRequestHandler.do_GET(self)
def end_headers(self): def end_headers(self):
+17 -104
View File
@@ -1,107 +1,20 @@
{ {
/**
** Commented-out options have their default values.
**/
"include": [
"src/**/*.ts",
"src/*.ts"
],
                                                              // "exclude": [],
// "files": [],                   // A list of relative or absolute file paths to include.
// "extends": "",                   // A string containing a path to another configuration file to inherit from.
// "references": [],                   // An array of objects `{"path": "./to/dirOrConfig"}` that specifies projects to reference.
// "compileOnSave": false,                   // Signals to the IDE to generate all files for a given tsconfig.json upon saving.
"compilerOptions": { "compilerOptions": {
                                                            // Main options "module": "commonjs",
"target": "esnext",                                         // Specify ECMAScript target version: 'es3' (default), 'es5', 'es2015', 'es2016', 'es2017','es2018' or 'esnext'. "preserveConstEnums": true,
"module": "esnext",                                         // Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. "noImplicitThis": true,
// "lib": ["esnext", "dom"],                 // Specify library files to be included in the compilation. "removeComments": false,
// "allowJs": false,                 // Allow javascript files to be compiled. "declaration": true,
// "checkJs": false,                 // Report errors in .js files. "target": "esnext",
// "outFile": "./",                 // Concatenate and emit output to single file. "outDir": "dist",
"outDir": "dist",                                           // Redirect output structure to the directory. "alwaysStrict": true,
// "rootDir": "./",                 // Specify the root directory of input files. Use to control the output directory structure with `--outDir`. "noUnusedLocals": true,
// "project": "",                 // Compile a project given a valid configuration file. "noUnusedParameters": false,
                                                            // Compilation options "noImplicitReturns": true,
// "composite": true,                 // Enable project compilation "noFallthroughCasesInSwitch": true,
// "diagnostics": false,                 // Show diagnostic information. "strictPropertyInitialization": true,
// "incremental": true,                 // Enable incremental compilation by reading/writing information from prior compilations to a file on disk. "strictNullChecks": true,
// "isolatedModules": false,                 // Transpile each file as a separate module (similar to 'ts.transpileModule'). "declarationDir": "dist/types"
// "listEmittedFiles": false,                 // Print names of generated files part of the compilation. },
// "listFiles": true,                 // Print names of files part of the compilation. "include": ["src/**/*.ts","src/*.ts"]
// "noErrorTruncation": false,                 // Do not truncate error messages.
// "preserveWatchOutput": false,                 // Keep outdated console output in watch mode instead of clearing the screen.
// "traceResolution": false,                 // Enable tracing of the name resolution process.
// "tsBuildInfoFile": ".tsbuildinfo",                 // Specify file to store incremental compilation information.
                                                            // Strict typechecking options
// "strict": false,                                         // Enable all strict type-checking options.
// "noImplicitAny": true,                 // Raise error on expressions and declarations with an implied 'any' type.
// "noImplicitThis": true,                 // Raise error on 'this' expressions with an implied 'any' type.
// "strictBindCallApply": true,                 // Enable stricter checking of of the `bind`, `call`, and `apply` methods on functions.
// "strictFunctionTypes": true,                 // Disable bivariant parameter checking for function types.
// "strictNullChecks": true,                 // In strict null checking mode, the null and undefined values are not in the domain of every type and are only assignable to themselves and any.
// "strictPropertyInitialization": true,                 // Ensure non-undefined class properties are initialized in the constructor. This option requires `--strictNullChecks` be enabled in order to take effect.
// "alwaysStrict": true,                 // Parse in strict mode and emit "use strict" for each source file.
                                                            // Additional checks
// "allowUnreachableCode": false,                 // Do not report errors on unreachable code.
// "allowUnusedLabels": false,                 // Do not report errors on unused labels.
"forceConsistentCasingInFileNames": true,                   // Disallow inconsistently-cased references to the same file.
// "noStrictGenericChecks": false,                 // Disable strict checking of generic signatures in function types.
"noUnusedLocals": true,                                     // Report errors on unused locals.
"noUnusedParameters": false,                                // Report errors on unused parameters.
"noImplicitReturns": true,                                  // Report error when not all code paths in function return a value.
"noFallthroughCasesInSwitch": true,                         // Report errors for fallthrough cases in switch statement.
// "skipLibCheck": false,                 // Skip type checking of all declaration files (*.d.ts).
// "suppressExcessPropertyErrors": false,                 // Suppress excess property checks for object literals.
// "suppressImplicitAnyIndexErrors": false,                 // Suppress noImplicitAny errors for indexing objects lacking index signatures.
                                                            // Module resolution options
"moduleResolution": "node",                                 // Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6).
// "baseUrl": "./",                 // Base directory to resolve non-absolute module names.
// "paths": {},                 // A series of entries which re-map imports to lookup locations relative to the 'baseUrl'.
// "rootDirs": [],                 // List of root folders whose combined content represents the structure of the project at runtime.
// "typeRoots": [],                 // List of folders to include type definitions from.
"types": [
"jest",
"node"
],                                                // Type declaration files to be included in compilation.
// "allowSyntheticDefaultImports": false                    // Allow default imports from modules with no default export. This does not affect code emit, just typechecking.
"esModuleInterop": true,                  // Emit '__importStar' and '__importDefault' helpers for runtime babel ecosystem compatibility and enable '--allowSyntheticDefaultImports' for typesystem compatibility.
// "maxNodeModuleJsDepth": 0,                 // The maximum dependency depth to search under node_modules and load JavaScript files. Only applicable with --allowJs.
// "preserveSymlinks": false,                 // Do not resolve the real path of symlinks.
"resolveJsonModule": true,                                  // Include modules imported with '.json' extension.
                                                            // Emit options
"declaration": true,                                        // Generates corresponding '.d.ts' file.
"declarationDir": "dist/types",                             // Output directory for generated declaration files.
// "declarationMap": false,                 // Generates a sourcemap for each corresponding '.d.ts' file.
// "emitBOM": false,                 // Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files.
// "emitDeclarationOnly": false,                 // Only emit .d.ts declaration files.
// "importHelpers": false,                 // Import emit helpers from 'tslib'.
// "newLine": "LF",                 // Use the specified end of line sequence to be used when emitting files: "crlf" (windows) or "lf" (unix).”
// "noEmit": true,                 // Do not emit outputs.
// "noEmitHelpers": false,                 // Do not generate custom helper functions like __extends in compiled output.
// "noEmitOnError": false,                 // Do not emit outputs if any errors were reported.
// "noImplicitUseStrict": false,                 // Do not emit "use strict" directives in module output.
// "noResolve": false,                 // Do not add triple-slash references or module import targets to the list of compiled files.
"preserveConstEnums": true,                                 // Do not erase const enum declarations in generated code.
// "removeComments": false,                 // Remove all comments except copy-right header comments beginning with
// "experimentalDecorators": true,                 // Enables experimental support for ES7 decorators.
// "emitDecoratorMetadata": true,                 // Enables experimental support for emitting type metadata for decorators.
                                                            // Source map options
// "sourceMap": false,                 // Generates corresponding '.map' file.
// "sourceRoot": "",                 // Specify the location where debugger should locate TypeScript files instead of source locations.
// "mapRoot": "",                 // Specify the location where debugger should locate map files instead of generated locations.
// "inlineSourceMap": true,                 // Emit a single file with source maps instead of having a separate file.
// "inlineSources": true,                 // Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set.
                                                            // JSX options
// "jsx": "preserve",                 // Specify JSX code generation: 'preserve', 'react-native', or 'react'.
// "jsxFactory": "React.createElement",                 // Specify the JSX factory function to use when targeting react JSX emit, e.g. 'React.createElement' or 'h'.
                                                            // Other options
// "allowUmdGlobalAccess": true,                 // Allow accessing UMD globals from modules.
// "charset": "utf8",                 // The character set of the input files.
// "downlevelIteration": false,                 // Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'.
// "disableSizeLimit": false,                 // Disable size limitation on JavaScript project.
// "keyofStringsOnly": false,                 // Resolve 'keyof' to string valued property names only (no numbers or symbols).
// "noLib": false,                 // Do not include the default library file (lib.d.ts).
// "pretty": true,                 // Stylize errors and messages using color and context.
}
} }