Skip to content

Commit e0fc6de

Browse files
authored
improve in element support (#2549)
1 parent 6e5ee34 commit e0fc6de

3 files changed

Lines changed: 131 additions & 156 deletions

File tree

ember_debug/libs/render-tree.js

Lines changed: 108 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@ class InElementSupportProvider {
66
constructor(owner) {
77
this.nodeMap = new Map();
88
this.remoteRoots = [];
9-
this.currentNode = null;
10-
this.nodeStack = [];
11-
this.remoteNodeStack = [];
129
this.runtime = this.require('@glimmer/runtime');
10+
this.reference = this.require('@glimmer/reference');
1311
try {
1412
this.Wormhole = requireModule('ember-wormhole/components/ember-wormhole');
1513
} catch (e) {
@@ -27,112 +25,122 @@ class InElementSupportProvider {
2725
reset() {
2826
this.nodeMap.clear();
2927
this.remoteRoots.length = 0;
30-
this.nodeStack.length = 0;
31-
this.remoteNodeStack.length = 0;
32-
this.currentRemoteNode = null;
33-
this.currentNode = null;
34-
}
35-
36-
buildInElementNode(node) {
37-
const obj = Object.create(null);
38-
obj.index = this.currentNode?.refs?.size || 0;
39-
obj.name = 'in-element';
40-
obj.type = 'component';
41-
obj.template = null;
42-
obj.isRemote = true;
43-
obj.args = {
44-
positional: [],
45-
named: {
46-
destination: node,
47-
},
48-
};
49-
obj.instance = {
50-
args: obj.args.named,
51-
constructor: {
52-
name: 'InElement',
53-
},
54-
};
55-
obj.bounds = {
56-
firstNode: node,
57-
lastNode: node,
58-
parentElement: node.parentElement,
59-
};
60-
obj.children = [];
61-
return obj;
6228
}
6329

6430
patch() {
6531
const self = this;
6632

67-
const captureNode = this.debugRenderTree.captureNode;
68-
this.debugRenderTree.captureNode = function (...args) {
69-
const capture = captureNode.call(this, ...args);
70-
const [id, state] = args;
71-
const node = this.nodeFor(state);
72-
self.setupNodeRemotes(node, id, capture);
73-
return capture;
74-
};
33+
const NewElementBuilder = this.NewElementBuilder;
34+
const remoteStack = [];
35+
const componentStack = [];
36+
37+
function createRef(value) {
38+
if (self.reference.createUnboundRef) {
39+
return self.reference.createUnboundRef(value);
40+
} else {
41+
return value;
42+
}
43+
}
7544

76-
const enter = this.debugRenderTree.enter;
77-
this.debugRenderTree.enter = function (...args) {
78-
const state = args[0];
79-
self.enter(this.nodeFor(state));
80-
return enter.call(this, ...args);
45+
const appendChild = this.debugRenderTree.appendChild;
46+
this.debugRenderTree.appendChild = function (node, state) {
47+
if (node.type === 'component') {
48+
componentStack.push(node);
49+
}
50+
return appendChild.call(this, node, state);
8151
};
8252

8353
const exit = this.debugRenderTree.exit;
84-
this.debugRenderTree.exit = function (...args) {
85-
self.exit();
86-
return exit.call(this, ...args);
54+
this.debugRenderTree.exit = function (state) {
55+
const node = this.nodeFor(this.stack.current);
56+
if (node?.type === 'component') {
57+
componentStack.pop();
58+
}
59+
exit.call(this, state);
8760
};
8861

89-
const NewElementBuilder = this.NewElementBuilder;
9062
const didAppendNode = NewElementBuilder.prototype.didAppendNode;
9163
NewElementBuilder.prototype.didAppendNode = function (...args) {
92-
args[0].__emberInspectorParentNode = self.currentNode;
64+
args[0].__emberInspectorParentNode = componentStack.at(-1);
9365
return didAppendNode.call(this, ...args);
9466
};
9567

9668
const pushElement = NewElementBuilder.prototype.pushElement;
9769
NewElementBuilder.prototype.pushElement = function (...args) {
98-
args[0].__emberInspectorParentNode = self.currentNode;
99-
return pushElement.call(this, ...args);
70+
pushElement.call(this, ...args);
71+
args[0].__emberInspectorParentNode = componentStack.at(-1);
10072
};
10173

10274
const pushRemoteElement = NewElementBuilder.prototype.pushRemoteElement;
103-
NewElementBuilder.prototype.pushRemoteElement = function (...args) {
104-
const block = pushRemoteElement.call(this, ...args);
105-
self.registerRemote(block, ...args);
106-
self.nodeStack.push(self.currentNode);
107-
self.remoteNodeStack.push(self.currentNode);
108-
self.currentRemoteNode = self.currentNode;
109-
return block;
75+
NewElementBuilder.prototype.pushRemoteElement = function (
76+
element,
77+
guid,
78+
insertBefore
79+
) {
80+
remoteStack.push({ element });
81+
const ref = createRef(element);
82+
const capturedArgs = {
83+
positional: [ref],
84+
named: {},
85+
};
86+
if (insertBefore) {
87+
capturedArgs.named.insertBefore = insertBefore;
88+
}
89+
const inElementArgs = self.reference.createUnboundRef
90+
? capturedArgs
91+
: {
92+
value() {
93+
return capturedArgs;
94+
},
95+
};
96+
const debugRenderTree = self.debugRenderTree;
97+
debugRenderTree?.create(remoteStack.at(-1), {
98+
type: 'keyword',
99+
name: 'in-element',
100+
args: inElementArgs,
101+
instance: {
102+
args: {
103+
named: {
104+
insertBefore,
105+
},
106+
positional: [element],
107+
},
108+
constructor: {
109+
name: 'InElement',
110+
},
111+
},
112+
});
113+
return pushRemoteElement.call(this, element, guid, insertBefore);
110114
};
111115

112116
const popRemoteElement = NewElementBuilder.prototype.popRemoteElement;
113117
NewElementBuilder.prototype.popRemoteElement = function (...args) {
114-
const block = popRemoteElement.call(this, ...args);
115-
self.remoteNodeStack.pop();
116-
self.nodeStack.pop();
117-
self.currentRemoteNode =
118-
self.remoteNodeStack[self.remoteNodeStack.length - 1];
119-
return block;
118+
const element = this.element;
119+
popRemoteElement.call(this, ...args);
120+
const parentElement = this.element;
121+
const debugRenderTree = self.debugRenderTree;
122+
debugRenderTree?.didRender(remoteStack.at(-1), {
123+
parentElement: () => parentElement,
124+
firstNode: () => element,
125+
lastNode: () => element,
126+
});
127+
remoteStack.pop();
120128
};
121129

122130
this.debugRenderTreeFunctions = {
131+
appendChild,
123132
exit,
124-
enter,
125-
captureNode,
126133
};
127134
this.NewElementBuilderFunctions = {
128135
pushElement,
129136
pushRemoteElement,
137+
popRemoteElement,
130138
didAppendNode,
131139
};
132140
}
133141

134142
teardown() {
135-
if (!this.debugRenderTreeFunctions) {
143+
if (!this.NewElementBuilderFunctions) {
136144
return;
137145
}
138146
Object.assign(this.debugRenderTree, this.debugRenderTreeFunctions);
@@ -147,81 +155,6 @@ class InElementSupportProvider {
147155
? requireModule(req)
148156
: EmberLoader.require(req);
149157
}
150-
151-
enter(node) {
152-
if (this.currentNode && this.currentNode === this.currentRemoteNode) {
153-
this.currentRemoteNode.children.push(node);
154-
node.remoteParent = this.currentRemoteNode;
155-
}
156-
this.currentNode = node;
157-
this.nodeStack.push(this.currentNode);
158-
}
159-
160-
exit() {
161-
this.nodeStack.pop();
162-
this.currentNode = this.nodeStack[this.nodeStack.length - 1];
163-
}
164-
165-
registerRemote(block, node) {
166-
const obj = this.buildInElementNode(node);
167-
if (this.currentNode) {
168-
if (!this.currentNode.remotes) {
169-
Object.defineProperty(this.currentNode, 'remotes', {
170-
value: [],
171-
});
172-
}
173-
this.currentNode.remotes.push(obj);
174-
}
175-
this.remoteRoots.push(obj);
176-
this.currentNode = obj;
177-
}
178-
179-
setupNodeRemotes(node, id, capture) {
180-
capture.isInRemote = !!node.remoteParent;
181-
this.nodeMap.set(node, id);
182-
if (node.remoteParent) {
183-
const idx = node.remoteParent.children.indexOf(node);
184-
if (idx >= 0) {
185-
node.remoteParent.children[idx] = capture;
186-
}
187-
}
188-
capture.children = capture.children.filter((c) => !c.isInRemote);
189-
node.remotes?.forEach((remote) => {
190-
remote.id = 'remote-render-node:' + this.remoteRoots.length;
191-
this.nodeMap.set(remote, remote.id);
192-
this.remoteRoots.push(remote);
193-
capture.children.splice(remote.index, 0, remote);
194-
});
195-
if (capture.instance?.__emberInspectorTargetNode) {
196-
Object.defineProperty(capture, 'bounds', {
197-
get() {
198-
return {
199-
firstNode: capture.instance.__emberInspectorTargetNode,
200-
lastNode: capture.instance.__emberInspectorTargetNode,
201-
parentElement:
202-
capture.instance.__emberInspectorTargetNode.parentElement,
203-
};
204-
},
205-
});
206-
}
207-
if (this.Wormhole && capture.instance instanceof this.Wormhole.default) {
208-
this.remoteRoots.push(capture);
209-
const bounds = capture.bounds;
210-
Object.defineProperty(capture, 'bounds', {
211-
get() {
212-
if (capture.instance._destination) {
213-
return {
214-
firstNode: capture.instance._destination,
215-
lastNode: capture.instance._destination,
216-
parentElement: capture.instance._destination.parentElement,
217-
};
218-
}
219-
return bounds;
220-
},
221-
});
222-
}
223-
return capture;
224-
}
225158
}
226159

227160
export default class RenderTree {
@@ -242,6 +175,7 @@ export default class RenderTree {
242175
try {
243176
this.inElementSupport = new InElementSupportProvider(owner);
244177
} catch (e) {
178+
console.error('failed to setup in element support', e);
245179
// not supported
246180
}
247181

@@ -325,7 +259,7 @@ export default class RenderTree {
325259
let hintNode = this._findUp(this.nodes[hint]);
326260
let hints = [hintNode];
327261
if (node.__emberInspectorParentNode) {
328-
const remoteNode = this.inElementSupport.nodeMap.get(node);
262+
const remoteNode = this.inElementSupport?.nodeMap.get(node);
329263
const n = remoteNode && this.nodes[remoteNode];
330264
hints.push(n);
331265
}
@@ -496,6 +430,31 @@ export default class RenderTree {
496430

497431
if (serialized === undefined) {
498432
this.nodes[node.id] = node;
433+
if (node.type === 'keyword') {
434+
node.type = 'component';
435+
this.inElementSupport?.nodeMap.set(node, node.id);
436+
this.inElementSupport?.remoteRoots.push(node);
437+
}
438+
439+
if (
440+
this.inElementSupport?.Wormhole &&
441+
node.instance instanceof this.inElementSupport.Wormhole.default
442+
) {
443+
this.inElementSupport?.remoteRoots.push(node);
444+
const bounds = node.bounds;
445+
Object.defineProperty(node, 'bounds', {
446+
get() {
447+
if (node.instance._destination) {
448+
return {
449+
firstNode: node.instance._destination,
450+
lastNode: node.instance._destination,
451+
parentElement: node.instance._destination.parentElement,
452+
};
453+
}
454+
return bounds;
455+
},
456+
});
457+
}
499458

500459
if (parentNode) {
501460
this.parentNodes[node.id] = parentNode;
@@ -683,11 +642,8 @@ function isSingleNode({ firstNode, lastNode }) {
683642
return firstNode === lastNode;
684643
}
685644

686-
function isAttached({ parentElement, firstNode, lastNode }) {
687-
return (
688-
parentElement === firstNode.parentElement &&
689-
parentElement === lastNode.parentElement
690-
);
645+
function isAttached({ firstNode, lastNode }) {
646+
return firstNode.isConnected && lastNode.isConnected;
691647
}
692648

693649
function isEmptyRect({ x, y, width, height }) {

0 commit comments

Comments
 (0)