diff --git a/doc/reference/misc.md b/doc/reference/misc.md
index f2a9e310..71bcbf54 100644
--- a/doc/reference/misc.md
+++ b/doc/reference/misc.md
@@ -33,7 +33,7 @@ The component `Portal` has no particular state, rather it is meant to be a slave
and ultimately just a way for the parent to teleport a piece of its own DOM elsewhere.
The `Portal`'s root node is always `` and is placed where the teleported content
-*would have* been. It is this element that the [teleported events](#expected-behaviors) are re-directed on.
+_would have_ been. It is this element that the [teleported events](#expected-behaviors) are re-directed on.
### Example
diff --git a/doc/tooling.md b/doc/tooling.md
index 57bd37d8..10e21e74 100644
--- a/doc/tooling.md
+++ b/doc/tooling.md
@@ -91,16 +91,15 @@ logging useful information is extremely valuable. There is a [javascript file](.
Once it is executed, it will log a lot of information on each component main hooks. The following code is a minified version to make it easier to copy/paste:
```
-let debugSetup = {
+function debugOwl(o,t){let e,n="[OWL_DEBUG]";function l(o){let t=JSON.stringify(o||{});return t.length>200&&(t=t.slice(0,200)+"..."),t}if(Object.defineProperty(o.Component,"current",{get:()=>e,set(s){e=s;const c=s.constructor.name;if(t.componentBlackList&&t.componentBlackList.test(c))return;if(t.componentWhiteList&&!t.componentWhiteList.test(c))return;let r;Object.defineProperty(e,"__owl__",{get:()=>r,set(e){!function(e,s,c){let r=`${s}`,i=o=>(!t.methodBlackList||!t.methodBlackList.includes(o))&&!(t.methodWhiteList&&!t.methodWhiteList.includes(o));i("constructor")&&console.log(`${n} ${r} constructor, props=${l(e.props)}`);i("willStart")&&o.hooks.onWillStart(()=>{console.log(`${n} ${r} willStart`)});i("mounted")&&o.hooks.onMounted(()=>{console.log(`${n} ${r} mounted`)});i("willUpdateProps")&&o.hooks.onWillUpdateProps(o=>{console.log(`${n} ${r} willUpdateProps, nextprops=${l(o)}`)});i("willPatch")&&o.hooks.onWillPatch(()=>{console.log(`${n} ${r} willPatch`)});i("patched")&&o.hooks.onPatched(()=>{console.log(`${n} ${r} patched`)});i("willUnmount")&&o.hooks.onWillUnmount(()=>{console.log(`${n} ${r} willUnmount`)});const u=e.__render.bind(e);e.__render=function(...o){console.log(`${n} ${r} rendering template`),u(...o)};const d=e.render.bind(e);e.render=function(...o){return console.log(`${n} ${r} render`),d(...o)};const p=e.mount.bind(e);e.mount=function(...o){return console.log(`${n} ${r} mount`),p(...o)}}(s,c,(r=e).id)}})}}),t.logScheduler){let t=o.Component.scheduler.start,e=o.Component.scheduler.stop;o.Component.scheduler.start=function(){console.log(`${n} scheduler: start running tasks queue`),t.call(this)},o.Component.scheduler.stop=function(){console.log(`${n} scheduler: stop running tasks queue`),e.call(this)}}if(t.logStore){let t=o.Store.prototype.dispatch;o.Store.prototype.dispatch=function(o,...e){return console.log(`${n} store: action '${o}' dispatched. Payload: '${l(e)}'`),t.call(this,o,...e)}}}
+debugOwl({
// componentBlackList: /App/, // regexp
// componentWhiteList: /SomeComponent/, // regexp
// methodBlackList: ["mounted"], // list of method names
// methodWhiteList: ["willStart"], // list of method names
logScheduler: false, // display/mute scheduler logs
logStore: true, // display/mute store logs
-};
-{let o,t="[OWL_DEBUG]";function toStr(o){let t=JSON.stringify(o||{});return t.length>200&&(t=t.slice(0,200)+"..."),t}function debugComponent(o,e,n){let l=`${e}`,r=o=>(!debugSetup.methodBlackList||!debugSetup.methodBlackList.includes(o))&&!(debugSetup.methodWhiteList&&!debugSetup.methodWhiteList.includes(o));r("constructor")&&console.log(`${t} ${l} constructor, props=${toStr(o.props)}`),r("willStart")&&owl.hooks.onWillStart(()=>{console.log(`${t} ${l} willStart`)}),r("mounted")&&owl.hooks.onMounted(()=>{console.log(`${t} ${l} mounted`)}),r("willUpdateProps")&&owl.hooks.onWillUpdateProps(o=>{console.log(`${t} ${l} willUpdateProps, nextprops=${toStr(o)}`)}),r("willPatch")&&owl.hooks.onWillPatch(()=>{console.log(`${t} ${l} willPatch`)}),r("patched")&&owl.hooks.onPatched(()=>{console.log(`${t} ${l} patched`)}),r("willUnmount")&&owl.hooks.onWillUnmount(()=>{console.log(`${t} ${l} willUnmount`)});const s=o.__render.bind(o);o.__render=function(...o){console.log(`${t} ${l} rendering template`),s(...o)};const u=o.render.bind(o);o.render=function(...o){return console.log(`${t} ${l} render`),u(...o)};const c=o.mount.bind(o);o.mount=function(...o){return console.log(`${t} ${l} mount`),c(...o)}}if(Object.defineProperty(owl.Component,"current",{get:()=>o,set(t){o=t;const e=t.constructor.name;if(debugSetup.componentBlackList&&debugSetup.componentBlackList.test(e))return;if(debugSetup.componentWhiteList&&!debugSetup.componentWhiteList.test(e))return;let n;Object.defineProperty(o,"__owl__",{get:()=>n,set(o){debugComponent(t,e,(n=o).id)}})}}),debugSetup.logScheduler){let o;Object.defineProperty(owl.Component.scheduler,"isRunning",{get:()=>o,set(e){e?console.log(`${t} scheduler: start running tasks queue`):console.log(`${t} scheduler: stop running tasks queue`),o=e}})}if(debugSetup.logStore){let o=owl.Store.prototype.dispatch;owl.Store.prototype.dispatch=function(e,...n){return console.log(`${t} store: action '${e}' dispatched. Payload: '${toStr(n)}'`),o.call(this,e,...n)}}}
-
+});
```
Note that it is certainly useful to run this code at some point in an application,
diff --git a/src/component/scheduler.ts b/src/component/scheduler.ts
index 5013702b..258a0c4f 100644
--- a/src/component/scheduler.ts
+++ b/src/component/scheduler.ts
@@ -25,6 +25,15 @@ export class Scheduler {
this.requestAnimationFrame = requestAnimationFrame;
}
+ start() {
+ this.isRunning = true;
+ this.scheduleTasks();
+ }
+
+ stop() {
+ this.isRunning = false;
+ }
+
addFiber(fiber): Promise {
// if the fiber was remapped into a larger rendering fiber, it may not be a
// root fiber. But we only want to register root fibers
@@ -43,7 +52,7 @@ export class Scheduler {
}
});
if (!this.isRunning) {
- this.scheduleTasks();
+ this.start();
}
});
}
@@ -74,16 +83,16 @@ export class Scheduler {
return true;
});
this.tasks = tasks.concat(this.tasks);
+ if (this.tasks.length === 0) {
+ this.stop();
+ }
}
scheduleTasks() {
- this.isRunning = true;
this.requestAnimationFrame(() => {
this.flush();
- if (this.tasks.length > 0) {
+ if (this.isRunning) {
this.scheduleTasks();
- } else {
- this.isRunning = false;
}
});
}
diff --git a/tests/misc/portal.test.ts b/tests/misc/portal.test.ts
index a0d12ecb..abc14a4b 100644
--- a/tests/misc/portal.test.ts
+++ b/tests/misc/portal.test.ts
@@ -740,7 +740,7 @@ describe("Portal: Events handling", () => {
`;
}
class Parent extends Component {
- static components = { Portal, Child, };
+ static components = { Portal, Child };
static template = xml`
diff --git a/tests/tooling/debug_script_1.test.ts b/tests/tooling/debug_script_1.test.ts
new file mode 100644
index 00000000..e9845dc9
--- /dev/null
+++ b/tests/tooling/debug_script_1.test.ts
@@ -0,0 +1,57 @@
+/**
+ * We can only make one test per file, since the debug tool modify in place
+ * the owl object in a way that is difficult to undo.
+ */
+
+import { debugOwl } from "../../tools/debug";
+import * as owl from "../../src/index";
+
+import { Component, Env } from "../../src/component/component";
+import { xml } from "../../src/tags";
+import { useState } from "../../src/hooks";
+import { makeTestFixture, makeTestEnv, nextTick } from "../helpers";
+
+let fixture: HTMLElement = makeTestFixture();
+let env: Env = makeTestEnv();
+Component.env = env;
+
+debugOwl(owl, {});
+
+test("can log full lifecycle", async () => {
+ const steps: string[] = [];
+ const log = console.log;
+ console.log = arg => steps.push(arg);
+
+ class Child extends Component {
+ static template = xml`