diff --git a/ember_debug/libs/render-tree.js b/ember_debug/libs/render-tree.js index e3a9659a81..62703acb95 100644 --- a/ember_debug/libs/render-tree.js +++ b/ember_debug/libs/render-tree.js @@ -6,10 +6,8 @@ class InElementSupportProvider { constructor(owner) { this.nodeMap = new Map(); this.remoteRoots = []; - this.currentNode = null; - this.nodeStack = []; - this.remoteNodeStack = []; this.runtime = this.require('@glimmer/runtime'); + this.reference = this.require('@glimmer/reference'); try { this.Wormhole = requireModule('ember-wormhole/components/ember-wormhole'); } catch (e) { @@ -27,115 +25,105 @@ class InElementSupportProvider { reset() { this.nodeMap.clear(); this.remoteRoots.length = 0; - this.nodeStack.length = 0; - this.remoteNodeStack.length = 0; - this.currentRemoteNode = null; - this.currentNode = null; - } - - buildInElementNode(node) { - const obj = Object.create(null); - obj.index = this.currentNode?.refs?.size || 0; - obj.name = 'in-element'; - obj.type = 'component'; - obj.template = null; - obj.isRemote = true; - obj.args = { - positional: [], - named: { - destination: node, - }, - }; - obj.instance = { - args: obj.args.named, - constructor: { - name: 'InElement', - }, - }; - obj.bounds = { - firstNode: node, - lastNode: node, - parentElement: node.parentElement, - }; - obj.children = []; - return obj; } patch() { const self = this; - const captureNode = this.debugRenderTree.captureNode; - this.debugRenderTree.captureNode = function (...args) { - const capture = captureNode.call(this, ...args); - const [id, state] = args; - const node = this.nodeFor(state); - self.setupNodeRemotes(node, id, capture); - return capture; - }; - - const enter = this.debugRenderTree.enter; - this.debugRenderTree.enter = function (...args) { - const state = args[0]; - self.enter(this.nodeFor(state)); - return enter.call(this, ...args); - }; - - const exit = this.debugRenderTree.exit; - this.debugRenderTree.exit = function (...args) { - self.exit(); - return exit.call(this, ...args); - }; - const NewElementBuilder = this.NewElementBuilder; + const remoteStack = []; + const didAppendNode = NewElementBuilder.prototype.didAppendNode; NewElementBuilder.prototype.didAppendNode = function (...args) { - args[0].__emberInspectorParentNode = self.currentNode; + const debugRenderTree = + this.env?.debugRenderTree || this.env?.extra?.debugRenderTree; + const parent = debugRenderTree.stack.current; + args[0].__emberInspectorParentNode = debugRenderTree.nodeFor(parent); return didAppendNode.call(this, ...args); }; const pushElement = NewElementBuilder.prototype.pushElement; NewElementBuilder.prototype.pushElement = function (...args) { - args[0].__emberInspectorParentNode = self.currentNode; + const debugRenderTree = + this.env?.debugRenderTree || this.env?.extra?.debugRenderTree; + // for first push element, .env is not yet set... + if (debugRenderTree) { + const parent = debugRenderTree.stack.current; + args[0].__emberInspectorParentNode = debugRenderTree.nodeFor(parent); + } return pushElement.call(this, ...args); }; const pushRemoteElement = NewElementBuilder.prototype.pushRemoteElement; NewElementBuilder.prototype.pushRemoteElement = function (...args) { - const block = pushRemoteElement.call(this, ...args); - self.registerRemote(block, ...args); - self.nodeStack.push(self.currentNode); - self.remoteNodeStack.push(self.currentNode); - self.currentRemoteNode = self.currentNode; - return block; + const element = args[0]; + remoteStack.push({}); + let capturedArgs, ref; + if (self.reference.createUnboundRef) { + ref = self.reference.createUnboundRef?.(element); + capturedArgs = { + positional: [], + named: { + destination: ref, + }, + }; + } else { + ref = new self.reference.ConstReference(() => element); + capturedArgs = { + value() { + return { + positional: [], + named: { + destination: ref, + }, + }; + }, + }; + } + const debugRenderTree = + this.env?.debugRenderTree || this.env?.extra?.debugRenderTree; + debugRenderTree?.create(remoteStack.at(-1), { + type: 'remote-element', + name: 'in-element', + args: capturedArgs, + instance: { + args: { + destination: element, + }, + constructor: { + name: 'InElement', + }, + }, + }); + return pushRemoteElement.call(this, ...args); }; const popRemoteElement = NewElementBuilder.prototype.popRemoteElement; NewElementBuilder.prototype.popRemoteElement = function (...args) { - const block = popRemoteElement.call(this, ...args); - self.remoteNodeStack.pop(); - self.nodeStack.pop(); - self.currentRemoteNode = - self.remoteNodeStack[self.remoteNodeStack.length - 1]; - return block; + const element = this.element; + const debugRenderTree = + this.env?.debugRenderTree || this.env?.extra?.debugRenderTree; + debugRenderTree?.didRender(remoteStack.at(-1), { + parentElement: () => element.parentElement, + firstNode: () => element, + lastNode: () => element, + }); + remoteStack.pop(); + return popRemoteElement.call(this, ...args); }; - this.debugRenderTreeFunctions = { - exit, - enter, - captureNode, - }; this.NewElementBuilderFunctions = { pushElement, pushRemoteElement, + popRemoteElement, didAppendNode, }; } teardown() { - if (!this.debugRenderTreeFunctions) { + if (!this.NewElementBuilderFunctions) { return; } - Object.assign(this.debugRenderTree, this.debugRenderTreeFunctions); Object.assign( this.NewElementBuilder.prototype, this.NewElementBuilderFunctions @@ -147,81 +135,6 @@ class InElementSupportProvider { ? requireModule(req) : EmberLoader.require(req); } - - enter(node) { - if (this.currentNode && this.currentNode === this.currentRemoteNode) { - this.currentRemoteNode.children.push(node); - node.remoteParent = this.currentRemoteNode; - } - this.currentNode = node; - this.nodeStack.push(this.currentNode); - } - - exit() { - this.nodeStack.pop(); - this.currentNode = this.nodeStack[this.nodeStack.length - 1]; - } - - registerRemote(block, node) { - const obj = this.buildInElementNode(node); - if (this.currentNode) { - if (!this.currentNode.remotes) { - Object.defineProperty(this.currentNode, 'remotes', { - value: [], - }); - } - this.currentNode.remotes.push(obj); - } - this.remoteRoots.push(obj); - this.currentNode = obj; - } - - setupNodeRemotes(node, id, capture) { - capture.isInRemote = !!node.remoteParent; - this.nodeMap.set(node, id); - if (node.remoteParent) { - const idx = node.remoteParent.children.indexOf(node); - if (idx >= 0) { - node.remoteParent.children[idx] = capture; - } - } - capture.children = capture.children.filter((c) => !c.isInRemote); - node.remotes?.forEach((remote) => { - remote.id = 'remote-render-node:' + this.remoteRoots.length; - this.nodeMap.set(remote, remote.id); - this.remoteRoots.push(remote); - capture.children.splice(remote.index, 0, remote); - }); - if (capture.instance?.__emberInspectorTargetNode) { - Object.defineProperty(capture, 'bounds', { - get() { - return { - firstNode: capture.instance.__emberInspectorTargetNode, - lastNode: capture.instance.__emberInspectorTargetNode, - parentElement: - capture.instance.__emberInspectorTargetNode.parentElement, - }; - }, - }); - } - if (this.Wormhole && capture.instance instanceof this.Wormhole.default) { - this.remoteRoots.push(capture); - const bounds = capture.bounds; - Object.defineProperty(capture, 'bounds', { - get() { - if (capture.instance._destination) { - return { - firstNode: capture.instance._destination, - lastNode: capture.instance._destination, - parentElement: capture.instance._destination.parentElement, - }; - } - return bounds; - }, - }); - } - return capture; - } } export default class RenderTree { @@ -496,6 +409,31 @@ export default class RenderTree { if (serialized === undefined) { this.nodes[node.id] = node; + if (node.type === 'remote-element') { + node.type = 'component'; + this.inElementSupport?.nodeMap.set(node, node.id); + this.inElementSupport?.remoteRoots.push(node); + } + + if ( + this.inElementSupport?.Wormhole && + node.instance instanceof this.inElementSupport.Wormhole.default + ) { + this.inElementSupport?.remoteRoots.push(node); + const bounds = node.bounds; + Object.defineProperty(node, 'bounds', { + get() { + if (node.instance._destination) { + return { + firstNode: node.instance._destination, + lastNode: node.instance._destination, + parentElement: node.instance._destination.parentElement, + }; + } + return bounds; + }, + }); + } if (parentNode) { this.parentNodes[node.id] = parentNode;