Skip to content
Closed
Show file tree
Hide file tree
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
2 changes: 2 additions & 0 deletions packages/@ember/-internals/glimmer/lib/base-renderer.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -516,6 +517,7 @@ export function renderComponent(
hasDOM: env && 'hasDOM' in env ? Boolean(env?.['hasDOM']) : true,
});
RENDERER_CACHE.set(owner, renderer);
registerComponentRenderer(owner, renderer);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ApplicationTestCase, ModuleBasedTestResolver, moduleFor } from 'internal-test-helpers';

import { ENV } from '@ember/-internals/environment';
import { Component, setComponentManager } from '@ember/-internals/glimmer';
import { Component, renderComponent, setComponentManager } from '@ember/-internals/glimmer';
import type { InternalOwner } from '@ember/-internals/owner';
import Route from '@ember/routing/route';
import Controller from '@ember/controller';
Expand Down Expand Up @@ -1127,6 +1127,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('<HelloWorld @name="world" />', {
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 <Input> components'() {
this.add(
'template:application',
Expand Down
30 changes: 26 additions & 4 deletions packages/@ember/debug/lib/capture-render-tree.ts
Original file line number Diff line number Diff line change
@@ -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<object, BaseRenderer>();

export function registerComponentRenderer(owner: object, renderer: BaseRenderer): void {
COMPONENT_RENDERERS.set(owner, renderer);
}

/**
@module @ember/debug
*/
Expand All @@ -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;
}
Loading