diff --git a/packages/@ember/-internals/glimmer/lib/base-renderer.ts b/packages/@ember/-internals/glimmer/lib/base-renderer.ts index 955511c4389..d4069d23cf4 100644 --- a/packages/@ember/-internals/glimmer/lib/base-renderer.ts +++ b/packages/@ember/-internals/glimmer/lib/base-renderer.ts @@ -1,6 +1,7 @@ import { ENV } from '@ember/-internals/environment/lib/env'; import type { InternalOwner } from '@ember/-internals/owner'; import { assert } from '@ember/debug'; +import { registerComponentRenderer } from '@ember/debug/lib/capture-render-tree'; import { _backburner, _getCurrentRunLoop } from '@ember/runloop'; import { associateDestroyableChild, @@ -516,6 +517,7 @@ export function renderComponent( hasDOM: env && 'hasDOM' in env ? Boolean(env?.['hasDOM']) : true, }); RENDERER_CACHE.set(owner, renderer); + registerComponentRenderer(owner, renderer); } /** diff --git a/packages/@ember/-internals/glimmer/tests/integration/application/debug-render-tree-test.ts b/packages/@ember/-internals/glimmer/tests/integration/application/debug-render-tree-test.ts index c92745f5a3a..2b0143f8621 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/application/debug-render-tree-test.ts +++ b/packages/@ember/-internals/glimmer/tests/integration/application/debug-render-tree-test.ts @@ -1,7 +1,11 @@ import { ApplicationTestCase, ModuleBasedTestResolver, moduleFor } from 'internal-test-helpers'; import { ENV } from '@ember/-internals/environment'; -import { Component as EmberComponent, setComponentManager } from '@ember/-internals/glimmer'; +import { + Component as EmberComponent, + renderComponent, + setComponentManager, +} from '@ember/-internals/glimmer'; import Component from '@glimmer/component'; import type { InternalOwner } from '@ember/-internals/owner'; import Route from '@ember/routing/route'; @@ -1128,6 +1132,53 @@ if (ENV._DEBUG_RENDER_TREE) { ]); } + async '@test components rendered with renderComponent'() { + await this.visit('/'); + + let HelloWorld = setComponentTemplate( + precompileTemplate('Hello {{@name}}', { strictMode: true }), + templateOnly() + ); + let Root = setComponentTemplate( + precompileTemplate('', { + strictMode: true, + scope: () => ({ HelloWorld }), + }), + templateOnly() + ); + let into = document.createElement('div'); + + let result = runTask(() => renderComponent(Root, { owner: this.owner, into })); + + let roots = captureRenderTree(this.owner).filter((node) => node.type === 'component'); + + this.assertRenderNodes( + roots, + [ + { + type: 'component', + name: '{ROOT}', + args: { positional: [], named: {} }, + instance: null, + bounds: this.elementBounds(into), + children: [ + { + type: 'component', + name: 'HelloWorld', + args: { positional: [], named: { name: 'world' } }, + instance: null, + bounds: this.elementBounds(into), + children: [], + }, + ], + }, + ], + 'root' + ); + + runTask(() => result.destroy()); + } + async '@test components'() { this.add( 'template:application', diff --git a/packages/@ember/debug/lib/capture-render-tree.ts b/packages/@ember/debug/lib/capture-render-tree.ts index 03c5cd4b3fa..13285172bc3 100644 --- a/packages/@ember/debug/lib/capture-render-tree.ts +++ b/packages/@ember/debug/lib/capture-render-tree.ts @@ -1,7 +1,21 @@ import type { Renderer } from '@ember/-internals/glimmer/lib/renderer'; +import type { BaseRenderer } from '@ember/-internals/glimmer/lib/base-renderer'; import type Owner from '@ember/owner'; import type { CapturedRenderNode } from '@glimmer/interfaces'; +/** + * `renderComponent` renders with its own renderer per owner, + * separate from the owner's `renderer:-dom`. + * + * The renderer registers itself here, + * because importing it would be a circular import. + */ +const COMPONENT_RENDERERS = new WeakMap(); + +export function registerComponentRenderer(owner: object, renderer: BaseRenderer): void { + COMPONENT_RENDERERS.set(owner, renderer); +} + /** @module @ember/debug */ @@ -19,14 +33,22 @@ import type { CapturedRenderNode } from '@glimmer/interfaces'; @since 3.14.0 */ export default function captureRenderTree(app: Owner): CapturedRenderNode[] { - let domRenderer = app.lookup('renderer:-dom') as Renderer; + let domRenderer = app.lookup('renderer:-dom') as Renderer | undefined; + let componentRenderer = COMPONENT_RENDERERS.get(app); - if (!domRenderer) { + if (!domRenderer && !componentRenderer) { throw new Error(`BUG: owner is missing renderer`); } // SAFETY: Ideally we'd assert here but that causes awkward circular requires since this is also in @ember/debug. // This is only for debug stuff so not very risky. - let renderer = domRenderer; - return renderer.debugRenderTree.capture(); + let nodes: CapturedRenderNode[] = domRenderer ? domRenderer.debugRenderTree.capture() : []; + + if (componentRenderer) { + for (let node of componentRenderer.debugRenderTree.capture()) { + nodes.push(node); + } + } + + return nodes; }