From 833cbbfc87dddb4bda63a832f3c8adde3978cd49 Mon Sep 17 00:00:00 2001 From: Peter Wagenet Date: Tue, 7 Jul 2026 23:14:37 -0400 Subject: [PATCH 1/2] Deprecate Evented and @ember/object/events per RFC 1111 Squashed from PR #20970 by Peter Wagenet, rebased onto current main. Co-authored-by: Peter Wagenet --- .gitignore | 1 + package.json | 1 + .../@ember/-internals/deprecations/index.ts | 9 +- .../glimmer/lib/component-managers/curly.ts | 27 +- .../glimmer/lib/component-managers/root.ts | 5 +- .../@ember/-internals/glimmer/lib/renderer.ts | 3 +- .../lib/utils/curly-component-state-bucket.ts | 5 +- .../integration/components/append-test.js | 34 +- .../components/curly-components-test.js | 85 +- .../integration/components/life-cycle-test.js | 3 +- .../integration/event-dispatcher-test.js | 21 +- .../tests/integration/syntax/each-test.js | 45 +- packages/@ember/-internals/meta/lib/meta.ts | 2 +- .../@ember/-internals/metal/lib/events.ts | 46 +- .../@ember/-internals/metal/lib/observer.ts | 11 +- .../-internals/metal/tests/computed_test.js | 4 +- .../-internals/metal/tests/events_test.js | 99 ++- packages/@ember/-internals/utils/index.ts | 6 + .../-internals/utils/lib/mixin-deprecation.ts | 44 + packages/@ember/-internals/views/index.ts | 1 + .../views/lib/views/core-view-utils.ts | 14 + .../-internals/views/lib/views/core_view.ts | 3 +- .../-internals/views/lib/views/states.ts | 7 +- packages/@ember/object/evented.ts | 43 +- packages/@ember/object/events.ts | 79 +- packages/@ember/object/mixin.ts | 27 +- .../object/tests/es-compatibility-test.js | 70 +- packages/@ember/object/tests/evented_test.js | 41 +- packages/@ember/object/tests/events_test.js | 363 +++++++-- packages/@ember/routing/route.ts | 11 +- packages/@ember/routing/router-service.ts | 110 ++- packages/@ember/routing/router.ts | 11 +- packages/ember/barrel.ts | 758 ++++++++++++++++++ .../tests/routing/decoupled_basic_test.js | 43 +- .../@ember/routing-test/router-service.ts | 2 +- 35 files changed, 1780 insertions(+), 254 deletions(-) create mode 100644 packages/@ember/-internals/utils/lib/mixin-deprecation.ts create mode 100644 packages/@ember/-internals/views/lib/views/core-view-utils.ts create mode 100644 packages/ember/barrel.ts diff --git a/.gitignore b/.gitignore index 9bf32df156f..72e4cfd78b3 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,4 @@ npm-debug.log # couple of the files. Once it is, we can switch this over to just ignoring # `types/stable` entirely. types/stable +.puppeteer-cache/ diff --git a/package.json b/package.json index 86c1d3d37db..c3455ab2707 100644 --- a/package.json +++ b/package.json @@ -204,6 +204,7 @@ "@ember/-internals/views/lib/mixins/action_support.js": "ember-source/@ember/-internals/views/lib/mixins/action_support.js", "@ember/-internals/views/lib/system/event_dispatcher.js": "ember-source/@ember/-internals/views/lib/system/event_dispatcher.js", "@ember/-internals/views/lib/system/utils.js": "ember-source/@ember/-internals/views/lib/system/utils.js", + "@ember/-internals/views/lib/views/core-view-utils.js": "ember-source/@ember/-internals/views/lib/views/core-view-utils.js", "@ember/-internals/views/lib/views/core_view.js": "ember-source/@ember/-internals/views/lib/views/core_view.js", "@ember/-internals/views/lib/views/states.js": "ember-source/@ember/-internals/views/lib/views/states.js", "@ember/application/index.js": "ember-source/@ember/application/index.js", diff --git a/packages/@ember/-internals/deprecations/index.ts b/packages/@ember/-internals/deprecations/index.ts index 9b1c7e93827..1bde5572dbc 100644 --- a/packages/@ember/-internals/deprecations/index.ts +++ b/packages/@ember/-internals/deprecations/index.ts @@ -20,7 +20,7 @@ export function isRemoved(options: DeprecationOptions) { return emberVersionGte(options.until); } -interface DeprecationObject { +export interface DeprecationObject { options: DeprecationOptions; test: boolean; isEnabled: boolean; @@ -119,6 +119,13 @@ export const DEPRECATIONS = { until: '7.5.0', url: 'https://deprecations.emberjs.com/id/deprecate-comparable-mixin', }), + DEPRECATE_EVENTED: deprecation({ + id: 'ember-evented', + for: 'ember-source', + since: { available: '6.12.0' }, + until: '8.0.0', + url: 'https://deprecations.emberjs.com/id/ember-evented', + }), }; export function deprecateUntil(message: string, deprecation: DeprecationObject) { diff --git a/packages/@ember/-internals/glimmer/lib/component-managers/curly.ts b/packages/@ember/-internals/glimmer/lib/component-managers/curly.ts index a79ef3392af..c7e1027ca35 100644 --- a/packages/@ember/-internals/glimmer/lib/component-managers/curly.ts +++ b/packages/@ember/-internals/glimmer/lib/component-managers/curly.ts @@ -10,6 +10,7 @@ import { setElementView, setViewElement, } from '@ember/-internals/views/lib/system/utils'; +import { sendCoreViewEvent } from '@ember/-internals/views/lib/views/core-view-utils'; import type { Nullable } from '@ember/-internals/utility-types'; import { assert, debugFreeze } from '@ember/debug'; import { _instrumentStart } from '@ember/instrumentation'; @@ -318,20 +319,20 @@ export default class CurlyComponentManager addChildView(parentView, component); } - component.trigger('didReceiveAttrs'); + sendCoreViewEvent(component, 'didReceiveAttrs'); let hasWrappedElement = component.tagName !== ''; // We usually do this in the `didCreateElement`, but that hook doesn't fire for tagless components if (!hasWrappedElement) { if (isInteractive) { - component.trigger('willRender'); + sendCoreViewEvent(component, 'willRender'); } component._transitionTo('hasElement'); if (isInteractive) { - component.trigger('willInsertElement'); + sendCoreViewEvent(component, 'willInsertElement'); } } @@ -355,7 +356,7 @@ export default class CurlyComponentManager } if (isInteractive && hasWrappedElement) { - component.trigger('willRender'); + sendCoreViewEvent(component, 'willRender'); } endUntrackFrame(); @@ -420,7 +421,7 @@ export default class CurlyComponentManager if (isInteractive) { beginUntrackFrame(); - component.trigger('willInsertElement'); + sendCoreViewEvent(component, 'willInsertElement'); endUntrackFrame(); } } @@ -433,8 +434,8 @@ export default class CurlyComponentManager didCreate({ component, isInteractive }: ComponentStateBucket): void { if (isInteractive) { component._transitionTo('inDOM'); - component.trigger('didInsertElement'); - component.trigger('didRender'); + sendCoreViewEvent(component, 'didInsertElement'); + sendCoreViewEvent(component, 'didRender'); } } @@ -456,13 +457,13 @@ export default class CurlyComponentManager component.setProperties(props); component[IS_DISPATCHING_ATTRS] = false; - component.trigger('didUpdateAttrs'); - component.trigger('didReceiveAttrs'); + sendCoreViewEvent(component, 'didUpdateAttrs'); + sendCoreViewEvent(component, 'didReceiveAttrs'); } if (isInteractive) { - component.trigger('willUpdate'); - component.trigger('willRender'); + sendCoreViewEvent(component, 'willUpdate'); + sendCoreViewEvent(component, 'willRender'); } endUntrackFrame(); @@ -477,8 +478,8 @@ export default class CurlyComponentManager didUpdate({ component, isInteractive }: ComponentStateBucket): void { if (isInteractive) { - component.trigger('didUpdate'); - component.trigger('didRender'); + sendCoreViewEvent(component, 'didUpdate'); + sendCoreViewEvent(component, 'didRender'); } } diff --git a/packages/@ember/-internals/glimmer/lib/component-managers/root.ts b/packages/@ember/-internals/glimmer/lib/component-managers/root.ts index 2ca09c5e158..259874172e4 100644 --- a/packages/@ember/-internals/glimmer/lib/component-managers/root.ts +++ b/packages/@ember/-internals/glimmer/lib/component-managers/root.ts @@ -21,6 +21,7 @@ import CurlyComponentManager, { initialRenderInstrumentDetails, processComponentInitializationAssertions, } from './curly'; +import { sendCoreViewEvent } from '@ember/-internals/views'; class RootComponentManager extends CurlyComponentManager { component: Component; @@ -48,13 +49,13 @@ class RootComponentManager extends CurlyComponentManager { // We usually do this in the `didCreateElement`, but that hook doesn't fire for tagless components if (!hasWrappedElement) { if (isInteractive) { - component.trigger('willRender'); + sendCoreViewEvent(component, 'willRender'); } component._transitionTo('hasElement'); if (isInteractive) { - component.trigger('willInsertElement'); + sendCoreViewEvent(component, 'willInsertElement'); } } diff --git a/packages/@ember/-internals/glimmer/lib/renderer.ts b/packages/@ember/-internals/glimmer/lib/renderer.ts index e533c4042ba..c404404ecd9 100644 --- a/packages/@ember/-internals/glimmer/lib/renderer.ts +++ b/packages/@ember/-internals/glimmer/lib/renderer.ts @@ -3,6 +3,7 @@ import type { InternalOwner } from '@ember/-internals/owner'; import { getOwner } from '@ember/-internals/owner'; import { guidFor } from '@ember/-internals/utils/lib/guid'; import { getViewElement, getViewId } from '@ember/-internals/views/lib/system/utils'; +import { sendCoreViewEvent } from '@ember/-internals/views/lib/views/core-view-utils'; import { assert } from '@ember/debug'; import { associateDestroyableChild, @@ -309,7 +310,7 @@ export class Renderer extends BaseRenderer { this.cleanupRootFor(view); if (this.state.isInteractive) { - view.trigger('didDestroyElement'); + sendCoreViewEvent(view, 'didDestroyElement'); } } diff --git a/packages/@ember/-internals/glimmer/lib/utils/curly-component-state-bucket.ts b/packages/@ember/-internals/glimmer/lib/utils/curly-component-state-bucket.ts index 49907136c01..1780214877f 100644 --- a/packages/@ember/-internals/glimmer/lib/utils/curly-component-state-bucket.ts +++ b/packages/@ember/-internals/glimmer/lib/utils/curly-component-state-bucket.ts @@ -3,6 +3,7 @@ import { clearViewElement, getViewElement, } from '@ember/-internals/views/lib/system/utils'; +import { sendCoreViewEvent } from '@ember/-internals/views/lib/views/core-view-utils'; import { registerDestructor } from '@glimmer/destroyable'; import type { CapturedNamedArguments } from '@glimmer/interfaces'; import type { Reference } from '@glimmer/reference/lib/reference'; @@ -52,8 +53,8 @@ export default class ComponentStateBucket { if (isInteractive) { beginUntrackFrame(); - component.trigger('willDestroyElement'); - component.trigger('willClearRender'); + sendCoreViewEvent(component, 'willDestroyElement'); + sendCoreViewEvent(component, 'willClearRender'); endUntrackFrame(); let element = getViewElement(component); diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/append-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/append-test.js index 8c00b604cc9..ecf50e8062d 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/append-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/append-test.js @@ -1,4 +1,12 @@ -import { moduleFor, RenderingTestCase, strip, runTask } from 'internal-test-helpers'; +import { + moduleFor, + RenderingTestCase, + strip, + runTask, + expectDeprecation, + testUnless, +} from 'internal-test-helpers'; +import { DEPRECATIONS } from '../../../../deprecations'; import { set } from '@ember/object'; import { setComponentTemplate } from '@glimmer/manager'; @@ -39,7 +47,9 @@ class AbstractAppendTest extends RenderingTestCase { this.ids.push(component.elementId); } - [`@test (new) lifecycle hooks during component append`](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_EVENTED.isRemoved)} @test (new) lifecycle hooks during component append`]( + assert + ) { let hooks = []; let componentsByName = {}; @@ -57,7 +67,13 @@ class AbstractAppendTest extends RenderingTestCase { } componentsByName[name] = this; pushHook('init'); - this.on('init', () => pushHook('on(init)')); + expectDeprecation( + () => { + this.on('init', () => pushHook('on(init)')); + }, + /Evented#on` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); } didReceiveAttrs() { @@ -279,7 +295,9 @@ class AbstractAppendTest extends RenderingTestCase { ); } - [`@test lifecycle hooks during component append`](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_EVENTED.isRemoved)} @test lifecycle hooks during component append`]( + assert + ) { let hooks = []; let componentsByName = {}; @@ -298,7 +316,13 @@ class AbstractAppendTest extends RenderingTestCase { } componentsByName[name] = this; pushHook('init'); - this.on('init', () => pushHook('on(init)')); + expectDeprecation( + () => { + this.on('init', () => pushHook('on(init)')); + }, + /Evented#on` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); } didReceiveAttrs() { diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/curly-components-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/curly-components-test.js index 5a1b7771b20..41420b751bf 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/curly-components-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/curly-components-test.js @@ -7,7 +7,10 @@ import { equalsElement, runTask, runLoopSettled, + expectDeprecation, + testUnless, } from 'internal-test-helpers'; +import { DEPRECATIONS } from '../../../../deprecations'; import { action } from '@ember/object'; import { run } from '@ember/runloop'; @@ -3440,48 +3443,60 @@ moduleFor( runTask(() => set(this.context, 'foo', 5)); } - ['@test triggering an event only attempts to invoke an identically named method, if it actually is a function (GH#15228)']( + [`${testUnless(DEPRECATIONS.DEPRECATE_EVENTED.isRemoved)} @test triggering an event only attempts to invoke an identically named method, if it actually is a function (GH#15228)`]( assert ) { - assert.expect(3); + assert.expect(5); let payload = ['arbitrary', 'event', 'data']; - this.owner.register( - 'component:evented-component', - Component.extend({ - someTruthyProperty: true, - - init() { - this._super(...arguments); - this.trigger('someMethod', ...payload); - this.trigger('someTruthyProperty', ...payload); - }, + expectDeprecation( + () => { + this.owner.register( + 'component:evented-component', + Component.extend({ + someTruthyProperty: true, + + init() { + this._super(...arguments); + expectDeprecation( + () => { + this.trigger('someMethod', ...payload); + this.trigger('someTruthyProperty', ...payload); + }, + /Evented#trigger` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); + }, - someMethod(...data) { - assert.deepEqual( - data, - payload, - 'the method `someMethod` should be called, when `someMethod` is triggered' - ); - }, + someMethod(...data) { + assert.deepEqual( + data, + payload, + 'the method `someMethod` should be called, when `someMethod` is triggered' + ); + }, - listenerForSomeMethod: on('someMethod', function (...data) { - assert.deepEqual( - data, - payload, - 'the listener `listenerForSomeMethod` should be called, when `someMethod` is triggered' - ); - }), - - listenerForSomeTruthyProperty: on('someTruthyProperty', function (...data) { - assert.deepEqual( - data, - payload, - 'the listener `listenerForSomeTruthyProperty` should be called, when `someTruthyProperty` is triggered' - ); - }), - }) + listenerForSomeMethod: on('someMethod', function (...data) { + assert.deepEqual( + data, + payload, + 'the listener `listenerForSomeMethod` should be called, when `someMethod` is triggered' + ); + }), + + listenerForSomeTruthyProperty: on('someTruthyProperty', function (...data) { + assert.deepEqual( + data, + payload, + 'the listener `listenerForSomeTruthyProperty` should be called, when `someTruthyProperty` is triggered' + ); + }), + }) + ); + }, + /`on\(\)` event decorator is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled ); this.render(`{{evented-component}}`); diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/life-cycle-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/life-cycle-test.js index 911f26a44e4..b989a47b24a 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/life-cycle-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/life-cycle-test.js @@ -8,6 +8,7 @@ import { precompileTemplate } from '@ember/template-compilation'; import { setComponentTemplate } from '@glimmer/manager'; import { Component } from '../../utils/helpers'; +import { addListener } from '@ember/-internals/metal'; class LifeCycleHooksTest extends RenderingTestCase { constructor() { @@ -176,7 +177,7 @@ class LifeCycleHooksTest extends RenderingTestCase { assertNoElement('init', this); assertState('init', 'preRender', this); - this.on('init', () => pushHook('on(init)')); + addListener(this, 'init', () => pushHook('on(init)')); schedule('afterRender', () => { this.isInitialRender = false; diff --git a/packages/@ember/-internals/glimmer/tests/integration/event-dispatcher-test.js b/packages/@ember/-internals/glimmer/tests/integration/event-dispatcher-test.js index 9b496a0ef36..bfb6816e210 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/event-dispatcher-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/event-dispatcher-test.js @@ -1,4 +1,11 @@ -import { moduleFor, RenderingTestCase, runTask } from 'internal-test-helpers'; +import { + expectDeprecation, + moduleFor, + RenderingTestCase, + runTask, + testUnless, +} from 'internal-test-helpers'; +import { DEPRECATIONS } from '../../../deprecations'; import { Component } from '../utils/helpers'; import { _getCurrentRunLoop } from '@ember/runloop'; @@ -149,7 +156,9 @@ moduleFor( } } - ['@test event listeners are called when event is triggered'](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_EVENTED.isRemoved)} @test event listeners are called when event is triggered`]( + assert + ) { let receivedEvent; let browserEvent; @@ -160,7 +169,13 @@ moduleFor( init() { super.init(); Object.keys(SUPPORTED_EMBER_EVENTS).forEach((browserEvent) => { - this.on(SUPPORTED_EMBER_EVENTS[browserEvent], (event) => (receivedEvent = event)); + expectDeprecation( + () => { + this.on(SUPPORTED_EMBER_EVENTS[browserEvent], (event) => (receivedEvent = event)); + }, + /Evented#on` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); }); } } diff --git a/packages/@ember/-internals/glimmer/tests/integration/syntax/each-test.js b/packages/@ember/-internals/glimmer/tests/integration/syntax/each-test.js index c1017feeaa1..1dd20bef531 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/syntax/each-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/syntax/each-test.js @@ -1,4 +1,12 @@ -import { moduleFor, RenderingTestCase, applyMixins, strip, runTask } from 'internal-test-helpers'; +import { + moduleFor, + RenderingTestCase, + applyMixins, + strip, + runTask, + expectDeprecation, +} from 'internal-test-helpers'; +import { DEPRECATIONS } from '../../../../deprecations'; import { notifyPropertyChange, on } from '@ember/-internals/metal'; import { get, set, computed } from '@ember/object'; @@ -1109,21 +1117,30 @@ moduleFor( } ); -moduleFor( - 'Syntax test: {{#each}} with array proxies, content is updated after init', - class extends EachTest { - createList(items) { - let wrapped = emberA(items); - let proxy = ArrayProxy.extend({ - setup: on('init', function () { - this.set('content', emberA(wrapped)); - }), - }).create(); +if (!DEPRECATIONS.DEPRECATE_EVENTED.isRemoved) { + moduleFor( + 'Syntax test: {{#each}} with array proxies, content is updated after init', + class extends EachTest { + createList(items) { + let wrapped = emberA(items); + let proxy; + expectDeprecation( + () => { + proxy = ArrayProxy.extend({ + setup: on('init', function () { + this.set('content', emberA(wrapped)); + }), + }).create(); + }, + /`on\(\)` event decorator is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); - return { list: proxy, delegate: wrapped }; + return { list: proxy, delegate: wrapped }; + } } - } -); + ); +} moduleFor( 'Syntax test: {{#each as}} undefined path', diff --git a/packages/@ember/-internals/meta/lib/meta.ts b/packages/@ember/-internals/meta/lib/meta.ts index 781566d20bd..77d703641c2 100644 --- a/packages/@ember/-internals/meta/lib/meta.ts +++ b/packages/@ember/-internals/meta/lib/meta.ts @@ -356,7 +356,7 @@ export class Meta { addToListeners( eventName: string, target: object | null, - method: Function | PropertyKey, + method: ((...args: any[]) => void) | PropertyKey, once: boolean, sync: boolean ) { diff --git a/packages/@ember/-internals/metal/lib/events.ts b/packages/@ember/-internals/metal/lib/events.ts index 56668b9bc9b..62f20d3f462 100644 --- a/packages/@ember/-internals/metal/lib/events.ts +++ b/packages/@ember/-internals/metal/lib/events.ts @@ -6,6 +6,7 @@ import { meta as metaFor, peekMeta } from '@ember/-internals/meta/lib/meta'; import { setListeners } from '@ember/-internals/utils/lib/super'; import type { AnyFn } from '@ember/-internals/utility-types'; import { assert } from '@ember/debug'; +import { DEPRECATIONS, deprecateUntil } from '@ember/-internals/deprecations'; /* The event system uses a series of nested hashes to store listeners on an @@ -38,11 +39,24 @@ import { assert } from '@ember/debug'; @param {Boolean} once A flag whether a function should only be called once @public */ +export function addListener( + obj: object, + eventName: string, + target: Target, + method: PropertyKey | ((this: Target, ...args: any[]) => void), + once?: boolean, + sync?: boolean +): void; +export function addListener( + obj: object, + eventName: string, + method: PropertyKey | ((...args: any[]) => void) +): void; export function addListener( obj: object, eventName: string, - target: object | Function | null, - method?: Function | PropertyKey, + target: object | PropertyKey | ((...args: any[]) => void) | null, + method?: PropertyKey | ((...args: any[]) => void), once?: boolean, sync = true ): void { @@ -52,10 +66,13 @@ export function addListener( ); if (!method && 'function' === typeof target) { - method = target; + // SAFETY: This should be correct. It may be possible to get TS to infer it. + method = target as (...args: any[]) => void; target = null; } + assert('target should be object or null', target === null || typeof target === 'object'); + metaFor(obj).addToListeners(eventName, target, method!, once === true, sync); } @@ -76,8 +93,19 @@ export function addListener( export function removeListener( obj: object, eventName: string, - targetOrFunction: object | Function | null, - functionOrName?: string | Function + target: object | null, + methodOrName: string | ((...args: any[]) => void) +): void; +export function removeListener( + obj: object, + eventName: string, + method: (...args: any[]) => void +): void; +export function removeListener( + obj: object, + eventName: string, + targetOrFunction: object | ((...args: any[]) => void) | null, + functionOrName?: string | ((...args: any[]) => void) ): void { assert( 'You must pass at least an object, event name, and method or target and method/method name to removeListener', @@ -143,7 +171,7 @@ export function sendEvent( continue; } if (once) { - removeListener(obj, eventName, target, method); + removeListener(obj, eventName, target, method as string | ((...args: any[]) => void)); } if (!target) { target = obj; @@ -199,6 +227,7 @@ export function hasListeners(obj: object, eventName: string): boolean { @method on @static + @deprecated Use native JavaScript events or a dedicated event library instead. @for @ember/object/evented @param {String} eventNames* @param {Function} func @@ -206,6 +235,11 @@ export function hasListeners(obj: object, eventName: string): boolean { @public */ export function on(...args: [...eventNames: string[], func: T]): T { + deprecateUntil( + 'The `on()` event decorator is deprecated. Use native JavaScript events or a dedicated event library instead.', + DEPRECATIONS.DEPRECATE_EVENTED + ); + let func = args.pop(); let events = args as string[]; diff --git a/packages/@ember/-internals/metal/lib/observer.ts b/packages/@ember/-internals/metal/lib/observer.ts index 08c5148cd9c..5127f2cde2b 100644 --- a/packages/@ember/-internals/metal/lib/observer.ts +++ b/packages/@ember/-internals/metal/lib/observer.ts @@ -44,7 +44,14 @@ export function addObserver( ): void { let eventName = changeEvent(path); - addListener(obj, eventName, target, method, false, sync); + addListener( + obj, + eventName, + target as any, + method as PropertyKey | ((...args: any[]) => void), + false, + sync + ); let meta = peekMeta(obj); @@ -78,7 +85,7 @@ export function removeObserver( deactivateObserver(obj, eventName, sync); } - removeListener(obj, eventName, target, method); + removeListener(obj, eventName, target as any, method as string | ((...args: any[]) => void)); } function getOrCreateActiveObserversFor(target: object, sync: boolean) { diff --git a/packages/@ember/-internals/metal/tests/computed_test.js b/packages/@ember/-internals/metal/tests/computed_test.js index 516da6905f6..1a20fc2166c 100644 --- a/packages/@ember/-internals/metal/tests/computed_test.js +++ b/packages/@ember/-internals/metal/tests/computed_test.js @@ -984,16 +984,16 @@ moduleFor( class LazyObject { value = 123; + /* eslint-disable no-dupe-class-members */ @computed('_value') - // eslint-disable-next-line no-dupe-class-members get value() { return get(this, '_value'); } - // eslint-disable-next-line no-dupe-class-members set value(value) { set(this, '_value', value); } + /* eslint-enable no-dupe-class-members */ static create() { obj = new LazyObject(); diff --git a/packages/@ember/-internals/metal/tests/events_test.js b/packages/@ember/-internals/metal/tests/events_test.js index 109792c39b7..c50f3e83bd3 100644 --- a/packages/@ember/-internals/metal/tests/events_test.js +++ b/packages/@ember/-internals/metal/tests/events_test.js @@ -1,6 +1,7 @@ import { on, addListener, removeListener, sendEvent, hasListeners } from '..'; import Mixin from '@ember/object/mixin'; -import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { moduleFor, AbstractTestCase, expectDeprecation, testUnless } from 'internal-test-helpers'; +import { DEPRECATIONS } from '../../deprecations'; moduleFor( 'system/props/events_test', @@ -139,17 +140,26 @@ moduleFor( assert.equal(hasListeners(obj, 'event!'), true, 'has listeners'); } - ['@test a listener can be added as part of a mixin'](assert) { + [`${testUnless( + DEPRECATIONS.DEPRECATE_EVENTED.isRemoved + )} @test a listener can be added as part of a mixin`](assert) { let triggered = 0; - let MyMixin = Mixin.create({ - foo1: on('bar', function () { - triggered++; - }), - - foo2: on('bar', function () { - triggered++; - }), - }); + let MyMixin; + expectDeprecation( + () => { + MyMixin = Mixin.create({ + foo1: on('bar', function () { + triggered++; + }), + + foo2: on('bar', function () { + triggered++; + }), + }); + }, + /`on\(\)` event decorator is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); let obj = {}; MyMixin.apply(obj); @@ -158,32 +168,63 @@ moduleFor( assert.equal(triggered, 2, 'should invoke listeners'); } - [`@test 'on' asserts for invalid arguments`]() { + [`${testUnless( + DEPRECATIONS.DEPRECATE_EVENTED.isRemoved + )} @test 'on' asserts for invalid arguments`]() { expectAssertion(() => { - Mixin.create({ - foo1: on('bar'), - }); + expectDeprecation( + () => { + Mixin.create({ + foo1: on('bar'), + }); + }, + /`on\(\)` event decorator is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); }, 'on expects function as last argument'); expectAssertion(() => { - Mixin.create({ - foo1: on(function () {}), - }); + expectDeprecation( + () => { + Mixin.create({ + foo1: on(function () {}), + }); + }, + /`on\(\)` event decorator is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); }, 'on called without valid event names'); } - ['@test a listener added as part of a mixin may be overridden'](assert) { + [`${testUnless( + DEPRECATIONS.DEPRECATE_EVENTED.isRemoved + )} @test a listener added as part of a mixin may be overridden`](assert) { let triggered = 0; - let FirstMixin = Mixin.create({ - foo: on('bar', function () { - triggered++; - }), - }); - let SecondMixin = Mixin.create({ - foo: on('baz', function () { - triggered++; - }), - }); + let FirstMixin; + expectDeprecation( + () => { + FirstMixin = Mixin.create({ + foo: on('bar', function () { + triggered++; + }), + }); + }, + /`on\(\)` event decorator is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); + + let SecondMixin; + expectDeprecation( + () => { + SecondMixin = Mixin.create({ + foo: on('baz', function () { + triggered++; + }), + }); + }, + /`on\(\)` event decorator is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); let obj = {}; FirstMixin.apply(obj); diff --git a/packages/@ember/-internals/utils/index.ts b/packages/@ember/-internals/utils/index.ts index 2811534d8b9..505cdd924c5 100644 --- a/packages/@ember/-internals/utils/index.ts +++ b/packages/@ember/-internals/utils/index.ts @@ -32,3 +32,9 @@ export { teardownMandatorySetter, setWithMandatorySetter, } from './lib/mandatory-setter'; +export { + DEPRECATION, + setDeprecation, + findDeprecation, + disableDeprecations, +} from './lib/mixin-deprecation'; diff --git a/packages/@ember/-internals/utils/lib/mixin-deprecation.ts b/packages/@ember/-internals/utils/lib/mixin-deprecation.ts new file mode 100644 index 00000000000..373e5181940 --- /dev/null +++ b/packages/@ember/-internals/utils/lib/mixin-deprecation.ts @@ -0,0 +1,44 @@ +import type { DeprecationObject } from '@ember/-internals/deprecations'; +import type Mixin from '@ember/object/mixin'; + +/** @internal */ +export const DEPRECATION = Symbol('DEPRECATION'); + +/** @internal */ +export function setDeprecation( + mixin: Mixin, + value: { message: string; deprecation: DeprecationObject } | null +) { + // SAFETY: Using symbol as index on Mixin - this is internal deprecated API tooling + (mixin as any)[DEPRECATION] = value; +} + +let deprecationsEnabled = true; + +export function findDeprecation( + mixin: Mixin +): { message: string; deprecation: DeprecationObject } | null { + if (!deprecationsEnabled) { + return null; + } + // SAFETY: Using symbol as index on Mixin - this is internal deprecated API tooling + if ((mixin as any)[DEPRECATION]) { + return (mixin as any)[DEPRECATION]; + } + for (let childMixin of mixin.mixins ?? []) { + let deprecation = findDeprecation(childMixin); + if (deprecation) { + return deprecation; + } + } + return null; +} + +export function disableDeprecations(callback: () => T): T { + try { + deprecationsEnabled = false; + return callback(); + } finally { + deprecationsEnabled = true; + } +} diff --git a/packages/@ember/-internals/views/index.ts b/packages/@ember/-internals/views/index.ts index d68ea2baff8..a217d978b06 100644 --- a/packages/@ember/-internals/views/index.ts +++ b/packages/@ember/-internals/views/index.ts @@ -17,6 +17,7 @@ export { } from './lib/system/utils'; export { default as EventDispatcher } from './lib/system/event_dispatcher'; export { default as CoreView } from './lib/views/core_view'; +export { sendCoreViewEvent, hasCoreViewListener } from './lib/views/core-view-utils'; export { default as ActionSupport } from './lib/mixins/action_support'; export { MUTABLE_CELL } from './lib/compat/attrs'; export { default as ViewStates } from './lib/views/states'; diff --git a/packages/@ember/-internals/views/lib/views/core-view-utils.ts b/packages/@ember/-internals/views/lib/views/core-view-utils.ts new file mode 100644 index 00000000000..5de7023627a --- /dev/null +++ b/packages/@ember/-internals/views/lib/views/core-view-utils.ts @@ -0,0 +1,14 @@ +import { hasListeners, sendEvent } from '@ember/-internals/metal'; +import type CoreView from './core_view'; + +export function sendCoreViewEvent(view: CoreView, name: string, args: any[] = []) { + sendEvent(view, name, args); + let method = (view as any)[name]; + if (typeof method === 'function') { + return method.apply(view, args); + } +} + +export function hasCoreViewListener(view: CoreView, name: string) { + return typeof (view as any)[name] === 'function' || hasListeners(view, name); +} diff --git a/packages/@ember/-internals/views/lib/views/core_view.ts b/packages/@ember/-internals/views/lib/views/core_view.ts index ceb6c0cfa8f..3f08d7cfba1 100644 --- a/packages/@ember/-internals/views/lib/views/core_view.ts +++ b/packages/@ember/-internals/views/lib/views/core_view.ts @@ -5,6 +5,7 @@ import Evented from '@ember/object/evented'; import { FrameworkObject } from '@ember/object/-internals'; import type { ViewState } from './states'; import states from './states'; +import { disableDeprecations } from '@ember/-internals/utils/lib/mixin-deprecation'; /** `CoreView` is an abstract class that exists to give view-like behavior @@ -24,7 +25,7 @@ import states from './states'; */ interface CoreView extends Evented, ActionHandler, View {} -class CoreView extends FrameworkObject.extend(Evented, ActionHandler) { +class CoreView extends disableDeprecations(() => FrameworkObject.extend(Evented, ActionHandler)) { isView = true; declare _states: typeof states; diff --git a/packages/@ember/-internals/views/lib/views/states.ts b/packages/@ember/-internals/views/lib/views/states.ts index aba69ba106b..3dead388b0f 100644 --- a/packages/@ember/-internals/views/lib/views/states.ts +++ b/packages/@ember/-internals/views/lib/views/states.ts @@ -4,6 +4,7 @@ import { assert } from '@ember/debug'; import { flaggedInstrument } from '@ember/instrumentation'; import { join } from '@ember/runloop'; import { DEBUG } from '@glimmer/env'; +import { hasCoreViewListener, sendCoreViewEvent } from './core-view-utils'; export interface ViewState { enter?(view: Component): void; @@ -45,11 +46,13 @@ const HAS_ELEMENT: Readonly = Object.freeze({ // Handle events from `Ember.EventDispatcher` handleEvent(view: Component, eventName: string, event: Event) { - if (view.has(eventName)) { + if (hasCoreViewListener(view, eventName)) { // Handler should be able to re-dispatch events, so we don't // preventDefault or stopPropagation. return flaggedInstrument(`interaction.${eventName}`, { event, view }, () => { - return join(view, view.trigger, eventName, event); + return join(() => { + return sendCoreViewEvent(view, eventName, [event]); + }); }); } else { return true; // continue event propagation diff --git a/packages/@ember/object/evented.ts b/packages/@ember/object/evented.ts index 097d274d4bf..fcb3a179654 100644 --- a/packages/@ember/object/evented.ts +++ b/packages/@ember/object/evented.ts @@ -4,6 +4,8 @@ import { hasListeners, sendEvent, } from '@ember/-internals/metal/lib/events'; +import { DEPRECATIONS, deprecateUntil } from '@ember/-internals/deprecations'; +import { setDeprecation } from '@ember/-internals/utils/lib/mixin-deprecation'; import Mixin from '@ember/object/mixin'; export { on } from '@ember/-internals/metal/lib/events'; @@ -51,6 +53,7 @@ export { on } from '@ember/-internals/metal/lib/events'; @class Evented @public + @deprecated Use native JavaScript events or a dedicated event library instead. */ interface Evented { /** @@ -68,6 +71,7 @@ interface Evented { parameter is used the callback method becomes the third argument. @method on + @deprecated Use native JavaScript events or a dedicated event library instead. @param {String} name The name of the event @param {Object} [target] The "this" binding for the callback @param {Function|String} method A function or the name of a function to be called on `target` @@ -90,6 +94,7 @@ interface Evented { becomes the third argument. @method one + @deprecated Use native JavaScript events or a dedicated event library instead. @param {String} name The name of the event @param {Object} [target] The "this" binding for the callback @param {Function|String} method A function or the name of a function to be called on `target` @@ -118,6 +123,7 @@ interface Evented { ``` @method trigger + @deprecated Use native JavaScript events or a dedicated event library instead. @param {String} name The name of the event @param {Object...} args Optional arguments to pass on @public @@ -127,6 +133,7 @@ interface Evented { Cancels subscription for given name, target, and method. @method off + @deprecated Use native JavaScript events or a dedicated event library instead. @param {String} name The name of the event @param {Object} target The target of the subscription @param {Function|String} method The function or the name of a function of the subscription @@ -143,6 +150,7 @@ interface Evented { Checks to see if object has any subscriptions for named event. @method has + @deprecated Use native JavaScript events or a dedicated event library instead. @param {String} name The name of the event @return {Boolean} does the object have a subscription for event @public @@ -151,27 +159,56 @@ interface Evented { } const Evented = Mixin.create({ on(name: string, target: object, method?: string | Function) { - addListener(this, name, target, method); + deprecateUntil( + '`Evented#on` is deprecated. Use native JavaScript events or a dedicated event library instead.', + DEPRECATIONS.DEPRECATE_EVENTED + ); + // SAFETY: The types are not actually correct, but it's not worth the effort to fix them, since we're deprecating this API. + addListener(this, name, target, method as PropertyKey | ((...args: any[]) => void)); return this; }, one(name: string, target: object, method?: string | Function) { - addListener(this, name, target, method, true); + deprecateUntil( + '`Evented#one` is deprecated. Use native JavaScript events or a dedicated event library instead.', + DEPRECATIONS.DEPRECATE_EVENTED + ); + // SAFETY: The types are not actually correct, but it's not worth the effort to fix them, since we're deprecating this API. + addListener(this, name, target, method as PropertyKey | ((...args: any[]) => void), true); return this; }, trigger(name: string, ...args: any[]) { + deprecateUntil( + '`Evented#trigger` is deprecated. Use native JavaScript events or a dedicated event library instead.', + DEPRECATIONS.DEPRECATE_EVENTED + ); sendEvent(this, name, args); }, off(name: string, target: object, method?: string | Function) { - removeListener(this, name, target, method); + deprecateUntil( + '`Evented#off` is deprecated. Use native JavaScript events or a dedicated event library instead.', + DEPRECATIONS.DEPRECATE_EVENTED + ); + // SAFETY: The types are not actually correct, but it's not worth the effort to fix them, since we're deprecating this API. + removeListener(this, name, target, method as string | ((...args: any[]) => void)); return this; }, has(name: string) { + deprecateUntil( + '`Evented#has` is deprecated. Use native JavaScript events or a dedicated event library instead.', + DEPRECATIONS.DEPRECATE_EVENTED + ); return hasListeners(this, name); }, }); +setDeprecation(Evented, { + message: + 'Evented is deprecated. Use native JavaScript events or a dedicated event library instead.', + deprecation: DEPRECATIONS.DEPRECATE_EVENTED, +}); + export default Evented; diff --git a/packages/@ember/object/events.ts b/packages/@ember/object/events.ts index 66224dac13f..7f542db3c31 100644 --- a/packages/@ember/object/events.ts +++ b/packages/@ember/object/events.ts @@ -1 +1,78 @@ -export { addListener, removeListener, sendEvent } from '@ember/-internals/metal/lib/events'; +import { + addListener as originalAddListener, + removeListener as originalRemoveListener, + sendEvent as originalSendEvent, +} from '@ember/-internals/metal/lib/events'; +import { DEPRECATIONS, deprecateUntil } from '@ember/-internals/deprecations'; + +export function addListener( + obj: object, + eventName: string, + target: Target, + method: PropertyKey | ((this: Target, ...args: any[]) => void), + once?: boolean, + sync?: boolean +): void; +export function addListener( + obj: object, + eventName: string, + method: PropertyKey | ((...args: any[]) => void) +): void; +export function addListener( + obj: object, + eventName: string, + target: object | PropertyKey | ((...args: any[]) => void) | null, + method?: PropertyKey | ((...args: any[]) => void), + once?: boolean, + sync?: boolean +): void { + deprecateUntil( + 'Importing `addListener` from `@ember/object/events` is deprecated. Use native JavaScript events or a dedicated event library instead.', + DEPRECATIONS.DEPRECATE_EVENTED + ); + return originalAddListener( + obj, + eventName, + target as any, + method as PropertyKey | ((...args: any[]) => void), + once, + sync + ); +} + +export function removeListener( + obj: object, + eventName: string, + target: Target, + method: PropertyKey | ((this: Target, ...args: any[]) => void) +): void; +export function removeListener( + obj: object, + eventName: string, + method: PropertyKey | ((...args: any[]) => void) +): void; +export function removeListener( + obj: object, + eventName: string, + target: object | PropertyKey | ((...args: any[]) => void) | null, + method?: PropertyKey | ((...args: any[]) => void) +): void { + deprecateUntil( + 'Importing `removeListener` from `@ember/object/events` is deprecated. Use native JavaScript events or a dedicated event library instead.', + DEPRECATIONS.DEPRECATE_EVENTED + ); + return originalRemoveListener( + obj, + eventName, + target as any, + method as string | ((...args: any[]) => void) + ); +} + +export const sendEvent = (...args: Parameters) => { + deprecateUntil( + 'Importing `sendEvent` from `@ember/object/events` is deprecated. Use native JavaScript events or a dedicated event library instead.', + DEPRECATIONS.DEPRECATE_EVENTED + ); + return originalSendEvent(...args); +}; diff --git a/packages/@ember/object/mixin.ts b/packages/@ember/object/mixin.ts index c76b57f7dbe..acaf82b032b 100644 --- a/packages/@ember/object/mixin.ts +++ b/packages/@ember/object/mixin.ts @@ -5,7 +5,12 @@ import { INIT_FACTORY } from '@ember/-internals/container/lib/container'; import type { Meta } from '@ember/-internals/meta/lib/meta'; import { meta as metaFor, peekMeta } from '@ember/-internals/meta/lib/meta'; import { observerListenerMetaFor, ROOT, wrap } from '@ember/-internals/utils/lib/super'; +import { + DEPRECATION, + findDeprecation, +} from '@ember/-internals/utils/lib/mixin-deprecation'; import { assert } from '@ember/debug'; +import { deprecateUntil, type DeprecationObject } from '@ember/-internals/deprecations'; import { DEBUG } from '@glimmer/env'; import type { ComputedDecorator, @@ -27,7 +32,7 @@ import { revalidateObservers, } from '@ember/-internals/metal/lib/observer'; import { defineDecorator, defineValue } from '@ember/-internals/metal/lib/properties'; -import { addListener, removeListener } from '@ember/object/events'; +import { addListener, removeListener } from '@ember/-internals/metal/lib/events'; const a_concat = Array.prototype.concat; const { isArray } = Array; @@ -385,10 +390,12 @@ function updateObserversAndListeners(obj: object, key: string, fn: Function, add } if (listeners !== undefined) { - let updateListener = add ? addListener : removeListener; - for (let listener of listeners) { - updateListener(obj, listener, null, key); + if (add) { + addListener(obj, listener, null, key as PropertyKey); + } else { + removeListener(obj, listener, null, key); + } } } } @@ -543,6 +550,9 @@ export default class Mixin { /** @internal */ properties: { [key: string]: any } | undefined; + /** @internal */ + [DEPRECATION]: { message: string; deprecation: DeprecationObject } | null = null; + /** @internal */ ownerConstructor: any; @@ -613,6 +623,15 @@ export default class Mixin { return this; } + if (DEBUG) { + for (let mixin of args) { + let dep = mixin instanceof Mixin ? findDeprecation(mixin) : null; + if (dep) { + deprecateUntil(dep.message, dep.deprecation); + } + } + } + if (this.properties) { let currentMixin = new Mixin(undefined, this.properties); this.properties = undefined; diff --git a/packages/@ember/object/tests/es-compatibility-test.js b/packages/@ember/object/tests/es-compatibility-test.js index 33cff30a067..35372ca5f70 100644 --- a/packages/@ember/object/tests/es-compatibility-test.js +++ b/packages/@ember/object/tests/es-compatibility-test.js @@ -9,7 +9,14 @@ import { sendEvent, } from '@ember/-internals/metal'; import Mixin from '@ember/object/mixin'; -import { moduleFor, AbstractTestCase, runLoopSettled } from 'internal-test-helpers'; +import { + moduleFor, + AbstractTestCase, + runLoopSettled, + expectDeprecation, + testUnless, +} from 'internal-test-helpers'; +import { DEPRECATIONS } from '../../-internals/deprecations'; moduleFor( 'EmberObject ES Compatibility', @@ -276,37 +283,48 @@ moduleFor( SubEmberObject.metaForProperty('foo'); } - '@test observes / removeObserver on / removeListener interop'(assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_EVENTED.isRemoved)} @test observes / removeObserver on / removeListener interop`]( + assert + ) { let fooDidChangeBase = 0; let fooDidChangeA = 0; let fooDidChangeB = 0; let someEventBase = 0; let someEventA = 0; let someEventB = 0; - class A extends EmberObject.extend({ - fooDidChange: observer('foo', function () { - fooDidChangeBase++; - }), - - onSomeEvent: on('someEvent', function () { - someEventBase++; - }), - }) { - init() { - super.init(); - this.foo = 'bar'; - } - - fooDidChange() { - super.fooDidChange(); - fooDidChangeA++; - } - - onSomeEvent() { - super.onSomeEvent(); - someEventA++; - } - } + let A; + expectDeprecation( + () => { + A = class extends ( + EmberObject.extend({ + fooDidChange: observer('foo', function () { + fooDidChangeBase++; + }), + + onSomeEvent: on('someEvent', function () { + someEventBase++; + }), + }) + ) { + init() { + super.init(); + this.foo = 'bar'; + } + + fooDidChange() { + super.fooDidChange(); + fooDidChangeA++; + } + + onSomeEvent() { + super.onSomeEvent(); + someEventA++; + } + }; + }, + /`on\(\)` event decorator is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); class B extends A { fooDidChange() { diff --git a/packages/@ember/object/tests/evented_test.js b/packages/@ember/object/tests/evented_test.js index 03c65149b8a..2e7a920252d 100644 --- a/packages/@ember/object/tests/evented_test.js +++ b/packages/@ember/object/tests/evented_test.js @@ -1,21 +1,44 @@ import CoreObject from '@ember/object/core'; import EventedMixin from '@ember/object/evented'; -import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { moduleFor, AbstractTestCase, expectDeprecation, testUnless } from 'internal-test-helpers'; +import { DEPRECATIONS } from '../../-internals/deprecations'; moduleFor( 'Ember.Evented', class extends AbstractTestCase { - ['@test works properly on proxy-ish objects'](assert) { - let eventedProxyObj = class extends CoreObject.extend(EventedMixin) { - unknownProperty() { - return true; - } - }.create(); + [`${testUnless( + DEPRECATIONS.DEPRECATE_EVENTED.isRemoved + )} @test works properly on proxy-ish objects`](assert) { + let eventedProxyObj; + expectDeprecation( + () => { + eventedProxyObj = class extends CoreObject.extend(EventedMixin) { + unknownProperty() { + return true; + } + }.create(); + }, + /Evented is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); let noop = function () {}; - eventedProxyObj.on('foo', noop); - eventedProxyObj.off('foo', noop); + expectDeprecation( + () => { + eventedProxyObj.on('foo', noop); + }, + /Evented#on` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); + + expectDeprecation( + () => { + eventedProxyObj.off('foo', noop); + }, + /Evented#off` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); assert.ok(true, 'An assertion was triggered'); } diff --git a/packages/@ember/object/tests/events_test.js b/packages/@ember/object/tests/events_test.js index fee30bd5bdb..c3daddf6c3d 100644 --- a/packages/@ember/object/tests/events_test.js +++ b/packages/@ember/object/tests/events_test.js @@ -1,29 +1,59 @@ import EmberObject from '@ember/object'; import Evented from '@ember/object/evented'; -import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { moduleFor, AbstractTestCase, expectDeprecation, testUnless } from 'internal-test-helpers'; +import { DEPRECATIONS } from '../../-internals/deprecations'; moduleFor( 'Object events', class extends AbstractTestCase { - ['@test a listener can be added to an object'](assert) { + [`${testUnless( + DEPRECATIONS.DEPRECATE_EVENTED.isRemoved + )} @test a listener can be added to an object`](assert) { let count = 0; let F = function () { count++; }; - let obj = EmberObject.extend(Evented).create(); - - obj.on('event!', F); - obj.trigger('event!'); + let obj; + expectDeprecation( + () => { + obj = EmberObject.extend(Evented).create(); + }, + /Evented is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); + + expectDeprecation( + () => { + obj.on('event!', F); + }, + /Evented#on` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); + expectDeprecation( + () => { + obj.trigger('event!'); + }, + /Evented#trigger` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); assert.equal(count, 1, 'the event was triggered'); - obj.trigger('event!'); + expectDeprecation( + () => { + obj.trigger('event!'); + }, + /Evented#trigger` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); assert.equal(count, 2, 'the event was triggered'); } - ['@test a listener can be added and removed automatically the first time it is triggered']( + [`${testUnless( + DEPRECATIONS.DEPRECATE_EVENTED.isRemoved + )} @test a listener can be added and removed automatically the first time it is triggered`]( assert ) { let count = 0; @@ -31,77 +61,174 @@ moduleFor( count++; }; - let obj = EmberObject.extend(Evented).create(); - - obj.one('event!', F); - obj.trigger('event!'); + let obj; + expectDeprecation( + () => { + obj = EmberObject.extend(Evented).create(); + }, + /Evented is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); + + expectDeprecation( + () => { + obj.one('event!', F); + }, + /Evented#one` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); + + expectDeprecation( + () => { + obj.trigger('event!'); + }, + /Evented#trigger` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); assert.equal(count, 1, 'the event was triggered'); - obj.trigger('event!'); + expectDeprecation( + () => { + obj.trigger('event!'); + }, + /Evented#trigger` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); assert.equal(count, 1, 'the event was not triggered again'); } - ['@test triggering an event can have arguments'](assert) { + [`${testUnless( + DEPRECATIONS.DEPRECATE_EVENTED.isRemoved + )} @test triggering an event can have arguments`](assert) { let self, args; - let obj = EmberObject.extend(Evented).create(); - - obj.on('event!', function () { - args = [].slice.call(arguments); - self = this; - }); - - obj.trigger('event!', 'foo', 'bar'); + let obj; + expectDeprecation( + () => { + obj = EmberObject.extend(Evented).create(); + }, + /Evented is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); + + expectDeprecation( + () => { + obj.on('event!', function () { + args = [].slice.call(arguments); + self = this; + }); + }, + /Evented#on` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); + + expectDeprecation( + () => { + obj.trigger('event!', 'foo', 'bar'); + }, + /Evented#trigger` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); assert.deepEqual(args, ['foo', 'bar']); assert.equal(self, obj); } - ['@test a listener can be added and removed automatically and have arguments'](assert) { + [`${testUnless( + DEPRECATIONS.DEPRECATE_EVENTED.isRemoved + )} @test a listener can be added and removed automatically and have arguments`](assert) { let self, args; let count = 0; - let obj = EmberObject.extend(Evented).create(); - - obj.one('event!', function () { - args = [].slice.call(arguments); - self = this; - count++; - }); - - obj.trigger('event!', 'foo', 'bar'); + let obj; + expectDeprecation( + () => { + obj = EmberObject.extend(Evented).create(); + }, + /Evented is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); + + expectDeprecation( + () => { + obj.one('event!', function () { + args = [].slice.call(arguments); + self = this; + count++; + }); + }, + /Evented#one` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); + + expectDeprecation( + () => { + obj.trigger('event!', 'foo', 'bar'); + }, + /Evented#trigger` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); assert.deepEqual(args, ['foo', 'bar']); assert.equal(self, obj); assert.equal(count, 1, 'the event is triggered once'); - obj.trigger('event!', 'baz', 'bat'); + expectDeprecation( + () => { + obj.trigger('event!', 'baz', 'bat'); + }, + /Evented#trigger` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); assert.deepEqual(args, ['foo', 'bar']); assert.equal(count, 1, 'the event was not triggered again'); assert.equal(self, obj); } - ['@test binding an event can specify a different target'](assert) { + [`${testUnless( + DEPRECATIONS.DEPRECATE_EVENTED.isRemoved + )} @test binding an event can specify a different target`](assert) { let self, args; - let obj = EmberObject.extend(Evented).create(); + let obj; + expectDeprecation( + () => { + obj = EmberObject.extend(Evented).create(); + }, + /Evented is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); let target = {}; - obj.on('event!', target, function () { - args = [].slice.call(arguments); - self = this; - }); - - obj.trigger('event!', 'foo', 'bar'); + expectDeprecation( + () => { + obj.on('event!', target, function () { + args = [].slice.call(arguments); + self = this; + }); + }, + /Evented#on` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); + + expectDeprecation( + () => { + obj.trigger('event!', 'foo', 'bar'); + }, + /Evented#trigger` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); assert.deepEqual(args, ['foo', 'bar']); assert.equal(self, target); } - ['@test a listener registered with one can take method as string and can be added with different target']( + [`${testUnless( + DEPRECATIONS.DEPRECATE_EVENTED.isRemoved + )} @test a listener registered with one can take method as string and can be added with different target`]( assert ) { let count = 0; @@ -110,46 +237,152 @@ moduleFor( count++; }; - let obj = EmberObject.extend(Evented).create(); - - obj.one('event!', target, 'fn'); - obj.trigger('event!'); + let obj; + expectDeprecation( + () => { + obj = EmberObject.extend(Evented).create(); + }, + /Evented is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); + + expectDeprecation( + () => { + obj.one('event!', target, 'fn'); + }, + /Evented#one` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); + + expectDeprecation( + () => { + obj.trigger('event!'); + }, + /Evented#trigger` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); assert.equal(count, 1, 'the event was triggered'); - obj.trigger('event!'); + expectDeprecation( + () => { + obj.trigger('event!'); + }, + /Evented#trigger` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); assert.equal(count, 1, 'the event was not triggered again'); } - ['@test a listener registered with one can be removed with off'](assert) { - let obj = class extends EmberObject.extend(Evented) { - F() {} - }.create(); + [`${testUnless( + DEPRECATIONS.DEPRECATE_EVENTED.isRemoved + )} @test a listener registered with one can be removed with off`](assert) { + let obj; + expectDeprecation( + () => { + obj = class extends EmberObject.extend(Evented) { + F() {} + }.create(); + }, + /Evented is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); let F = function () {}; - obj.one('event!', F); - obj.one('event!', obj, 'F'); - - assert.equal(obj.has('event!'), true, 'has events'); - - obj.off('event!', F); - obj.off('event!', obj, 'F'); - - assert.equal(obj.has('event!'), false, 'has no more events'); + expectDeprecation( + () => { + obj.one('event!', F); + }, + /Evented#one` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); + + expectDeprecation( + () => { + obj.one('event!', obj, 'F'); + }, + /Evented#one` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); + + let objHas; + expectDeprecation( + () => { + objHas = obj.has('event!'); + }, + /Evented#has` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); + assert.equal(objHas, true, 'has events'); + + expectDeprecation( + () => { + obj.off('event!', F); + }, + /Evented#off` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); + + expectDeprecation( + () => { + obj.off('event!', obj, 'F'); + }, + /Evented#off` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); + + expectDeprecation( + () => { + objHas = obj.has('event!'); + }, + /Evented#has` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); + + assert.equal(objHas, false, 'has no more events'); } - ['@test adding and removing listeners should be chainable'](assert) { - let obj = EmberObject.extend(Evented).create(); + [`${testUnless( + DEPRECATIONS.DEPRECATE_EVENTED.isRemoved + )} @test adding and removing listeners should be chainable`](assert) { + let obj; + expectDeprecation( + () => { + obj = EmberObject.extend(Evented).create(); + }, + /Evented is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); let F = function () {}; - let ret = obj.on('event!', F); + let ret; + + expectDeprecation( + () => { + ret = obj.on('event!', F); + }, + /Evented#on` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); assert.equal(ret, obj, '#on returns self'); - ret = obj.off('event!', F); + expectDeprecation( + () => { + ret = obj.off('event!', F); + }, + /Evented#off` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); assert.equal(ret, obj, '#off returns self'); - ret = obj.one('event!', F); + expectDeprecation( + () => { + ret = obj.one('event!', F); + }, + /Evented#one` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); assert.equal(ret, obj, '#one returns self'); } } diff --git a/packages/@ember/routing/route.ts b/packages/@ember/routing/route.ts index 28831d60328..1bf7cc7f96d 100644 --- a/packages/@ember/routing/route.ts +++ b/packages/@ember/routing/route.ts @@ -2,6 +2,7 @@ import { privatize as P } from '@ember/-internals/container/lib/registry'; import { addObserver, flushAsyncObservers } from '@ember/-internals/metal/lib/observer'; import { defineProperty } from '@ember/-internals/metal/lib/properties'; import { descriptorForProperty } from '@ember/-internals/metal/lib/decorator'; +import { sendEvent } from '@ember/-internals/metal/lib/events'; import type Owner from '@ember/owner'; import { getOwner } from '@ember/-internals/owner'; import type { default as BucketCache } from './lib/cache'; @@ -40,6 +41,7 @@ import { prefixRouteNameArg, stashParamNames, } from './lib/utils'; +import { disableDeprecations } from '@ember/-internals/utils/lib/mixin-deprecation'; export interface ExtendedInternalRouteInfo extends InternalRouteInfo { _names?: unknown[]; @@ -256,7 +258,10 @@ interface Route extends IRoute, ActionHandler, Evented { error?(error: Error, transition: Transition): boolean | void; } -class Route extends EmberObject.extend(ActionHandler, Evented) implements IRoute { +class Route + extends disableDeprecations(() => EmberObject.extend(ActionHandler, Evented)) + implements IRoute +{ static isRouteFactory = true; // These properties will end up appearing in the public interface because we @@ -773,7 +778,7 @@ class Route extends EmberObject.extend(ActionHandler, Evented) */ exit(transition?: Transition) { this.deactivate(transition); - this.trigger('deactivate', transition); + sendEvent(this, 'deactivate', [transition]); this.teardownViews(); } @@ -799,7 +804,7 @@ class Route extends EmberObject.extend(ActionHandler, Evented) enter(transition: Transition) { this[RENDER_STATE] = undefined; this.activate(transition); - this.trigger('activate', transition); + sendEvent(this, 'activate', [transition]); } /** diff --git a/packages/@ember/routing/router-service.ts b/packages/@ember/routing/router-service.ts index 9fc5e3b4df1..d5cf9f5c053 100644 --- a/packages/@ember/routing/router-service.ts +++ b/packages/@ember/routing/router-service.ts @@ -2,7 +2,6 @@ * @module @ember/routing/router-service */ import { getOwner } from '@ember/-internals/owner'; -import Evented from '@ember/object/evented'; import { assert } from '@ember/debug'; import { readOnly } from '@ember/object/computed'; import Service from '@ember/service'; @@ -14,6 +13,7 @@ import EmberRouter from '@ember/routing/router'; import type { RouteInfo, RouteInfoWithAttributes } from './lib/route-info'; import type { RouteArgs, RouteOptions } from './lib/utils'; import { extractRouteArgs, resemblesURL, shallowEqual } from './lib/utils'; +import { addListener, hasListeners, removeListener, sendEvent } from '@ember/-internals/metal'; export const ROUTER = Symbol('ROUTER'); @@ -25,6 +25,8 @@ function cleanURL(url: string, rootURL: string) { return url.substring(rootURL.length); } +type EventName = 'routeWillChange' | 'routeDidChange'; + /** The Router service is the public API that provides access to the router. @@ -56,14 +58,108 @@ function cleanURL(url: string, rootURL: string) { @extends Service @class RouterService */ -interface RouterService extends Evented { +class RouterService extends Service { + [ROUTER]?: EmberRouter; + + /** + Subscribes to a named event with given function. + + @method on + @param {String} name The name of the event + @param {Object} [target] The "this" binding for the callback + @param {Function|String} method A function or the name of a function to be called on `target` + @return this + */ + on(name: 'routeWillChange' | 'routeDidChange', callback: (transition: Transition) => void): this; + on( + name: EventName, + target: Target, + method: string | ((this: Target, ...args: any[]) => void) + ): this; + on(name: EventName, method: ((...args: any[]) => void) | string): this; on( - eventName: 'routeWillChange' | 'routeDidChange', - callback: (transition: Transition) => void + name: EventName, + target: object | ((...args: any[]) => void) | string, + method?: string | ((...args: any[]) => void) + ) { + // SAFETY: The types are not actually correct, but it's not worth the effort to fix them, since we'll be deprecating this API soon. + addListener(this, name, target, method as any); + return this; + } + + /** + Subscribes a function to a named event and then cancels the subscription + after the first time the event is triggered. + + @method one + @param {String} name The name of the event + @param {Object} [target] The "this" binding for the callback + @param {Function|String} method A function or the name of a function to be called on `target` + @return this + */ + one( + name: string, + target: Target, + method: string | ((this: Target, ...args: any[]) => void) ): this; -} -class RouterService extends Service.extend(Evented) { - [ROUTER]?: EmberRouter; + one(name: string, method: string | ((...args: any[]) => void)): this; + one( + name: string, + target: object | string | ((...args: any[]) => void), + method?: string | Function + ) { + // SAFETY: The types are not actually correct, but it's not worth the effort to fix them, since we'll be deprecating this API soon. + addListener(this, name, target, method as any, true); + return this; + } + + /** + Triggers a named event for the object. + + @method trigger + @param {String} name The name of the event + @param {Object...} args Optional arguments to pass on + @return {boolean} true if listeners were notified, false otherwise + */ + trigger(name: string, ...args: any[]): boolean { + return sendEvent(this, name, args); + } + + /** + Cancels subscription for given name, target, and method. + + @method off + @param {String} name The name of the event + @param {Object} target The target of the subscription + @param {Function|String} method The function or the name of a function of the subscription + @return this + */ + off( + name: string, + target: Target, + method: string | ((this: Target, ...args: any[]) => void) + ): this; + off(name: string, method: string | ((...args: any[]) => void)): this; + off( + name: string, + target: object | string | ((...args: any[]) => void), + method?: string | Function + ) { + // SAFETY: The types are not actually correct, but it's not worth the effort to fix them, since we'll be deprecating this API soon. + removeListener(this, name, target as any, method as any); + return this; + } + + /** + Checks to see if object has any subscriptions for named event. + + @method has + @param {String} name The name of the event + @return {Boolean} does the object have a subscription for event + */ + has(name: string) { + return hasListeners(this, name); + } get _router(): EmberRouter { let router = this[ROUTER]; diff --git a/packages/@ember/routing/router.ts b/packages/@ember/routing/router.ts index 7f6d760dd9d..55922caf655 100644 --- a/packages/@ember/routing/router.ts +++ b/packages/@ember/routing/router.ts @@ -57,6 +57,8 @@ import type { QueryParams } from 'route-recognizer'; import type { AnyFn, MethodNamesOf, OmitFirst } from '@ember/-internals/utility-types'; import type { Template } from '@glimmer/interfaces'; import type ApplicationInstance from '@ember/application/instance'; +import { sendEvent } from '@ember/-internals/metal'; +import { disableDeprecations } from '@ember/-internals/utils'; /** @module @ember/routing/router @@ -142,7 +144,10 @@ const { slice } = Array.prototype; @uses Evented @public */ -class EmberRouter extends EmberObject.extend(Evented) implements Evented { +class EmberRouter + extends disableDeprecations(() => EmberObject.extend(Evented)) + implements Evented +{ /** Represents the URL of the root of the application, often '/'. This prefix is assumed on all routes defined on this router. @@ -422,7 +427,7 @@ class EmberRouter extends EmberObject.extend(Evented) implements Evented { } routeWillChange(transition: Transition) { - router.trigger('routeWillChange', transition); + sendEvent(router, 'routeWillChange', [transition]); if (DEBUG) { freezeRouteInfo(transition); @@ -440,7 +445,7 @@ class EmberRouter extends EmberObject.extend(Evented) implements Evented { routeDidChange(transition: Transition) { router.set('currentRoute', transition.to); once(() => { - router.trigger('routeDidChange', transition); + sendEvent(router, 'routeDidChange', [transition]); if (DEBUG) { freezeRouteInfo(transition); diff --git a/packages/ember/barrel.ts b/packages/ember/barrel.ts new file mode 100644 index 00000000000..afa6b18c8cf --- /dev/null +++ b/packages/ember/barrel.ts @@ -0,0 +1,758 @@ +/** +@module ember +*/ + +import { getENV, getLookup, setLookup } from '@ember/-internals/environment'; +import * as utils from '@ember/-internals/utils'; +import { + Registry as InternalRegistry, + Container as InternalContainer, +} from '@ember/-internals/container'; +import * as instrumentation from '@ember/instrumentation'; +import { meta as internalMeta } from '@ember/-internals/meta'; +import * as metal from '@ember/-internals/metal'; +import { FEATURES as EmberFEATURES, isEnabled } from '@ember/canary-features'; +import * as EmberDebug from '@ember/debug'; +import { assert as emberAssert, captureRenderTree } from '@ember/debug'; +import Backburner from 'backburner.js'; +import EmberController, { + inject as injectController, + ControllerMixin as EmberControllerMixin, +} from '@ember/controller'; +import EmberService, { service } from '@ember/service'; + +import EmberObject, { + action, + computed as emberComputed, + defineProperty as emberDefineProperty, + notifyPropertyChange as emberNotifyPropertyChange, + observer as emberObserver, + get as emberGet, + getProperties as emberGetProperties, + set as emberSet, + setProperties as emberSetProperties, + trySet as emberTrySet, +} from '@ember/object'; +import { cacheFor as emberCacheFor } from '@ember/object/-internals'; +import { dependentKeyCompat } from '@ember/object/compat'; +import EmberComputedProperty, { + expandProperties as emberExpandProperties, +} from '@ember/object/computed'; +import { + addListener as emberAddListener, + removeListener as emberRemoveListener, + sendEvent as emberSendEvent, +} from '@ember/object/events'; + +import { + RegistryProxyMixin, + ContainerProxyMixin, + _ProxyMixin as internalProxyMixin, + RSVP as _RSVP, + Comparable as InternalComparable, + ActionHandler as InternalActionHandler, +} from '@ember/-internals/runtime'; +import { + componentCapabilities, + modifierCapabilities, + setComponentManager, + getTemplates, + setTemplates, + template, + isSerializationFirstNode, + type TemplatesRegistry, +} from '@ember/-internals/glimmer'; +import Version from './version'; +import * as views from '@ember/-internals/views'; +import EmberContainerDebugAdapter from '@ember/debug/container-debug-adapter'; +import EmberDataAdapter from '@ember/debug/data-adapter'; +import { run as emberRun } from '@ember/runloop'; +import { getOnerror, setOnerror } from '@ember/-internals/error-handling'; +import EmberArray, { + A as EmberA, + NativeArray as EmberNativeArray, + isArray as emberIsArray, + makeArray as emberMakeArray, +} from '@ember/array'; +import EmberMutableArray from '@ember/array/mutable'; +import EmberArrayProxy from '@ember/array/proxy'; +import EmberApplication, { + getOwner as applicationGetOwner, + setOwner as applicationSetOwner, + onLoad as applicationOnLoad, + runLoadHooks as applicationRunLoadHooks, +} from '@ember/application'; +import EmberApplicationInstance from '@ember/application/instance'; +import EmberNamespace from '@ember/application/namespace'; +import EmberComponent, { Input as EmberInput } from '@ember/component'; +import EmberHelper from '@ember/component/helper'; +import EmberEngine from '@ember/engine'; +import EmberEngineInstance from '@ember/engine/instance'; +import EmberEnumerable from '@ember/enumerable'; +import EmberMutableEnumerable from '@ember/enumerable/mutable'; +import EmberCoreObject from '@ember/object/core'; +import EmberEvented, { on as emberOn } from '@ember/object/evented'; +import EmberMixin, { mixin as emberMixin } from '@ember/object/mixin'; +import EmberObservable from '@ember/object/observable'; +import { + addObserver as emberAddObserver, + removeObserver as emberRemoveObserver, +} from '@ember/object/observers'; +import EmberObjectProxy from '@ember/object/proxy'; +import EmberPromiseProxyMixin from '@ember/object/promise-proxy-mixin'; +import EmberHashLocation from '@ember/routing/hash-location'; +import EmberHistoryLocation from '@ember/routing/history-location'; +import EmberNoneLocation from '@ember/routing/none-location'; +import EmberRoute from '@ember/routing/route'; +import EmberRouter from '@ember/routing/router'; +import { + controllerFor as emberControllerFor, + generateController as emberGenerateController, + generateControllerFactory as emberGenerateControllerFactory, + DSL as EmberRouterDSL, +} from '@ember/routing/-internals'; +import { + isNone as emberIsNone, + isBlank as emberIsBlank, + isEmpty as emberIsEmpty, + isPresent as emberIsPresent, + isEqual as emberIsEqual, + typeOf as emberTypeOf, + compare as emberCompare, +} from '@ember/utils'; + +import * as glimmerRuntime from '@glimmer/runtime'; + +import { + helperCapabilities, + setModifierManager, + setComponentTemplate, + getComponentTemplate, + setHelperManager, +} from '@glimmer/manager'; + +import { + assertDestroyablesDestroyed, + associateDestroyableChild, + destroy as emberDestroy, + enableDestroyableTracking, + isDestroying, + isDestroyed, + registerDestructor, + unregisterDestructor, +} from '@ember/destroyable'; + +import type { precompile, compile } from 'ember-template-compiler'; +import { _impl as EmberTestingImpl } from '@ember/test'; +import * as templateCompilation from '@ember/template-compilation'; + +// eslint-disable-next-line @typescript-eslint/no-namespace +namespace Ember { + export const isNamespace = true; + + export function toString() { + return 'Ember'; + } + + // ****@ember/-internals/container**** + export const Container = InternalContainer; + export type Container = InternalContainer; + export const Registry = InternalRegistry; + export type Registry = InternalRegistry; + + // ****@ember/-internals/glimmer**** + // Partially re-exported from @glimmer/manager + export const _setComponentManager = setComponentManager; + export const _componentManagerCapabilities = componentCapabilities; + export const _modifierManagerCapabilities = modifierCapabilities; + + // ****@ember/-internals/meta**** + export const meta = internalMeta; + + // ****@ember/-internals/metal**** + export const _createCache = metal.createCache; // Also @glimmer/validator + export const _cacheGetValue = metal.getValue; // Also @glimmer/validator + export const _cacheIsConst = metal.isConst; // Also @glimmer/validator + export const _descriptor = metal.nativeDescDecorator; + export const _getPath = metal._getPath; + export const _setClassicDecorator = metal.setClassicDecorator; + export const _tracked = metal.tracked; // Also exported from @glimmer/tracking + export const beginPropertyChanges = metal.beginPropertyChanges; + export const changeProperties = metal.changeProperties; + export const endPropertyChanges = metal.endPropertyChanges; + export const hasListeners = metal.hasListeners; + export const libraries = metal.libraries; + + // ****@ember/-internals/runtime**** + export const _ContainerProxyMixin = ContainerProxyMixin; + export const _ProxyMixin = internalProxyMixin; + export const _RegistryProxyMixin = RegistryProxyMixin; + export const ActionHandler = InternalActionHandler; + export type ActionHandler = InternalActionHandler; + export const Comparable = InternalComparable; + export type Comparable = InternalComparable; + + // ****@ember/-internals/view**** + export const ComponentLookup = views.ComponentLookup; + export const EventDispatcher = views.EventDispatcher; + + // ****@ember/-internals/utils**** + export const _Cache = utils.Cache; + export const GUID_KEY = utils.GUID_KEY; + export const canInvoke = utils.canInvoke; + export const generateGuid = utils.generateGuid; + export const guidFor = utils.guidFor; + export const uuid = utils.uuid; + export const wrap = utils.wrap; + + // ****@ember/application**** + export const getOwner = applicationGetOwner; + export const onLoad = applicationOnLoad; + export const runLoadHooks = applicationRunLoadHooks; + export const setOwner = applicationSetOwner; + export const Application = EmberApplication; + export type Application = EmberApplication; + + // ****@ember/application/instance**** + export const ApplicationInstance = EmberApplicationInstance; + export type ApplicationInstance = EmberApplicationInstance; + + // // ****@ember/application/namespace**** + export const Namespace = EmberNamespace; + export type Namespace = EmberNamespace; + + // ****@ember/array**** + export const A = EmberA; + export const Array = EmberArray; + export type Array = EmberArray; + export const NativeArray = EmberNativeArray; + export type NativeArray = EmberNativeArray; + export const isArray = emberIsArray; + export const makeArray = emberMakeArray; + + // ****@ember/array/mutable**** + export const MutableArray = EmberMutableArray; + export type MutableArray = EmberMutableArray; + + // ****@ember/array/proxy**** + export const ArrayProxy = EmberArrayProxy; + export type ArrayProxy = EmberArrayProxy; + + // ****@ember/canary-features**** + export const FEATURES = { isEnabled, ...EmberFEATURES }; + + // ****@ember/component**** + export const _Input = EmberInput; + export const Component = EmberComponent; + export type Component = EmberComponent; + + // // ****@ember/component/helper**** + export const Helper = EmberHelper; + export type Helper = EmberHelper; + + // ****@ember/controller**** + export const Controller = EmberController; + export type Controller = EmberController; + export const ControllerMixin = EmberControllerMixin; + export type ControllerMixin = EmberControllerMixin; + + // ****@ember/debug**** + export const _captureRenderTree = captureRenderTree; + export const assert: typeof EmberDebug.assert = EmberDebug.assert; + export const warn: typeof EmberDebug.warn = EmberDebug.warn; + export const debug = EmberDebug.debug; + export const deprecate = EmberDebug.deprecate; + export const deprecateFunc = EmberDebug.deprecateFunc; + export const runInDebug = EmberDebug.runInDebug; + export const inspect = EmberDebug.inspect; + + export const Debug: { + registerDeprecationHandler: typeof EmberDebug.registerDeprecationHandler; + registerWarnHandler: typeof EmberDebug.registerWarnHandler; + isComputed: typeof metal.isComputed; + } = { + registerDeprecationHandler: EmberDebug.registerDeprecationHandler, + registerWarnHandler: EmberDebug.registerWarnHandler, + // ****@ember/-internals/metal**** + isComputed: metal.isComputed, + }; + + // ****@ember/debug/container-debug-adapter**** + export const ContainerDebugAdapter = EmberContainerDebugAdapter; + export type ContainerDebugAdapter = EmberContainerDebugAdapter; + + // ****@ember/debug/data-adapter**** + export const DataAdapter = EmberDataAdapter; + export type DataAdapter = EmberDataAdapter; + + // ****@ember/destroyable**** + export const _assertDestroyablesDestroyed = assertDestroyablesDestroyed; + export const _associateDestroyableChild = associateDestroyableChild; + export const _enableDestroyableTracking = enableDestroyableTracking; + export const _isDestroying = isDestroying; + export const _isDestroyed = isDestroyed; + export const _registerDestructor = registerDestructor; + export const _unregisterDestructor = unregisterDestructor; + export const destroy = emberDestroy; + + // ****@ember/engine**** + export const Engine = EmberEngine; + export type Engine = EmberEngine; + + // ****@ember/engine/instance**** + export const EngineInstance = EmberEngineInstance; + export type EngineInstance = EmberEngineInstance; + + // ****@ember/enumerable**** + export const Enumerable = EmberEnumerable; + export type Enumerable = EmberEnumerable; + + // ****@ember/enumerable/mutable**** + export const MutableEnumerable = EmberMutableEnumerable; + export type MutableEnumerable = EmberMutableEnumerable; + + // ****@ember/instrumentation**** + /** @private */ + export const instrument = instrumentation.instrument; + /** @private */ + export const subscribe = instrumentation.subscribe; + + /** @private */ + export const Instrumentation = { + instrument: instrumentation.instrument, + subscribe: instrumentation.subscribe, + unsubscribe: instrumentation.unsubscribe, + reset: instrumentation.reset, + }; + + // ****@ember/object**** + export const Object = EmberObject; + export type Object = EmberObject; + export const _action = action; + export const computed = emberComputed; + export const defineProperty = emberDefineProperty; + export const get = emberGet; + export const getProperties = emberGetProperties; + export const notifyPropertyChange = emberNotifyPropertyChange; + export const observer = emberObserver; + export const set = emberSet; + export const trySet = emberTrySet; + export const setProperties = emberSetProperties; + + // ****@ember/object/-internals**** + export const cacheFor = emberCacheFor; + + // ****@ember/object/compat**** + export const _dependentKeyCompat = dependentKeyCompat; + + // ****@ember/object/computed**** + export const ComputedProperty = EmberComputedProperty; + export type ComputedProperty = EmberComputedProperty; + export const expandProperties = emberExpandProperties; + + // ****@ember/object/core**** + export const CoreObject = EmberCoreObject; + export type CoreObject = EmberCoreObject; + + // ****@ember/object/evented**** + export const Evented = EmberEvented; + export type Evented = EmberEvented; + export const on = emberOn; + + // ****@ember/object/events**** + export const addListener = emberAddListener; + export const removeListener = emberRemoveListener; + export const sendEvent = emberSendEvent; + + // ****@ember/object/mixin**** + export const Mixin = EmberMixin; + export type Mixin = EmberMixin; + export const mixin = emberMixin; + + // ****@ember/object/observable**** + export const Observable = EmberObservable; + export type Observable = EmberObservable; + + // ****@ember/object/observers**** + export const addObserver = emberAddObserver; + export const removeObserver = emberRemoveObserver; + + // ****@ember/object/promise-proxy-mixin**** + export const PromiseProxyMixin = EmberPromiseProxyMixin; + export type PromiseProxyMixin = EmberPromiseProxyMixin; + + // ****@ember/object/proxy**** + export const ObjectProxy = EmberObjectProxy; + export type ObjectProxy = EmberObjectProxy; + + // ****@ember/routing/-internals**** + export const RouterDSL = EmberRouterDSL; + export type RouterDSL = EmberRouterDSL; + export const controllerFor = emberControllerFor; + export const generateController = emberGenerateController; + export const generateControllerFactory = emberGenerateControllerFactory; + + // ****@ember/routing/hash-location**** + export const HashLocation = EmberHashLocation; + export type HashLocation = EmberHashLocation; + + // ****@ember/routing/history-location**** + export const HistoryLocation = EmberHistoryLocation; + export type HistoryLocation = EmberHistoryLocation; + + // ****@ember/routing/none-location**** + export const NoneLocation = EmberNoneLocation; + export type NoneLocation = EmberNoneLocation; + + // ****@ember/routing/route**** + export const Route = EmberRoute; + export type Route = EmberRoute; + + // ****@ember/routing/router**** + export const Router = EmberRouter; + export type Router = EmberRouter; + + // // ****@ember/runloop**** + export const run = emberRun; + + // // ****@ember/service**** + export const Service = EmberService; + export type Service = EmberService; + + // ****@ember/utils**** + export const compare = emberCompare; + export const isBlank = emberIsBlank; + export const isEmpty = emberIsEmpty; + export const isEqual = emberIsEqual; + export const isNone = emberIsNone; + export const isPresent = emberIsPresent; + export const typeOf = emberTypeOf; + + // ****@ember/version**** + /** + The semantic version + + @property VERSION + @type String + @public + */ + export const VERSION = Version; + + export const ViewUtils = { + // ****@ember/-internals/views**** + getChildViews: views.getChildViews, + getElementView: views.getElementView, + getRootViews: views.getRootViews, + getViewBounds: views.getViewBounds, + getViewBoundingClientRect: views.getViewBoundingClientRect, + getViewClientRects: views.getViewClientRects, + getViewElement: views.getViewElement, + isSimpleClick: views.isSimpleClick, + + // ****@ember/-internals/glimmer**** + isSerializationFirstNode, + }; + + // ****@glimmer/manager**** + export const _getComponentTemplate = getComponentTemplate; + export const _helperManagerCapabilities = helperCapabilities; + export const _setComponentTemplate = setComponentTemplate; + export const _setHelperManager = setHelperManager; + export const _setModifierManager = setModifierManager; + + // ****@glimmer/runtime**** + export const _templateOnlyComponent = glimmerRuntime.templateOnlyComponent; + export const _invokeHelper = glimmerRuntime.invokeHelper; + export const _hash = glimmerRuntime.hash; + export const _array = glimmerRuntime.array; + export const _concat = glimmerRuntime.concat; + export const _get = glimmerRuntime.get; + export const _on = glimmerRuntime.on; + export const _fn = glimmerRuntime.fn; + + // Backburner + export const _Backburner = Backburner; + export type _Backburner = Backburner; + + // // ****@ember/controller, @ember/service**** + /** + Namespace for injection helper methods. + + @class inject + @namespace Ember + @static + @public + */ + export function inject() { + // uses `globalThis` to avoid clobbering with `Ember.Object` in TS namespace + emberAssert( + `Injected properties must be created through helpers, see '${globalThis.Object.keys(inject) + .map((k) => `'inject.${k}'`) + .join(' or ')}'` + ); + } + // ****@ember/controller**** + inject.controller = injectController; + // ****@ember/service**** + inject.service = service; + + export const __loader = { + get require() { + return (globalThis as any).require; + }, + get define() { + return (globalThis as any).define; + }, + get registry() { + let g = globalThis as any; + return g.requirejs?.entries ?? g.require.entries; + }, + }; + + // ------------------------------------------------------------------------ // + // These properties are assigned to the namespace with getters (and, in some + // cases setters) with `Object.defineProperty` below. + // ------------------------------------------------------------------------ // + + export declare const ENV: Readonly; + + // ****@ember/-internals/environment**** + export declare let lookup: Record; + + /** + A function may be assigned to `Ember.onerror` to be called when Ember + internals encounter an error. This is useful for specialized error handling + and reporting code. + + ```javascript + + Ember.onerror = function(error) { + const payload = { + stack: error.stack, + otherInformation: 'whatever app state you want to provide' + }; + + fetch('/report-error', { + method: 'POST', + body: JSON.stringify(payload) + }); + }; + ``` + + Internally, `Ember.onerror` is used as Backburner's error handler. + + @event onerror + @for Ember + @param {Error} error the error object + @public + */ + // ****@ember/-internals/error-handling**** + export declare let onerror: ((error: Error) => unknown) | undefined; + + export declare let testing: boolean; + + /** + Whether searching on the global for new Namespace instances is enabled. + + This is only exported here as to not break any addons. Given the new + visit API, you will have issues if you treat this as a indicator of + booted. + + Internally this is only exposing a flag in Namespace. + + @property BOOTED + @for Ember + @type Boolean + @private + */ + export declare let BOOTED: boolean; + + /** + Global hash of shared templates. This will automatically be populated + by the build tools so that you can store your Handlebars templates in + separate files that get loaded into JavaScript at buildtime. + + @property TEMPLATES + @for Ember + @type Object + @private + */ + export declare let TEMPLATES: TemplatesRegistry; + + export declare let HTMLBars: EmberHTMLBars; + export declare let Handlebars: EmberHandlebars; + export declare let Test: + | (NonNullable['Test'] & { + Adapter: NonNullable['Adapter']; + QUnitAdapter: NonNullable['QUnitAdapter']; + }) + | undefined; + export declare let setupForTesting: + | NonNullable['setupForTesting'] + | undefined; +} + +// This syntax is not reliably implemented by TypeScript transpilers, but +// we need to re-export the`RSVP` *namespace* for type compatibility. +// To achieve this, we use a type-only `declare namespace` block to get the +// types to behave correctly, and separately set the `RSVP` property on the +// `Ember` object dynamically. (The types behave correctly because of +// namespace merging semantics.) +// eslint-disable-next-line @typescript-eslint/no-namespace +declare namespace Ember { + export import RSVP = _RSVP; +} + +Reflect.set(Ember, 'RSVP', _RSVP); + +interface EmberHandlebars { + template: typeof template; + // eslint-disable-next-line @typescript-eslint/no-empty-object-type + Utils: {}; + compile?: typeof compile; + precompile?: typeof precompile; +} + +interface EmberHTMLBars { + template: typeof template; + compile?: typeof compile; + precompile?: typeof precompile; +} + +Object.defineProperty(Ember, 'ENV', { + get: getENV, + enumerable: false, +}); + +Object.defineProperty(Ember, 'lookup', { + get: getLookup, + set: setLookup, + enumerable: false, +}); + +Object.defineProperty(Ember, 'onerror', { + get: getOnerror, + set: setOnerror, + enumerable: false, +}); + +Object.defineProperty(Ember, 'testing', { + get: EmberDebug.isTesting, + set: EmberDebug.setTesting, + enumerable: false, +}); + +Object.defineProperty(Ember, 'BOOTED', { + configurable: false, + enumerable: false, + get: metal.isNamespaceSearchDisabled, + set: metal.setNamespaceSearchDisabled, +}); + +Object.defineProperty(Ember, 'TEMPLATES', { + get: getTemplates, + set: setTemplates, + configurable: false, + enumerable: false, +}); + +Object.defineProperty(Ember, 'TEMPLATES', { + get: getTemplates, + set: setTemplates, + configurable: false, + enumerable: false, +}); + +// ****@ember/debug**** +Object.defineProperty(Ember, 'testing', { + get: EmberDebug.isTesting, + set: EmberDebug.setTesting, + enumerable: false, +}); + +applicationRunLoadHooks('Ember.Application', EmberApplication); + +let EmberHandlebars: EmberHandlebars = { + template, + Utils: {}, +}; + +let EmberHTMLBars: EmberHTMLBars = { + template, +}; + +function defineEmberTemplateCompilerLazyLoad(key: 'HTMLBars' | 'Handlebars') { + Object.defineProperty(Ember, key, { + configurable: true, + enumerable: true, + get() { + if (templateCompilation.__emberTemplateCompiler) { + EmberHTMLBars.precompile = EmberHandlebars.precompile = + templateCompilation.__emberTemplateCompiler.precompile; + EmberHTMLBars.compile = EmberHandlebars.compile = templateCompilation.compileTemplate; + + Object.defineProperty(Ember, 'HTMLBars', { + configurable: true, + writable: true, + enumerable: true, + value: EmberHTMLBars, + }); + Object.defineProperty(Ember, 'Handlebars', { + configurable: true, + writable: true, + enumerable: true, + value: EmberHandlebars, + }); + } + + return key === 'Handlebars' ? EmberHandlebars : EmberHTMLBars; + }, + }); +} + +defineEmberTemplateCompilerLazyLoad('HTMLBars'); +defineEmberTemplateCompilerLazyLoad('Handlebars'); + +// do this to ensure that Ember.Test is defined properly on the global +// if it is present. +function defineEmberTestingLazyLoad(key: 'Test' | 'setupForTesting') { + Object.defineProperty(Ember, key, { + configurable: true, + enumerable: true, + get() { + if (EmberTestingImpl) { + let { Test, Adapter, QUnitAdapter, setupForTesting } = EmberTestingImpl; + + // @ts-expect-error We should not do this + Test.Adapter = Adapter; + // @ts-expect-error We should not do this + Test.QUnitAdapter = QUnitAdapter; + + Object.defineProperty(Ember, 'Test', { + configurable: true, + writable: true, + enumerable: true, + value: Test, + }); + Object.defineProperty(Ember, 'setupForTesting', { + configurable: true, + writable: true, + enumerable: true, + value: setupForTesting, + }); + + return key === 'Test' ? Test : setupForTesting; + } + + return undefined; + }, + }); +} + +defineEmberTestingLazyLoad('Test'); +defineEmberTestingLazyLoad('setupForTesting'); + +// @ts-expect-error Per types, runLoadHooks requires a second parameter. Should we loosen types? +applicationRunLoadHooks('Ember'); + +export default Ember; diff --git a/packages/ember/tests/routing/decoupled_basic_test.js b/packages/ember/tests/routing/decoupled_basic_test.js index 0b81cb807ed..92461f5796b 100644 --- a/packages/ember/tests/routing/decoupled_basic_test.js +++ b/packages/ember/tests/routing/decoupled_basic_test.js @@ -13,7 +13,10 @@ import { ModuleBasedTestResolver, runDestroy, runTask, + expectDeprecation, + testUnless, } from 'internal-test-helpers'; +import { DEPRECATIONS } from '@ember/-internals/deprecations'; import { run } from '@ember/runloop'; import { addObserver } from '@ember/-internals/metal'; import { service } from '@ember/service'; @@ -888,8 +891,10 @@ moduleFor( }); } - ['@test `activate` event fires on the route'](assert) { - assert.expect(4); + [`${testUnless(DEPRECATIONS.DEPRECATE_EVENTED.isRemoved)} @test \`activate\` event fires on the route`]( + assert + ) { + assert.expect(5); let eventFired = 0; @@ -903,10 +908,16 @@ moduleFor( init() { super.init(...arguments); - this.on('activate', function (transition) { - assert.equal(++eventFired, 1, 'activate event is fired once'); - assert.ok(transition, 'transition is passed to activate event'); - }); + expectDeprecation( + () => { + this.on('activate', function (transition) { + assert.equal(++eventFired, 1, 'activate event is fired once'); + assert.ok(transition, 'transition is passed to activate event'); + }); + }, + /Evented#on` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); } activate(transition) { @@ -919,8 +930,10 @@ moduleFor( return this.visit('/nork'); } - ['@test `deactivate` event fires on the route'](assert) { - assert.expect(4); + [`${testUnless(DEPRECATIONS.DEPRECATE_EVENTED.isRemoved)} @test \`deactivate\` event fires on the route`]( + assert + ) { + assert.expect(5); let eventFired = 0; @@ -935,10 +948,16 @@ moduleFor( init() { super.init(...arguments); - this.on('deactivate', function (transition) { - assert.equal(++eventFired, 1, 'deactivate event is fired once'); - assert.ok(transition, 'transition is passed'); - }); + expectDeprecation( + () => { + this.on('deactivate', function (transition) { + assert.equal(++eventFired, 1, 'deactivate event is fired once'); + assert.ok(transition, 'transition is passed'); + }); + }, + /Evented#on` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); } deactivate(transition) { diff --git a/type-tests/@ember/routing-test/router-service.ts b/type-tests/@ember/routing-test/router-service.ts index 2b6083b8a34..0944a281c4e 100644 --- a/type-tests/@ember/routing-test/router-service.ts +++ b/type-tests/@ember/routing-test/router-service.ts @@ -22,7 +22,7 @@ expectTypeOf(router.on('routeWillChange', routeWillChangeHandler)).toEqualTypeOf expectTypeOf(router.has('routeWillChange')).toEqualTypeOf(); expectTypeOf(router.off('routeWillChange', routeWillChangeHandler)).toEqualTypeOf(); expectTypeOf(router.one('routeWillChange', routeWillChangeHandler)).toEqualTypeOf(); -expectTypeOf(router.trigger('routeWillChange', 'boo')).toBeAny(); +expectTypeOf(router.trigger('routeWillChange', 'boo')).toEqualTypeOf(); const transition = router.transitionTo('someRoute'); From 66ad2069965fbacc88d65e7edbf01b55d9ecaefd Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Tue, 7 Jul 2026 23:32:13 -0400 Subject: [PATCH 2/2] Finish RFC 1111 implementation: id/versions, deep imports, main compatibility - Register the deprecation as `deprecate-evented` (since 7.3.0, until 8.0.0), matching the current DEPRECATIONS id conventions - Use deep module imports per current main conventions - Drop the resurrected packages/ember/barrel.ts (deleted on main) - Drop the mixin-deprecation re-exports from the eager utils barrel --- .gitignore | 1 - .../@ember/-internals/deprecations/index.ts | 6 +- .../glimmer/lib/component-managers/root.ts | 2 +- packages/@ember/-internals/utils/index.ts | 6 - .../views/lib/views/core-view-utils.ts | 2 +- packages/@ember/object/mixin.ts | 5 +- packages/@ember/routing/router-service.ts | 7 +- packages/@ember/routing/router.ts | 4 +- packages/ember/barrel.ts | 758 ------------------ 9 files changed, 14 insertions(+), 777 deletions(-) delete mode 100644 packages/ember/barrel.ts diff --git a/.gitignore b/.gitignore index 72e4cfd78b3..9bf32df156f 100644 --- a/.gitignore +++ b/.gitignore @@ -55,4 +55,3 @@ npm-debug.log # couple of the files. Once it is, we can switch this over to just ignoring # `types/stable` entirely. types/stable -.puppeteer-cache/ diff --git a/packages/@ember/-internals/deprecations/index.ts b/packages/@ember/-internals/deprecations/index.ts index 1bde5572dbc..ec164970692 100644 --- a/packages/@ember/-internals/deprecations/index.ts +++ b/packages/@ember/-internals/deprecations/index.ts @@ -120,11 +120,11 @@ export const DEPRECATIONS = { url: 'https://deprecations.emberjs.com/id/deprecate-comparable-mixin', }), DEPRECATE_EVENTED: deprecation({ - id: 'ember-evented', + id: 'deprecate-evented', for: 'ember-source', - since: { available: '6.12.0' }, + since: { available: '7.3.0' }, until: '8.0.0', - url: 'https://deprecations.emberjs.com/id/ember-evented', + url: 'https://deprecations.emberjs.com/id/deprecate-evented', }), }; diff --git a/packages/@ember/-internals/glimmer/lib/component-managers/root.ts b/packages/@ember/-internals/glimmer/lib/component-managers/root.ts index 259874172e4..abcb5383a05 100644 --- a/packages/@ember/-internals/glimmer/lib/component-managers/root.ts +++ b/packages/@ember/-internals/glimmer/lib/component-managers/root.ts @@ -21,7 +21,7 @@ import CurlyComponentManager, { initialRenderInstrumentDetails, processComponentInitializationAssertions, } from './curly'; -import { sendCoreViewEvent } from '@ember/-internals/views'; +import { sendCoreViewEvent } from '@ember/-internals/views/lib/views/core-view-utils'; class RootComponentManager extends CurlyComponentManager { component: Component; diff --git a/packages/@ember/-internals/utils/index.ts b/packages/@ember/-internals/utils/index.ts index 505cdd924c5..2811534d8b9 100644 --- a/packages/@ember/-internals/utils/index.ts +++ b/packages/@ember/-internals/utils/index.ts @@ -32,9 +32,3 @@ export { teardownMandatorySetter, setWithMandatorySetter, } from './lib/mandatory-setter'; -export { - DEPRECATION, - setDeprecation, - findDeprecation, - disableDeprecations, -} from './lib/mixin-deprecation'; diff --git a/packages/@ember/-internals/views/lib/views/core-view-utils.ts b/packages/@ember/-internals/views/lib/views/core-view-utils.ts index 5de7023627a..dfc273e84bd 100644 --- a/packages/@ember/-internals/views/lib/views/core-view-utils.ts +++ b/packages/@ember/-internals/views/lib/views/core-view-utils.ts @@ -1,4 +1,4 @@ -import { hasListeners, sendEvent } from '@ember/-internals/metal'; +import { hasListeners, sendEvent } from '@ember/-internals/metal/lib/events'; import type CoreView from './core_view'; export function sendCoreViewEvent(view: CoreView, name: string, args: any[] = []) { diff --git a/packages/@ember/object/mixin.ts b/packages/@ember/object/mixin.ts index acaf82b032b..6e3951e9a63 100644 --- a/packages/@ember/object/mixin.ts +++ b/packages/@ember/object/mixin.ts @@ -5,10 +5,7 @@ import { INIT_FACTORY } from '@ember/-internals/container/lib/container'; import type { Meta } from '@ember/-internals/meta/lib/meta'; import { meta as metaFor, peekMeta } from '@ember/-internals/meta/lib/meta'; import { observerListenerMetaFor, ROOT, wrap } from '@ember/-internals/utils/lib/super'; -import { - DEPRECATION, - findDeprecation, -} from '@ember/-internals/utils/lib/mixin-deprecation'; +import { DEPRECATION, findDeprecation } from '@ember/-internals/utils/lib/mixin-deprecation'; import { assert } from '@ember/debug'; import { deprecateUntil, type DeprecationObject } from '@ember/-internals/deprecations'; import { DEBUG } from '@glimmer/env'; diff --git a/packages/@ember/routing/router-service.ts b/packages/@ember/routing/router-service.ts index d5cf9f5c053..24ed95c46ed 100644 --- a/packages/@ember/routing/router-service.ts +++ b/packages/@ember/routing/router-service.ts @@ -13,7 +13,12 @@ import EmberRouter from '@ember/routing/router'; import type { RouteInfo, RouteInfoWithAttributes } from './lib/route-info'; import type { RouteArgs, RouteOptions } from './lib/utils'; import { extractRouteArgs, resemblesURL, shallowEqual } from './lib/utils'; -import { addListener, hasListeners, removeListener, sendEvent } from '@ember/-internals/metal'; +import { + addListener, + hasListeners, + removeListener, + sendEvent, +} from '@ember/-internals/metal/lib/events'; export const ROUTER = Symbol('ROUTER'); diff --git a/packages/@ember/routing/router.ts b/packages/@ember/routing/router.ts index 55922caf655..283fce08297 100644 --- a/packages/@ember/routing/router.ts +++ b/packages/@ember/routing/router.ts @@ -57,8 +57,8 @@ import type { QueryParams } from 'route-recognizer'; import type { AnyFn, MethodNamesOf, OmitFirst } from '@ember/-internals/utility-types'; import type { Template } from '@glimmer/interfaces'; import type ApplicationInstance from '@ember/application/instance'; -import { sendEvent } from '@ember/-internals/metal'; -import { disableDeprecations } from '@ember/-internals/utils'; +import { sendEvent } from '@ember/-internals/metal/lib/events'; +import { disableDeprecations } from '@ember/-internals/utils/lib/mixin-deprecation'; /** @module @ember/routing/router diff --git a/packages/ember/barrel.ts b/packages/ember/barrel.ts deleted file mode 100644 index afa6b18c8cf..00000000000 --- a/packages/ember/barrel.ts +++ /dev/null @@ -1,758 +0,0 @@ -/** -@module ember -*/ - -import { getENV, getLookup, setLookup } from '@ember/-internals/environment'; -import * as utils from '@ember/-internals/utils'; -import { - Registry as InternalRegistry, - Container as InternalContainer, -} from '@ember/-internals/container'; -import * as instrumentation from '@ember/instrumentation'; -import { meta as internalMeta } from '@ember/-internals/meta'; -import * as metal from '@ember/-internals/metal'; -import { FEATURES as EmberFEATURES, isEnabled } from '@ember/canary-features'; -import * as EmberDebug from '@ember/debug'; -import { assert as emberAssert, captureRenderTree } from '@ember/debug'; -import Backburner from 'backburner.js'; -import EmberController, { - inject as injectController, - ControllerMixin as EmberControllerMixin, -} from '@ember/controller'; -import EmberService, { service } from '@ember/service'; - -import EmberObject, { - action, - computed as emberComputed, - defineProperty as emberDefineProperty, - notifyPropertyChange as emberNotifyPropertyChange, - observer as emberObserver, - get as emberGet, - getProperties as emberGetProperties, - set as emberSet, - setProperties as emberSetProperties, - trySet as emberTrySet, -} from '@ember/object'; -import { cacheFor as emberCacheFor } from '@ember/object/-internals'; -import { dependentKeyCompat } from '@ember/object/compat'; -import EmberComputedProperty, { - expandProperties as emberExpandProperties, -} from '@ember/object/computed'; -import { - addListener as emberAddListener, - removeListener as emberRemoveListener, - sendEvent as emberSendEvent, -} from '@ember/object/events'; - -import { - RegistryProxyMixin, - ContainerProxyMixin, - _ProxyMixin as internalProxyMixin, - RSVP as _RSVP, - Comparable as InternalComparable, - ActionHandler as InternalActionHandler, -} from '@ember/-internals/runtime'; -import { - componentCapabilities, - modifierCapabilities, - setComponentManager, - getTemplates, - setTemplates, - template, - isSerializationFirstNode, - type TemplatesRegistry, -} from '@ember/-internals/glimmer'; -import Version from './version'; -import * as views from '@ember/-internals/views'; -import EmberContainerDebugAdapter from '@ember/debug/container-debug-adapter'; -import EmberDataAdapter from '@ember/debug/data-adapter'; -import { run as emberRun } from '@ember/runloop'; -import { getOnerror, setOnerror } from '@ember/-internals/error-handling'; -import EmberArray, { - A as EmberA, - NativeArray as EmberNativeArray, - isArray as emberIsArray, - makeArray as emberMakeArray, -} from '@ember/array'; -import EmberMutableArray from '@ember/array/mutable'; -import EmberArrayProxy from '@ember/array/proxy'; -import EmberApplication, { - getOwner as applicationGetOwner, - setOwner as applicationSetOwner, - onLoad as applicationOnLoad, - runLoadHooks as applicationRunLoadHooks, -} from '@ember/application'; -import EmberApplicationInstance from '@ember/application/instance'; -import EmberNamespace from '@ember/application/namespace'; -import EmberComponent, { Input as EmberInput } from '@ember/component'; -import EmberHelper from '@ember/component/helper'; -import EmberEngine from '@ember/engine'; -import EmberEngineInstance from '@ember/engine/instance'; -import EmberEnumerable from '@ember/enumerable'; -import EmberMutableEnumerable from '@ember/enumerable/mutable'; -import EmberCoreObject from '@ember/object/core'; -import EmberEvented, { on as emberOn } from '@ember/object/evented'; -import EmberMixin, { mixin as emberMixin } from '@ember/object/mixin'; -import EmberObservable from '@ember/object/observable'; -import { - addObserver as emberAddObserver, - removeObserver as emberRemoveObserver, -} from '@ember/object/observers'; -import EmberObjectProxy from '@ember/object/proxy'; -import EmberPromiseProxyMixin from '@ember/object/promise-proxy-mixin'; -import EmberHashLocation from '@ember/routing/hash-location'; -import EmberHistoryLocation from '@ember/routing/history-location'; -import EmberNoneLocation from '@ember/routing/none-location'; -import EmberRoute from '@ember/routing/route'; -import EmberRouter from '@ember/routing/router'; -import { - controllerFor as emberControllerFor, - generateController as emberGenerateController, - generateControllerFactory as emberGenerateControllerFactory, - DSL as EmberRouterDSL, -} from '@ember/routing/-internals'; -import { - isNone as emberIsNone, - isBlank as emberIsBlank, - isEmpty as emberIsEmpty, - isPresent as emberIsPresent, - isEqual as emberIsEqual, - typeOf as emberTypeOf, - compare as emberCompare, -} from '@ember/utils'; - -import * as glimmerRuntime from '@glimmer/runtime'; - -import { - helperCapabilities, - setModifierManager, - setComponentTemplate, - getComponentTemplate, - setHelperManager, -} from '@glimmer/manager'; - -import { - assertDestroyablesDestroyed, - associateDestroyableChild, - destroy as emberDestroy, - enableDestroyableTracking, - isDestroying, - isDestroyed, - registerDestructor, - unregisterDestructor, -} from '@ember/destroyable'; - -import type { precompile, compile } from 'ember-template-compiler'; -import { _impl as EmberTestingImpl } from '@ember/test'; -import * as templateCompilation from '@ember/template-compilation'; - -// eslint-disable-next-line @typescript-eslint/no-namespace -namespace Ember { - export const isNamespace = true; - - export function toString() { - return 'Ember'; - } - - // ****@ember/-internals/container**** - export const Container = InternalContainer; - export type Container = InternalContainer; - export const Registry = InternalRegistry; - export type Registry = InternalRegistry; - - // ****@ember/-internals/glimmer**** - // Partially re-exported from @glimmer/manager - export const _setComponentManager = setComponentManager; - export const _componentManagerCapabilities = componentCapabilities; - export const _modifierManagerCapabilities = modifierCapabilities; - - // ****@ember/-internals/meta**** - export const meta = internalMeta; - - // ****@ember/-internals/metal**** - export const _createCache = metal.createCache; // Also @glimmer/validator - export const _cacheGetValue = metal.getValue; // Also @glimmer/validator - export const _cacheIsConst = metal.isConst; // Also @glimmer/validator - export const _descriptor = metal.nativeDescDecorator; - export const _getPath = metal._getPath; - export const _setClassicDecorator = metal.setClassicDecorator; - export const _tracked = metal.tracked; // Also exported from @glimmer/tracking - export const beginPropertyChanges = metal.beginPropertyChanges; - export const changeProperties = metal.changeProperties; - export const endPropertyChanges = metal.endPropertyChanges; - export const hasListeners = metal.hasListeners; - export const libraries = metal.libraries; - - // ****@ember/-internals/runtime**** - export const _ContainerProxyMixin = ContainerProxyMixin; - export const _ProxyMixin = internalProxyMixin; - export const _RegistryProxyMixin = RegistryProxyMixin; - export const ActionHandler = InternalActionHandler; - export type ActionHandler = InternalActionHandler; - export const Comparable = InternalComparable; - export type Comparable = InternalComparable; - - // ****@ember/-internals/view**** - export const ComponentLookup = views.ComponentLookup; - export const EventDispatcher = views.EventDispatcher; - - // ****@ember/-internals/utils**** - export const _Cache = utils.Cache; - export const GUID_KEY = utils.GUID_KEY; - export const canInvoke = utils.canInvoke; - export const generateGuid = utils.generateGuid; - export const guidFor = utils.guidFor; - export const uuid = utils.uuid; - export const wrap = utils.wrap; - - // ****@ember/application**** - export const getOwner = applicationGetOwner; - export const onLoad = applicationOnLoad; - export const runLoadHooks = applicationRunLoadHooks; - export const setOwner = applicationSetOwner; - export const Application = EmberApplication; - export type Application = EmberApplication; - - // ****@ember/application/instance**** - export const ApplicationInstance = EmberApplicationInstance; - export type ApplicationInstance = EmberApplicationInstance; - - // // ****@ember/application/namespace**** - export const Namespace = EmberNamespace; - export type Namespace = EmberNamespace; - - // ****@ember/array**** - export const A = EmberA; - export const Array = EmberArray; - export type Array = EmberArray; - export const NativeArray = EmberNativeArray; - export type NativeArray = EmberNativeArray; - export const isArray = emberIsArray; - export const makeArray = emberMakeArray; - - // ****@ember/array/mutable**** - export const MutableArray = EmberMutableArray; - export type MutableArray = EmberMutableArray; - - // ****@ember/array/proxy**** - export const ArrayProxy = EmberArrayProxy; - export type ArrayProxy = EmberArrayProxy; - - // ****@ember/canary-features**** - export const FEATURES = { isEnabled, ...EmberFEATURES }; - - // ****@ember/component**** - export const _Input = EmberInput; - export const Component = EmberComponent; - export type Component = EmberComponent; - - // // ****@ember/component/helper**** - export const Helper = EmberHelper; - export type Helper = EmberHelper; - - // ****@ember/controller**** - export const Controller = EmberController; - export type Controller = EmberController; - export const ControllerMixin = EmberControllerMixin; - export type ControllerMixin = EmberControllerMixin; - - // ****@ember/debug**** - export const _captureRenderTree = captureRenderTree; - export const assert: typeof EmberDebug.assert = EmberDebug.assert; - export const warn: typeof EmberDebug.warn = EmberDebug.warn; - export const debug = EmberDebug.debug; - export const deprecate = EmberDebug.deprecate; - export const deprecateFunc = EmberDebug.deprecateFunc; - export const runInDebug = EmberDebug.runInDebug; - export const inspect = EmberDebug.inspect; - - export const Debug: { - registerDeprecationHandler: typeof EmberDebug.registerDeprecationHandler; - registerWarnHandler: typeof EmberDebug.registerWarnHandler; - isComputed: typeof metal.isComputed; - } = { - registerDeprecationHandler: EmberDebug.registerDeprecationHandler, - registerWarnHandler: EmberDebug.registerWarnHandler, - // ****@ember/-internals/metal**** - isComputed: metal.isComputed, - }; - - // ****@ember/debug/container-debug-adapter**** - export const ContainerDebugAdapter = EmberContainerDebugAdapter; - export type ContainerDebugAdapter = EmberContainerDebugAdapter; - - // ****@ember/debug/data-adapter**** - export const DataAdapter = EmberDataAdapter; - export type DataAdapter = EmberDataAdapter; - - // ****@ember/destroyable**** - export const _assertDestroyablesDestroyed = assertDestroyablesDestroyed; - export const _associateDestroyableChild = associateDestroyableChild; - export const _enableDestroyableTracking = enableDestroyableTracking; - export const _isDestroying = isDestroying; - export const _isDestroyed = isDestroyed; - export const _registerDestructor = registerDestructor; - export const _unregisterDestructor = unregisterDestructor; - export const destroy = emberDestroy; - - // ****@ember/engine**** - export const Engine = EmberEngine; - export type Engine = EmberEngine; - - // ****@ember/engine/instance**** - export const EngineInstance = EmberEngineInstance; - export type EngineInstance = EmberEngineInstance; - - // ****@ember/enumerable**** - export const Enumerable = EmberEnumerable; - export type Enumerable = EmberEnumerable; - - // ****@ember/enumerable/mutable**** - export const MutableEnumerable = EmberMutableEnumerable; - export type MutableEnumerable = EmberMutableEnumerable; - - // ****@ember/instrumentation**** - /** @private */ - export const instrument = instrumentation.instrument; - /** @private */ - export const subscribe = instrumentation.subscribe; - - /** @private */ - export const Instrumentation = { - instrument: instrumentation.instrument, - subscribe: instrumentation.subscribe, - unsubscribe: instrumentation.unsubscribe, - reset: instrumentation.reset, - }; - - // ****@ember/object**** - export const Object = EmberObject; - export type Object = EmberObject; - export const _action = action; - export const computed = emberComputed; - export const defineProperty = emberDefineProperty; - export const get = emberGet; - export const getProperties = emberGetProperties; - export const notifyPropertyChange = emberNotifyPropertyChange; - export const observer = emberObserver; - export const set = emberSet; - export const trySet = emberTrySet; - export const setProperties = emberSetProperties; - - // ****@ember/object/-internals**** - export const cacheFor = emberCacheFor; - - // ****@ember/object/compat**** - export const _dependentKeyCompat = dependentKeyCompat; - - // ****@ember/object/computed**** - export const ComputedProperty = EmberComputedProperty; - export type ComputedProperty = EmberComputedProperty; - export const expandProperties = emberExpandProperties; - - // ****@ember/object/core**** - export const CoreObject = EmberCoreObject; - export type CoreObject = EmberCoreObject; - - // ****@ember/object/evented**** - export const Evented = EmberEvented; - export type Evented = EmberEvented; - export const on = emberOn; - - // ****@ember/object/events**** - export const addListener = emberAddListener; - export const removeListener = emberRemoveListener; - export const sendEvent = emberSendEvent; - - // ****@ember/object/mixin**** - export const Mixin = EmberMixin; - export type Mixin = EmberMixin; - export const mixin = emberMixin; - - // ****@ember/object/observable**** - export const Observable = EmberObservable; - export type Observable = EmberObservable; - - // ****@ember/object/observers**** - export const addObserver = emberAddObserver; - export const removeObserver = emberRemoveObserver; - - // ****@ember/object/promise-proxy-mixin**** - export const PromiseProxyMixin = EmberPromiseProxyMixin; - export type PromiseProxyMixin = EmberPromiseProxyMixin; - - // ****@ember/object/proxy**** - export const ObjectProxy = EmberObjectProxy; - export type ObjectProxy = EmberObjectProxy; - - // ****@ember/routing/-internals**** - export const RouterDSL = EmberRouterDSL; - export type RouterDSL = EmberRouterDSL; - export const controllerFor = emberControllerFor; - export const generateController = emberGenerateController; - export const generateControllerFactory = emberGenerateControllerFactory; - - // ****@ember/routing/hash-location**** - export const HashLocation = EmberHashLocation; - export type HashLocation = EmberHashLocation; - - // ****@ember/routing/history-location**** - export const HistoryLocation = EmberHistoryLocation; - export type HistoryLocation = EmberHistoryLocation; - - // ****@ember/routing/none-location**** - export const NoneLocation = EmberNoneLocation; - export type NoneLocation = EmberNoneLocation; - - // ****@ember/routing/route**** - export const Route = EmberRoute; - export type Route = EmberRoute; - - // ****@ember/routing/router**** - export const Router = EmberRouter; - export type Router = EmberRouter; - - // // ****@ember/runloop**** - export const run = emberRun; - - // // ****@ember/service**** - export const Service = EmberService; - export type Service = EmberService; - - // ****@ember/utils**** - export const compare = emberCompare; - export const isBlank = emberIsBlank; - export const isEmpty = emberIsEmpty; - export const isEqual = emberIsEqual; - export const isNone = emberIsNone; - export const isPresent = emberIsPresent; - export const typeOf = emberTypeOf; - - // ****@ember/version**** - /** - The semantic version - - @property VERSION - @type String - @public - */ - export const VERSION = Version; - - export const ViewUtils = { - // ****@ember/-internals/views**** - getChildViews: views.getChildViews, - getElementView: views.getElementView, - getRootViews: views.getRootViews, - getViewBounds: views.getViewBounds, - getViewBoundingClientRect: views.getViewBoundingClientRect, - getViewClientRects: views.getViewClientRects, - getViewElement: views.getViewElement, - isSimpleClick: views.isSimpleClick, - - // ****@ember/-internals/glimmer**** - isSerializationFirstNode, - }; - - // ****@glimmer/manager**** - export const _getComponentTemplate = getComponentTemplate; - export const _helperManagerCapabilities = helperCapabilities; - export const _setComponentTemplate = setComponentTemplate; - export const _setHelperManager = setHelperManager; - export const _setModifierManager = setModifierManager; - - // ****@glimmer/runtime**** - export const _templateOnlyComponent = glimmerRuntime.templateOnlyComponent; - export const _invokeHelper = glimmerRuntime.invokeHelper; - export const _hash = glimmerRuntime.hash; - export const _array = glimmerRuntime.array; - export const _concat = glimmerRuntime.concat; - export const _get = glimmerRuntime.get; - export const _on = glimmerRuntime.on; - export const _fn = glimmerRuntime.fn; - - // Backburner - export const _Backburner = Backburner; - export type _Backburner = Backburner; - - // // ****@ember/controller, @ember/service**** - /** - Namespace for injection helper methods. - - @class inject - @namespace Ember - @static - @public - */ - export function inject() { - // uses `globalThis` to avoid clobbering with `Ember.Object` in TS namespace - emberAssert( - `Injected properties must be created through helpers, see '${globalThis.Object.keys(inject) - .map((k) => `'inject.${k}'`) - .join(' or ')}'` - ); - } - // ****@ember/controller**** - inject.controller = injectController; - // ****@ember/service**** - inject.service = service; - - export const __loader = { - get require() { - return (globalThis as any).require; - }, - get define() { - return (globalThis as any).define; - }, - get registry() { - let g = globalThis as any; - return g.requirejs?.entries ?? g.require.entries; - }, - }; - - // ------------------------------------------------------------------------ // - // These properties are assigned to the namespace with getters (and, in some - // cases setters) with `Object.defineProperty` below. - // ------------------------------------------------------------------------ // - - export declare const ENV: Readonly; - - // ****@ember/-internals/environment**** - export declare let lookup: Record; - - /** - A function may be assigned to `Ember.onerror` to be called when Ember - internals encounter an error. This is useful for specialized error handling - and reporting code. - - ```javascript - - Ember.onerror = function(error) { - const payload = { - stack: error.stack, - otherInformation: 'whatever app state you want to provide' - }; - - fetch('/report-error', { - method: 'POST', - body: JSON.stringify(payload) - }); - }; - ``` - - Internally, `Ember.onerror` is used as Backburner's error handler. - - @event onerror - @for Ember - @param {Error} error the error object - @public - */ - // ****@ember/-internals/error-handling**** - export declare let onerror: ((error: Error) => unknown) | undefined; - - export declare let testing: boolean; - - /** - Whether searching on the global for new Namespace instances is enabled. - - This is only exported here as to not break any addons. Given the new - visit API, you will have issues if you treat this as a indicator of - booted. - - Internally this is only exposing a flag in Namespace. - - @property BOOTED - @for Ember - @type Boolean - @private - */ - export declare let BOOTED: boolean; - - /** - Global hash of shared templates. This will automatically be populated - by the build tools so that you can store your Handlebars templates in - separate files that get loaded into JavaScript at buildtime. - - @property TEMPLATES - @for Ember - @type Object - @private - */ - export declare let TEMPLATES: TemplatesRegistry; - - export declare let HTMLBars: EmberHTMLBars; - export declare let Handlebars: EmberHandlebars; - export declare let Test: - | (NonNullable['Test'] & { - Adapter: NonNullable['Adapter']; - QUnitAdapter: NonNullable['QUnitAdapter']; - }) - | undefined; - export declare let setupForTesting: - | NonNullable['setupForTesting'] - | undefined; -} - -// This syntax is not reliably implemented by TypeScript transpilers, but -// we need to re-export the`RSVP` *namespace* for type compatibility. -// To achieve this, we use a type-only `declare namespace` block to get the -// types to behave correctly, and separately set the `RSVP` property on the -// `Ember` object dynamically. (The types behave correctly because of -// namespace merging semantics.) -// eslint-disable-next-line @typescript-eslint/no-namespace -declare namespace Ember { - export import RSVP = _RSVP; -} - -Reflect.set(Ember, 'RSVP', _RSVP); - -interface EmberHandlebars { - template: typeof template; - // eslint-disable-next-line @typescript-eslint/no-empty-object-type - Utils: {}; - compile?: typeof compile; - precompile?: typeof precompile; -} - -interface EmberHTMLBars { - template: typeof template; - compile?: typeof compile; - precompile?: typeof precompile; -} - -Object.defineProperty(Ember, 'ENV', { - get: getENV, - enumerable: false, -}); - -Object.defineProperty(Ember, 'lookup', { - get: getLookup, - set: setLookup, - enumerable: false, -}); - -Object.defineProperty(Ember, 'onerror', { - get: getOnerror, - set: setOnerror, - enumerable: false, -}); - -Object.defineProperty(Ember, 'testing', { - get: EmberDebug.isTesting, - set: EmberDebug.setTesting, - enumerable: false, -}); - -Object.defineProperty(Ember, 'BOOTED', { - configurable: false, - enumerable: false, - get: metal.isNamespaceSearchDisabled, - set: metal.setNamespaceSearchDisabled, -}); - -Object.defineProperty(Ember, 'TEMPLATES', { - get: getTemplates, - set: setTemplates, - configurable: false, - enumerable: false, -}); - -Object.defineProperty(Ember, 'TEMPLATES', { - get: getTemplates, - set: setTemplates, - configurable: false, - enumerable: false, -}); - -// ****@ember/debug**** -Object.defineProperty(Ember, 'testing', { - get: EmberDebug.isTesting, - set: EmberDebug.setTesting, - enumerable: false, -}); - -applicationRunLoadHooks('Ember.Application', EmberApplication); - -let EmberHandlebars: EmberHandlebars = { - template, - Utils: {}, -}; - -let EmberHTMLBars: EmberHTMLBars = { - template, -}; - -function defineEmberTemplateCompilerLazyLoad(key: 'HTMLBars' | 'Handlebars') { - Object.defineProperty(Ember, key, { - configurable: true, - enumerable: true, - get() { - if (templateCompilation.__emberTemplateCompiler) { - EmberHTMLBars.precompile = EmberHandlebars.precompile = - templateCompilation.__emberTemplateCompiler.precompile; - EmberHTMLBars.compile = EmberHandlebars.compile = templateCompilation.compileTemplate; - - Object.defineProperty(Ember, 'HTMLBars', { - configurable: true, - writable: true, - enumerable: true, - value: EmberHTMLBars, - }); - Object.defineProperty(Ember, 'Handlebars', { - configurable: true, - writable: true, - enumerable: true, - value: EmberHandlebars, - }); - } - - return key === 'Handlebars' ? EmberHandlebars : EmberHTMLBars; - }, - }); -} - -defineEmberTemplateCompilerLazyLoad('HTMLBars'); -defineEmberTemplateCompilerLazyLoad('Handlebars'); - -// do this to ensure that Ember.Test is defined properly on the global -// if it is present. -function defineEmberTestingLazyLoad(key: 'Test' | 'setupForTesting') { - Object.defineProperty(Ember, key, { - configurable: true, - enumerable: true, - get() { - if (EmberTestingImpl) { - let { Test, Adapter, QUnitAdapter, setupForTesting } = EmberTestingImpl; - - // @ts-expect-error We should not do this - Test.Adapter = Adapter; - // @ts-expect-error We should not do this - Test.QUnitAdapter = QUnitAdapter; - - Object.defineProperty(Ember, 'Test', { - configurable: true, - writable: true, - enumerable: true, - value: Test, - }); - Object.defineProperty(Ember, 'setupForTesting', { - configurable: true, - writable: true, - enumerable: true, - value: setupForTesting, - }); - - return key === 'Test' ? Test : setupForTesting; - } - - return undefined; - }, - }); -} - -defineEmberTestingLazyLoad('Test'); -defineEmberTestingLazyLoad('setupForTesting'); - -// @ts-expect-error Per types, runLoadHooks requires a second parameter. Should we loosen types? -applicationRunLoadHooks('Ember'); - -export default Ember;