diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/dynamic-components-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/dynamic-components-test.js index c9ed2aa9765..bc62ed4a559 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/dynamic-components-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/dynamic-components-test.js @@ -1,5 +1,5 @@ import { DEBUG } from '@glimmer/env'; -import { moduleFor, RenderingTestCase, strip, runTask } from 'internal-test-helpers'; +import { moduleFor, RenderingTestCase, strip, runTask, runAppend } from 'internal-test-helpers'; import { set, computed } from '@ember/object'; import { precompileTemplate } from '@ember/template-compilation'; @@ -87,6 +87,50 @@ moduleFor( }); } + ['@test it throws a useful assertion for an invalid dynamic component value in non-strict mode']( + assert + ) { + if (!DEBUG) { + assert.expect(0); + return; + } + + assert.throws(() => { + this.render('{{component this.componentName}}', { + componentName: { name: 'not-a-component' }, + }); + }, /The `{{component}}` helper received an invalid value\. It expects a component definition or a string component name\./); + } + + ['@test it throws a useful assertion for an invalid dynamic component value in strict mode']( + assert + ) { + if (!DEBUG) { + assert.expect(0); + return; + } + + assert.throws(() => { + this.owner.register( + 'template:-top-level', + this.compile('{{component this.componentName}}', { + moduleName: '-top-level', + strictMode: true, + }) + ); + + let attrs = { + componentName: { name: 'not-a-component' }, + tagName: '', + layoutName: '-top-level', + }; + + this.owner.register('component:-top-level', Component.extend(attrs)); + this.component = this.owner.lookup('component:-top-level'); + runAppend(this.component); + }, /The `{{component}}` helper received an invalid value\. In strict mode, it expects a component definition\./); + } + ['@test it has an element']() { let instance; diff --git a/packages/@glimmer/runtime/lib/compiled/opcodes/component.ts b/packages/@glimmer/runtime/lib/compiled/opcodes/component.ts index ec5b2c206d1..959e1ce7be8 100644 --- a/packages/@glimmer/runtime/lib/compiled/opcodes/component.ts +++ b/packages/@glimmer/runtime/lib/compiled/opcodes/component.ts @@ -72,6 +72,7 @@ import { expect, unwrap } from '@glimmer/debug-util/lib/platform-utils'; import assert from '@glimmer/debug-util/lib/assert'; import { unwrapTemplate } from '@glimmer/debug-util/lib/template'; import { registerDestructor } from '@glimmer/destroyable'; +import { hasInternalComponentManager } from '@glimmer/manager/lib/internal/api'; import { managerHasCapability } from '@glimmer/manager/lib/util/capabilities'; import { isConstRef, valueForRef } from '@glimmer/reference/lib/reference'; import { assign } from '@glimmer/util/lib/object-utils'; @@ -168,6 +169,20 @@ APPEND_OPCODES.add(VM_RESOLVE_DYNAMIC_COMPONENT_OP, (vm, { op1: _isStrict }) => vm.loadValue($t1, null); // Clear the temp register + if (DEBUG) { + assert( + component === null || + component === undefined || + typeof component === 'string' || + isCurriedValue(component) || + ((typeof component === 'object' || typeof component === 'function') && + hasInternalComponentManager(component)), + isStrict + ? 'The `{{component}}` helper received an invalid value. In strict mode, it expects a component definition.' + : 'The `{{component}}` helper received an invalid value. It expects a component definition or a string component name.' + ); + } + let definition: ComponentDefinition | CurriedValue; if (typeof component === 'string') {