From 5a7067125fe80722503ae42779818ec178291b50 Mon Sep 17 00:00:00 2001 From: Peter Wagenet Date: Thu, 4 Sep 2025 15:30:19 -0700 Subject: [PATCH 01/10] Mixin deprecation tooling --- .../-internals/utils/lib/mixin-deprecation.ts | 42 +++++++++++++++++++ packages/@ember/object/mixin.ts | 16 ++++++- 2 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 packages/@ember/-internals/utils/lib/mixin-deprecation.ts 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..adf0f084c46 --- /dev/null +++ b/packages/@ember/-internals/utils/lib/mixin-deprecation.ts @@ -0,0 +1,42 @@ +import type { DeprecationOptions } from '@ember/debug'; +import type Mixin from '@ember/object/mixin'; + +/** @internal */ +export const DEPRECATION = Symbol('DEPRECATION'); + +/** @internal */ +export function setDeprecation( + mixin: Mixin, + value: { message: string; options: DeprecationOptions } | null +) { + mixin[DEPRECATION] = value; +} + +let deprecationsEnabled = true; + +export function findDeprecation( + mixin: Mixin +): { message: string; options: DeprecationOptions } | null { + if (!deprecationsEnabled) { + return null; + } + if (mixin[DEPRECATION]) { + return mixin[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/object/mixin.ts b/packages/@ember/object/mixin.ts index 32b550c2cf9..f169beab847 100644 --- a/packages/@ember/object/mixin.ts +++ b/packages/@ember/object/mixin.ts @@ -5,7 +5,7 @@ import { INIT_FACTORY } from '@ember/-internals/container'; import type { Meta } from '@ember/-internals/meta'; import { meta as metaFor, peekMeta } from '@ember/-internals/meta'; import { guidFor, observerListenerMetaFor, ROOT, wrap } from '@ember/-internals/utils'; -import { assert } from '@ember/debug'; +import { assert, deprecate, DeprecationOptions } from '@ember/debug'; import { DEBUG } from '@glimmer/env'; import { type ComputedDecorator, @@ -14,6 +14,8 @@ import { type ComputedPropertySetter, type ComputedDescriptor, isClassicDecorator, + addListener, + removeListener, } from '@ember/-internals/metal'; import { ComputedProperty, @@ -27,7 +29,7 @@ import { defineDecorator, defineValue, } from '@ember/-internals/metal'; -import { addListener, removeListener } from '@ember/object/events'; +import { DEPRECATION, findDeprecation } from '@ember/-internals/utils/lib/mixin-deprecation'; const a_concat = Array.prototype.concat; const { isArray } = Array; @@ -543,6 +545,9 @@ export default class Mixin { /** @internal */ properties: { [key: string]: any } | undefined; + /*** @internal */ + [DEPRECATION]: { message: string; options: DeprecationOptions } | null = null; + /** @internal */ ownerConstructor: any; @@ -621,6 +626,13 @@ export default class Mixin { return this; } + if (DEBUG) { + for (let mixin of args) { + let deprecation = mixin instanceof Mixin ? findDeprecation(mixin) : null; + deprecate(deprecation?.message ?? 'Huh???', !deprecation, deprecation?.options); + } + } + if (this.properties) { let currentMixin = new Mixin(undefined, this.properties); this.properties = undefined; From 8e310c6db2e2136fc8615f6e1cedd6b0accabeca Mon Sep 17 00:00:00 2001 From: Peter Wagenet Date: Thu, 4 Sep 2025 20:13:38 -0700 Subject: [PATCH 02/10] Update tests --- .../integration/components/append-test.js | 16 ++- .../components/curly-components-test.js | 71 +++++----- .../integration/components/life-cycle-test.js | 12 +- .../integration/event-dispatcher-test.js | 6 +- .../tests/integration/syntax/each-test.js | 22 ++- .../-internals/metal/tests/events_test.js | 66 +++++---- .../object/tests/es-compatibility-test.js | 60 ++++---- packages/@ember/object/tests/evented_test.js | 24 ++-- packages/@ember/object/tests/events_test.js | 134 +++++++++++++----- .../tests/routing/decoupled_basic_test.js | 25 ++-- 10 files changed, 284 insertions(+), 152 deletions(-) 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 3e89c2297a3..cea41705a8a 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,10 @@ -import { moduleFor, RenderingTestCase, strip, runTask } from 'internal-test-helpers'; +import { + moduleFor, + RenderingTestCase, + strip, + runTask, + expectDeprecation, +} from 'internal-test-helpers'; import { set } from '@ember/object'; @@ -56,7 +62,9 @@ class AbstractAppendTest extends RenderingTestCase { } componentsByName[name] = this; pushHook('init'); - this.on('init', () => pushHook('on(init)')); + expectDeprecation(() => { + this.on('init', () => pushHook('on(init)')); + }, /`on` is deprecated/); } didReceiveAttrs() { @@ -299,7 +307,9 @@ class AbstractAppendTest extends RenderingTestCase { } componentsByName[name] = this; pushHook('init'); - this.on('init', () => pushHook('on(init)')); + expectDeprecation(() => { + this.on('init', () => pushHook('on(init)')); + }, /`on` is deprecated/); } 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 2bf955d092e..8ba4f8bb396 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,6 +7,7 @@ import { equalsElement, runTask, runLoopSettled, + expectDeprecation, } from 'internal-test-helpers'; import { action } from '@ember/object'; @@ -3162,45 +3163,49 @@ moduleFor( ['@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.registerComponent('evented-component', { - ComponentClass: Component.extend({ - someTruthyProperty: true, - - init() { - this._super(...arguments); - this.trigger('someMethod', ...payload); - this.trigger('someTruthyProperty', ...payload); - }, - - someMethod(...data) { - assert.deepEqual( - data, - payload, - 'the method `someMethod` should be called, when `someMethod` is triggered' - ); - }, + expectDeprecation(() => { + this.registerComponent('evented-component', { + ComponentClass: Component.extend({ + someTruthyProperty: true, + + init() { + this._super(...arguments); + expectDeprecation(() => { + this.trigger('someMethod', ...payload); + this.trigger('someTruthyProperty', ...payload); + }, /`trigger` is deprecated/); + }, - listenerForSomeMethod: on('someMethod', function (...data) { - assert.deepEqual( - data, - payload, - 'the listener `listenerForSomeMethod` should be called, when `someMethod` is triggered' - ); - }), + someMethod(...data) { + assert.deepEqual( + data, + payload, + 'the method `someMethod` 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` is deprecated/); 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 1c4f3bed6c0..8bfeec4cab6 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 @@ -1,4 +1,11 @@ -import { classes, moduleFor, RenderingTestCase, runTask, strip } from 'internal-test-helpers'; +import { + classes, + expectDeprecation, + moduleFor, + RenderingTestCase, + runTask, + strip, +} from 'internal-test-helpers'; import { schedule } from '@ember/runloop'; import { set, setProperties } from '@ember/object'; @@ -6,6 +13,7 @@ import { A as emberA } from '@ember/array'; import { getViewElement, getViewId } from '@ember/-internals/views'; import { Component } from '../../utils/helpers'; +import { addListener } from '@ember/-internals/metal'; class LifeCycleHooksTest extends RenderingTestCase { constructor() { @@ -174,7 +182,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 60c37711e7d..43bbcb27b41 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,4 @@ -import { moduleFor, RenderingTestCase, runTask } from 'internal-test-helpers'; +import { expectDeprecation, moduleFor, RenderingTestCase, runTask } from 'internal-test-helpers'; import { Component } from '../utils/helpers'; import { _getCurrentRunLoop } from '@ember/runloop'; @@ -159,7 +159,9 @@ 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)); + }, /`on` is deprecated/); }); } }, 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 e4ed8e69213..1cd3e989d3d 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,11 @@ -import { moduleFor, RenderingTestCase, applyMixins, strip, runTask } from 'internal-test-helpers'; +import { + moduleFor, + RenderingTestCase, + applyMixins, + strip, + runTask, + expectDeprecation, +} from 'internal-test-helpers'; import { notifyPropertyChange, on } from '@ember/-internals/metal'; import { get, set, computed } from '@ember/object'; @@ -1121,11 +1128,14 @@ moduleFor( class extends EachTest { createList(items) { let wrapped = emberA(items); - let proxy = ArrayProxy.extend({ - setup: on('init', function () { - this.set('content', emberA(wrapped)); - }), - }).create(); + let proxy; + expectDeprecation(() => { + proxy = ArrayProxy.extend({ + setup: on('init', function () { + this.set('content', emberA(wrapped)); + }), + }).create(); + }, /`on` is deprecated/); return { list: proxy, delegate: wrapped }; } diff --git a/packages/@ember/-internals/metal/tests/events_test.js b/packages/@ember/-internals/metal/tests/events_test.js index 109792c39b7..257e1b66a50 100644 --- a/packages/@ember/-internals/metal/tests/events_test.js +++ b/packages/@ember/-internals/metal/tests/events_test.js @@ -1,6 +1,6 @@ import { on, addListener, removeListener, sendEvent, hasListeners } from '..'; import Mixin from '@ember/object/mixin'; -import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { moduleFor, AbstractTestCase, expectDeprecation } from 'internal-test-helpers'; moduleFor( 'system/props/events_test', @@ -141,15 +141,18 @@ moduleFor( ['@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` is deprecated/); let obj = {}; MyMixin.apply(obj); @@ -160,30 +163,41 @@ moduleFor( [`@test 'on' asserts for invalid arguments`]() { expectAssertion(() => { - Mixin.create({ - foo1: on('bar'), - }); + expectDeprecation(() => { + Mixin.create({ + foo1: on('bar'), + }); + }, /`on` is deprecated/); }, 'on expects function as last argument'); expectAssertion(() => { - Mixin.create({ - foo1: on(function () {}), - }); + expectDeprecation(() => { + Mixin.create({ + foo1: on(function () {}), + }); + }, /`on` is deprecated/); }, 'on called without valid event names'); } ['@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` is deprecated/); + + let SecondMixin; + expectDeprecation(() => { + SecondMixin = Mixin.create({ + foo: on('baz', function () { + triggered++; + }), + }); + }, /`on` is deprecated/); let obj = {}; FirstMixin.apply(obj); diff --git a/packages/@ember/object/tests/es-compatibility-test.js b/packages/@ember/object/tests/es-compatibility-test.js index 33cff30a067..b4f9bb140d6 100644 --- a/packages/@ember/object/tests/es-compatibility-test.js +++ b/packages/@ember/object/tests/es-compatibility-test.js @@ -9,7 +9,12 @@ 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, +} from 'internal-test-helpers'; moduleFor( 'EmberObject ES Compatibility', @@ -283,30 +288,35 @@ moduleFor( 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` is deprecated/); 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..5da69ae1001 100644 --- a/packages/@ember/object/tests/evented_test.js +++ b/packages/@ember/object/tests/evented_test.js @@ -1,21 +1,29 @@ import CoreObject from '@ember/object/core'; import EventedMixin from '@ember/object/evented'; -import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { moduleFor, AbstractTestCase, expectDeprecation } from 'internal-test-helpers'; 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(); + let eventedProxyObj; + expectDeprecation(() => { + eventedProxyObj = class extends CoreObject.extend(EventedMixin) { + unknownProperty() { + return true; + } + }.create(); + }, /Evented mixin is deprecated/); let noop = function () {}; - eventedProxyObj.on('foo', noop); - eventedProxyObj.off('foo', noop); + expectDeprecation(() => { + eventedProxyObj.on('foo', noop); + }, /`on` is deprecated/); + + expectDeprecation(() => { + eventedProxyObj.off('foo', noop); + }, /`off` is deprecated/); 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..5745c9a5f26 100644 --- a/packages/@ember/object/tests/events_test.js +++ b/packages/@ember/object/tests/events_test.js @@ -1,6 +1,6 @@ import EmberObject from '@ember/object'; -import Evented from '@ember/object/evented'; -import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { Evented } from '@ember/-internals/runtime'; +import { moduleFor, AbstractTestCase, expectDeprecation } from 'internal-test-helpers'; moduleFor( 'Object events', @@ -13,12 +13,18 @@ moduleFor( let obj = EmberObject.extend(Evented).create(); - obj.on('event!', F); - obj.trigger('event!'); + expectDeprecation(() => { + obj.on('event!', F); + }, /`on` is deprecated/); + expectDeprecation(() => { + obj.trigger('event!'); + }, /`trigger` is deprecated/); assert.equal(count, 1, 'the event was triggered'); - obj.trigger('event!'); + expectDeprecation(() => { + obj.trigger('event!'); + }, /`trigger` is deprecated/); assert.equal(count, 2, 'the event was triggered'); } @@ -33,12 +39,19 @@ moduleFor( let obj = EmberObject.extend(Evented).create(); - obj.one('event!', F); - obj.trigger('event!'); + expectDeprecation(() => { + obj.one('event!', F); + }, /`one` is deprecated/); + + expectDeprecation(() => { + obj.trigger('event!'); + }, /`trigger` is deprecated/); assert.equal(count, 1, 'the event was triggered'); - obj.trigger('event!'); + expectDeprecation(() => { + obj.trigger('event!'); + }, /`trigger` is deprecated/); assert.equal(count, 1, 'the event was not triggered again'); } @@ -48,12 +61,16 @@ moduleFor( let obj = EmberObject.extend(Evented).create(); - obj.on('event!', function () { - args = [].slice.call(arguments); - self = this; - }); + expectDeprecation(() => { + obj.on('event!', function () { + args = [].slice.call(arguments); + self = this; + }); + }, /`on` is deprecated/); - obj.trigger('event!', 'foo', 'bar'); + expectDeprecation(() => { + obj.trigger('event!', 'foo', 'bar'); + }, /`trigger` is deprecated/); assert.deepEqual(args, ['foo', 'bar']); assert.equal(self, obj); @@ -65,19 +82,25 @@ moduleFor( let obj = EmberObject.extend(Evented).create(); - obj.one('event!', function () { - args = [].slice.call(arguments); - self = this; - count++; - }); + expectDeprecation(() => { + obj.one('event!', function () { + args = [].slice.call(arguments); + self = this; + count++; + }); + }, /`one` is deprecated/); - obj.trigger('event!', 'foo', 'bar'); + expectDeprecation(() => { + obj.trigger('event!', 'foo', 'bar'); + }, /`trigger` is deprecated/); 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'); + }, /`trigger` is deprecated/); assert.deepEqual(args, ['foo', 'bar']); assert.equal(count, 1, 'the event was not triggered again'); @@ -90,12 +113,16 @@ moduleFor( let obj = EmberObject.extend(Evented).create(); let target = {}; - obj.on('event!', target, function () { - args = [].slice.call(arguments); - self = this; - }); + expectDeprecation(() => { + obj.on('event!', target, function () { + args = [].slice.call(arguments); + self = this; + }); + }, /`on` is deprecated/); - obj.trigger('event!', 'foo', 'bar'); + expectDeprecation(() => { + obj.trigger('event!', 'foo', 'bar'); + }, /`trigger` is deprecated/); assert.deepEqual(args, ['foo', 'bar']); assert.equal(self, target); @@ -112,12 +139,19 @@ moduleFor( let obj = EmberObject.extend(Evented).create(); - obj.one('event!', target, 'fn'); - obj.trigger('event!'); + expectDeprecation(() => { + obj.one('event!', target, 'fn'); + }, /`one` is deprecated/); + + expectDeprecation(() => { + obj.trigger('event!'); + }, /`trigger` is deprecated/); assert.equal(count, 1, 'the event was triggered'); - obj.trigger('event!'); + expectDeprecation(() => { + obj.trigger('event!'); + }, /`trigger` is deprecated/); assert.equal(count, 1, 'the event was not triggered again'); } @@ -128,28 +162,54 @@ moduleFor( }.create(); let F = function () {}; - obj.one('event!', F); - obj.one('event!', obj, 'F'); + expectDeprecation(() => { + obj.one('event!', F); + }, /`one` is deprecated/); + + expectDeprecation(() => { + obj.one('event!', obj, 'F'); + }, /`one` is deprecated/); - assert.equal(obj.has('event!'), true, 'has events'); + let objHas; + expectDeprecation(() => { + objHas = obj.has('event!'); + }, /`has` is deprecated/); + assert.equal(objHas, true, 'has events'); - obj.off('event!', F); - obj.off('event!', obj, 'F'); + expectDeprecation(() => { + obj.off('event!', F); + }, /`off` is deprecated/); - assert.equal(obj.has('event!'), false, 'has no more events'); + expectDeprecation(() => { + obj.off('event!', obj, 'F'); + }, /`off` is deprecated/); + + expectDeprecation(() => { + objHas = obj.has('event!'); + }, /`has` is deprecated/); + + assert.equal(objHas, false, 'has no more events'); } ['@test adding and removing listeners should be chainable'](assert) { let obj = EmberObject.extend(Evented).create(); let F = function () {}; - let ret = obj.on('event!', F); + let ret; + + expectDeprecation(() => { + ret = obj.on('event!', F); + }, /`on` is deprecated/); assert.equal(ret, obj, '#on returns self'); - ret = obj.off('event!', F); + expectDeprecation(() => { + ret = obj.off('event!', F); + }, /`off` is deprecated/); assert.equal(ret, obj, '#off returns self'); - ret = obj.one('event!', F); + expectDeprecation(() => { + ret = obj.one('event!', F); + }, /`one` is deprecated/); assert.equal(ret, obj, '#one returns self'); } } diff --git a/packages/ember/tests/routing/decoupled_basic_test.js b/packages/ember/tests/routing/decoupled_basic_test.js index a71b245d919..ae4ca4c4daf 100644 --- a/packages/ember/tests/routing/decoupled_basic_test.js +++ b/packages/ember/tests/routing/decoupled_basic_test.js @@ -13,6 +13,7 @@ import { ModuleBasedTestResolver, runDestroy, runTask, + expectDeprecation, } from 'internal-test-helpers'; import { run } from '@ember/runloop'; import { addObserver } from '@ember/-internals/metal'; @@ -879,7 +880,7 @@ moduleFor( } ['@test `activate` event fires on the route'](assert) { - assert.expect(4); + assert.expect(5); let eventFired = 0; @@ -893,10 +894,12 @@ 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'); + }); + }, /`on` is deprecated/); } activate(transition) { @@ -910,7 +913,7 @@ moduleFor( } ['@test `deactivate` event fires on the route'](assert) { - assert.expect(4); + assert.expect(5); let eventFired = 0; @@ -925,10 +928,12 @@ 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'); + }); + }, /`on` is deprecated/); } deactivate(transition) { From 3af7f9f7a13be772260c31ad045869803dc5cdae Mon Sep 17 00:00:00 2001 From: Peter Wagenet Date: Thu, 4 Sep 2025 21:41:52 -0700 Subject: [PATCH 03/10] Deprecate Evented and related --- package.json | 2 + .../glimmer/lib/component-managers/curly.ts | 33 +++-- .../glimmer/lib/component-managers/root.ts | 5 +- .../@ember/-internals/glimmer/lib/renderer.ts | 4 +- .../lib/utils/curly-component-state-bucket.ts | 11 +- packages/@ember/-internals/meta/lib/meta.ts | 2 +- .../@ember/-internals/metal/lib/events.ts | 51 ++++++- packages/@ember/-internals/runtime/index.ts | 1 + .../-internals/runtime/lib/mixins/evented.ts | 127 ++++++++++++++++++ packages/@ember/-internals/views/index.ts | 1 + .../views/lib/views/core-view-utils.ts | 14 ++ .../-internals/views/lib/views/core_view.ts | 5 +- .../-internals/views/lib/views/states.ts | 7 +- packages/@ember/object/evented.ts | 69 ++++++++++ packages/@ember/object/events.ts | 40 +++++- packages/@ember/routing/route.ts | 11 +- packages/@ember/routing/router-service.ts | 108 ++++++++++++++- packages/@ember/routing/router.ts | 11 +- 18 files changed, 456 insertions(+), 46 deletions(-) create mode 100644 packages/@ember/-internals/runtime/lib/mixins/evented.ts create mode 100644 packages/@ember/-internals/views/lib/views/core-view-utils.ts diff --git a/package.json b/package.json index b01ee55a34f..af739b62692 100644 --- a/package.json +++ b/package.json @@ -199,6 +199,7 @@ "@ember/-internals/runtime/lib/mixins/action_handler.js": "ember-source/@ember/-internals/runtime/lib/mixins/action_handler.js", "@ember/-internals/runtime/lib/mixins/comparable.js": "ember-source/@ember/-internals/runtime/lib/mixins/comparable.js", "@ember/-internals/runtime/lib/mixins/container_proxy.js": "ember-source/@ember/-internals/runtime/lib/mixins/container_proxy.js", + "@ember/-internals/runtime/lib/mixins/evented.js": "ember-source/@ember/-internals/runtime/lib/mixins/evented.js", "@ember/-internals/runtime/lib/mixins/registry_proxy.js": "ember-source/@ember/-internals/runtime/lib/mixins/registry_proxy.js", "@ember/-internals/runtime/lib/mixins/target_action_support.js": "ember-source/@ember/-internals/runtime/lib/mixins/target_action_support.js", "@ember/-internals/string/index.js": "ember-source/@ember/-internals/string/index.js", @@ -211,6 +212,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/glimmer/lib/component-managers/curly.ts b/packages/@ember/-internals/glimmer/lib/component-managers/curly.ts index 0b0da6ec97d..7021b4c8d3d 100644 --- a/packages/@ember/-internals/glimmer/lib/component-managers/curly.ts +++ b/packages/@ember/-internals/glimmer/lib/component-managers/curly.ts @@ -5,7 +5,12 @@ import { setOwner, } from '@ember/-internals/owner'; import { enumerableSymbol, guidFor } from '@ember/-internals/utils'; -import { addChildView, setElementView, setViewElement } from '@ember/-internals/views'; +import { + addChildView, + sendCoreViewEvent, + setElementView, + setViewElement, +} from '@ember/-internals/views'; import type { Nullable } from '@ember/-internals/utility-types'; import { assert, debugFreeze } from '@ember/debug'; import { _instrumentStart } from '@ember/instrumentation'; @@ -305,20 +310,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'); } } @@ -342,7 +347,7 @@ export default class CurlyComponentManager } if (isInteractive && hasWrappedElement) { - component.trigger('willRender'); + sendCoreViewEvent(component, 'willRender'); } endUntrackFrame(); @@ -407,7 +412,7 @@ export default class CurlyComponentManager if (isInteractive) { beginUntrackFrame(); - component.trigger('willInsertElement'); + sendCoreViewEvent(component, 'willInsertElement'); endUntrackFrame(); } } @@ -420,8 +425,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'); } } @@ -443,13 +448,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(); @@ -464,8 +469,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 00aca8f633c..2edd819fde1 100644 --- a/packages/@ember/-internals/glimmer/lib/component-managers/root.ts +++ b/packages/@ember/-internals/glimmer/lib/component-managers/root.ts @@ -20,6 +20,7 @@ import CurlyComponentManager, { initialRenderInstrumentDetails, processComponentInitializationAssertions, } from './curly'; +import { sendCoreViewEvent } from '@ember/-internals/views'; class RootComponentManager extends CurlyComponentManager { component: Component; @@ -47,13 +48,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 2a0847d4818..6cb7976a4b0 100644 --- a/packages/@ember/-internals/glimmer/lib/renderer.ts +++ b/packages/@ember/-internals/glimmer/lib/renderer.ts @@ -3,7 +3,7 @@ import { ENV } from '@ember/-internals/environment'; import type { InternalOwner } from '@ember/-internals/owner'; import { getOwner } from '@ember/-internals/owner'; import { guidFor } from '@ember/-internals/utils'; -import { getViewElement, getViewId } from '@ember/-internals/views'; +import { getViewElement, getViewId, sendCoreViewEvent } from '@ember/-internals/views'; import { assert } from '@ember/debug'; import { _backburner, _getCurrentRunLoop } from '@ember/runloop'; import { @@ -917,7 +917,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 5a4fb529a6f..e07d740200f 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 @@ -1,4 +1,9 @@ -import { clearElementView, clearViewElement, getViewElement } from '@ember/-internals/views'; +import { + clearElementView, + clearViewElement, + getViewElement, + sendCoreViewEvent, +} from '@ember/-internals/views'; import { registerDestructor } from '@glimmer/destroyable'; import type { CapturedNamedArguments } from '@glimmer/interfaces'; import type { Reference } from '@glimmer/reference'; @@ -46,8 +51,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/meta/lib/meta.ts b/packages/@ember/-internals/meta/lib/meta.ts index 7d429b5f13e..c366af2c88a 100644 --- a/packages/@ember/-internals/meta/lib/meta.ts +++ b/packages/@ember/-internals/meta/lib/meta.ts @@ -355,7 +355,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 ab77fb52c86..cec8469f0f0 100644 --- a/packages/@ember/-internals/metal/lib/events.ts +++ b/packages/@ember/-internals/metal/lib/events.ts @@ -5,7 +5,7 @@ import type { Meta } from '@ember/-internals/meta'; import { meta as metaFor, peekMeta } from '@ember/-internals/meta'; import { setListeners } from '@ember/-internals/utils'; import type { AnyFn } from '@ember/-internals/utility-types'; -import { assert } from '@ember/debug'; +import { assert, deprecate } from '@ember/debug'; /* The event system uses a series of nested hashes to store listeners on an @@ -38,11 +38,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 +65,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 +92,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', @@ -199,6 +226,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 +234,17 @@ export function hasListeners(obj: object, eventName: string): boolean { @public */ export function on(...args: [...eventNames: string[], func: T]): T { + deprecate( + '`on` is deprecated. Use native JavaScript events or a dedicated event library instead.', + false, + { + for: 'ember-source', + id: 'ember-evented', + since: { available: '6.8.0' }, + until: '7.0.0', + } + ); + let func = args.pop(); let events = args as string[]; diff --git a/packages/@ember/-internals/runtime/index.ts b/packages/@ember/-internals/runtime/index.ts index ec028a7a2da..947400434bb 100644 --- a/packages/@ember/-internals/runtime/index.ts +++ b/packages/@ember/-internals/runtime/index.ts @@ -5,5 +5,6 @@ export { default as ActionHandler } from './lib/mixins/action_handler'; export { default as _ProxyMixin, contentFor as _contentFor } from './lib/mixins/-proxy'; export { default as MutableEnumerable } from '@ember/enumerable/mutable'; export { default as TargetActionSupport } from './lib/mixins/target_action_support'; +export { default as Evented, on } from './lib/mixins/evented'; export { default as RSVP, onerrorDefault } from './lib/ext/rsvp'; // just for side effect of extending Ember.RSVP diff --git a/packages/@ember/-internals/runtime/lib/mixins/evented.ts b/packages/@ember/-internals/runtime/lib/mixins/evented.ts new file mode 100644 index 00000000000..701364837e1 --- /dev/null +++ b/packages/@ember/-internals/runtime/lib/mixins/evented.ts @@ -0,0 +1,127 @@ +import { addListener, removeListener, hasListeners, sendEvent } from '@ember/-internals/metal'; +import { deprecate } from '@ember/debug'; +import Mixin from '@ember/object/mixin'; + +export { on } from '@ember/-internals/metal'; + +/** + @module @ember/-internals/runtime +*/ + +/** + Internal implementation of the Evented mixin. + This mixin allows for Ember objects to subscribe to and emit events. + + @private + @deprecated + */ +interface Evented { + /** @deprecated */ + on( + name: string, + target: Target, + method: string | ((this: Target, ...args: any[]) => void) + ): this; + on(name: string, method: ((...args: any[]) => void) | string): this; + + /** @deprecated */ + one( + name: string, + target: Target, + method: string | ((this: Target, ...args: any[]) => void) + ): this; + one(name: string, method: string | ((...args: any[]) => void)): this; + + /** @deprecated */ + trigger(name: string, ...args: any[]): any; + + /** @deprecated */ + off( + name: string, + target: Target, + method: string | ((this: Target, ...args: any[]) => void) + ): this; + off(name: string, method: string | ((...args: any[]) => void)): this; + + /** @deprecated */ + has(name: string): boolean; +} + +const Evented = Mixin.create({ + on(name: string, target: object, method?: string | Function) { + deprecate( + '`on` is deprecated. Use native JavaScript events or a dedicated event library instead.', + false, + { + for: 'ember-source', + id: 'ember-evented', + since: { available: '6.8.0' }, + until: '7.0.0', + } + ); + // SAFETY: The types are not actaully 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; + }, + + one(name: string, target: object, method?: string | Function) { + deprecate( + '`one` is deprecated. Use native JavaScript events or a dedicated event library instead.', + false, + { + for: 'ember-source', + id: 'ember-evented', + since: { available: '6.8.0' }, + until: '7.0.0', + } + ); + // SAFETY: The types are not actaully 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; + }, + + trigger(name: string, ...args: any[]) { + deprecate( + '`trigger` is deprecated. Use native JavaScript events or a dedicated event library instead.', + false, + { + for: 'ember-source', + id: 'ember-evented', + since: { available: '6.8.0' }, + until: '7.0.0', + } + ); + sendEvent(this, name, args); + }, + + off(name: string, target: object, method?: string | Function) { + deprecate( + '`off` is deprecated. Use native JavaScript events or a dedicated event library instead.', + false, + { + for: 'ember-source', + id: 'ember-evented', + since: { available: '6.8.0' }, + until: '7.0.0', + } + ); + removeListener(this, name, target, method); + return this; + }, + + has(name: string) { + deprecate( + '`has` is deprecated. Use native JavaScript events or a dedicated event library instead.', + false, + { + for: 'ember-source', + id: 'ember-evented', + since: { available: '6.8.0' }, + until: '7.0.0', + } + ); + return hasListeners(this, name); + }, +}); + +export default Evented; diff --git a/packages/@ember/-internals/views/index.ts b/packages/@ember/-internals/views/index.ts index a84d01a080b..43c1afd840c 100644 --- a/packages/@ember/-internals/views/index.ts +++ b/packages/@ember/-internals/views/index.ts @@ -18,6 +18,7 @@ export { export { default as EventDispatcher } from './lib/system/event_dispatcher'; export { default as ComponentLookup } from './lib/component_lookup'; 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 241b233da65..7c371b741ed 100644 --- a/packages/@ember/-internals/views/lib/views/core_view.ts +++ b/packages/@ember/-internals/views/lib/views/core_view.ts @@ -1,10 +1,11 @@ import type { Renderer, View } from '@ember/-internals/glimmer'; import { inject } from '@ember/-internals/metal'; import { ActionHandler } from '@ember/-internals/runtime'; -import Evented from '@ember/object/evented'; import { FrameworkObject } from '@ember/object/-internals'; import type { ViewState } from './states'; import states from './states'; +import Evented from '@ember/object/evented'; +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 c9480ffbdf4..1bdf1a1df98 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 2134d344206..adf94249edc 100644 --- a/packages/@ember/object/evented.ts +++ b/packages/@ember/object/evented.ts @@ -1,4 +1,6 @@ import { addListener, removeListener, hasListeners, sendEvent } from '@ember/-internals/metal'; +import { setDeprecation } from '@ember/-internals/utils/lib/mixin-deprecation'; +import { deprecate } from '@ember/debug'; import Mixin from '@ember/object/mixin'; export { on } from '@ember/-internals/metal'; @@ -46,6 +48,7 @@ export { on } from '@ember/-internals/metal'; @class Evented @public + @deprecated Use native JavaScript events or a dedicated event library instead. */ interface Evented { /** @@ -63,6 +66,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` @@ -85,6 +89,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` @@ -113,6 +118,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 @@ -122,6 +128,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 @@ -138,6 +145,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 @@ -146,27 +154,88 @@ interface Evented { } const Evented = Mixin.create({ on(name: string, target: object, method?: string | Function) { + deprecate( + '`on` is deprecated. Use native JavaScript events or a dedicated event library instead.', + false, + { + for: 'ember-source', + id: 'ember-evented', + since: { available: '6.8.0' }, + until: '7.0.0', + } + ); addListener(this, name, target, method); return this; }, one(name: string, target: object, method?: string | Function) { + deprecate( + '`one` is deprecated. Use native JavaScript events or a dedicated event library instead.', + false, + { + for: 'ember-source', + id: 'ember-evented', + since: { available: '6.8.0' }, + until: '7.0.0', + } + ); addListener(this, name, target, method, true); return this; }, trigger(name: string, ...args: any[]) { + deprecate( + '`trigger` is deprecated. Use native JavaScript events or a dedicated event library instead.', + false, + { + for: 'ember-source', + id: 'ember-evented', + since: { available: '6.8.0' }, + until: '7.0.0', + } + ); sendEvent(this, name, args); }, off(name: string, target: object, method?: string | Function) { + deprecate( + '`off` is deprecated. Use native JavaScript events or a dedicated event library instead.', + false, + { + for: 'ember-source', + id: 'ember-evented', + since: { available: '6.8.0' }, + until: '7.0.0', + } + ); removeListener(this, name, target, method); return this; }, has(name: string) { + deprecate( + '`has` is deprecated. Use native JavaScript events or a dedicated event library instead.', + false, + { + for: 'ember-source', + id: 'ember-evented', + since: { available: '6.8.0' }, + until: '7.0.0', + } + ); return hasListeners(this, name); }, }); +setDeprecation(Evented, { + message: + 'Evented mixin is deprecated. Use native JavaScript events or a dedicated event library instead.', + options: { + for: 'ember-source', + id: 'ember-evented', + since: { available: '6.8.0' }, + until: '7.0.0', + }, +}); + export default Evented; diff --git a/packages/@ember/object/events.ts b/packages/@ember/object/events.ts index 3f4559266fc..28c8429d419 100644 --- a/packages/@ember/object/events.ts +++ b/packages/@ember/object/events.ts @@ -1 +1,39 @@ -export { addListener, removeListener, sendEvent } from '@ember/-internals/metal'; +import { + addListener as originalAddListener, + removeListener as originalRemoveListener, + sendEvent as originalSendEvent, +} from '@ember/-internals/metal'; +import { deprecateFunc } from '@ember/debug'; + +export const addListener = deprecateFunc( + '`addListener is deprecated', + { + for: 'ember-source', + id: 'ember-object-add-listener', + since: { available: '6.8.0' }, + until: '7.0.0', + }, + originalAddListener +); + +export const removeListener = deprecateFunc( + '`removeListener is deprecated', + { + for: 'ember-source', + id: 'ember-object-remove-listener', + since: { available: '6.8.0' }, + until: '7.0.0', + }, + originalRemoveListener +); + +export const sendEvent = deprecateFunc( + '`sendEvent is deprecated', + { + for: 'ember-source', + id: 'ember-object-send-event', + since: { available: '6.8.0' }, + until: '7.0.0', + }, + originalSendEvent +); diff --git a/packages/@ember/routing/route.ts b/packages/@ember/routing/route.ts index 7b4a8f24eac..df6dcf8e7a9 100644 --- a/packages/@ember/routing/route.ts +++ b/packages/@ember/routing/route.ts @@ -4,6 +4,7 @@ import { defineProperty, descriptorForProperty, flushAsyncObservers, + sendEvent, } from '@ember/-internals/metal'; import type Owner from '@ember/owner'; import { getOwner } from '@ember/-internals/owner'; @@ -36,6 +37,7 @@ import { prefixRouteNameArg, stashParamNames, } from './lib/utils'; +import { disableDeprecations } from '@ember/-internals/utils/lib/mixin-deprecation'; export interface ExtendedInternalRouteInfo extends InternalRouteInfo { _names?: unknown[]; @@ -252,7 +254,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 @@ -769,7 +774,7 @@ class Route extends EmberObject.extend(ActionHandler, Evented) */ exit(transition?: Transition) { this.deactivate(transition); - this.trigger('deactivate', transition); + sendEvent(this, 'deactivate', [transition]); this.teardownViews(); } @@ -795,7 +800,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 936e7539d1c..20457727e76 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'; @@ -13,6 +12,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'); @@ -24,6 +24,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. @@ -55,14 +57,106 @@ 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: 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 actaully 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 actaully 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 + */ + trigger(name: string, ...args: any[]) { + 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 actaully 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 a52b28f0a32..fb8a0302a2f 100644 --- a/packages/@ember/routing/router.ts +++ b/packages/@ember/routing/router.ts @@ -50,6 +50,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/lib/mixin-deprecation'; /** @module @ember/routing/router @@ -135,7 +137,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. @@ -415,7 +420,7 @@ class EmberRouter extends EmberObject.extend(Evented) implements Evented { } routeWillChange(transition: Transition) { - router.trigger('routeWillChange', transition); + sendEvent(router, 'routeWillChange', [transition]); if (DEBUG) { freezeRouteInfo(transition); @@ -433,7 +438,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); From b9876d5088e67b573f978901728fd661533c0d2f Mon Sep 17 00:00:00 2001 From: Peter Wagenet Date: Mon, 16 Feb 2026 22:21:04 -0800 Subject: [PATCH 04/10] Use centralized DEPRECATIONS pattern for Evented deprecation - Add DEPRECATE_EVENTED to centralized deprecations registry - Consolidate all deprecation IDs to single 'ember-evented' ID - Switch all deprecate() calls to deprecateUntil() pattern - Update mixin deprecation infrastructure for DeprecationObject - Fix deprecation messages for consistency - Remove internal Evented copy from runtime package - Update all test files to use DEPRECATIONS.DEPRECATE_EVENTED.isEnabled - Revert unrelated barrel.ts onerror doc change Co-Authored-By: Claude Sonnet 4.5 --- .../@ember/-internals/deprecations/index.ts | 9 +- .../integration/components/append-test.js | 5 +- .../components/curly-components-test.js | 5 +- .../integration/event-dispatcher-test.js | 10 +- .../@ember/-internals/metal/lib/events.ts | 17 +-- .../-internals/metal/tests/events_test.js | 30 +++-- packages/@ember/-internals/runtime/index.ts | 1 - .../-internals/runtime/lib/mixins/evented.ts | 127 ------------------ .../-internals/utils/lib/mixin-deprecation.ts | 14 +- packages/@ember/object/evented.ts | 80 ++++------- packages/@ember/object/events.ts | 53 +++----- packages/@ember/object/mixin.ts | 11 +- .../object/tests/es-compatibility-test.js | 3 +- packages/@ember/object/tests/evented_test.js | 18 ++- packages/@ember/object/tests/events_test.js | 92 ++++++++----- packages/ember/barrel.ts | 31 +++-- .../tests/routing/decoupled_basic_test.js | 5 +- 17 files changed, 205 insertions(+), 306 deletions(-) delete mode 100644 packages/@ember/-internals/runtime/lib/mixins/evented.ts diff --git a/packages/@ember/-internals/deprecations/index.ts b/packages/@ember/-internals/deprecations/index.ts index 89ad88ed625..ac5395192c5 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; @@ -122,6 +122,13 @@ export const DEPRECATIONS = { until: '7.0.0', url: 'https://deprecations.emberjs.com/id/using-amd-bundles', }), + 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/tests/integration/components/append-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/append-test.js index cea41705a8a..dac9ebca72e 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/append-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/append-test.js @@ -5,6 +5,7 @@ import { runTask, expectDeprecation, } from 'internal-test-helpers'; +import { DEPRECATIONS } from '../../../../deprecations'; import { set } from '@ember/object'; @@ -64,7 +65,7 @@ class AbstractAppendTest extends RenderingTestCase { pushHook('init'); expectDeprecation(() => { this.on('init', () => pushHook('on(init)')); - }, /`on` is deprecated/); + }, /Evented#on` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); } didReceiveAttrs() { @@ -309,7 +310,7 @@ class AbstractAppendTest extends RenderingTestCase { pushHook('init'); expectDeprecation(() => { this.on('init', () => pushHook('on(init)')); - }, /`on` is deprecated/); + }, /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 8ba4f8bb396..53a485f2d83 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 @@ -9,6 +9,7 @@ import { runLoopSettled, expectDeprecation, } from 'internal-test-helpers'; +import { DEPRECATIONS } from '../../../../deprecations'; import { action } from '@ember/object'; import { run } from '@ember/runloop'; @@ -3177,7 +3178,7 @@ moduleFor( expectDeprecation(() => { this.trigger('someMethod', ...payload); this.trigger('someTruthyProperty', ...payload); - }, /`trigger` is deprecated/); + }, /Evented#trigger` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); }, someMethod(...data) { @@ -3205,7 +3206,7 @@ moduleFor( }), }), }); - }, /`on` is deprecated/); + }, /`on\(\)` event decorator is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); this.render(`{{evented-component}}`); } 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 43bbcb27b41..19afd7b20ff 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,10 @@ -import { expectDeprecation, moduleFor, RenderingTestCase, runTask } from 'internal-test-helpers'; +import { + expectDeprecation, + moduleFor, + RenderingTestCase, + runTask, +} from 'internal-test-helpers'; +import { DEPRECATIONS } from '../../../deprecations'; import { Component } from '../utils/helpers'; import { _getCurrentRunLoop } from '@ember/runloop'; @@ -161,7 +167,7 @@ moduleFor( Object.keys(SUPPORTED_EMBER_EVENTS).forEach((browserEvent) => { expectDeprecation(() => { this.on(SUPPORTED_EMBER_EVENTS[browserEvent], (event) => (receivedEvent = event)); - }, /`on` is deprecated/); + }, /Evented#on` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); }); } }, diff --git a/packages/@ember/-internals/metal/lib/events.ts b/packages/@ember/-internals/metal/lib/events.ts index cec8469f0f0..eb6a645a7bd 100644 --- a/packages/@ember/-internals/metal/lib/events.ts +++ b/packages/@ember/-internals/metal/lib/events.ts @@ -5,7 +5,8 @@ import type { Meta } from '@ember/-internals/meta'; import { meta as metaFor, peekMeta } from '@ember/-internals/meta'; import { setListeners } from '@ember/-internals/utils'; import type { AnyFn } from '@ember/-internals/utility-types'; -import { assert, deprecate } from '@ember/debug'; +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 @@ -170,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; @@ -234,15 +235,9 @@ export function hasListeners(obj: object, eventName: string): boolean { @public */ export function on(...args: [...eventNames: string[], func: T]): T { - deprecate( - '`on` is deprecated. Use native JavaScript events or a dedicated event library instead.', - false, - { - for: 'ember-source', - id: 'ember-evented', - since: { available: '6.8.0' }, - until: '7.0.0', - } + deprecateUntil( + 'The `on()` event decorator is deprecated. Use native JavaScript events or a dedicated event library instead.', + DEPRECATIONS.DEPRECATE_EVENTED ); let func = args.pop(); diff --git a/packages/@ember/-internals/metal/tests/events_test.js b/packages/@ember/-internals/metal/tests/events_test.js index 257e1b66a50..965f7e0ca4f 100644 --- a/packages/@ember/-internals/metal/tests/events_test.js +++ b/packages/@ember/-internals/metal/tests/events_test.js @@ -1,6 +1,12 @@ import { on, addListener, removeListener, sendEvent, hasListeners } from '..'; import Mixin from '@ember/object/mixin'; -import { moduleFor, AbstractTestCase, expectDeprecation } from 'internal-test-helpers'; +import { + moduleFor, + AbstractTestCase, + expectDeprecation, + testUnless, +} from 'internal-test-helpers'; +import { DEPRECATIONS } from '../../deprecations'; moduleFor( 'system/props/events_test', @@ -139,7 +145,9 @@ 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; expectDeprecation(() => { @@ -152,7 +160,7 @@ moduleFor( triggered++; }), }); - }, /`on` is deprecated/); + }, /`on\(\)` event decorator is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); let obj = {}; MyMixin.apply(obj); @@ -161,13 +169,15 @@ 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(() => { expectDeprecation(() => { Mixin.create({ foo1: on('bar'), }); - }, /`on` is deprecated/); + }, /`on\(\)` event decorator is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); }, 'on expects function as last argument'); expectAssertion(() => { @@ -175,11 +185,13 @@ moduleFor( Mixin.create({ foo1: on(function () {}), }); - }, /`on` is deprecated/); + }, /`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; expectDeprecation(() => { @@ -188,7 +200,7 @@ moduleFor( triggered++; }), }); - }, /`on` is deprecated/); + }, /`on\(\)` event decorator is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); let SecondMixin; expectDeprecation(() => { @@ -197,7 +209,7 @@ moduleFor( triggered++; }), }); - }, /`on` is deprecated/); + }, /`on\(\)` event decorator is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); let obj = {}; FirstMixin.apply(obj); diff --git a/packages/@ember/-internals/runtime/index.ts b/packages/@ember/-internals/runtime/index.ts index 947400434bb..ec028a7a2da 100644 --- a/packages/@ember/-internals/runtime/index.ts +++ b/packages/@ember/-internals/runtime/index.ts @@ -5,6 +5,5 @@ export { default as ActionHandler } from './lib/mixins/action_handler'; export { default as _ProxyMixin, contentFor as _contentFor } from './lib/mixins/-proxy'; export { default as MutableEnumerable } from '@ember/enumerable/mutable'; export { default as TargetActionSupport } from './lib/mixins/target_action_support'; -export { default as Evented, on } from './lib/mixins/evented'; export { default as RSVP, onerrorDefault } from './lib/ext/rsvp'; // just for side effect of extending Ember.RSVP diff --git a/packages/@ember/-internals/runtime/lib/mixins/evented.ts b/packages/@ember/-internals/runtime/lib/mixins/evented.ts deleted file mode 100644 index 701364837e1..00000000000 --- a/packages/@ember/-internals/runtime/lib/mixins/evented.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { addListener, removeListener, hasListeners, sendEvent } from '@ember/-internals/metal'; -import { deprecate } from '@ember/debug'; -import Mixin from '@ember/object/mixin'; - -export { on } from '@ember/-internals/metal'; - -/** - @module @ember/-internals/runtime -*/ - -/** - Internal implementation of the Evented mixin. - This mixin allows for Ember objects to subscribe to and emit events. - - @private - @deprecated - */ -interface Evented { - /** @deprecated */ - on( - name: string, - target: Target, - method: string | ((this: Target, ...args: any[]) => void) - ): this; - on(name: string, method: ((...args: any[]) => void) | string): this; - - /** @deprecated */ - one( - name: string, - target: Target, - method: string | ((this: Target, ...args: any[]) => void) - ): this; - one(name: string, method: string | ((...args: any[]) => void)): this; - - /** @deprecated */ - trigger(name: string, ...args: any[]): any; - - /** @deprecated */ - off( - name: string, - target: Target, - method: string | ((this: Target, ...args: any[]) => void) - ): this; - off(name: string, method: string | ((...args: any[]) => void)): this; - - /** @deprecated */ - has(name: string): boolean; -} - -const Evented = Mixin.create({ - on(name: string, target: object, method?: string | Function) { - deprecate( - '`on` is deprecated. Use native JavaScript events or a dedicated event library instead.', - false, - { - for: 'ember-source', - id: 'ember-evented', - since: { available: '6.8.0' }, - until: '7.0.0', - } - ); - // SAFETY: The types are not actaully 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; - }, - - one(name: string, target: object, method?: string | Function) { - deprecate( - '`one` is deprecated. Use native JavaScript events or a dedicated event library instead.', - false, - { - for: 'ember-source', - id: 'ember-evented', - since: { available: '6.8.0' }, - until: '7.0.0', - } - ); - // SAFETY: The types are not actaully 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; - }, - - trigger(name: string, ...args: any[]) { - deprecate( - '`trigger` is deprecated. Use native JavaScript events or a dedicated event library instead.', - false, - { - for: 'ember-source', - id: 'ember-evented', - since: { available: '6.8.0' }, - until: '7.0.0', - } - ); - sendEvent(this, name, args); - }, - - off(name: string, target: object, method?: string | Function) { - deprecate( - '`off` is deprecated. Use native JavaScript events or a dedicated event library instead.', - false, - { - for: 'ember-source', - id: 'ember-evented', - since: { available: '6.8.0' }, - until: '7.0.0', - } - ); - removeListener(this, name, target, method); - return this; - }, - - has(name: string) { - deprecate( - '`has` is deprecated. Use native JavaScript events or a dedicated event library instead.', - false, - { - for: 'ember-source', - id: 'ember-evented', - since: { available: '6.8.0' }, - until: '7.0.0', - } - ); - return hasListeners(this, name); - }, -}); - -export default Evented; diff --git a/packages/@ember/-internals/utils/lib/mixin-deprecation.ts b/packages/@ember/-internals/utils/lib/mixin-deprecation.ts index adf0f084c46..373e5181940 100644 --- a/packages/@ember/-internals/utils/lib/mixin-deprecation.ts +++ b/packages/@ember/-internals/utils/lib/mixin-deprecation.ts @@ -1,4 +1,4 @@ -import type { DeprecationOptions } from '@ember/debug'; +import type { DeprecationObject } from '@ember/-internals/deprecations'; import type Mixin from '@ember/object/mixin'; /** @internal */ @@ -7,21 +7,23 @@ export const DEPRECATION = Symbol('DEPRECATION'); /** @internal */ export function setDeprecation( mixin: Mixin, - value: { message: string; options: DeprecationOptions } | null + value: { message: string; deprecation: DeprecationObject } | null ) { - mixin[DEPRECATION] = value; + // 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; options: DeprecationOptions } | null { +): { message: string; deprecation: DeprecationObject } | null { if (!deprecationsEnabled) { return null; } - if (mixin[DEPRECATION]) { - return mixin[DEPRECATION]; + // 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); diff --git a/packages/@ember/object/evented.ts b/packages/@ember/object/evented.ts index adf94249edc..db52744d23d 100644 --- a/packages/@ember/object/evented.ts +++ b/packages/@ember/object/evented.ts @@ -1,6 +1,6 @@ import { addListener, removeListener, hasListeners, sendEvent } from '@ember/-internals/metal'; +import { DEPRECATIONS, deprecateUntil } from '@ember/-internals/deprecations'; import { setDeprecation } from '@ember/-internals/utils/lib/mixin-deprecation'; -import { deprecate } from '@ember/debug'; import Mixin from '@ember/object/mixin'; export { on } from '@ember/-internals/metal'; @@ -154,74 +154,47 @@ interface Evented { } const Evented = Mixin.create({ on(name: string, target: object, method?: string | Function) { - deprecate( - '`on` is deprecated. Use native JavaScript events or a dedicated event library instead.', - false, - { - for: 'ember-source', - id: 'ember-evented', - since: { available: '6.8.0' }, - until: '7.0.0', - } + deprecateUntil( + '`Evented#on` is deprecated. Use native JavaScript events or a dedicated event library instead.', + DEPRECATIONS.DEPRECATE_EVENTED ); - addListener(this, name, target, method); + // 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) { - deprecate( - '`one` is deprecated. Use native JavaScript events or a dedicated event library instead.', - false, - { - for: 'ember-source', - id: 'ember-evented', - since: { available: '6.8.0' }, - until: '7.0.0', - } + deprecateUntil( + '`Evented#one` is deprecated. Use native JavaScript events or a dedicated event library instead.', + DEPRECATIONS.DEPRECATE_EVENTED ); - addListener(this, name, target, method, true); + // 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[]) { - deprecate( - '`trigger` is deprecated. Use native JavaScript events or a dedicated event library instead.', - false, - { - for: 'ember-source', - id: 'ember-evented', - since: { available: '6.8.0' }, - until: '7.0.0', - } + 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) { - deprecate( - '`off` is deprecated. Use native JavaScript events or a dedicated event library instead.', - false, - { - for: 'ember-source', - id: 'ember-evented', - since: { available: '6.8.0' }, - until: '7.0.0', - } + deprecateUntil( + '`Evented#off` is deprecated. Use native JavaScript events or a dedicated event library instead.', + DEPRECATIONS.DEPRECATE_EVENTED ); - removeListener(this, name, target, method); + // 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) { - deprecate( - '`has` is deprecated. Use native JavaScript events or a dedicated event library instead.', - false, - { - for: 'ember-source', - id: 'ember-evented', - since: { available: '6.8.0' }, - until: '7.0.0', - } + deprecateUntil( + '`Evented#has` is deprecated. Use native JavaScript events or a dedicated event library instead.', + DEPRECATIONS.DEPRECATE_EVENTED ); return hasListeners(this, name); }, @@ -229,13 +202,8 @@ const Evented = Mixin.create({ setDeprecation(Evented, { message: - 'Evented mixin is deprecated. Use native JavaScript events or a dedicated event library instead.', - options: { - for: 'ember-source', - id: 'ember-evented', - since: { available: '6.8.0' }, - until: '7.0.0', - }, + '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 28c8429d419..a35e039e57f 100644 --- a/packages/@ember/object/events.ts +++ b/packages/@ember/object/events.ts @@ -3,37 +3,28 @@ import { removeListener as originalRemoveListener, sendEvent as originalSendEvent, } from '@ember/-internals/metal'; -import { deprecateFunc } from '@ember/debug'; +import { DEPRECATIONS, deprecateUntil } from '@ember/-internals/deprecations'; -export const addListener = deprecateFunc( - '`addListener is deprecated', - { - for: 'ember-source', - id: 'ember-object-add-listener', - since: { available: '6.8.0' }, - until: '7.0.0', - }, - originalAddListener -); +export const addListener = (...args: Parameters) => { + deprecateUntil( + 'Importing `addListener` from `@ember/object/events` is deprecated. Use native JavaScript events or a dedicated event library instead.', + DEPRECATIONS.DEPRECATE_EVENTED + ); + return originalAddListener(...args); +}; -export const removeListener = deprecateFunc( - '`removeListener is deprecated', - { - for: 'ember-source', - id: 'ember-object-remove-listener', - since: { available: '6.8.0' }, - until: '7.0.0', - }, - originalRemoveListener -); +export const removeListener = (...args: Parameters) => { + deprecateUntil( + 'Importing `removeListener` from `@ember/object/events` is deprecated. Use native JavaScript events or a dedicated event library instead.', + DEPRECATIONS.DEPRECATE_EVENTED + ); + return originalRemoveListener(...args); +}; -export const sendEvent = deprecateFunc( - '`sendEvent is deprecated', - { - for: 'ember-source', - id: 'ember-object-send-event', - since: { available: '6.8.0' }, - until: '7.0.0', - }, - originalSendEvent -); +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 f169beab847..5e1d130ca85 100644 --- a/packages/@ember/object/mixin.ts +++ b/packages/@ember/object/mixin.ts @@ -5,7 +5,8 @@ import { INIT_FACTORY } from '@ember/-internals/container'; import type { Meta } from '@ember/-internals/meta'; import { meta as metaFor, peekMeta } from '@ember/-internals/meta'; import { guidFor, observerListenerMetaFor, ROOT, wrap } from '@ember/-internals/utils'; -import { assert, deprecate, DeprecationOptions } from '@ember/debug'; +import { assert } from '@ember/debug'; +import { deprecateUntil, type DeprecationObject } from '@ember/-internals/deprecations'; import { DEBUG } from '@glimmer/env'; import { type ComputedDecorator, @@ -546,7 +547,7 @@ export default class Mixin { properties: { [key: string]: any } | undefined; /*** @internal */ - [DEPRECATION]: { message: string; options: DeprecationOptions } | null = null; + [DEPRECATION]: { message: string; deprecation: DeprecationObject } | null = null; /** @internal */ ownerConstructor: any; @@ -628,8 +629,10 @@ export default class Mixin { if (DEBUG) { for (let mixin of args) { - let deprecation = mixin instanceof Mixin ? findDeprecation(mixin) : null; - deprecate(deprecation?.message ?? 'Huh???', !deprecation, deprecation?.options); + let dep = mixin instanceof Mixin ? findDeprecation(mixin) : null; + if (dep) { + deprecateUntil(dep.message, dep.deprecation); + } } } diff --git a/packages/@ember/object/tests/es-compatibility-test.js b/packages/@ember/object/tests/es-compatibility-test.js index b4f9bb140d6..5950b149427 100644 --- a/packages/@ember/object/tests/es-compatibility-test.js +++ b/packages/@ember/object/tests/es-compatibility-test.js @@ -15,6 +15,7 @@ import { runLoopSettled, expectDeprecation, } from 'internal-test-helpers'; +import { DEPRECATIONS } from '../../-internals/deprecations'; moduleFor( 'EmberObject ES Compatibility', @@ -316,7 +317,7 @@ moduleFor( someEventA++; } }; - }, /`on` is deprecated/); + }, /`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 5da69ae1001..f5800508c1d 100644 --- a/packages/@ember/object/tests/evented_test.js +++ b/packages/@ember/object/tests/evented_test.js @@ -1,11 +1,19 @@ import CoreObject from '@ember/object/core'; import EventedMixin from '@ember/object/evented'; -import { moduleFor, AbstractTestCase, expectDeprecation } 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) { + [`${testUnless( + DEPRECATIONS.DEPRECATE_EVENTED.isRemoved + )} @test works properly on proxy-ish objects`](assert) { let eventedProxyObj; expectDeprecation(() => { eventedProxyObj = class extends CoreObject.extend(EventedMixin) { @@ -13,17 +21,17 @@ moduleFor( return true; } }.create(); - }, /Evented mixin is deprecated/); + }, /Evented is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); let noop = function () {}; expectDeprecation(() => { eventedProxyObj.on('foo', noop); - }, /`on` is deprecated/); + }, /Evented#on` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); expectDeprecation(() => { eventedProxyObj.off('foo', noop); - }, /`off` is deprecated/); + }, /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 5745c9a5f26..b16fdd860ce 100644 --- a/packages/@ember/object/tests/events_test.js +++ b/packages/@ember/object/tests/events_test.js @@ -1,11 +1,19 @@ import EmberObject from '@ember/object'; -import { Evented } from '@ember/-internals/runtime'; -import { moduleFor, AbstractTestCase, expectDeprecation } from 'internal-test-helpers'; +import Evented from '@ember/object/evented'; +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++; @@ -15,21 +23,23 @@ moduleFor( expectDeprecation(() => { obj.on('event!', F); - }, /`on` is deprecated/); + }, /Evented#on` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); expectDeprecation(() => { obj.trigger('event!'); - }, /`trigger` is deprecated/); + }, /Evented#trigger` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); assert.equal(count, 1, 'the event was triggered'); expectDeprecation(() => { obj.trigger('event!'); - }, /`trigger` is deprecated/); + }, /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; @@ -41,22 +51,24 @@ moduleFor( expectDeprecation(() => { obj.one('event!', F); - }, /`one` is deprecated/); + }, /Evented#one` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); expectDeprecation(() => { obj.trigger('event!'); - }, /`trigger` is deprecated/); + }, /Evented#trigger` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); assert.equal(count, 1, 'the event was triggered'); expectDeprecation(() => { obj.trigger('event!'); - }, /`trigger` is deprecated/); + }, /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(); @@ -66,17 +78,19 @@ moduleFor( args = [].slice.call(arguments); self = this; }); - }, /`on` is deprecated/); + }, /Evented#on` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); expectDeprecation(() => { obj.trigger('event!', 'foo', 'bar'); - }, /`trigger` is deprecated/); + }, /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; @@ -88,11 +102,11 @@ moduleFor( self = this; count++; }); - }, /`one` is deprecated/); + }, /Evented#one` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); expectDeprecation(() => { obj.trigger('event!', 'foo', 'bar'); - }, /`trigger` is deprecated/); + }, /Evented#trigger` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); assert.deepEqual(args, ['foo', 'bar']); assert.equal(self, obj); @@ -100,14 +114,16 @@ moduleFor( expectDeprecation(() => { obj.trigger('event!', 'baz', 'bat'); - }, /`trigger` is deprecated/); + }, /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(); @@ -118,17 +134,19 @@ moduleFor( args = [].slice.call(arguments); self = this; }); - }, /`on` is deprecated/); + }, /Evented#on` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); expectDeprecation(() => { obj.trigger('event!', 'foo', 'bar'); - }, /`trigger` is deprecated/); + }, /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; @@ -141,22 +159,24 @@ moduleFor( expectDeprecation(() => { obj.one('event!', target, 'fn'); - }, /`one` is deprecated/); + }, /Evented#one` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); expectDeprecation(() => { obj.trigger('event!'); - }, /`trigger` is deprecated/); + }, /Evented#trigger` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); assert.equal(count, 1, 'the event was triggered'); expectDeprecation(() => { obj.trigger('event!'); - }, /`trigger` is deprecated/); + }, /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) { + [`${testUnless( + DEPRECATIONS.DEPRECATE_EVENTED.isRemoved + )} @test a listener registered with one can be removed with off`](assert) { let obj = class extends EmberObject.extend(Evented) { F() {} }.create(); @@ -164,34 +184,36 @@ moduleFor( expectDeprecation(() => { obj.one('event!', F); - }, /`one` is deprecated/); + }, /Evented#one` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); expectDeprecation(() => { obj.one('event!', obj, 'F'); - }, /`one` is deprecated/); + }, /Evented#one` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); let objHas; expectDeprecation(() => { objHas = obj.has('event!'); - }, /`has` is deprecated/); + }, /Evented#has` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); assert.equal(objHas, true, 'has events'); expectDeprecation(() => { obj.off('event!', F); - }, /`off` is deprecated/); + }, /Evented#off` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); expectDeprecation(() => { obj.off('event!', obj, 'F'); - }, /`off` is deprecated/); + }, /Evented#off` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); expectDeprecation(() => { objHas = obj.has('event!'); - }, /`has` is deprecated/); + }, /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) { + [`${testUnless( + DEPRECATIONS.DEPRECATE_EVENTED.isRemoved + )} @test adding and removing listeners should be chainable`](assert) { let obj = EmberObject.extend(Evented).create(); let F = function () {}; @@ -199,17 +221,17 @@ moduleFor( expectDeprecation(() => { ret = obj.on('event!', F); - }, /`on` is deprecated/); + }, /Evented#on` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); assert.equal(ret, obj, '#on returns self'); expectDeprecation(() => { ret = obj.off('event!', F); - }, /`off` is deprecated/); + }, /Evented#off` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); assert.equal(ret, obj, '#off returns self'); expectDeprecation(() => { ret = obj.one('event!', F); - }, /`one` is deprecated/); + }, /Evented#one` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); assert.equal(ret, obj, '#one returns self'); } } diff --git a/packages/ember/barrel.ts b/packages/ember/barrel.ts index 55b88a16432..afa6b18c8cf 100644 --- a/packages/ember/barrel.ts +++ b/packages/ember/barrel.ts @@ -520,17 +520,26 @@ namespace Ember { export declare let lookup: Record; /** - This should not be needed. - You may be able to use an event listener for the error event on window. - - `window.addEventListener('error', eventHandler);` - - For promise rejections, you'll want to use the unhandledrejection event. - `window.addEventListener('unhandledrejection', eventHandler);` - - If you really need the original behavior: - - `import { getOnerror, setOnerror } from '@ember/-internals/error-handling';` + 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 diff --git a/packages/ember/tests/routing/decoupled_basic_test.js b/packages/ember/tests/routing/decoupled_basic_test.js index ae4ca4c4daf..d9ba72cb97c 100644 --- a/packages/ember/tests/routing/decoupled_basic_test.js +++ b/packages/ember/tests/routing/decoupled_basic_test.js @@ -15,6 +15,7 @@ import { runTask, expectDeprecation, } 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'; @@ -899,7 +900,7 @@ moduleFor( assert.equal(++eventFired, 1, 'activate event is fired once'); assert.ok(transition, 'transition is passed to activate event'); }); - }, /`on` is deprecated/); + }, /Evented#on` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); } activate(transition) { @@ -933,7 +934,7 @@ moduleFor( assert.equal(++eventFired, 1, 'deactivate event is fired once'); assert.ok(transition, 'transition is passed'); }); - }, /`on` is deprecated/); + }, /Evented#on` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); } deactivate(transition) { From 575d0b54d18d4ab19b9c08521578a548d960f9ea Mon Sep 17 00:00:00 2001 From: Peter Wagenet Date: Mon, 16 Feb 2026 22:31:19 -0800 Subject: [PATCH 05/10] Fix review issues from aggressive-reviewer - Fix regex mismatch in each-test.js (/`on` is/ -> /`on()` event decorator/) - Add missing DEPRECATIONS.DEPRECATE_EVENTED.isEnabled argument - Fix 'actaully' typo in router-service.ts (3 instances) - Fix triple-asterisk JSDoc comment in mixin.ts Co-Authored-By: Claude Sonnet 4.5 --- .../glimmer/tests/integration/syntax/each-test.js | 3 ++- packages/@ember/object/mixin.ts | 2 +- packages/@ember/routing/router-service.ts | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) 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 1cd3e989d3d..348428415d1 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/syntax/each-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/syntax/each-test.js @@ -6,6 +6,7 @@ import { runTask, expectDeprecation, } from 'internal-test-helpers'; +import { DEPRECATIONS } from '../../../../deprecations'; import { notifyPropertyChange, on } from '@ember/-internals/metal'; import { get, set, computed } from '@ember/object'; @@ -1135,7 +1136,7 @@ moduleFor( this.set('content', emberA(wrapped)); }), }).create(); - }, /`on` is deprecated/); + }, /`on\(\)` event decorator is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); return { list: proxy, delegate: wrapped }; } diff --git a/packages/@ember/object/mixin.ts b/packages/@ember/object/mixin.ts index 5e1d130ca85..07e5b9d964f 100644 --- a/packages/@ember/object/mixin.ts +++ b/packages/@ember/object/mixin.ts @@ -546,7 +546,7 @@ export default class Mixin { /** @internal */ properties: { [key: string]: any } | undefined; - /*** @internal */ + /** @internal */ [DEPRECATION]: { message: string; deprecation: DeprecationObject } | null = null; /** @internal */ diff --git a/packages/@ember/routing/router-service.ts b/packages/@ember/routing/router-service.ts index 20457727e76..51a7f42148f 100644 --- a/packages/@ember/routing/router-service.ts +++ b/packages/@ember/routing/router-service.ts @@ -80,7 +80,7 @@ class RouterService extends Service { target: object | ((...args: any[]) => void) | string, method?: string | ((...args: any[]) => void) ) { - // SAFETY: The types are not actaully correct, but it's not worth the effort to fix them, since we'll be deprecating this API soon. + // 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; } @@ -106,7 +106,7 @@ class RouterService extends Service { target: object | string | ((...args: any[]) => void), method?: string | Function ) { - // SAFETY: The types are not actaully correct, but it's not worth the effort to fix them, since we'll be deprecating this API soon. + // 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; } @@ -142,7 +142,7 @@ class RouterService extends Service { target: object | string | ((...args: any[]) => void), method?: string | Function ) { - // SAFETY: The types are not actaully correct, but it's not worth the effort to fix them, since we'll be deprecating this API soon. + // 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; } From a9fd9f44c3d0344a4d777c26d9e71a2509391b30 Mon Sep 17 00:00:00 2001 From: Peter Wagenet Date: Mon, 16 Feb 2026 22:43:24 -0800 Subject: [PATCH 06/10] Fix lint errors Remove unused expectDeprecation import and fix eslint-disable directives for duplicate class members. Apply prettier formatting to test files. Co-Authored-By: Claude Sonnet 4.5 --- .../integration/components/append-test.js | 20 +- .../components/curly-components-test.js | 84 ++--- .../integration/components/life-cycle-test.js | 9 +- .../integration/event-dispatcher-test.js | 17 +- .../tests/integration/syntax/each-test.js | 18 +- .../-internals/metal/tests/computed_test.js | 4 +- .../-internals/metal/tests/events_test.js | 97 +++--- .../object/tests/es-compatibility-test.js | 60 ++-- packages/@ember/object/tests/evented_test.js | 45 +-- packages/@ember/object/tests/events_test.js | 293 ++++++++++++------ .../tests/routing/decoupled_basic_test.js | 32 +- 11 files changed, 410 insertions(+), 269 deletions(-) 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 dac9ebca72e..989006fcb59 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/append-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/append-test.js @@ -63,9 +63,13 @@ class AbstractAppendTest extends RenderingTestCase { } componentsByName[name] = this; pushHook('init'); - expectDeprecation(() => { - this.on('init', () => pushHook('on(init)')); - }, /Evented#on` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); + expectDeprecation( + () => { + this.on('init', () => pushHook('on(init)')); + }, + /Evented#on` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); } didReceiveAttrs() { @@ -308,9 +312,13 @@ class AbstractAppendTest extends RenderingTestCase { } componentsByName[name] = this; pushHook('init'); - expectDeprecation(() => { - this.on('init', () => pushHook('on(init)')); - }, /Evented#on` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); + 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 53a485f2d83..d38c869feca 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 @@ -3168,45 +3168,53 @@ moduleFor( let payload = ['arbitrary', 'event', 'data']; - expectDeprecation(() => { - this.registerComponent('evented-component', { - ComponentClass: 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' - ); - }, - - 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' - ); + expectDeprecation( + () => { + this.registerComponent('evented-component', { + ComponentClass: 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' + ); + }, + + 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); + }); + }, + /`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 8bfeec4cab6..e953ffef663 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 @@ -1,11 +1,4 @@ -import { - classes, - expectDeprecation, - moduleFor, - RenderingTestCase, - runTask, - strip, -} from 'internal-test-helpers'; +import { classes, moduleFor, RenderingTestCase, runTask, strip } from 'internal-test-helpers'; import { schedule } from '@ember/runloop'; import { set, setProperties } from '@ember/object'; 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 19afd7b20ff..2e5f12c0c84 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/event-dispatcher-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/event-dispatcher-test.js @@ -1,9 +1,4 @@ -import { - expectDeprecation, - moduleFor, - RenderingTestCase, - runTask, -} from 'internal-test-helpers'; +import { expectDeprecation, moduleFor, RenderingTestCase, runTask } from 'internal-test-helpers'; import { DEPRECATIONS } from '../../../deprecations'; import { Component } from '../utils/helpers'; @@ -165,9 +160,13 @@ moduleFor( init() { super.init(); Object.keys(SUPPORTED_EMBER_EVENTS).forEach((browserEvent) => { - expectDeprecation(() => { - this.on(SUPPORTED_EMBER_EVENTS[browserEvent], (event) => (receivedEvent = event)); - }, /Evented#on` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); + 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 348428415d1..0fb0cf8e457 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/syntax/each-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/syntax/each-test.js @@ -1130,13 +1130,17 @@ moduleFor( 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); + 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 }; } 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 965f7e0ca4f..c50f3e83bd3 100644 --- a/packages/@ember/-internals/metal/tests/events_test.js +++ b/packages/@ember/-internals/metal/tests/events_test.js @@ -1,11 +1,6 @@ import { on, addListener, removeListener, sendEvent, hasListeners } from '..'; import Mixin from '@ember/object/mixin'; -import { - moduleFor, - AbstractTestCase, - expectDeprecation, - testUnless, -} from 'internal-test-helpers'; +import { moduleFor, AbstractTestCase, expectDeprecation, testUnless } from 'internal-test-helpers'; import { DEPRECATIONS } from '../../deprecations'; moduleFor( @@ -150,17 +145,21 @@ moduleFor( )} @test a listener can be added as part of a mixin`](assert) { let triggered = 0; 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); + 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); @@ -173,19 +172,27 @@ moduleFor( DEPRECATIONS.DEPRECATE_EVENTED.isRemoved )} @test 'on' asserts for invalid arguments`]() { expectAssertion(() => { - expectDeprecation(() => { - Mixin.create({ - foo1: on('bar'), - }); - }, /`on\(\)` event decorator is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); + expectDeprecation( + () => { + Mixin.create({ + foo1: on('bar'), + }); + }, + /`on\(\)` event decorator is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); }, 'on expects function as last argument'); expectAssertion(() => { - expectDeprecation(() => { - Mixin.create({ - foo1: on(function () {}), - }); - }, /`on\(\)` event decorator is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); + expectDeprecation( + () => { + Mixin.create({ + foo1: on(function () {}), + }); + }, + /`on\(\)` event decorator is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); }, 'on called without valid event names'); } @@ -194,22 +201,30 @@ moduleFor( )} @test a listener added as part of a mixin may be overridden`](assert) { let triggered = 0; let FirstMixin; - expectDeprecation(() => { - FirstMixin = Mixin.create({ - foo: on('bar', function () { - triggered++; - }), - }); - }, /`on\(\)` event decorator is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); + 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); + 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/object/tests/es-compatibility-test.js b/packages/@ember/object/tests/es-compatibility-test.js index 5950b149427..14d729e35ff 100644 --- a/packages/@ember/object/tests/es-compatibility-test.js +++ b/packages/@ember/object/tests/es-compatibility-test.js @@ -290,34 +290,38 @@ moduleFor( let someEventA = 0; let someEventB = 0; 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); + 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 f5800508c1d..2e7a920252d 100644 --- a/packages/@ember/object/tests/evented_test.js +++ b/packages/@ember/object/tests/evented_test.js @@ -1,11 +1,6 @@ import CoreObject from '@ember/object/core'; import EventedMixin from '@ember/object/evented'; -import { - moduleFor, - AbstractTestCase, - expectDeprecation, - testUnless, -} from 'internal-test-helpers'; +import { moduleFor, AbstractTestCase, expectDeprecation, testUnless } from 'internal-test-helpers'; import { DEPRECATIONS } from '../../-internals/deprecations'; moduleFor( @@ -15,23 +10,35 @@ moduleFor( 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); + expectDeprecation( + () => { + eventedProxyObj = class extends CoreObject.extend(EventedMixin) { + unknownProperty() { + return true; + } + }.create(); + }, + /Evented is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); let noop = function () {}; - expectDeprecation(() => { - eventedProxyObj.on('foo', noop); - }, /Evented#on` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); + 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); + 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 b16fdd860ce..3c6a5ce4803 100644 --- a/packages/@ember/object/tests/events_test.js +++ b/packages/@ember/object/tests/events_test.js @@ -1,11 +1,6 @@ import EmberObject from '@ember/object'; import Evented from '@ember/object/evented'; -import { - moduleFor, - AbstractTestCase, - expectDeprecation, - testUnless, -} from 'internal-test-helpers'; +import { moduleFor, AbstractTestCase, expectDeprecation, testUnless } from 'internal-test-helpers'; import { DEPRECATIONS } from '../../-internals/deprecations'; moduleFor( @@ -21,18 +16,30 @@ moduleFor( let obj = EmberObject.extend(Evented).create(); - 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); + 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'); - expectDeprecation(() => { - obj.trigger('event!'); - }, /Evented#trigger` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); + expectDeprecation( + () => { + obj.trigger('event!'); + }, + /Evented#trigger` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); assert.equal(count, 2, 'the event was triggered'); } @@ -49,19 +56,31 @@ moduleFor( let obj = EmberObject.extend(Evented).create(); - 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); + 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'); - expectDeprecation(() => { - obj.trigger('event!'); - }, /Evented#trigger` 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 not triggered again'); } @@ -73,16 +92,24 @@ moduleFor( let obj = EmberObject.extend(Evented).create(); - 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); + 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); @@ -96,25 +123,37 @@ moduleFor( let obj = EmberObject.extend(Evented).create(); - 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); + 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'); - expectDeprecation(() => { - obj.trigger('event!', 'baz', 'bat'); - }, /Evented#trigger` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); + 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'); @@ -129,16 +168,24 @@ moduleFor( let obj = EmberObject.extend(Evented).create(); let target = {}; - 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); + 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); @@ -157,19 +204,31 @@ moduleFor( let obj = EmberObject.extend(Evented).create(); - 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); + 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'); - expectDeprecation(() => { - obj.trigger('event!'); - }, /Evented#trigger` 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 not triggered again'); } @@ -182,31 +241,55 @@ moduleFor( }.create(); let F = function () {}; - 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); + 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); + 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); + 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'); } @@ -219,19 +302,31 @@ moduleFor( let ret; - expectDeprecation(() => { - ret = obj.on('event!', F); - }, /Evented#on` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); + expectDeprecation( + () => { + ret = obj.on('event!', F); + }, + /Evented#on` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); assert.equal(ret, obj, '#on returns self'); - expectDeprecation(() => { - ret = obj.off('event!', F); - }, /Evented#off` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); + expectDeprecation( + () => { + ret = obj.off('event!', F); + }, + /Evented#off` is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); assert.equal(ret, obj, '#off returns self'); - expectDeprecation(() => { - ret = obj.one('event!', F); - }, /Evented#one` is deprecated/, DEPRECATIONS.DEPRECATE_EVENTED.isEnabled); + 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/tests/routing/decoupled_basic_test.js b/packages/ember/tests/routing/decoupled_basic_test.js index d9ba72cb97c..991bf4bdd93 100644 --- a/packages/ember/tests/routing/decoupled_basic_test.js +++ b/packages/ember/tests/routing/decoupled_basic_test.js @@ -895,12 +895,16 @@ moduleFor( init() { super.init(...arguments); - 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); + 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) { @@ -929,12 +933,16 @@ moduleFor( init() { super.init(...arguments); - 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); + 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) { From 8c05689202ff3742e6e60f2748ec495d5e1b6a8a Mon Sep 17 00:00:00 2001 From: Peter Wagenet Date: Tue, 17 Feb 2026 12:38:38 -0800 Subject: [PATCH 07/10] Fix TypeScript compilation errors - Export mixin-deprecation utilities from @ember/-internals/utils - Update imports to use package exports instead of deep imports - Add type assertions for observer and listener methods - Restore function overloads for addListener/removeListener exports --- .gitignore | 1 + .../@ember/-internals/metal/lib/observer.ts | 16 ++++- packages/@ember/-internals/utils/index.ts | 6 ++ .../-internals/views/lib/views/core_view.ts | 2 +- packages/@ember/object/evented.ts | 2 +- packages/@ember/object/events.ts | 60 +++++++++++++++++-- packages/@ember/object/mixin.ts | 18 ++++-- packages/@ember/routing/route.ts | 2 +- packages/@ember/routing/router.ts | 2 +- 9 files changed, 92 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index 4a4c99b4f3e..06c00d62e84 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,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/packages/@ember/-internals/metal/lib/observer.ts b/packages/@ember/-internals/metal/lib/observer.ts index f1f8035fdbe..b1776587f6a 100644 --- a/packages/@ember/-internals/metal/lib/observer.ts +++ b/packages/@ember/-internals/metal/lib/observer.ts @@ -43,7 +43,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); @@ -77,7 +84,12 @@ 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/utils/index.ts b/packages/@ember/-internals/utils/index.ts index 329a6f7e793..2e310ef344d 100644 --- a/packages/@ember/-internals/utils/index.ts +++ b/packages/@ember/-internals/utils/index.ts @@ -33,3 +33,9 @@ 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.ts b/packages/@ember/-internals/views/lib/views/core_view.ts index 7c371b741ed..8b0fa45f8d5 100644 --- a/packages/@ember/-internals/views/lib/views/core_view.ts +++ b/packages/@ember/-internals/views/lib/views/core_view.ts @@ -5,7 +5,7 @@ import { FrameworkObject } from '@ember/object/-internals'; import type { ViewState } from './states'; import states from './states'; import Evented from '@ember/object/evented'; -import { disableDeprecations } from '@ember/-internals/utils/lib/mixin-deprecation'; +import { disableDeprecations } from '@ember/-internals/utils'; /** `CoreView` is an abstract class that exists to give view-like behavior diff --git a/packages/@ember/object/evented.ts b/packages/@ember/object/evented.ts index db52744d23d..83be072e82f 100644 --- a/packages/@ember/object/evented.ts +++ b/packages/@ember/object/evented.ts @@ -1,6 +1,6 @@ import { addListener, removeListener, hasListeners, sendEvent } from '@ember/-internals/metal'; import { DEPRECATIONS, deprecateUntil } from '@ember/-internals/deprecations'; -import { setDeprecation } from '@ember/-internals/utils/lib/mixin-deprecation'; +import { setDeprecation } from '@ember/-internals/utils'; import Mixin from '@ember/object/mixin'; export { on } from '@ember/-internals/metal'; diff --git a/packages/@ember/object/events.ts b/packages/@ember/object/events.ts index a35e039e57f..af1230fbc07 100644 --- a/packages/@ember/object/events.ts +++ b/packages/@ember/object/events.ts @@ -5,21 +5,69 @@ import { } from '@ember/-internals/metal'; import { DEPRECATIONS, deprecateUntil } from '@ember/-internals/deprecations'; -export const addListener = (...args: Parameters) => { +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(...args); -}; + return originalAddListener( + obj, + eventName, + target as any, + method as PropertyKey | ((...args: any[]) => void), + once, + sync + ); +} -export const removeListener = (...args: Parameters) => { +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(...args); -}; + return originalRemoveListener( + obj, + eventName, + target as any, + method as string | ((...args: any[]) => void) + ); +} export const sendEvent = (...args: Parameters) => { deprecateUntil( diff --git a/packages/@ember/object/mixin.ts b/packages/@ember/object/mixin.ts index 07e5b9d964f..77e88ce0137 100644 --- a/packages/@ember/object/mixin.ts +++ b/packages/@ember/object/mixin.ts @@ -4,7 +4,14 @@ import { INIT_FACTORY } from '@ember/-internals/container'; import type { Meta } from '@ember/-internals/meta'; import { meta as metaFor, peekMeta } from '@ember/-internals/meta'; -import { guidFor, observerListenerMetaFor, ROOT, wrap } from '@ember/-internals/utils'; +import { + guidFor, + observerListenerMetaFor, + ROOT, + wrap, + DEPRECATION, + findDeprecation, +} from '@ember/-internals/utils'; import { assert } from '@ember/debug'; import { deprecateUntil, type DeprecationObject } from '@ember/-internals/deprecations'; import { DEBUG } from '@glimmer/env'; @@ -30,7 +37,6 @@ import { defineDecorator, defineValue, } from '@ember/-internals/metal'; -import { DEPRECATION, findDeprecation } from '@ember/-internals/utils/lib/mixin-deprecation'; const a_concat = Array.prototype.concat; const { isArray } = Array; @@ -388,10 +394,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); + } } } } diff --git a/packages/@ember/routing/route.ts b/packages/@ember/routing/route.ts index df6dcf8e7a9..066eb847101 100644 --- a/packages/@ember/routing/route.ts +++ b/packages/@ember/routing/route.ts @@ -37,7 +37,7 @@ import { prefixRouteNameArg, stashParamNames, } from './lib/utils'; -import { disableDeprecations } from '@ember/-internals/utils/lib/mixin-deprecation'; +import { disableDeprecations } from '@ember/-internals/utils'; export interface ExtendedInternalRouteInfo extends InternalRouteInfo { _names?: unknown[]; diff --git a/packages/@ember/routing/router.ts b/packages/@ember/routing/router.ts index fb8a0302a2f..735fe11187d 100644 --- a/packages/@ember/routing/router.ts +++ b/packages/@ember/routing/router.ts @@ -51,7 +51,7 @@ import type { AnyFn, MethodNamesOf, OmitFirst } from '@ember/-internals/utility- 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/lib/mixin-deprecation'; +import { disableDeprecations } from '@ember/-internals/utils'; /** @module @ember/routing/router From bde904e5365ccd3172802882c1f3e0f2a2a5776c Mon Sep 17 00:00:00 2001 From: Peter Wagenet Date: Tue, 17 Feb 2026 13:42:49 -0800 Subject: [PATCH 08/10] Fix RouterService types to match actual behavior - Add specific on() overload for route events to properly type transition callbacks - Change trigger() return type from any to boolean (matches sendEvent behavior) - Format observer.ts with prettier Co-Authored-By: Claude Sonnet 4.5 --- packages/@ember/-internals/metal/lib/observer.ts | 7 +------ packages/@ember/routing/router-service.ts | 6 ++++-- type-tests/@ember/routing-test/router-service.ts | 2 +- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/@ember/-internals/metal/lib/observer.ts b/packages/@ember/-internals/metal/lib/observer.ts index b1776587f6a..0cabeb7532b 100644 --- a/packages/@ember/-internals/metal/lib/observer.ts +++ b/packages/@ember/-internals/metal/lib/observer.ts @@ -84,12 +84,7 @@ export function removeObserver( deactivateObserver(obj, eventName, sync); } - removeListener( - obj, - eventName, - target as any, - method as string | ((...args: any[]) => void) - ); + removeListener(obj, eventName, target as any, method as string | ((...args: any[]) => void)); } function getOrCreateActiveObserversFor(target: object, sync: boolean) { diff --git a/packages/@ember/routing/router-service.ts b/packages/@ember/routing/router-service.ts index 51a7f42148f..053c77af4bc 100644 --- a/packages/@ember/routing/router-service.ts +++ b/packages/@ember/routing/router-service.ts @@ -69,6 +69,7 @@ class RouterService extends Service { @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, @@ -117,9 +118,10 @@ class RouterService extends Service { @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[]) { - sendEvent(this, name, args); + trigger(name: string, ...args: any[]): boolean { + return sendEvent(this, name, args); } /** 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 57b589aee128a7c07b362a7fda54154d48889517 Mon Sep 17 00:00:00 2001 From: Peter Wagenet Date: Wed, 18 Feb 2026 07:59:16 -0800 Subject: [PATCH 09/10] Fix Evented deprecation tests for ALL_DEPRECATIONS_ENABLED and OVERRIDE_DEPRECATION_VERSION variants - Wrap EmberObject.extend(Evented).create() calls in expectDeprecation in events_test.js, since the mixin-level deprecation fires at class creation time - Add testUnless(DEPRECATIONS.DEPRECATE_EVENTED.isRemoved) guards to tests that use on(), Evented methods, or the on() event decorator, so they skip when the API is removed - Guard the 'each with array proxies' moduleFor with if (!isRemoved) since createList() uses on() and can't run at all when the API is removed at OVERRIDE_DEPRECATION_VERSION=15.0.0 Co-Authored-By: Claude Opus 4.6 --- .../integration/components/append-test.js | 9 ++- .../components/curly-components-test.js | 3 +- .../integration/event-dispatcher-test.js | 12 ++- .../tests/integration/syntax/each-test.js | 42 +++++----- .../object/tests/es-compatibility-test.js | 5 +- packages/@ember/object/tests/events_test.js | 76 ++++++++++++++++--- .../tests/routing/decoupled_basic_test.js | 9 ++- 7 files changed, 118 insertions(+), 38 deletions(-) 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 989006fcb59..17df9c34069 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/append-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/append-test.js @@ -4,6 +4,7 @@ import { strip, runTask, expectDeprecation, + testUnless, } from 'internal-test-helpers'; import { DEPRECATIONS } from '../../../../deprecations'; @@ -45,7 +46,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 = {}; @@ -291,7 +294,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 oldRegisterComponent = this.registerComponent; 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 d38c869feca..45e8a361702 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 @@ -8,6 +8,7 @@ import { runTask, runLoopSettled, expectDeprecation, + testUnless, } from 'internal-test-helpers'; import { DEPRECATIONS } from '../../../../deprecations'; @@ -3161,7 +3162,7 @@ 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(5); 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 2e5f12c0c84..7a40f1f169d 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,10 @@ -import { expectDeprecation, 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'; @@ -150,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; 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 0fb0cf8e457..3c704fffbda 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/syntax/each-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/syntax/each-test.js @@ -1124,28 +1124,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; - expectDeprecation( - () => { - proxy = ArrayProxy.extend({ - setup: on('init', function () { - this.set('content', emberA(wrapped)); - }), - }).create(); - }, - /`on\(\)` event decorator is deprecated/, - DEPRECATIONS.DEPRECATE_EVENTED.isEnabled - ); +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/object/tests/es-compatibility-test.js b/packages/@ember/object/tests/es-compatibility-test.js index 14d729e35ff..35372ca5f70 100644 --- a/packages/@ember/object/tests/es-compatibility-test.js +++ b/packages/@ember/object/tests/es-compatibility-test.js @@ -14,6 +14,7 @@ import { AbstractTestCase, runLoopSettled, expectDeprecation, + testUnless, } from 'internal-test-helpers'; import { DEPRECATIONS } from '../../-internals/deprecations'; @@ -282,7 +283,9 @@ 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; diff --git a/packages/@ember/object/tests/events_test.js b/packages/@ember/object/tests/events_test.js index 3c6a5ce4803..c3daddf6c3d 100644 --- a/packages/@ember/object/tests/events_test.js +++ b/packages/@ember/object/tests/events_test.js @@ -14,7 +14,14 @@ moduleFor( count++; }; - let obj = EmberObject.extend(Evented).create(); + let obj; + expectDeprecation( + () => { + obj = EmberObject.extend(Evented).create(); + }, + /Evented is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); expectDeprecation( () => { @@ -54,7 +61,14 @@ moduleFor( count++; }; - let obj = EmberObject.extend(Evented).create(); + let obj; + expectDeprecation( + () => { + obj = EmberObject.extend(Evented).create(); + }, + /Evented is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); expectDeprecation( () => { @@ -90,7 +104,14 @@ moduleFor( )} @test triggering an event can have arguments`](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 + ); expectDeprecation( () => { @@ -121,7 +142,14 @@ moduleFor( let self, args; let count = 0; - let obj = EmberObject.extend(Evented).create(); + let obj; + expectDeprecation( + () => { + obj = EmberObject.extend(Evented).create(); + }, + /Evented is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); expectDeprecation( () => { @@ -165,7 +193,14 @@ moduleFor( )} @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 = {}; expectDeprecation( @@ -202,7 +237,14 @@ moduleFor( count++; }; - let obj = EmberObject.extend(Evented).create(); + let obj; + expectDeprecation( + () => { + obj = EmberObject.extend(Evented).create(); + }, + /Evented is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); expectDeprecation( () => { @@ -236,9 +278,16 @@ moduleFor( [`${testUnless( DEPRECATIONS.DEPRECATE_EVENTED.isRemoved )} @test a listener registered with one can be removed with off`](assert) { - let obj = class extends EmberObject.extend(Evented) { - F() {} - }.create(); + let obj; + expectDeprecation( + () => { + obj = class extends EmberObject.extend(Evented) { + F() {} + }.create(); + }, + /Evented is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); let F = function () {}; expectDeprecation( @@ -297,7 +346,14 @@ moduleFor( [`${testUnless( DEPRECATIONS.DEPRECATE_EVENTED.isRemoved )} @test adding and removing listeners should be chainable`](assert) { - let obj = EmberObject.extend(Evented).create(); + let obj; + expectDeprecation( + () => { + obj = EmberObject.extend(Evented).create(); + }, + /Evented is deprecated/, + DEPRECATIONS.DEPRECATE_EVENTED.isEnabled + ); let F = function () {}; let ret; diff --git a/packages/ember/tests/routing/decoupled_basic_test.js b/packages/ember/tests/routing/decoupled_basic_test.js index 991bf4bdd93..832517ec48e 100644 --- a/packages/ember/tests/routing/decoupled_basic_test.js +++ b/packages/ember/tests/routing/decoupled_basic_test.js @@ -14,6 +14,7 @@ import { runDestroy, runTask, expectDeprecation, + testUnless, } from 'internal-test-helpers'; import { DEPRECATIONS } from '@ember/-internals/deprecations'; import { run } from '@ember/runloop'; @@ -880,7 +881,9 @@ moduleFor( }); } - ['@test `activate` event fires on the route'](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_EVENTED.isRemoved)} @test \`activate\` event fires on the route`]( + assert + ) { assert.expect(5); let eventFired = 0; @@ -917,7 +920,9 @@ moduleFor( return this.visit('/nork'); } - ['@test `deactivate` event fires on the route'](assert) { + [`${testUnless(DEPRECATIONS.DEPRECATE_EVENTED.isRemoved)} @test \`deactivate\` event fires on the route`]( + assert + ) { assert.expect(5); let eventFired = 0; From 74d8f88b1652f8027808305a19d31927fb3687f7 Mon Sep 17 00:00:00 2001 From: Peter Wagenet Date: Wed, 18 Feb 2026 15:28:37 -0800 Subject: [PATCH 10/10] Update package.json --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index af739b62692..6d0acd143dc 100644 --- a/package.json +++ b/package.json @@ -199,7 +199,6 @@ "@ember/-internals/runtime/lib/mixins/action_handler.js": "ember-source/@ember/-internals/runtime/lib/mixins/action_handler.js", "@ember/-internals/runtime/lib/mixins/comparable.js": "ember-source/@ember/-internals/runtime/lib/mixins/comparable.js", "@ember/-internals/runtime/lib/mixins/container_proxy.js": "ember-source/@ember/-internals/runtime/lib/mixins/container_proxy.js", - "@ember/-internals/runtime/lib/mixins/evented.js": "ember-source/@ember/-internals/runtime/lib/mixins/evented.js", "@ember/-internals/runtime/lib/mixins/registry_proxy.js": "ember-source/@ember/-internals/runtime/lib/mixins/registry_proxy.js", "@ember/-internals/runtime/lib/mixins/target_action_support.js": "ember-source/@ember/-internals/runtime/lib/mixins/target_action_support.js", "@ember/-internals/string/index.js": "ember-source/@ember/-internals/string/index.js",