mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[IMP] devtools: add lifecycle hooks management
This commit adds lifecycle hooks to the interface of the selected component and allows to inject breakpoints into these hooks.
This commit is contained in:
committed by
Géry Debongnie
parent
aff4019cde
commit
ccd31f12d9
+13
@@ -75,6 +75,19 @@
|
||||
<ObjectTreeElement object="instance"/>
|
||||
</t>
|
||||
</div>
|
||||
<div t-if="store.activeComponent.hooks?.children.length > 0" id="hooks" class="details-panel ps-2 py-1">
|
||||
<div class="d-flex mb-2">
|
||||
<div class="w-100" t-on-click.stop="(ev) => this.toggleCategory(ev, 'hooks')">
|
||||
<i class="fa mx-1 pointer-icon"
|
||||
t-att-class="{'fa-caret-right': !store.activeComponent.hooks.toggled, 'fa-caret-down': store.activeComponent.hooks.toggled}"
|
||||
/><b>hooks</b>
|
||||
</div>
|
||||
<i title="Remove breakpoints" class="fa fa-times utility-icon p-1" t-on-click.stop="() => this.store.removeBreakpoints()"></i>
|
||||
</div>
|
||||
<t t-if="store.activeComponent.hooks.toggled" t-foreach="store.activeComponent.hooks.children" t-as="hook" t-key="hook_index">
|
||||
<ObjectTreeElement object="hook"/>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
|
||||
+34
@@ -64,6 +64,40 @@ export class ObjectTreeElement extends Component {
|
||||
show: this.props.object.contentType === "function",
|
||||
action: () => this.store.inspectFunctionSource(this.props.object.path),
|
||||
},
|
||||
{
|
||||
title: "Inject breakpoint on component",
|
||||
show: this.props.object.contentType === "array" && this.props.object.objectType === "hook",
|
||||
action: () =>
|
||||
this.store.injectBreakpoint(this.props.object.name, this.store.activeComponent.path),
|
||||
},
|
||||
{
|
||||
title: "Inject conditional breakpoint on component",
|
||||
show: this.props.object.contentType === "array" && this.props.object.objectType === "hook",
|
||||
action: () => {
|
||||
const condition = window.prompt("Enter the condition");
|
||||
if (condition) {
|
||||
this.store.injectBreakpoint(
|
||||
this.props.object.name,
|
||||
this.store.activeComponent.path,
|
||||
false,
|
||||
condition
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Inject breakpoint on instance",
|
||||
show:
|
||||
this.props.object.contentType === "array" &&
|
||||
this.props.object.objectType === "hook" &&
|
||||
!["mounted", "willStart"].includes(this.props.object.name),
|
||||
action: () =>
|
||||
this.store.injectBreakpoint(
|
||||
this.props.object.name,
|
||||
this.store.activeComponent.path,
|
||||
true
|
||||
),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ export const store = reactive({
|
||||
props: { toggled: true, children: [] },
|
||||
env: { toggled: false, children: [] },
|
||||
instance: { toggled: true, children: [] },
|
||||
hooks: { toggled: true, children: [] },
|
||||
version: "1.0",
|
||||
},
|
||||
selectedElement: null,
|
||||
@@ -564,6 +565,21 @@ export const store = reactive({
|
||||
}
|
||||
},
|
||||
|
||||
async injectBreakpoint(hook, path, instanceOnly = false, condition = "1") {
|
||||
path = [...path];
|
||||
await evalFunctionInWindow(
|
||||
"injectBreakpoint",
|
||||
[hook, path, instanceOnly, condition],
|
||||
this.activeFrame
|
||||
);
|
||||
await this.loadComponentsTree(true);
|
||||
},
|
||||
|
||||
async removeBreakpoints() {
|
||||
await evalFunctionInWindow("removeBreakpoints", [], this.activeFrame);
|
||||
await this.loadComponentsTree(true);
|
||||
},
|
||||
|
||||
// Trigger the highlight on the component in the page
|
||||
highlightComponent(path) {
|
||||
evalFunctionInWindow("highlightComponent", [path], this.activeFrame);
|
||||
|
||||
@@ -51,6 +51,8 @@
|
||||
this.requestedFrame = false;
|
||||
this.enabledSelector = false;
|
||||
this.eventsBatch = [];
|
||||
this.breakpointsClassMap = new Map();
|
||||
this.breakpointsHookMap = new Map();
|
||||
// Object which defines how different types of data should be displayed when passed to the devtools
|
||||
this.serializer = {
|
||||
// Defines how leaf object nodes should be displayed in the extension when inside a bigger structure
|
||||
@@ -744,6 +746,9 @@
|
||||
}
|
||||
// Path to the component node is only strings, becomes objects for properties
|
||||
const index = path.findIndex((key) => typeof key !== "string");
|
||||
if (index === -1) {
|
||||
return this.getComponentNode(path);
|
||||
}
|
||||
const componentNode = this.getComponentNode(path.slice(0, index));
|
||||
const obj = this.getObject(componentNode, path.slice(index));
|
||||
return obj;
|
||||
@@ -896,12 +901,15 @@
|
||||
path.shift();
|
||||
}
|
||||
// the value is either "props" or "env" here
|
||||
if (objType !== "instance") {
|
||||
if (objType !== "instance" && objType !== "hook") {
|
||||
obj = oldTree[path[0].value].children;
|
||||
path.shift();
|
||||
// there is nothing otherwise but extension side it is in instance
|
||||
} else {
|
||||
} else if (objType === "instance") {
|
||||
obj = oldTree.instance.children;
|
||||
} else {
|
||||
obj = oldTree.hooks.children;
|
||||
path.shift();
|
||||
}
|
||||
// the first element here is directly in an array instead of a children array
|
||||
obj = obj[path[0].childIndex];
|
||||
@@ -1377,6 +1385,39 @@
|
||||
component.subscriptions.children.push(subscription);
|
||||
});
|
||||
}
|
||||
// Load hooks of the component
|
||||
if (!isApp) {
|
||||
component.hooks = { toggled: oldTree ? oldTree.hooks.toggled : true, children: [] };
|
||||
const hooksList = [
|
||||
"mounted",
|
||||
"patched",
|
||||
"willDestroy",
|
||||
"willPatch",
|
||||
"willStart",
|
||||
"willUnmount",
|
||||
"willUpdateProps",
|
||||
];
|
||||
const hooksPath = [...instancePath, { type: "item", value: "__owl__" }];
|
||||
Reflect.ownKeys(instance.__owl__)
|
||||
.sort(compareKeys)
|
||||
.forEach((key) => {
|
||||
if (hooksList.includes(key)) {
|
||||
let oldBranch = oldTree?.hooks.children[component.hooks.children.length];
|
||||
const property = this.serializeObjectChild(
|
||||
instance.__owl__,
|
||||
{ type: "item", value: key, childIndex: component.hooks.children.length },
|
||||
0,
|
||||
"hook",
|
||||
hooksPath,
|
||||
oldBranch,
|
||||
oldTree
|
||||
);
|
||||
if (property) {
|
||||
component.hooks.children.push(property);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return component;
|
||||
}
|
||||
// Replace the content of a parsed getter object with the result of the corresponding get method
|
||||
@@ -1714,6 +1755,48 @@
|
||||
}
|
||||
}
|
||||
|
||||
injectBreakpoint(hook, path, instanceOnly, condition) {
|
||||
const componentNode = this.getObjectProperty(path);
|
||||
const injectFunctionInHook = (comp, hook, fn) => {
|
||||
comp[hook].push(fn);
|
||||
};
|
||||
const originalHook = [...componentNode.component.__owl__[hook]];
|
||||
injectFunctionInHook(componentNode.component.__owl__, hook, () => {
|
||||
debugger;
|
||||
});
|
||||
if (!this.breakpointsHookMap.get([componentNode.component.__owl__, hook])) {
|
||||
this.breakpointsHookMap.set([componentNode.component.__owl__, hook], originalHook);
|
||||
}
|
||||
if (!instanceOnly) {
|
||||
const componentClass = componentNode.component.constructor;
|
||||
const originalSetup = componentClass.prototype.setup;
|
||||
if (!this.breakpointsClassMap.get(componentClass)) {
|
||||
this.breakpointsClassMap.set(componentClass, originalSetup);
|
||||
}
|
||||
componentClass.prototype.setup = function () {
|
||||
const debuggerFunc = () => {
|
||||
if (eval(condition)) {
|
||||
this;
|
||||
debugger;
|
||||
}
|
||||
};
|
||||
injectFunctionInHook(this.__owl__, hook, debuggerFunc);
|
||||
originalSetup.call(this, ...arguments);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
removeBreakpoints() {
|
||||
for (const [component, setup] of this.breakpointsClassMap) {
|
||||
component.prototype.setup = setup;
|
||||
}
|
||||
this.breakpointsClassMap.clear();
|
||||
for (const [ref, originalHook] of this.breakpointsHookMap) {
|
||||
ref[0][ref[1]] = originalHook;
|
||||
}
|
||||
this.breakpointsHookMap.clear();
|
||||
}
|
||||
|
||||
targetName(target, node) {
|
||||
// check on component
|
||||
const { component } = node;
|
||||
|
||||
Reference in New Issue
Block a user