Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: [Inspector] drop Proxies, set selective Object.defineProperties traps #432

Merged
merged 2 commits into from
Nov 5, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 61 additions & 44 deletions src/main-api/Inspector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,14 @@ const gradientColorPropertyMap = [
'colorBr',
];

const knownProperties = new Set<string>([
...Object.keys(stylePropertyMap),
...Object.keys(domPropertyMap),
// ...gradientColorPropertyMap,
'src',
'parent',
]);

export class Inspector {
private root: HTMLElement | null = null;
private canvas: HTMLCanvasElement | null = null;
Expand Down Expand Up @@ -239,72 +247,81 @@ export class Inspector {

createNode(node: CoreNode): CoreNode {
const div = this.createDiv(node.id, node.props);

// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
(div as any).node = node;

// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
(node as any).div = div;

node.on('inViewport', () => {
div.setAttribute('state', 'inViewport');
});

node.on('inBounds', () => {
div.setAttribute('state', 'inBounds');
});

node.on('outOfBounds', () => {
div.setAttribute('state', 'outOfBounds');
});
node.on('inViewport', () => div.setAttribute('state', 'inViewport'));
node.on('inBounds', () => div.setAttribute('state', 'inBounds'));
node.on('outOfBounds', () => div.setAttribute('state', 'outOfBounds'));

// Monitor only relevant properties by trapping with selective assignment
return this.createProxy(node, div);
}

createTextNode(node: CoreNode): CoreTextNode {
const div = this.createDiv(node.id, node.props);
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
(div as any).node = node;

// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
(node as any).div = div;

return this.createProxy(node, div) as CoreTextNode;
}

createProxy(
node: CoreNode | CoreTextNode,
div: HTMLElement,
): CoreNode | CoreTextNode {
return new Proxy(node, {
set: (target, property: keyof CoreNodeProps, value) => {
this.updateNodeProperty(div, property, value);
return Reflect.set(target, property, value);
},
get: (target, property: keyof CoreNode, receiver: any): any => {
if (property === 'destroy') {
this.destroyNode(target.id);
}
// Define traps for each property in knownProperties
knownProperties.forEach((property) => {
const originalProp = Object.getOwnPropertyDescriptor(node, property);
if (!originalProp) {
return;
}

if (property === 'animate') {
return (props: CoreNodeAnimateProps, settings: AnimationSettings) => {
const anim = target.animate(props, settings);

// Trap the animate start function so we can update the inspector accordingly
return new Proxy(anim, {
get: (target, property: keyof IAnimationController, receiver) => {
if (property === 'start') {
this.animateNode(div, props, settings);
}

return Reflect.get(target, property, receiver);
},
});
};
}
Object.defineProperties(node, {
[property]: {
get() {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return originalProp.get?.call(node);
},
set(value) {
originalProp.set?.call(node, value);
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
this.updateNodeProperty(div, property, value);
},
configurable: true,
enumerable: true,
},
});
});

return Reflect.get(target, property, receiver);
const originalDestroy = node.destroy;
Object.defineProperty(node, 'destroy', {
value: () => {
this.destroyNode(node.id);
originalDestroy.call(node);
},
});

// eslint-disable-next-line @typescript-eslint/unbound-method
const originalAnimate = node.animate;
Object.defineProperty(node, 'animate', {
value: (
props: CoreNodeAnimateProps,
settings: AnimationSettings,
): IAnimationController => {
const animationController = originalAnimate.call(node, props, settings);

return {
...animationController,
start: () => {
this.animateNode(div, props, settings);
return animationController.start();
},
};
},
});

return node;
}

destroyNode(id: number) {
Expand Down
Loading