diff --git a/packages/@glimmer-workspace/integration-tests/test/debug-render-tree-test.ts b/packages/@glimmer-workspace/integration-tests/test/debug-render-tree-test.ts
index 0a1b3c8885..f1acdc5d49 100644
--- a/packages/@glimmer-workspace/integration-tests/test/debug-render-tree-test.ts
+++ b/packages/@glimmer-workspace/integration-tests/test/debug-render-tree-test.ts
@@ -283,6 +283,54 @@ class DebugRenderTreeTest extends RenderTest {
]);
}
+ @test 'in-element in tree'() {
+ this.registerComponent('Glimmer', 'HiWorld', 'Hi World');
+ this.registerComponent(
+ 'Glimmer',
+ 'HelloWorld',
+ '{{#in-element this.destinationElement}}{{/in-element}}',
+ class extends GlimmerishComponent {
+ get destinationElement() {
+ return document.getElementById('target');
+ }
+ }
+ );
+
+ this.render(`
`);
+
+ this.assertRenderTree([
+ {
+ type: 'component',
+ name: 'HelloWorld',
+ args: { positional: [], named: { arg: 'first' } },
+ instance: (instance: GlimmerishComponent) => instance.args['arg'] === 'first',
+ template: '(unknown template module)',
+ bounds: this.nodeBounds(this.element.firstChild!.nextSibling),
+ children: [
+ {
+ type: 'keyword',
+ name: 'in-element',
+ args: { positional: [this.element.firstChild], named: {} },
+ instance: (instance: GlimmerishComponent) => instance === null,
+ template: null,
+ bounds: this.elementBounds(this.element.firstChild! as unknown as Element),
+ children: [
+ {
+ type: 'component',
+ name: 'HiWorld',
+ args: { positional: [], named: {} },
+ instance: (instance: GlimmerishComponent) => instance,
+ template: '(unknown template module)',
+ bounds: this.nodeBounds(this.element.firstChild!.firstChild),
+ children: [],
+ },
+ ],
+ },
+ ],
+ },
+ ]);
+ }
+
@test 'getDebugCustomRenderTree works'() {
let bucket1 = {};
let instance1 = {};
@@ -532,7 +580,7 @@ class DebugRenderTreeTest extends RenderTest {
this.assertRenderNode(actualNode, expected, `${actualNode.type}:${actualNode.name}`);
});
} else {
- this.assert.deepEqual(actual, [], path);
+ this.assert.deepEqual(actual, expectedNodes, path);
}
}
diff --git a/packages/@glimmer/interfaces/lib/dom/attributes.d.ts b/packages/@glimmer/interfaces/lib/dom/attributes.d.ts
index 370671a937..82adb3bcb6 100644
--- a/packages/@glimmer/interfaces/lib/dom/attributes.d.ts
+++ b/packages/@glimmer/interfaces/lib/dom/attributes.d.ts
@@ -37,8 +37,8 @@ export interface DOMStack {
element: SimpleElement,
guid: string,
insertBefore: Maybe
- ): Nullable;
- popRemoteElement(): void;
+ ): RemoteLiveBlock;
+ popRemoteElement(): RemoteLiveBlock;
popElement(): void;
openElement(tag: string, _operations?: ElementOperations): SimpleElement;
flushElement(modifiers: Nullable): void;
diff --git a/packages/@glimmer/interfaces/lib/runtime/debug-render-tree.d.ts b/packages/@glimmer/interfaces/lib/runtime/debug-render-tree.d.ts
index f9f8ae5943..a2edcedce5 100644
--- a/packages/@glimmer/interfaces/lib/runtime/debug-render-tree.d.ts
+++ b/packages/@glimmer/interfaces/lib/runtime/debug-render-tree.d.ts
@@ -3,7 +3,7 @@ import type { SimpleElement, SimpleNode } from '@simple-dom/interface';
import type { Bounds } from '../dom/bounds.js';
import type { Arguments, CapturedArguments } from './arguments.js';
-export type RenderNodeType = 'outlet' | 'engine' | 'route-template' | 'component';
+export type RenderNodeType = 'outlet' | 'engine' | 'route-template' | 'component' | 'keyword';
export interface RenderNode {
type: RenderNodeType;
diff --git a/packages/@glimmer/node/lib/serialize-builder.ts b/packages/@glimmer/node/lib/serialize-builder.ts
index e161956c36..baa8c1dacc 100644
--- a/packages/@glimmer/node/lib/serialize-builder.ts
+++ b/packages/@glimmer/node/lib/serialize-builder.ts
@@ -129,7 +129,7 @@ class SerializeBuilder extends NewElementBuilder implements ElementBuilder {
element: SimpleElement,
cursorId: string,
insertBefore: Maybe = null
- ): Nullable {
+ ): RemoteLiveBlock {
let { dom } = this;
let script = dom.createElement('script');
script.setAttribute('glmr', cursorId);
diff --git a/packages/@glimmer/runtime/lib/compiled/opcodes/dom.ts b/packages/@glimmer/runtime/lib/compiled/opcodes/dom.ts
index d6c2a377dc..94542550d0 100644
--- a/packages/@glimmer/runtime/lib/compiled/opcodes/dom.ts
+++ b/packages/@glimmer/runtime/lib/compiled/opcodes/dom.ts
@@ -19,7 +19,7 @@ import {
CheckOption,
CheckString,
} from '@glimmer/debug';
-import { associateDestroyableChild, destroy } from '@glimmer/destroyable';
+import { associateDestroyableChild, destroy, registerDestructor } from '@glimmer/destroyable';
import { getInternalModifierManager } from '@glimmer/manager';
import { createComputeRef, isConstRef, valueForRef } from '@glimmer/reference';
import { debugToString, expect, isObject } from '@glimmer/util';
@@ -32,6 +32,7 @@ import type { DynamicAttribute } from '../../vm/attributes/dynamic';
import { isCurriedType, resolveCurriedValue } from '../../curried-value';
import { APPEND_OPCODES } from '../../opcodes';
import { CONSTANTS } from '../../symbols';
+import { createCapturedArgs } from '../../vm/arguments';
import { CheckArguments, CheckOperations, CheckReference } from './-debug-strip';
import { Assert } from './vm';
@@ -71,10 +72,36 @@ APPEND_OPCODES.add(Op.PushRemoteElement, (vm) => {
let block = vm.elements().pushRemoteElement(element, guid, insertBefore);
if (block) vm.associateDestroyable(block);
+
+ if (vm.env.debugRenderTree !== undefined) {
+ // Note that there is nothing to update – when the args for an
+ // {{#in-element}} changes it gets torn down and a new one is
+ // re-created/rendered in its place (see the `Assert`s above)
+ let args = createCapturedArgs(
+ insertBefore === undefined ? {} : { insertBefore: insertBeforeRef },
+ [elementRef]
+ );
+
+ vm.env.debugRenderTree.create(block, {
+ type: 'keyword',
+ name: 'in-element',
+ args,
+ instance: null,
+ });
+
+ registerDestructor(block, () => {
+ vm.env.debugRenderTree?.willDestroy(block);
+ });
+ }
});
APPEND_OPCODES.add(Op.PopRemoteElement, (vm) => {
- vm.elements().popRemoteElement();
+ let bounds = vm.elements().popRemoteElement();
+
+ if (vm.env.debugRenderTree !== undefined) {
+ // The RemoteLiveBlock is also its bounds
+ vm.env.debugRenderTree.didRender(bounds, bounds);
+ }
});
APPEND_OPCODES.add(Op.FlushElement, (vm) => {
diff --git a/packages/@glimmer/runtime/lib/vm/element-builder.ts b/packages/@glimmer/runtime/lib/vm/element-builder.ts
index e4e6d30012..f79273694f 100644
--- a/packages/@glimmer/runtime/lib/vm/element-builder.ts
+++ b/packages/@glimmer/runtime/lib/vm/element-builder.ts
@@ -100,7 +100,6 @@ export class NewElementBuilder implements ElementBuilder {
constructor(env: Environment, parentNode: SimpleElement, nextSibling: Nullable) {
this.pushElement(parentNode, nextSibling);
-
this.env = env;
this.dom = env.getAppendOperations();
this.updateOperations = env.getDOM();
@@ -214,7 +213,7 @@ export class NewElementBuilder implements ElementBuilder {
element: SimpleElement,
guid: string,
insertBefore: Maybe
- ): Nullable {
+ ): RemoteLiveBlock {
return this.__pushRemoteElement(element, guid, insertBefore);
}
@@ -222,7 +221,7 @@ export class NewElementBuilder implements ElementBuilder {
element: SimpleElement,
_guid: string,
insertBefore: Maybe
- ): Nullable {
+ ): RemoteLiveBlock {
this.pushElement(element, insertBefore);
if (insertBefore === undefined) {
@@ -236,12 +235,14 @@ export class NewElementBuilder implements ElementBuilder {
return this.pushLiveBlock(block, true);
}
- popRemoteElement() {
- this.popBlock();
+ popRemoteElement(): RemoteLiveBlock {
+ const block = this.popBlock();
+ assert(block instanceof RemoteLiveBlock, '[BUG] expecting a RemoteLiveBlock');
this.popElement();
+ return block;
}
- protected pushElement(element: SimpleElement, nextSibling: Maybe = null) {
+ protected pushElement(element: SimpleElement, nextSibling: Maybe = null): void {
this[CURSOR_STACK].push(new CursorImpl(element, nextSibling));
}
@@ -268,7 +269,7 @@ export class NewElementBuilder implements ElementBuilder {
return element;
}
- willCloseElement() {
+ willCloseElement(): void {
this.block().closeElement();
}
diff --git a/packages/@glimmer/runtime/lib/vm/rehydrate-builder.ts b/packages/@glimmer/runtime/lib/vm/rehydrate-builder.ts
index dbb91bd300..66501924e4 100644
--- a/packages/@glimmer/runtime/lib/vm/rehydrate-builder.ts
+++ b/packages/@glimmer/runtime/lib/vm/rehydrate-builder.ts
@@ -455,7 +455,7 @@ export class RehydrateBuilder extends NewElementBuilder implements ElementBuilde
element: SimpleElement,
cursorId: string,
insertBefore: Maybe
- ): Nullable {
+ ): RemoteLiveBlock {
const marker = this.getMarker(castToBrowser(element, 'HTML'), cursorId);
assert(